/** * @license * Copyright 2026 Qwen * SPDX-License-Identifier: Apache-2.0 */ import type { SocketLike } from './socket.js'; import type { DebugArchive } from '../log/debug-archive.js'; import { type RecoveryInput } from './recovery-input.js'; import { type RealtimeNotificationLanguage } from './notification-context.js'; import { type RealtimeFunctionOutputOptions } from './tool-confirmation.js'; export type { RealtimeFunctionOutputOptions } from './tool-confirmation.js'; export type RealtimeCallEpoch = string | number; export declare const QWEN_REALTIME_INPUT_SAMPLE_RATE = 16000; export declare const QWEN_REALTIME_OUTPUT_SAMPLE_RATE = 24000; export declare const MAX_REALTIME_INSTRUCTIONS_CHARS = 100000; export declare const QWEN_REALTIME_LIMITS: { readonly maxInputAudioFrameBytes: number; readonly maxInputImageBytes: number; readonly maxOutputAudioFrameBytes: number; readonly maxBufferedSocketBytes: number; readonly maxIncomingMessageBytes: number; readonly maxTranscriptChars: number; readonly maxTextDeltaChars: number; readonly maxFunctionArgumentsChars: number; readonly maxFunctionOutputChars: number; readonly maxContextChars: number; readonly maxPendingFunctionCalls: 8; readonly maxIdentifierChars: 256; }; export declare const REMAIN_SILENT_TOOL_NAME = "remain_silent"; export type { RealtimeNotificationLanguage } from './notification-context.js'; /** * OpenAI-style function tool declaration forwarded to the realtime provider. * * `capturesTranscript` marks handoff-style tools: when the model calls one, * the session captures the live transcript tail (so the orchestrator can pack * the recent voice context into the backend prompt) and marks the response as * delegated, which keeps the same user turn out of the direct-answer * transcript collection. * * `continuesResponse` marks receipt tools whose result needs another model * response. Async admissions also consume their receipt response before later * results are injected; only the duplicate confirmation audio may be suppressed. */ export interface RealtimeToolDefinition { type: 'function'; function: { name: string; description: string; parameters: Record; }; capturesTranscript?: boolean; continuesResponse?: boolean; } export interface QwenRealtimeConfig { endpoint: string; apiKey?: string; model: string; callEpoch: RealtimeCallEpoch; voice?: string; instructions: string; tools: readonly RealtimeToolDefinition[]; debugArchive?: DebugArchive; debugContext?: Record; } export interface QwenRealtimeDeps { createWebSocket?: (url: string, options: { headers: Record; maxPayload: number; perMessageDeflate: false; handshakeTimeout: number; }) => SocketLike; abortSignal?: AbortSignal; connectTimeoutMs?: number; responseCreatedTimeoutMs?: number; responseDoneTimeoutMs?: number; cancellationGraceMs?: number; functionOutputAckTimeoutMs?: number; notificationItemAckTimeoutMs?: number; notificationProvenanceTimeoutMs?: number; } export interface RealtimeEventContext { callEpoch: RealtimeCallEpoch; eventId?: string; /** Provider-issued session identifier; never the local call epoch. */ sessionId?: string; } export interface RealtimeSpeechEvent extends RealtimeEventContext { itemId?: string; audioStartMs?: number; audioEndMs?: number; } export interface RealtimeInputTranscriptEvent extends RealtimeEventContext { itemId?: string; text: string; stash?: string; language?: string; emotion?: string; } export interface RealtimeResponseEvent extends RealtimeEventContext { responseId: string; inputItemId?: string; status?: string; } export type RealtimeResponseAuthority = 'direct' | 'tool_continuation' | 'backend_speech' | 'permission' | 'task_result' | 'peer_report' | 'search_result' | 'visual_result' | 'proactive' | 'proactive_repair'; export type RealtimeResponseCancellationReason = 'user_interrupted' | 'client_cancelled' | 'superseded'; export interface RealtimeResponseDoneEvent extends RealtimeResponseEvent { authority?: RealtimeResponseAuthority; cancellationReason?: RealtimeResponseCancellationReason; } export interface RealtimeResponseCreatedEvent extends RealtimeResponseEvent { authority: RealtimeResponseAuthority; } export interface RealtimeOutputTextEvent extends RealtimeResponseEvent { itemId?: string; text: string; source: 'text' | 'audio_transcript'; /** Receipt-only text remains diagnostic/history, not user-heard dialogue. */ audioSuppressed?: boolean; } export interface RealtimeOutputAudioEvent extends RealtimeResponseEvent { itemId?: string; audio: Uint8Array; } export interface RealtimeFunctionArgumentsEvent extends RealtimeResponseEvent { itemId?: string; callId: string; delta: string; } export interface RealtimeTranscriptEntry { role: 'user' | 'assistant'; text: string; } export interface RealtimeDirectTranscriptEvent extends RealtimeEventContext { responseId?: string; inputItemId?: string; entries: readonly RealtimeTranscriptEntry[]; } export interface RealtimeImageDroppedEvent extends RealtimeEventContext { reason: 'audio_not_started' | 'connection_unavailable' | 'socket_backpressure'; bufferedBytes: number; } export interface RealtimeFunctionCall extends RealtimeResponseEvent { itemId?: string; callId: string; name: string; /** Raw JSON argument string as sent by the model; parsing is the caller's job. */ arguments: string; /** Final real-user transcript from this call's bound input item, when available. */ inputTranscript?: string; /** Transcript tail captured at call time for `capturesTranscript` tools; empty otherwise. */ activeTranscript: readonly RealtimeTranscriptEntry[]; } export interface RealtimeIgnoredEvent extends RealtimeEventContext { type: string; reason: 'duplicate_event' | 'stale_response' | 'stale_input' | 'stale_call' | 'cancelled_response'; } export interface RealtimeCloseInfo { reason: 'client' | 'remote' | 'error'; error?: QwenRealtimeError; } export interface RealtimeRecoveryRequest extends RealtimeEventContext { code: 'response_created_timeout' | 'response_done_timeout' | 'response_cancel_timeout' | 'silent_receipt_tool_loop' | 'provider_model_serving_error'; responseId: string; authority: RealtimeResponseAuthority; input: RecoveryInput; /** Sanitized provider evidence; never an instruction or replay payload. */ cause?: QwenRealtimeError; } export interface RealtimeTransportRecoveryEvent extends RealtimeEventContext { phase: 'started' | 'restoring' | 'completed'; inputKind: RecoveryInput['kind']; inputReason?: 'completed' | 'tool_dispatched' | 'unavailable'; code: RealtimeRecoveryRequest['code']; responseId: string; authority: RealtimeResponseAuthority; } export interface QwenRealtimeCallbacks { onDialogue?: (event: RealtimeEventContext & { inputItemId: string; role: 'user' | 'assistant'; text: string; source?: 'normal' | 'filler'; interrupted?: boolean; }) => void; onReady?: (event: RealtimeEventContext) => void; onSpeechStarted?: (event: RealtimeSpeechEvent) => void; onSpeechStopped?: (event: RealtimeSpeechEvent) => void; onInputCommitted?: (event: RealtimeSpeechEvent & { responsePending: boolean; }) => void; /** An uncommitted VAD candidate was discarded, not a completed user turn. */ onInputRejected?: (event: RealtimeEventContext & { itemId: string; reason: 'semantic_vad'; }) => void; onInputTranscriptDelta?: (event: RealtimeInputTranscriptEvent) => void; onInputTranscriptDone?: (event: RealtimeInputTranscriptEvent) => void; onOutputTextDelta?: (event: RealtimeOutputTextEvent) => void; onOutputTextDone?: (event: RealtimeOutputTextEvent) => void; onOutputAudioDelta?: (event: RealtimeOutputAudioEvent) => void; onOutputAudioDone?: (event: RealtimeResponseEvent & { itemId?: string; }) => void; onFunctionArgumentsDelta?: (event: RealtimeFunctionArgumentsEvent) => void; onFunctionCall?: (event: RealtimeFunctionCall) => void; /** A receipt-only rejected task batch needs a separate, truthful readout. */ onTaskActionRejected?: (event: RealtimeResponseEvent & { tools: readonly string[]; }) => void; onResponseCreated?: (event: RealtimeResponseCreatedEvent) => void; onResponseDone?: (event: RealtimeResponseDoneEvent) => void; onDirectTranscript?: (event: RealtimeDirectTranscriptEvent) => void; onBargeIn?: (event: RealtimeResponseEvent) => void; onIgnoredEvent?: (event: RealtimeIgnoredEvent) => void; onProtocolDebug?: (details: Record) => void; /** A fenced transport may be replaced without replaying dispatched tools. */ onRecoveryNeeded?: (request: RealtimeRecoveryRequest) => void; onTransportRecovery?: (event: RealtimeTransportRecoveryEvent) => void; onInputHeartbeat?: (event: RealtimeEventContext & { bytes: number; durationMs: number; intervalMs: number; }) => void; onAudioDropped?: (event: RealtimeEventContext) => void; onImageDropped?: (event: RealtimeImageDroppedEvent) => void; onError?: (error: QwenRealtimeError) => void; onClose?: (info: RealtimeCloseInfo) => void; } export interface RealtimeFunctionCallRef { callEpoch: RealtimeCallEpoch; callId: string; } export interface RealtimeCloseOptions { discardPendingInput?: boolean; } export interface QwenRealtimeSession { readonly callEpoch: RealtimeCallEpoch; readonly closed: Promise; /** True only while no input, response or tool receipt is awaiting settlement. */ canDeliverExternalAudio?: () => boolean; /** Read-only admission check for an independent result/permission narrator. */ canStartExternalSpeech?: () => boolean; flushDialogue: () => void; configure: (update: { tools: readonly RealtimeToolDefinition[]; }) => boolean; pushAudio: (pcm16: Uint8Array) => boolean; /** Keep only the foreground transport alive while its microphone is muted. */ setInputMuted: (muted: boolean) => void; resumeUserText?: (input: { itemId: string; text: string; }) => boolean; finishRecoveredAudio?: () => boolean; pushImage: (jpegBase64: string) => boolean; commitInputAudio: () => boolean; clearInputAudio: () => boolean; cancelResponse: () => boolean; /** Submit the (receipt-style) output for a dispatched function call. */ submitFunctionOutput: (ref: RealtimeFunctionCallRef, output: string, options?: RealtimeFunctionOutputOptions) => boolean; sendBackendContext: (text: string) => boolean; speakToUser: (message: string) => boolean; /** Ask about an untrusted pending action in the user's conversation language. */ askPermission?: (message: string, language?: RealtimeNotificationLanguage) => boolean; /** Summarize the runtime-confirmed outcome without any tool authority. */ respondToTaskResult?: (message: string, language?: RealtimeNotificationLanguage) => boolean; /** Read an external quotation in a separate response with no tool authority. */ speakPeerReport?: (message: string, language?: RealtimeNotificationLanguage) => boolean; /** Answer from asynchronous search evidence in a response without tool authority. */ respondToSearchResult?: (message: string, language?: RealtimeNotificationLanguage) => boolean; /** Answer from a read-only snapshot analysis, never granting tool authority. */ respondToVisualResult?: (message: string, language?: RealtimeNotificationLanguage) => boolean; respondToProactiveEvent: (event: string) => boolean; requestProactiveRepair: (instruction: string, allowedToolNames: readonly string[]) => boolean; takeTranscriptTail: () => readonly RealtimeTranscriptEntry[]; close: (options?: RealtimeCloseOptions) => void; } export type QwenRealtimeErrorKind = 'configuration' | 'quota' | 'transient' | 'protocol'; export interface QwenRealtimeErrorOptions { kind?: QwenRealtimeErrorKind; status?: number; providerType?: string; param?: string; closeCode?: number; } export declare class QwenRealtimeError extends Error { readonly code?: string; readonly fatal: boolean; readonly kind: QwenRealtimeErrorKind; readonly status?: number; readonly providerType?: string; readonly param?: string; readonly closeCode?: number; constructor(message: string, code?: string, fatal?: boolean, options?: QwenRealtimeErrorOptions); } export declare function deriveQwenOmniRealtimeUrl(endpoint: string, model: string): string; export declare function openQwenRealtimeSession(config: QwenRealtimeConfig, callbacks?: QwenRealtimeCallbacks, deps?: QwenRealtimeDeps): Promise;