82 lines
2.7 KiB
TypeScript
82 lines
2.7 KiB
TypeScript
// server/supabase/functions/realtime-token/policy.ts
|
|
// Realtime 세션 발급 정책 — 순수 함수만 둔다 (IO·Deno 전역·네트워크 import 없음).
|
|
// 티어별 허용 모델 결정과 OpenAI client_secrets 세션 설정 조립을 담당한다.
|
|
|
|
import type { PlanQuotaTier } from '../_shared/core-contract.generated.ts'
|
|
|
|
export type RealtimeTier = PlanQuotaTier
|
|
|
|
export interface RealtimeTokenRequest {
|
|
model?: unknown
|
|
voice?: unknown
|
|
instructions?: unknown
|
|
}
|
|
|
|
/** 티어별 허용 Realtime 모델 — free 차단, pro는 mini만, 상위 티어는 풀 모델까지 */
|
|
export const TIER_MODELS: Readonly<Record<RealtimeTier, readonly string[]>> = {
|
|
free: [],
|
|
pro: ['gpt-realtime-2.1-mini'],
|
|
pro_plus: ['gpt-realtime-2.1', 'gpt-realtime-2.1-mini'],
|
|
team: ['gpt-realtime-2.1', 'gpt-realtime-2.1-mini'],
|
|
enterprise: ['gpt-realtime-2.1', 'gpt-realtime-2.1-mini'],
|
|
}
|
|
|
|
export const DEFAULT_MODEL: Readonly<Record<RealtimeTier, string | null>> = {
|
|
free: null,
|
|
pro: 'gpt-realtime-2.1-mini',
|
|
pro_plus: 'gpt-realtime-2.1',
|
|
team: 'gpt-realtime-2.1',
|
|
enterprise: 'gpt-realtime-2.1',
|
|
}
|
|
|
|
export const DEFAULT_VOICE = 'marin'
|
|
export const MAX_INSTRUCTIONS_LENGTH = 2000
|
|
|
|
export type ModelResolution =
|
|
| { allowed: true; model: string }
|
|
| {
|
|
allowed: false
|
|
error: 'tier_not_allowed' | 'model_not_allowed'
|
|
requested: string | null
|
|
allowedModels: readonly string[]
|
|
}
|
|
|
|
/** 요청 모델(없으면 티어 기본값)이 티어에서 허용되는지 판정한다. */
|
|
export function resolveRealtimeModel(tier: RealtimeTier, requested: unknown): ModelResolution {
|
|
const requestedModel = typeof requested === 'string' ? requested : null
|
|
const allowedModels = TIER_MODELS[tier] ?? []
|
|
const model = requestedModel ?? DEFAULT_MODEL[tier] ?? null
|
|
if (model && allowedModels.includes(model)) {
|
|
return { allowed: true, model }
|
|
}
|
|
return {
|
|
allowed: false,
|
|
error: tier === 'free' ? 'tier_not_allowed' : 'model_not_allowed',
|
|
requested: requestedModel,
|
|
allowedModels,
|
|
}
|
|
}
|
|
|
|
export interface RealtimeSessionConfig {
|
|
type: 'realtime'
|
|
model: string
|
|
instructions?: string
|
|
audio: { output: { voice: string } }
|
|
}
|
|
|
|
/** OpenAI client_secrets 요청의 session 객체를 조립한다. */
|
|
export function buildRealtimeSessionConfig(
|
|
body: RealtimeTokenRequest,
|
|
model: string,
|
|
): RealtimeSessionConfig {
|
|
const instructions = typeof body.instructions === 'string'
|
|
? body.instructions.slice(0, MAX_INSTRUCTIONS_LENGTH)
|
|
: undefined
|
|
const voice = typeof body.voice === 'string' && body.voice ? body.voice : DEFAULT_VOICE
|
|
return {
|
|
type: 'realtime',
|
|
model,
|
|
...(instructions ? { instructions } : {}),
|
|
audio: { output: { voice } },
|
|
}
|
|
}
|