feat: add speech telemetry and bundle guard

This commit is contained in:
colinislit
2025-11-25 13:37:00 +01:00
parent b6c5d1a6d3
commit 7033329e0f
17 changed files with 2258 additions and 481 deletions

View File

@@ -12,31 +12,6 @@ export type Database = {
__InternalSupabase: {
PostgrestVersion: "13.0.5"
}
graphql_public: {
Tables: {
[_ in never]: never
}
Views: {
[_ in never]: never
}
Functions: {
graphql: {
Args: {
extensions?: Json
operationName?: string
query?: string
variables?: Json
}
Returns: Json
}
}
Enums: {
[_ in never]: never
}
CompositeTypes: {
[_ in never]: never
}
}
public: {
Tables: {
ai_events: {
@@ -1103,6 +1078,78 @@ export type Database = {
},
]
}
reports: {
Row: {
ai_confidence: number | null
ai_reasoning: string | null
audio_duration_seconds: number | null
audio_url: string | null
content: string
created_at: string
created_by: string | null
deleted_at: string | null
id: string
parent_report_id: string | null
patient_id: string
structured_data: Json | null
type: string
updated_at: string | null
updated_by: string | null
version: string | null
}
Insert: {
ai_confidence?: number | null
ai_reasoning?: string | null
audio_duration_seconds?: number | null
audio_url?: string | null
content: string
created_at?: string
created_by?: string | null
deleted_at?: string | null
id?: string
parent_report_id?: string | null
patient_id: string
structured_data?: Json | null
type: string
updated_at?: string | null
updated_by?: string | null
version?: string | null
}
Update: {
ai_confidence?: number | null
ai_reasoning?: string | null
audio_duration_seconds?: number | null
audio_url?: string | null
content?: string
created_at?: string
created_by?: string | null
deleted_at?: string | null
id?: string
parent_report_id?: string | null
patient_id?: string
structured_data?: Json | null
type?: string
updated_at?: string | null
updated_by?: string | null
version?: string | null
}
Relationships: [
{
foreignKeyName: "reports_parent_report_id_fkey"
columns: ["parent_report_id"]
isOneToOne: false
referencedRelation: "reports"
referencedColumns: ["id"]
},
{
foreignKeyName: "reports_patient_id_fkey"
columns: ["patient_id"]
isOneToOne: false
referencedRelation: "patients"
referencedColumns: ["id"]
},
]
}
risk_assessments: {
Row: {
assessment_date: string
@@ -1320,6 +1367,42 @@ export type Database = {
},
]
}
speech_usage_events: {
Row: {
action: string
context: string
created_at: string
id: string
intake_id: string | null
metadata: Json
patient_id: string | null
report_id: string | null
user_id: string
}
Insert: {
action: string
context: string
created_at?: string
id?: string
intake_id?: string | null
metadata?: Json
patient_id?: string | null
report_id?: string | null
user_id: string
}
Update: {
action?: string
context?: string
created_at?: string
id?: string
intake_id?: string | null
metadata?: Json
patient_id?: string | null
report_id?: string | null
user_id?: string
}
Relationships: []
}
treatment_plans: {
Row: {
client_id: string
@@ -1364,92 +1447,6 @@ export type Database = {
},
]
}
reports: {
Row: {
ai_confidence: number | null
ai_reasoning: string | null
audio_duration_seconds: number | null
audio_url: string | null
content: string
created_at: string
created_by: string | null
deleted_at: string | null
id: string
parent_report_id: string | null
patient_id: string
structured_data: Json
type: string
updated_at: string | null
updated_by: string | null
version: string | null
}
Insert: {
ai_confidence?: number | null
ai_reasoning?: string | null
audio_duration_seconds?: number | null
audio_url?: string | null
content: string
created_at?: string
created_by?: string | null
deleted_at?: string | null
id?: string
parent_report_id?: string | null
patient_id: string
structured_data?: Json
type: string
updated_at?: string | null
updated_by?: string | null
version?: string | null
}
Update: {
ai_confidence?: number | null
ai_reasoning?: string | null
audio_duration_seconds?: number | null
audio_url?: string | null
content?: string
created_at?: string
created_by?: string | null
deleted_at?: string | null
id?: string
parent_report_id?: string | null
patient_id?: string
structured_data?: Json
type?: string
updated_at?: string | null
updated_by?: string | null
version?: string | null
}
Relationships: [
{
foreignKeyName: "reports_created_by_fkey"
columns: ["created_by"]
isOneToOne: false
referencedRelation: "practitioners"
referencedColumns: ["id"]
},
{
foreignKeyName: "reports_parent_report_id_fkey"
columns: ["parent_report_id"]
isOneToOne: false
referencedRelation: "reports"
referencedColumns: ["id"]
},
{
foreignKeyName: "reports_patient_id_fkey"
columns: ["patient_id"]
isOneToOne: false
referencedRelation: "patients"
referencedColumns: ["id"]
},
{
foreignKeyName: "reports_updated_by_fkey"
columns: ["updated_by"]
isOneToOne: false
referencedRelation: "practitioners"
referencedColumns: ["id"]
},
]
}
}
Views: {
active_intakes_overview: {
@@ -1646,9 +1643,6 @@ export type CompositeTypes<
: never
export const Constants = {
graphql_public: {
Enums: {},
},
public: {
Enums: {
careplan_status: [

42
lib/telemetry/speech.ts Normal file
View File

@@ -0,0 +1,42 @@
interface SpeechTelemetryContext {
context: string
patientId?: string
intakeId?: string
reportId?: string
}
export type SpeechTelemetryAction = 'start' | 'stop' | 'final'
export type SpeechTelemetryPayload = SpeechTelemetryContext & {
action: SpeechTelemetryAction
metadata?: Record<string, unknown>
}
export function logSpeechUsage(payload: SpeechTelemetryPayload) {
if (typeof window === 'undefined') return
const body = JSON.stringify(payload)
if (navigator.sendBeacon) {
try {
const blob = new Blob([body], { type: 'application/json' })
navigator.sendBeacon('/api/telemetry/speech', blob)
return
} catch (error) {
console.warn('sendBeacon speech telemetry failed, falling back to fetch', error)
}
}
fetch('/api/telemetry/speech', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body,
credentials: 'same-origin',
keepalive: true,
}).catch((error) => {
console.warn('Speech telemetry fetch failed', error)
})
}
export type SpeechTelemetryOptions = SpeechTelemetryContext