UNPKG

@ai-sdk/mistral

Version:
122 lines (114 loc) • 3.75 kB
import { InvalidArgumentError, type Experimental_RealtimeModelV4SessionConfig as RealtimeModelV4SessionConfig, } from '@ai-sdk/provider'; import { resolve, type Resolvable, createJsonResponseHandler, deleteFromApi, lazySchema, postJsonToApi, zodSchema, type FetchFunction, } from '@ai-sdk/provider-utils'; import { z } from 'zod/v4'; import type { MistralChatModelId } from '../mistral-chat-language-model-options'; import { mistralFailedResponseHandler } from '../mistral-error'; import { MistralRealtimeModel } from './mistral-realtime-model'; import { buildMistralRealtimeSessionConfig } from './mistral-realtime-model-options'; /** Credentials for one browser voice conversation. */ export type MistralRealtimeSession = { id: string; participantToken: string; serverUrl: string; }; export interface MistralRealtimeFactory { (modelId: MistralChatModelId): MistralRealtimeModel; /** Server-only: create a voice session using the Mistral API key. */ createSession(options: { model: MistralChatModelId; sessionConfig?: RealtimeModelV4SessionConfig; abortSignal?: AbortSignal; }): Promise<MistralRealtimeSession>; /** Server-only: release a voice session after the browser disconnects. */ closeSession(options: { sessionId: string; abortSignal?: AbortSignal; }): Promise<void>; } export function createMistralRealtimeFactory(config: { baseURL: string; headers: Resolvable<Record<string, string | undefined>>; fetch?: FetchFunction; }): MistralRealtimeFactory { return Object.assign( (modelId: MistralChatModelId) => new MistralRealtimeModel(modelId), { async createSession({ model, sessionConfig = {}, abortSignal, }: Parameters<MistralRealtimeFactory['createSession']>[0]) { if (model.trim().length === 0) { throw new InvalidArgumentError({ argument: 'model', message: 'Mistral voice sessions require a model ID.', }); } const body = await buildMistralRealtimeSessionConfig(sessionConfig, { modelId: model, }); const { value } = await postJsonToApi({ url: `${config.baseURL}/audio/sessions`, headers: await resolve(config.headers), body, failedResponseHandler: mistralFailedResponseHandler, successfulResponseHandler: createJsonResponseHandler(sessionSchema), abortSignal, fetch: config.fetch, }); return { id: value.id, participantToken: value.participant_token, serverUrl: value.server_url, }; }, async closeSession({ sessionId, abortSignal, }: Parameters<MistralRealtimeFactory['closeSession']>[0]) { if ( sessionId.trim().length === 0 || sessionId === '.' || sessionId === '..' ) { throw new InvalidArgumentError({ argument: 'sessionId', message: 'Mistral voice sessions require a valid session ID.', }); } await deleteFromApi({ url: `${config.baseURL}/audio/sessions/${encodeURIComponent(sessionId)}`, headers: await resolve(config.headers), failedResponseHandler: mistralFailedResponseHandler, successfulResponseHandler: async ({ response }) => { await response.body?.cancel(); return { value: undefined }; }, abortSignal, fetch: config.fetch, }); }, }, ); } const sessionSchema = lazySchema(() => zodSchema( z.object({ id: z.string().min(1), participant_token: z.string().min(1), server_url: z.string().min(1), }), ), );