add recording store request header (#1197)

* add recording store request header

* generated protobuf

* wip

* tidy

* generated protobuf

---------

Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com>
This commit is contained in:
Paul Wells
2025-09-15 21:31:36 -07:00
committed by GitHub
parent b89809faea
commit 641bae4c29
5 changed files with 130 additions and 22 deletions
+37 -8
View File
@@ -162,13 +162,14 @@ func checkOutputForCredentials(output any) error {
}
type ClaimGrants struct {
Identity string `json:"identity,omitempty"`
Name string `json:"name,omitempty"`
Kind string `json:"kind,omitempty"`
Video *VideoGrant `json:"video,omitempty"`
SIP *SIPGrant `json:"sip,omitempty"`
Agent *AgentGrant `json:"agent,omitempty"`
Inference *InferenceGrant `json:"inference,omitempty"`
Identity string `json:"identity,omitempty"`
Name string `json:"name,omitempty"`
Kind string `json:"kind,omitempty"`
Video *VideoGrant `json:"video,omitempty"`
SIP *SIPGrant `json:"sip,omitempty"`
Agent *AgentGrant `json:"agent,omitempty"`
Inference *InferenceGrant `json:"inference,omitempty"`
Observability *ObservabilityGrant `json:"observability,omitempty"`
// Room configuration to use if this participant initiates the room
RoomConfig *RoomConfiguration `json:"roomConfig,omitempty"`
// Cloud-only, config preset to use
@@ -206,6 +207,7 @@ func (c *ClaimGrants) Clone() *ClaimGrants {
clone.SIP = c.SIP.Clone()
clone.Agent = c.Agent.Clone()
clone.Inference = c.Inference.Clone()
clone.Observability = c.Observability.Clone()
clone.Attributes = maps.Clone(c.Attributes)
clone.RoomConfig = c.RoomConfig.Clone()
@@ -223,6 +225,7 @@ func (c *ClaimGrants) MarshalLogObject(e zapcore.ObjectEncoder) error {
e.AddObject("SIP", c.SIP)
e.AddObject("Agent", c.Agent)
e.AddObject("Inference", c.Inference)
e.AddObject("Observability", c.Observability)
e.AddObject("RoomConfig", logger.Proto((*livekit.RoomConfiguration)(c.RoomConfig)))
e.AddString("RoomPreset", c.RoomPreset)
return nil
@@ -560,7 +563,7 @@ func (s *AgentGrant) MarshalLogObject(e zapcore.ObjectEncoder) error {
// ------------------------------------------------------------------
type InferenceGrant struct {
// Admin grants to all inference features (LLM, STT, TTS)
// Perform grants to all inference features (LLM, STT, TTS)
Perform bool `json:"perform,omitempty"`
}
@@ -585,6 +588,32 @@ func (s *InferenceGrant) MarshalLogObject(e zapcore.ObjectEncoder) error {
// ------------------------------------------------------------------
type ObservabilityGrant struct {
// Write grants to publish observability data
Write bool `json:"write,omitempty"`
}
func (s *ObservabilityGrant) Clone() *ObservabilityGrant {
if s == nil {
return nil
}
clone := *s
return &clone
}
func (s *ObservabilityGrant) MarshalLogObject(e zapcore.ObjectEncoder) error {
if s == nil {
return nil
}
e.AddBool("Write", s.Write)
return nil
}
// ------------------------------------------------------------------
func sourceToString(source livekit.TrackSource) string {
return strings.ToLower(source.String())
}
+71 -13
View File
@@ -454,6 +454,58 @@ func (x *EventMetric) GetRid() uint32 {
return 0
}
type MetricsRecordingHeader struct {
state protoimpl.MessageState `protogen:"open.v1"`
RoomId string `protobuf:"bytes,1,opt,name=room_id,json=roomId,proto3" json:"room_id,omitempty"`
EnableUserDataTraining *bool `protobuf:"varint,2,opt,name=enable_user_data_training,json=enableUserDataTraining,proto3,oneof" json:"enable_user_data_training,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *MetricsRecordingHeader) Reset() {
*x = MetricsRecordingHeader{}
mi := &file_livekit_metrics_proto_msgTypes[4]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *MetricsRecordingHeader) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*MetricsRecordingHeader) ProtoMessage() {}
func (x *MetricsRecordingHeader) ProtoReflect() protoreflect.Message {
mi := &file_livekit_metrics_proto_msgTypes[4]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use MetricsRecordingHeader.ProtoReflect.Descriptor instead.
func (*MetricsRecordingHeader) Descriptor() ([]byte, []int) {
return file_livekit_metrics_proto_rawDescGZIP(), []int{4}
}
func (x *MetricsRecordingHeader) GetRoomId() string {
if x != nil {
return x.RoomId
}
return ""
}
func (x *MetricsRecordingHeader) GetEnableUserDataTraining() bool {
if x != nil && x.EnableUserDataTraining != nil {
return *x.EnableUserDataTraining
}
return false
}
var File_livekit_metrics_proto protoreflect.FileDescriptor
const file_livekit_metrics_proto_rawDesc = "" +
@@ -487,7 +539,11 @@ const file_livekit_metrics_proto_rawDesc = "" +
"\bmetadata\x18\b \x01(\tR\bmetadata\x12\x10\n" +
"\x03rid\x18\t \x01(\rR\x03ridB\x13\n" +
"\x11_end_timestamp_msB\x1b\n" +
"\x19_normalized_end_timestamp*\x81\a\n" +
"\x19_normalized_end_timestamp\"\x8f\x01\n" +
"\x16MetricsRecordingHeader\x12\x17\n" +
"\aroom_id\x18\x01 \x01(\tR\x06roomId\x12>\n" +
"\x19enable_user_data_training\x18\x02 \x01(\bH\x00R\x16enableUserDataTraining\x88\x01\x01B\x1c\n" +
"\x1a_enable_user_data_training*\x81\a\n" +
"\vMetricLabel\x12\x13\n" +
"\x0fAGENTS_LLM_TTFT\x10\x00\x12\x13\n" +
"\x0fAGENTS_STT_TTFT\x10\x01\x12\x13\n" +
@@ -525,23 +581,24 @@ func file_livekit_metrics_proto_rawDescGZIP() []byte {
}
var file_livekit_metrics_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
var file_livekit_metrics_proto_msgTypes = make([]protoimpl.MessageInfo, 4)
var file_livekit_metrics_proto_msgTypes = make([]protoimpl.MessageInfo, 5)
var file_livekit_metrics_proto_goTypes = []any{
(MetricLabel)(0), // 0: livekit.MetricLabel
(*MetricsBatch)(nil), // 1: livekit.MetricsBatch
(*TimeSeriesMetric)(nil), // 2: livekit.TimeSeriesMetric
(*MetricSample)(nil), // 3: livekit.MetricSample
(*EventMetric)(nil), // 4: livekit.EventMetric
(*timestamppb.Timestamp)(nil), // 5: google.protobuf.Timestamp
(MetricLabel)(0), // 0: livekit.MetricLabel
(*MetricsBatch)(nil), // 1: livekit.MetricsBatch
(*TimeSeriesMetric)(nil), // 2: livekit.TimeSeriesMetric
(*MetricSample)(nil), // 3: livekit.MetricSample
(*EventMetric)(nil), // 4: livekit.EventMetric
(*MetricsRecordingHeader)(nil), // 5: livekit.MetricsRecordingHeader
(*timestamppb.Timestamp)(nil), // 6: google.protobuf.Timestamp
}
var file_livekit_metrics_proto_depIdxs = []int32{
5, // 0: livekit.MetricsBatch.normalized_timestamp:type_name -> google.protobuf.Timestamp
6, // 0: livekit.MetricsBatch.normalized_timestamp:type_name -> google.protobuf.Timestamp
2, // 1: livekit.MetricsBatch.time_series:type_name -> livekit.TimeSeriesMetric
4, // 2: livekit.MetricsBatch.events:type_name -> livekit.EventMetric
3, // 3: livekit.TimeSeriesMetric.samples:type_name -> livekit.MetricSample
5, // 4: livekit.MetricSample.normalized_timestamp:type_name -> google.protobuf.Timestamp
5, // 5: livekit.EventMetric.normalized_start_timestamp:type_name -> google.protobuf.Timestamp
5, // 6: livekit.EventMetric.normalized_end_timestamp:type_name -> google.protobuf.Timestamp
6, // 4: livekit.MetricSample.normalized_timestamp:type_name -> google.protobuf.Timestamp
6, // 5: livekit.EventMetric.normalized_start_timestamp:type_name -> google.protobuf.Timestamp
6, // 6: livekit.EventMetric.normalized_end_timestamp:type_name -> google.protobuf.Timestamp
7, // [7:7] is the sub-list for method output_type
7, // [7:7] is the sub-list for method input_type
7, // [7:7] is the sub-list for extension type_name
@@ -555,13 +612,14 @@ func file_livekit_metrics_proto_init() {
return
}
file_livekit_metrics_proto_msgTypes[3].OneofWrappers = []any{}
file_livekit_metrics_proto_msgTypes[4].OneofWrappers = []any{}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_livekit_metrics_proto_rawDesc), len(file_livekit_metrics_proto_rawDesc)),
NumEnums: 1,
NumMessages: 4,
NumMessages: 5,
NumExtensions: 0,
NumServices: 0,
},
+6 -1
View File
@@ -53,7 +53,7 @@ message MetricsBatch {
// This is useful for storing participant identities, track names, etc.
// There is also a predefined list of labels that can be used to reference common metrics.
// They have reserved indices from 0 to (METRIC_LABEL_PREDEFINED_MAX_VALUE - 1).
// Indexes pointing at str_data should start from METRIC_LABEL_PREDEFINED_MAX_VALUE,
// Indexes pointing at str_data should start from METRIC_LABEL_PREDEFINED_MAX_VALUE,
// such that str_data[0] == index of METRIC_LABEL_PREDEFINED_MAX_VALUE.
repeated string str_data = 3;
repeated TimeSeriesMetric time_series = 4;
@@ -87,3 +87,8 @@ message EventMetric {
string metadata = 8;
uint32 rid = 9; // index into 'str_data'
}
message MetricsRecordingHeader {
string room_id = 1;
optional bool enable_user_data_training = 2;
}
+7
View File
@@ -21,6 +21,7 @@ import (
"fmt"
mrand "math/rand/v2"
"os"
"regexp"
"sync"
"unsafe"
@@ -199,3 +200,9 @@ func Unmarshal[T livekit.Guid](b livekit.GuidBlock) T {
}
return T(unsafe.String(unsafe.SliceData(id), len(id)))
}
var validIDPattern = regexp.MustCompile(`^([a-zA-Z0-9]{1,16}_){1,2}[a-zA-Z0-9]{0,12}$`)
func IsValidID[T ~string](id T) bool {
return validIDPattern.MatchString(string(id))
}
+9
View File
@@ -55,3 +55,12 @@ func BenchmarkNew(b *testing.B) {
_ = guid
})
}
func TestIsValidID(t *testing.T) {
require.True(t, IsValidID("A_SFo4igEG5Dg5"))
require.True(t, IsValidID("NM_OJOHANNESBURG1A_K6SMQw2ZCZyB"))
require.False(t, IsValidID("A_A_A_SFo4igEG5Dg5"))
require.False(t, IsValidID("_A_SFo4igEG5Dg5"))
require.False(t, IsValidID("_SFo4igEG5Dg5"))
require.False(t, IsValidID("SFo4igEG5Dg5"))
}