feat(frontend): port realtime audio and session handling to TypeScript
This commit is contained in:
@@ -0,0 +1,152 @@
|
||||
import { z } from "zod";
|
||||
|
||||
export const recordSchema = z.record(z.string(), z.unknown());
|
||||
export type Profile = z.infer<typeof recordSchema>;
|
||||
|
||||
export const configSchema = z.object({
|
||||
search: z.boolean().default(false),
|
||||
lb: z.boolean().default(false),
|
||||
allowDirect: z.boolean().default(true),
|
||||
s2sUrl: z.string().default(""),
|
||||
primaryPrompt: z.string().default(""),
|
||||
reasoningType: z.enum(["PROXY", "OPENAI", "NODE_RED"]).catch("PROXY"),
|
||||
auth: z.boolean().default(false),
|
||||
injectProfile: z.boolean().default(false),
|
||||
debugPrompt: z.boolean().default(false),
|
||||
startSessionMode: z.string().default(""),
|
||||
theme: z.string().default(""),
|
||||
apipilot: z.boolean().default(false),
|
||||
});
|
||||
export type ServerConfig = z.infer<typeof configSchema>;
|
||||
export const defaultConfig = configSchema.parse({});
|
||||
|
||||
export const reportsSchema = z.object({
|
||||
count: z.number().default(0),
|
||||
hints: z.array(z.string()).default([]),
|
||||
entries: z
|
||||
.array(
|
||||
z.object({
|
||||
sourceScheduleLabel: z.string().optional(),
|
||||
date: z.union([z.string(), z.number()]).nullish(),
|
||||
}),
|
||||
)
|
||||
.default([]),
|
||||
reason: z.string().optional(),
|
||||
truncated: z.boolean().optional(),
|
||||
note: z.string().optional(),
|
||||
});
|
||||
export type Reports = z.infer<typeof reportsSchema>;
|
||||
|
||||
export const accountSchema = z.object({
|
||||
enabled: z.boolean(),
|
||||
loggedIn: z.boolean().default(false),
|
||||
auth: z.boolean().default(false),
|
||||
tier: z.string().default("anon"),
|
||||
username: z.string().nullish(),
|
||||
avatar: z.string().nullish(),
|
||||
loginUrl: z.string().nullish(),
|
||||
logoutUrl: z.string().nullish(),
|
||||
remainingSec: z.number().nullish(),
|
||||
limitSec: z.number().nullish(),
|
||||
});
|
||||
export type AccountInfo = z.infer<typeof accountSchema>;
|
||||
|
||||
export const toolSchema = z.object({
|
||||
name: z.string(),
|
||||
description: z.string().default(""),
|
||||
parameters: recordSchema.default({}),
|
||||
requires_auth: z.boolean().default(false),
|
||||
});
|
||||
export type DynamicTool = z.infer<typeof toolSchema>;
|
||||
export interface FunctionTool {
|
||||
type: "function";
|
||||
name: string;
|
||||
description: string;
|
||||
parameters: Record<string, unknown>;
|
||||
}
|
||||
export const toolsEnvelopeSchema = z.object({
|
||||
success: z.boolean(),
|
||||
data: z.object({ tools: z.array(toolSchema), count: z.number().optional() }).nullish(),
|
||||
error: z.unknown().optional(),
|
||||
});
|
||||
|
||||
export const grantSchema = z.object({
|
||||
session_id: z.string(),
|
||||
connect_url: z.string(),
|
||||
pending_timeout_s: z.number().default(60),
|
||||
tier: z.string().default("anon"),
|
||||
limited: z.boolean().default(false),
|
||||
heartbeatSec: z.number().default(5),
|
||||
remainingSec: z.number().nullish(),
|
||||
});
|
||||
export type SessionGrant = z.infer<typeof grantSchema>;
|
||||
export const queueSchema = z.object({
|
||||
state: z.literal("queued"),
|
||||
queue_id: z.string(),
|
||||
position: z.number().default(0),
|
||||
poll_interval_s: z.number().default(2),
|
||||
});
|
||||
export const sessionSchema = z.union([queueSchema, grantSchema]);
|
||||
|
||||
export type ConnectionStatus =
|
||||
| "idle"
|
||||
| "connecting"
|
||||
| "queued"
|
||||
| "your-turn"
|
||||
| "listening"
|
||||
| "user-speaking"
|
||||
| "processing"
|
||||
| "ai-speaking"
|
||||
| "closed"
|
||||
| "error";
|
||||
export interface Transcript {
|
||||
role: "user" | "assistant";
|
||||
text: string;
|
||||
partial: boolean;
|
||||
id: string;
|
||||
}
|
||||
export interface FinishedResponse {
|
||||
responseId: string;
|
||||
status: string;
|
||||
transcript: string;
|
||||
audible: boolean;
|
||||
}
|
||||
export interface ToolCall {
|
||||
name: string;
|
||||
arguments: string;
|
||||
callId: string;
|
||||
}
|
||||
export interface NoiseGate {
|
||||
enabled: boolean;
|
||||
thresholdDb: number;
|
||||
}
|
||||
|
||||
const contentSchema = z.object({
|
||||
type: z.string().optional(),
|
||||
text: z.string().optional(),
|
||||
transcript: z.string().optional(),
|
||||
});
|
||||
// The wire protocol is additive; unknown event fields are ignored, while consumed fields are validated.
|
||||
export const realtimeEventSchema = z.object({
|
||||
type: z.string(),
|
||||
response_id: z.string().optional(),
|
||||
item_id: z.string().optional(),
|
||||
delta: z.string().optional(),
|
||||
transcript: z.string().optional(),
|
||||
text: z.string().optional(),
|
||||
name: z.string().optional(),
|
||||
arguments: z.string().optional(),
|
||||
call_id: z.string().optional(),
|
||||
response: z
|
||||
.object({
|
||||
id: z.string().optional(),
|
||||
status: z.string().optional(),
|
||||
output: z.array(z.object({ content: z.array(contentSchema).optional() })).optional(),
|
||||
})
|
||||
.optional(),
|
||||
part: contentSchema.optional(),
|
||||
error: z
|
||||
.object({ type: z.string().optional(), code: z.string().optional(), message: z.string().optional() })
|
||||
.optional(),
|
||||
});
|
||||
export type RealtimeEvent = z.infer<typeof realtimeEventSchema>;
|
||||
Reference in New Issue
Block a user