From 0c92243ae06ba1f8d470f26b61be4a1feb27cf01 Mon Sep 17 00:00:00 2001 From: shishir gowda Date: Mon, 24 Aug 2026 13:00:41 -0400 Subject: [PATCH 1/3] update room obs for feature usage Signed-off-by: shishir gowda --- observability/roomobs/gen_reporter.go | 4 +++- observability/roomobs/gen_reporter_noop.go | 5 +++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/observability/roomobs/gen_reporter.go b/observability/roomobs/gen_reporter.go index 1b3cf6a65..82ed1529d 100644 --- a/observability/roomobs/gen_reporter.go +++ b/observability/roomobs/gen_reporter.go @@ -6,7 +6,7 @@ import ( "time" ) -const Version_HCR54L8 = true +const Version_5CO3ESG = true type KeyResolver interface { Resolve(string) @@ -19,6 +19,8 @@ type Reporter interface { } type projectReporter interface { + ReportFeatureName(v string) + ReportFeatureDuration(v uint64) } type ProjectTx interface { diff --git a/observability/roomobs/gen_reporter_noop.go b/observability/roomobs/gen_reporter_noop.go index ddedd1771..ea3cb20fe 100644 --- a/observability/roomobs/gen_reporter_noop.go +++ b/observability/roomobs/gen_reporter_noop.go @@ -50,6 +50,8 @@ func NewNoopProjectReporter() ProjectReporter { func (r *noopProjectReporter) RegisterFunc(f func(ts time.Time, tx ProjectTx) bool) {} func (r *noopProjectReporter) Tx(f func(ProjectTx)) {} func (r *noopProjectReporter) TxAt(ts time.Time, f func(ProjectTx)) {} +func (r *noopProjectReporter) ReportFeatureName(v string) {} +func (r *noopProjectReporter) ReportFeatureDuration(v uint64) {} func (r *noopProjectReporter) WithRoom(name string) RoomReporter { return &noopRoomReporter{} } @@ -59,6 +61,9 @@ func (r *noopProjectReporter) WithDeferredRoom() (RoomReporter, KeyResolver) { type noopProjectTx struct{} +func (t *noopProjectTx) ReportFeatureName(v string) {} +func (t *noopProjectTx) ReportFeatureDuration(v uint64) {} + type noopRoomReporter struct{} func NewNoopRoomReporter() RoomReporter { From 56d1c4f29c3303671d8a0b2bc71bf4be0aadce58 Mon Sep 17 00:00:00 2001 From: shishir gowda Date: Mon, 24 Aug 2026 14:01:49 -0400 Subject: [PATCH 2/3] add GetFeatureNameFromFeature Signed-off-by: shishir gowda --- observability/roomobs/room.go | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/observability/roomobs/room.go b/observability/roomobs/room.go index 6186376a8..1519c947b 100644 --- a/observability/roomobs/room.go +++ b/observability/roomobs/room.go @@ -180,3 +180,25 @@ func ParticipantKindCode(k livekit.ParticipantInfo_Kind) int32 { func ParticipantKindDetailsCodes(d []livekit.ParticipantInfo_KindDetail) []int32 { return *(*[]int32)(unsafe.Pointer(&d)) } + +func GetFeatureNameFromFeature(info *livekit.FeatureUsageInfo) string { + if info == nil { + return "" + } + switch info.GetFeature() { + case livekit.FeatureUsageInfo_KRISP_NOISE_CANCELLATION: + return "krisp_noise_cancellation" + case livekit.FeatureUsageInfo_KRISP_BACKGROUND_VOICE_CANCELLATION: + return "krisp_background_voice_cancellation" + case livekit.FeatureUsageInfo_AIC_AUDIO_ENHANCEMENT: + // if the feature is aic_audio_enhancement, we need to return the id of the feature + for k, v := range info.GetFeatureInfo() { + return fmt.Sprintf("%s_%s", k, v) + } + return "aic_audio_enhancement" + case livekit.FeatureUsageInfo_KRISP_VIVA: + return "krisp_viva" + default: + return "unknown" + } +} From 5f6baf782224278625d25f65460aa86070f66a99 Mon Sep 17 00:00:00 2001 From: shishir gowda Date: Mon, 24 Aug 2026 17:39:05 -0400 Subject: [PATCH 3/3] add GetFeatureDurationFromFeature Signed-off-by: shishir gowda --- observability/roomobs/room.go | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/observability/roomobs/room.go b/observability/roomobs/room.go index 1519c947b..7f1bf8449 100644 --- a/observability/roomobs/room.go +++ b/observability/roomobs/room.go @@ -3,6 +3,7 @@ package roomobs import ( "fmt" "strings" + "time" "unsafe" "github.com/livekit/protocol/livekit" @@ -181,6 +182,9 @@ func ParticipantKindDetailsCodes(d []livekit.ParticipantInfo_KindDetail) []int32 return *(*[]int32)(unsafe.Pointer(&d)) } +// GetFeatureNameFromFeature returns the canonical name used to identify a +// feature usage in the feature_usages rollup. It returns "" when info is nil or +// the feature is not recognized, so callers can skip reporting. func GetFeatureNameFromFeature(info *livekit.FeatureUsageInfo) string { if info == nil { return "" @@ -190,15 +194,31 @@ func GetFeatureNameFromFeature(info *livekit.FeatureUsageInfo) string { return "krisp_noise_cancellation" case livekit.FeatureUsageInfo_KRISP_BACKGROUND_VOICE_CANCELLATION: return "krisp_background_voice_cancellation" + case livekit.FeatureUsageInfo_KRISP_VIVA: + return "krisp_viva" case livekit.FeatureUsageInfo_AIC_AUDIO_ENHANCEMENT: - // if the feature is aic_audio_enhancement, we need to return the id of the feature + // AIC enhancement is keyed by the specific feature metadata (_). for k, v := range info.GetFeatureInfo() { return fmt.Sprintf("%s_%s", k, v) } return "aic_audio_enhancement" - case livekit.FeatureUsageInfo_KRISP_VIVA: - return "krisp_viva" default: - return "unknown" + return "" + } +} + +// GetFeatureDurationFromFeature returns the total active duration of a feature +// usage, summed across its (non-negative) time ranges. +func GetFeatureDurationFromFeature(info *livekit.FeatureUsageInfo) time.Duration { + if info == nil { + return 0 + } + var d time.Duration + for _, tr := range info.GetTimeRanges() { + start, end := tr.GetStartedAt().AsTime(), tr.GetEndedAt().AsTime() + if end.After(start) { + d += end.Sub(start) + } } + return d }