openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
179 lines (178 loc) • 7.02 kB
JavaScript
import { c as normalizeOptionalString } from "./string-coerce-mnp54Vah.js";
import { i as asOptionalRecord } from "./record-coerce-DHZ4bFlT.js";
import { g as parseFiniteNumber } from "./number-coercion-CJQ8TR--.js";
import { u as normalizeResolvedSecretInputString } from "./types.secrets-_0JOMGE5.js";
import "./string-coerce-runtime-CEGJWkQ_.js";
import "./secret-input-DVCzFGxN.js";
import { t as createRealtimeTranscriptionWebSocketSession } from "./realtime-transcription-DMdRb-KG.js";
//#region extensions/mistral/realtime-transcription-provider.ts
const MISTRAL_REALTIME_DEFAULT_BASE_URL = "wss://api.mistral.ai";
const MISTRAL_REALTIME_DEFAULT_MODEL = "voxtral-mini-transcribe-realtime-2602";
const MISTRAL_REALTIME_DEFAULT_SAMPLE_RATE = 8e3;
const MISTRAL_REALTIME_DEFAULT_ENCODING = "pcm_mulaw";
const MISTRAL_REALTIME_DEFAULT_DELAY_MS = 800;
const MISTRAL_REALTIME_CONNECT_TIMEOUT_MS = 1e4;
const MISTRAL_REALTIME_CLOSE_TIMEOUT_MS = 5e3;
const MISTRAL_REALTIME_MAX_RECONNECT_ATTEMPTS = 5;
const MISTRAL_REALTIME_RECONNECT_DELAY_MS = 1e3;
const MISTRAL_REALTIME_MAX_QUEUED_BYTES = 2 * 1024 * 1024;
function readNestedMistralConfig(rawConfig) {
const raw = asOptionalRecord(rawConfig);
return asOptionalRecord(asOptionalRecord(raw?.providers)?.mistral ?? raw?.mistral ?? raw) ?? {};
}
function normalizeMistralEncoding(value) {
const normalized = normalizeOptionalString(value)?.toLowerCase();
if (!normalized) return;
switch (normalized) {
case "pcm":
case "linear16":
case "pcm_s16le": return "pcm_s16le";
case "pcm_s32le":
case "pcm_f16le":
case "pcm_f32le": return normalized;
case "mulaw":
case "ulaw":
case "g711_ulaw":
case "g711-mulaw":
case "pcm_mulaw": return "pcm_mulaw";
case "alaw":
case "g711_alaw":
case "g711-alaw":
case "pcm_alaw": return "pcm_alaw";
default: throw new Error(`Invalid Mistral realtime transcription encoding: ${normalized}`);
}
}
function normalizeMistralRealtimeBaseUrl(value) {
const raw = normalizeOptionalString(value ?? process.env.MISTRAL_REALTIME_BASE_URL);
if (!raw) return MISTRAL_REALTIME_DEFAULT_BASE_URL;
const url = new URL(raw);
url.protocol = url.protocol === "http:" ? "ws:" : url.protocol === "https:" ? "wss:" : url.protocol;
url.pathname = url.pathname.replace(/\/v1\/?$/, "").replace(/\/+$/, "");
return url.toString().replace(/\/+$/, "");
}
function toMistralRealtimeWsUrl(config) {
const base = new URL(`${normalizeMistralRealtimeBaseUrl(config.baseUrl)}/`);
const url = new URL("v1/audio/transcriptions/realtime", base);
url.searchParams.set("model", config.model);
if (config.targetStreamingDelayMs != null) url.searchParams.set("target_streaming_delay_ms", String(config.targetStreamingDelayMs));
return url.toString();
}
function normalizeProviderConfig(config) {
const raw = readNestedMistralConfig(config);
return {
apiKey: normalizeResolvedSecretInputString({
value: raw.apiKey,
path: "plugins.entries.voice-call.config.streaming.providers.mistral.apiKey"
}),
baseUrl: normalizeOptionalString(raw.baseUrl),
model: normalizeOptionalString(raw.model ?? raw.sttModel),
sampleRate: parseFiniteNumber(raw.sampleRate ?? raw.sample_rate),
encoding: normalizeMistralEncoding(raw.encoding),
targetStreamingDelayMs: parseFiniteNumber(raw.targetStreamingDelayMs ?? raw.target_streaming_delay_ms ?? raw.delayMs)
};
}
function readErrorDetail(event) {
const message = event.error?.message;
if (typeof message === "string") return message;
if (message && typeof message === "object") return JSON.stringify(message);
if (typeof event.error?.code === "number") return `Mistral realtime transcription error (${event.error.code})`;
return "Mistral realtime transcription error";
}
function createMistralRealtimeTranscriptionSession(config) {
let partialText = "";
const handleEvent = (event, transport) => {
if (event.type === "session.created") {
transport.sendJson({
type: "session.update",
session: { audio_format: {
encoding: config.encoding,
sample_rate: config.sampleRate
} }
});
transport.markReady();
return;
}
if (!transport.isReady() && event.type === "error") {
transport.failConnect(new Error(readErrorDetail(event)));
return;
}
switch (event.type) {
case "transcription.text.delta":
if (event.text) {
partialText += event.text;
config.onPartial?.(partialText);
}
return;
case "transcription.segment":
if (event.text) {
config.onTranscript?.(event.text);
partialText = "";
}
return;
case "transcription.done":
if (partialText.trim()) {
config.onTranscript?.(partialText);
partialText = "";
}
transport.closeNow();
return;
case "error": config.onError?.(new Error(readErrorDetail(event)));
default:
}
};
return createRealtimeTranscriptionWebSocketSession({
providerId: "mistral",
callbacks: config,
url: () => toMistralRealtimeWsUrl(config),
headers: { Authorization: `Bearer ${config.apiKey}` },
connectTimeoutMs: MISTRAL_REALTIME_CONNECT_TIMEOUT_MS,
closeTimeoutMs: MISTRAL_REALTIME_CLOSE_TIMEOUT_MS,
maxReconnectAttempts: MISTRAL_REALTIME_MAX_RECONNECT_ATTEMPTS,
reconnectDelayMs: MISTRAL_REALTIME_RECONNECT_DELAY_MS,
maxQueuedBytes: MISTRAL_REALTIME_MAX_QUEUED_BYTES,
connectTimeoutMessage: "Mistral realtime transcription connection timeout",
reconnectLimitMessage: "Mistral realtime transcription reconnect limit reached",
sendAudio: (audio, transport) => {
transport.sendJson({
type: "input_audio.append",
audio: audio.toString("base64")
});
},
onClose: (transport) => {
transport.sendJson({ type: "input_audio.flush" });
transport.sendJson({ type: "input_audio.end" });
},
onMessage: handleEvent
});
}
function buildMistralRealtimeTranscriptionProvider() {
return {
id: "mistral",
label: "Mistral Realtime Transcription",
aliases: ["mistral-realtime", "voxtral-realtime"],
defaultModel: MISTRAL_REALTIME_DEFAULT_MODEL,
autoSelectOrder: 45,
resolveConfig: ({ rawConfig }) => normalizeProviderConfig(rawConfig),
isConfigured: ({ providerConfig }) => Boolean(normalizeProviderConfig(providerConfig).apiKey || process.env.MISTRAL_API_KEY),
createSession: (req) => {
const config = normalizeProviderConfig(req.providerConfig);
const apiKey = config.apiKey || process.env.MISTRAL_API_KEY;
if (!apiKey) throw new Error("Mistral API key missing");
return createMistralRealtimeTranscriptionSession({
...req,
apiKey,
baseUrl: normalizeMistralRealtimeBaseUrl(config.baseUrl),
model: config.model ?? MISTRAL_REALTIME_DEFAULT_MODEL,
sampleRate: config.sampleRate ?? MISTRAL_REALTIME_DEFAULT_SAMPLE_RATE,
encoding: config.encoding ?? MISTRAL_REALTIME_DEFAULT_ENCODING,
targetStreamingDelayMs: config.targetStreamingDelayMs ?? MISTRAL_REALTIME_DEFAULT_DELAY_MS
});
}
};
}
const testing = {
normalizeProviderConfig,
toMistralRealtimeWsUrl
};
//#endregion
export { testing as n, buildMistralRealtimeTranscriptionProvider as t };