fix(realtime-token): spend the session quota only after a token is minted and stop leaking provider errors
This commit is contained in:
parent
83c2deb561
commit
2251fe2da1
7 changed files with 673 additions and 155 deletions
52
server/supabase/functions/realtime-token/policy.test.ts
Normal file
52
server/supabase/functions/realtime-token/policy.test.ts
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
import {
|
||||
buildRealtimeSessionConfig,
|
||||
DEFAULT_VOICE,
|
||||
MAX_INSTRUCTIONS_LENGTH,
|
||||
resolveRealtimeModel,
|
||||
} from './policy.ts'
|
||||
|
||||
function assertEquals(actual: unknown, expected: unknown, message: string): void {
|
||||
const a = JSON.stringify(actual)
|
||||
const e = JSON.stringify(expected)
|
||||
if (a !== e) throw new Error(`${message}: expected ${e}, got ${a}`)
|
||||
}
|
||||
|
||||
Deno.test('resolveRealtimeModel applies tier defaults and allow-lists', () => {
|
||||
assertEquals(resolveRealtimeModel('pro', undefined), { allowed: true, model: 'gpt-realtime-2.1-mini' }, 'pro default')
|
||||
assertEquals(resolveRealtimeModel('pro_plus', undefined), { allowed: true, model: 'gpt-realtime-2.1' }, 'pro+ default')
|
||||
assertEquals(
|
||||
resolveRealtimeModel('team', 'gpt-realtime-2.1-mini'),
|
||||
{ allowed: true, model: 'gpt-realtime-2.1-mini' },
|
||||
'team explicit mini',
|
||||
)
|
||||
assertEquals(resolveRealtimeModel('free', undefined), {
|
||||
allowed: false,
|
||||
error: 'tier_not_allowed',
|
||||
requested: null,
|
||||
allowedModels: [],
|
||||
}, 'free blocked')
|
||||
assertEquals(resolveRealtimeModel('pro', 'gpt-realtime-2.1'), {
|
||||
allowed: false,
|
||||
error: 'model_not_allowed',
|
||||
requested: 'gpt-realtime-2.1',
|
||||
allowedModels: ['gpt-realtime-2.1-mini'],
|
||||
}, 'pro cannot pick full model')
|
||||
})
|
||||
|
||||
Deno.test('buildRealtimeSessionConfig clamps instructions and defaults voice', () => {
|
||||
const long = 'x'.repeat(MAX_INSTRUCTIONS_LENGTH + 50)
|
||||
const config = buildRealtimeSessionConfig({ instructions: long }, 'gpt-realtime-2.1-mini')
|
||||
assertEquals(config.instructions?.length, MAX_INSTRUCTIONS_LENGTH, 'instructions clamped')
|
||||
assertEquals(config.audio.output.voice, DEFAULT_VOICE, 'default voice')
|
||||
|
||||
assertEquals(
|
||||
buildRealtimeSessionConfig({ voice: 'cedar', instructions: '' }, 'm'),
|
||||
{ type: 'realtime', model: 'm', audio: { output: { voice: 'cedar' } } },
|
||||
'empty instructions omitted',
|
||||
)
|
||||
assertEquals(
|
||||
buildRealtimeSessionConfig({ voice: 42, instructions: 7 }, 'm'),
|
||||
{ type: 'realtime', model: 'm', audio: { output: { voice: DEFAULT_VOICE } } },
|
||||
'non-string fields ignored',
|
||||
)
|
||||
})
|
||||
Loading…
Add table
Add a link
Reference in a new issue