@openclaw/mistral-provider 2026.9.1 → 2026.9.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/api.js CHANGED
@@ -1,4 +1,4 @@
1
- import { t as buildMistralProvider } from "./provider-catalog-CTPBG3Dh.js";
1
+ import { t as buildMistralProvider } from "./provider-catalog-EHDptC3c.js";
2
2
  import { MISTRAL_BASE_URL, MISTRAL_DEFAULT_MODEL_ID, MISTRAL_DEFAULT_MODEL_REF, buildMistralModelDefinition } from "./model-definitions.js";
3
3
  import { applyMistralConfig, applyMistralProviderConfig } from "./onboard.js";
4
4
  const MISTRAL_MODEL_TRANSPORT_PATCH = {
@@ -0,0 +1,5 @@
1
+ import { buildMistralRealtimeTranscriptionProvider } from "./realtime-transcription-provider-factory.js";
2
+ //#region extensions/mistral/capability-catalog.ts
3
+ var capability_catalog_default = ((context) => ({ realtimeTranscriptionProviders: [buildMistralRealtimeTranscriptionProvider(context)] }));
4
+ //#endregion
5
+ export { capability_catalog_default as default };
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { n as openclaw_plugin_default } from "./provider-catalog-CTPBG3Dh.js";
1
+ import { n as openclaw_plugin_default } from "./provider-catalog-EHDptC3c.js";
2
2
  import { applyMistralConfig } from "./onboard.js";
3
3
  import { applyMistralModelCompat } from "./api.js";
4
4
  import { mistralMediaUnderstandingProvider } from "./media-understanding-provider.js";
@@ -1,4 +1,4 @@
1
- import { n as openclaw_plugin_default, t as buildMistralProvider } from "./provider-catalog-CTPBG3Dh.js";
1
+ import { n as openclaw_plugin_default, t as buildMistralProvider } from "./provider-catalog-EHDptC3c.js";
2
2
  import { readManifestProviderDefaultModelRef } from "openclaw/plugin-sdk/provider-catalog-shared";
3
3
  const MISTRAL_BASE_URL = openclaw_plugin_default.modelCatalog.providers.mistral.baseUrl;
4
4
  const MISTRAL_DEFAULT_MODEL_REF = readManifestProviderDefaultModelRef(openclaw_plugin_default, "mistral");
@@ -2,7 +2,7 @@ import { buildManifestModelProviderConfig } from "openclaw/plugin-sdk/provider-c
2
2
  //#region extensions/mistral/openclaw.plugin.json
3
3
  var openclaw_plugin_default = {
4
4
  id: "mistral",
5
- icon: "https://cdn.simpleicons.org/mistralai",
5
+ capabilityCatalogEntry: "./capability-catalog.ts",
6
6
  activation: { "onStartup": false },
7
7
  enabledByDefault: true,
8
8
  providers: ["mistral"],
@@ -12,6 +12,7 @@ var openclaw_plugin_default = {
12
12
  }],
13
13
  providerRequest: { "providers": { "mistral": { "family": "mistral" } } },
14
14
  modelCatalog: {
15
+ "modelsDev": { "mistral": "mistral" },
15
16
  "providers": { "mistral": {
16
17
  "baseUrl": "https://api.mistral.ai/v1",
17
18
  "api": "openai-completions",
@@ -1,2 +1,2 @@
1
- import { t as buildMistralProvider } from "./provider-catalog-CTPBG3Dh.js";
1
+ import { t as buildMistralProvider } from "./provider-catalog-EHDptC3c.js";
2
2
  export { buildMistralProvider };
@@ -0,0 +1,217 @@
1
+ import { normalizeResolvedSecretInputString, normalizeSecretInput } from "openclaw/plugin-sdk/secret-input";
2
+ import { asOptionalRecord, normalizeOptionalString, parseFiniteNumber } from "openclaw/plugin-sdk/string-coerce-runtime";
3
+ //#region extensions/mistral/realtime-transcription-provider-factory.ts
4
+ const MISTRAL_REALTIME_DEFAULT_BASE_URL = "wss://api.mistral.ai";
5
+ const MISTRAL_REALTIME_DEFAULT_MODEL = "voxtral-mini-transcribe-realtime-2602";
6
+ const MISTRAL_REALTIME_DEFAULT_SAMPLE_RATE = 8e3;
7
+ const MISTRAL_REALTIME_DEFAULT_ENCODING = "pcm_mulaw";
8
+ const MISTRAL_REALTIME_DEFAULT_DELAY_MS = 800;
9
+ const MISTRAL_REALTIME_CONNECT_TIMEOUT_MS = 1e4;
10
+ const MISTRAL_REALTIME_CLOSE_TIMEOUT_MS = 5e3;
11
+ const MISTRAL_REALTIME_MAX_RECONNECT_ATTEMPTS = 5;
12
+ const MISTRAL_REALTIME_RECONNECT_DELAY_MS = 1e3;
13
+ const MISTRAL_REALTIME_MAX_QUEUED_BYTES = 2097152;
14
+ const MISTRAL_REALTIME_SPEECH_CONTENT = /[\p{L}\p{N}]/u;
15
+ const MISTRAL_REALTIME_MAX_PARTIAL_TRANSCRIPT_BYTES = 262144;
16
+ const MISTRAL_REALTIME_PARTIAL_TRANSCRIPT_OVERFLOW_MESSAGE = "Mistral realtime transcription exceeded the 256 KiB in-progress transcript limit";
17
+ function readNestedMistralConfig(rawConfig) {
18
+ const raw = asOptionalRecord(rawConfig);
19
+ const providers = asOptionalRecord(raw?.providers);
20
+ return asOptionalRecord(providers?.mistral ?? raw?.mistral ?? raw) ?? {};
21
+ }
22
+ function normalizeMistralEncoding(value) {
23
+ const normalized = normalizeOptionalString(value)?.toLowerCase();
24
+ if (!normalized) return;
25
+ switch (normalized) {
26
+ case "pcm":
27
+ case "linear16":
28
+ case "pcm_s16le": return "pcm_s16le";
29
+ case "pcm_s32le":
30
+ case "pcm_f16le":
31
+ case "pcm_f32le": return normalized;
32
+ case "mulaw":
33
+ case "ulaw":
34
+ case "g711_ulaw":
35
+ case "g711-mulaw":
36
+ case "pcm_mulaw": return "pcm_mulaw";
37
+ case "alaw":
38
+ case "g711_alaw":
39
+ case "g711-alaw":
40
+ case "pcm_alaw": return "pcm_alaw";
41
+ default: throw new Error(`Invalid Mistral realtime transcription encoding: ${normalized}`);
42
+ }
43
+ }
44
+ function normalizeMistralRealtimeBaseUrl(value) {
45
+ const raw = normalizeOptionalString(value ?? process.env.MISTRAL_REALTIME_BASE_URL);
46
+ if (!raw) return MISTRAL_REALTIME_DEFAULT_BASE_URL;
47
+ const url = new URL(raw);
48
+ url.protocol = url.protocol === "http:" ? "ws:" : url.protocol === "https:" ? "wss:" : url.protocol;
49
+ url.pathname = url.pathname.replace(/\/v1\/?$/, "").replace(/\/+$/, "");
50
+ return url.toString().replace(/\/+$/, "");
51
+ }
52
+ function toMistralRealtimeWsUrl(config) {
53
+ const base = new URL(`${normalizeMistralRealtimeBaseUrl(config.baseUrl)}/`);
54
+ const url = new URL("v1/audio/transcriptions/realtime", base);
55
+ url.searchParams.set("model", config.model);
56
+ if (config.targetStreamingDelayMs != null) url.searchParams.set("target_streaming_delay_ms", String(config.targetStreamingDelayMs));
57
+ return url.toString();
58
+ }
59
+ function normalizeProviderConfig(config) {
60
+ const raw = readNestedMistralConfig(config);
61
+ return {
62
+ apiKey: normalizeMistralApiKey(raw.apiKey),
63
+ baseUrl: normalizeOptionalString(raw.baseUrl),
64
+ model: normalizeOptionalString(raw.model ?? raw.sttModel),
65
+ sampleRate: parseFiniteNumber(raw.sampleRate ?? raw.sample_rate),
66
+ encoding: normalizeMistralEncoding(raw.encoding),
67
+ targetStreamingDelayMs: parseFiniteNumber(raw.targetStreamingDelayMs ?? raw.target_streaming_delay_ms ?? raw.delayMs)
68
+ };
69
+ }
70
+ function normalizeMistralApiKey(value) {
71
+ const resolved = normalizeResolvedSecretInputString({
72
+ value,
73
+ path: "plugins.entries.voice-call.config.streaming.providers.mistral.apiKey"
74
+ });
75
+ return normalizeSecretInput(resolved) || void 0;
76
+ }
77
+ function readErrorDetail(event) {
78
+ const message = event.error?.message;
79
+ if (typeof message === "string") return message;
80
+ if (message && typeof message === "object") return JSON.stringify(message);
81
+ if (typeof event.error?.code === "number") return `Mistral realtime transcription error (${event.error.code})`;
82
+ return "Mistral realtime transcription error";
83
+ }
84
+ function measureTranscriptDeltaBytes(partialText, delta) {
85
+ const previousCodeUnit = partialText.charCodeAt(partialText.length - 1);
86
+ const nextCodeUnit = delta.charCodeAt(0);
87
+ const completesSplitSurrogatePair = previousCodeUnit >= 55296 && previousCodeUnit <= 56319 && nextCodeUnit >= 56320 && nextCodeUnit <= 57343;
88
+ return Buffer.byteLength(delta, "utf8") - (completesSplitSurrogatePair ? 2 : 0);
89
+ }
90
+ function createMistralRealtimeTranscriptionSession(config, createRealtimeTranscriptionWebSocketSession) {
91
+ let partialText = "";
92
+ let partialBytes = 0;
93
+ let hasFinalSegment = false;
94
+ let terminal = false;
95
+ const clearPartial = () => {
96
+ partialText = "";
97
+ partialBytes = 0;
98
+ };
99
+ const emitFinalTranscript = (text, source) => {
100
+ if (!text.trim() || source === "pending" && !MISTRAL_REALTIME_SPEECH_CONTENT.test(text)) return;
101
+ hasFinalSegment ||= source === "segment";
102
+ config.onTranscript?.(text);
103
+ };
104
+ const failTerminal = (error, transport) => {
105
+ if (terminal) return;
106
+ terminal = true;
107
+ clearPartial();
108
+ transport.closeNow();
109
+ try {
110
+ config.onError?.(error);
111
+ } catch {}
112
+ };
113
+ const handleEvent = (event, transport) => {
114
+ if (terminal) return;
115
+ if (event.type === "session.created") {
116
+ clearPartial();
117
+ hasFinalSegment = false;
118
+ transport.sendJson({
119
+ type: "session.update",
120
+ session: { audio_format: {
121
+ encoding: config.encoding,
122
+ sample_rate: config.sampleRate
123
+ } }
124
+ });
125
+ transport.markReady();
126
+ return;
127
+ }
128
+ if (!transport.isReady() && event.type === "error") {
129
+ transport.failConnect(new Error(readErrorDetail(event)));
130
+ return;
131
+ }
132
+ switch (event.type) {
133
+ case "transcription.text.delta":
134
+ if (event.text) {
135
+ const deltaBytes = measureTranscriptDeltaBytes(partialText, event.text);
136
+ if (deltaBytes > MISTRAL_REALTIME_MAX_PARTIAL_TRANSCRIPT_BYTES - partialBytes) {
137
+ failTerminal(/* @__PURE__ */ new Error(MISTRAL_REALTIME_PARTIAL_TRANSCRIPT_OVERFLOW_MESSAGE), transport);
138
+ return;
139
+ }
140
+ partialText += event.text;
141
+ partialBytes += deltaBytes;
142
+ config.onPartial?.(partialText);
143
+ }
144
+ return;
145
+ case "transcription.segment":
146
+ if (event.text?.trim()) {
147
+ emitFinalTranscript(event.text, "segment");
148
+ clearPartial();
149
+ }
150
+ return;
151
+ case "transcription.done": {
152
+ terminal = true;
153
+ const source = hasFinalSegment ? "pending" : "terminal";
154
+ const terminalText = source === "pending" ? partialText : event.text?.trim() ? event.text : partialText;
155
+ clearPartial();
156
+ try {
157
+ emitFinalTranscript(terminalText, source);
158
+ } finally {
159
+ transport.closeNow();
160
+ }
161
+ return;
162
+ }
163
+ case "error": failTerminal(new Error(readErrorDetail(event)), transport);
164
+ }
165
+ };
166
+ return createRealtimeTranscriptionWebSocketSession({
167
+ providerId: "mistral",
168
+ callbacks: config,
169
+ url: () => toMistralRealtimeWsUrl(config),
170
+ headers: { Authorization: `Bearer ${config.apiKey}` },
171
+ connectTimeoutMs: MISTRAL_REALTIME_CONNECT_TIMEOUT_MS,
172
+ closeTimeoutMs: MISTRAL_REALTIME_CLOSE_TIMEOUT_MS,
173
+ maxReconnectAttempts: MISTRAL_REALTIME_MAX_RECONNECT_ATTEMPTS,
174
+ reconnectDelayMs: MISTRAL_REALTIME_RECONNECT_DELAY_MS,
175
+ maxQueuedBytes: MISTRAL_REALTIME_MAX_QUEUED_BYTES,
176
+ connectTimeoutMessage: "Mistral realtime transcription connection timeout",
177
+ reconnectLimitMessage: "Mistral realtime transcription reconnect limit reached",
178
+ sendAudio: (audio, transport) => {
179
+ transport.sendJson({
180
+ type: "input_audio.append",
181
+ audio: audio.toString("base64")
182
+ });
183
+ },
184
+ onClose: (transport) => {
185
+ transport.sendJson({ type: "input_audio.flush" });
186
+ transport.sendJson({ type: "input_audio.end" });
187
+ },
188
+ onMessage: handleEvent
189
+ });
190
+ }
191
+ function buildMistralRealtimeTranscriptionProvider({ createRealtimeTranscriptionWebSocketSession }) {
192
+ return {
193
+ id: "mistral",
194
+ label: "Mistral Realtime Transcription",
195
+ aliases: ["mistral-realtime", "voxtral-realtime"],
196
+ defaultModel: MISTRAL_REALTIME_DEFAULT_MODEL,
197
+ autoSelectOrder: 45,
198
+ resolveConfig: ({ rawConfig }) => normalizeProviderConfig(rawConfig),
199
+ isConfigured: ({ providerConfig }) => Boolean(normalizeProviderConfig(providerConfig).apiKey || normalizeMistralApiKey(process.env.MISTRAL_API_KEY)),
200
+ createSession: (req) => {
201
+ const config = normalizeProviderConfig(req.providerConfig);
202
+ const apiKey = config.apiKey || normalizeMistralApiKey(process.env.MISTRAL_API_KEY);
203
+ if (!apiKey) throw new Error("Mistral API key missing");
204
+ return createMistralRealtimeTranscriptionSession({
205
+ ...req,
206
+ apiKey,
207
+ baseUrl: normalizeMistralRealtimeBaseUrl(config.baseUrl),
208
+ model: config.model ?? MISTRAL_REALTIME_DEFAULT_MODEL,
209
+ sampleRate: config.sampleRate ?? MISTRAL_REALTIME_DEFAULT_SAMPLE_RATE,
210
+ encoding: config.encoding ?? MISTRAL_REALTIME_DEFAULT_ENCODING,
211
+ targetStreamingDelayMs: config.targetStreamingDelayMs ?? MISTRAL_REALTIME_DEFAULT_DELAY_MS
212
+ }, createRealtimeTranscriptionWebSocketSession);
213
+ }
214
+ };
215
+ }
216
+ //#endregion
217
+ export { buildMistralRealtimeTranscriptionProvider };
@@ -1,218 +1,8 @@
1
- import { createRealtimeTranscriptionWebSocketSession } from "openclaw/plugin-sdk/realtime-transcription";
2
- import { normalizeResolvedSecretInputString, normalizeSecretInput } from "openclaw/plugin-sdk/secret-input";
3
- import { asOptionalRecord, normalizeOptionalString, parseFiniteNumber } from "openclaw/plugin-sdk/string-coerce-runtime";
1
+ import { buildMistralRealtimeTranscriptionProvider as buildMistralRealtimeTranscriptionProvider$1 } from "./realtime-transcription-provider-factory.js";
2
+ import { createRealtimeTranscriptionWebSocketSession } from "openclaw/plugin-sdk/realtime-transcription-session";
4
3
  //#region extensions/mistral/realtime-transcription-provider.ts
5
- const MISTRAL_REALTIME_DEFAULT_BASE_URL = "wss://api.mistral.ai";
6
- const MISTRAL_REALTIME_DEFAULT_MODEL = "voxtral-mini-transcribe-realtime-2602";
7
- const MISTRAL_REALTIME_DEFAULT_SAMPLE_RATE = 8e3;
8
- const MISTRAL_REALTIME_DEFAULT_ENCODING = "pcm_mulaw";
9
- const MISTRAL_REALTIME_DEFAULT_DELAY_MS = 800;
10
- const MISTRAL_REALTIME_CONNECT_TIMEOUT_MS = 1e4;
11
- const MISTRAL_REALTIME_CLOSE_TIMEOUT_MS = 5e3;
12
- const MISTRAL_REALTIME_MAX_RECONNECT_ATTEMPTS = 5;
13
- const MISTRAL_REALTIME_RECONNECT_DELAY_MS = 1e3;
14
- const MISTRAL_REALTIME_MAX_QUEUED_BYTES = 2097152;
15
- const MISTRAL_REALTIME_SPEECH_CONTENT = /[\p{L}\p{N}]/u;
16
- const MISTRAL_REALTIME_MAX_PARTIAL_TRANSCRIPT_BYTES = 262144;
17
- const MISTRAL_REALTIME_PARTIAL_TRANSCRIPT_OVERFLOW_MESSAGE = "Mistral realtime transcription exceeded the 256 KiB in-progress transcript limit";
18
- function readNestedMistralConfig(rawConfig) {
19
- const raw = asOptionalRecord(rawConfig);
20
- const providers = asOptionalRecord(raw?.providers);
21
- return asOptionalRecord(providers?.mistral ?? raw?.mistral ?? raw) ?? {};
22
- }
23
- function normalizeMistralEncoding(value) {
24
- const normalized = normalizeOptionalString(value)?.toLowerCase();
25
- if (!normalized) return;
26
- switch (normalized) {
27
- case "pcm":
28
- case "linear16":
29
- case "pcm_s16le": return "pcm_s16le";
30
- case "pcm_s32le":
31
- case "pcm_f16le":
32
- case "pcm_f32le": return normalized;
33
- case "mulaw":
34
- case "ulaw":
35
- case "g711_ulaw":
36
- case "g711-mulaw":
37
- case "pcm_mulaw": return "pcm_mulaw";
38
- case "alaw":
39
- case "g711_alaw":
40
- case "g711-alaw":
41
- case "pcm_alaw": return "pcm_alaw";
42
- default: throw new Error(`Invalid Mistral realtime transcription encoding: ${normalized}`);
43
- }
44
- }
45
- function normalizeMistralRealtimeBaseUrl(value) {
46
- const raw = normalizeOptionalString(value ?? process.env.MISTRAL_REALTIME_BASE_URL);
47
- if (!raw) return MISTRAL_REALTIME_DEFAULT_BASE_URL;
48
- const url = new URL(raw);
49
- url.protocol = url.protocol === "http:" ? "ws:" : url.protocol === "https:" ? "wss:" : url.protocol;
50
- url.pathname = url.pathname.replace(/\/v1\/?$/, "").replace(/\/+$/, "");
51
- return url.toString().replace(/\/+$/, "");
52
- }
53
- function toMistralRealtimeWsUrl(config) {
54
- const base = new URL(`${normalizeMistralRealtimeBaseUrl(config.baseUrl)}/`);
55
- const url = new URL("v1/audio/transcriptions/realtime", base);
56
- url.searchParams.set("model", config.model);
57
- if (config.targetStreamingDelayMs != null) url.searchParams.set("target_streaming_delay_ms", String(config.targetStreamingDelayMs));
58
- return url.toString();
59
- }
60
- function normalizeProviderConfig(config) {
61
- const raw = readNestedMistralConfig(config);
62
- return {
63
- apiKey: normalizeMistralApiKey(raw.apiKey),
64
- baseUrl: normalizeOptionalString(raw.baseUrl),
65
- model: normalizeOptionalString(raw.model ?? raw.sttModel),
66
- sampleRate: parseFiniteNumber(raw.sampleRate ?? raw.sample_rate),
67
- encoding: normalizeMistralEncoding(raw.encoding),
68
- targetStreamingDelayMs: parseFiniteNumber(raw.targetStreamingDelayMs ?? raw.target_streaming_delay_ms ?? raw.delayMs)
69
- };
70
- }
71
- function normalizeMistralApiKey(value) {
72
- const resolved = normalizeResolvedSecretInputString({
73
- value,
74
- path: "plugins.entries.voice-call.config.streaming.providers.mistral.apiKey"
75
- });
76
- return normalizeSecretInput(resolved) || void 0;
77
- }
78
- function readErrorDetail(event) {
79
- const message = event.error?.message;
80
- if (typeof message === "string") return message;
81
- if (message && typeof message === "object") return JSON.stringify(message);
82
- if (typeof event.error?.code === "number") return `Mistral realtime transcription error (${event.error.code})`;
83
- return "Mistral realtime transcription error";
84
- }
85
- function measureTranscriptDeltaBytes(partialText, delta) {
86
- const previousCodeUnit = partialText.charCodeAt(partialText.length - 1);
87
- const nextCodeUnit = delta.charCodeAt(0);
88
- const completesSplitSurrogatePair = previousCodeUnit >= 55296 && previousCodeUnit <= 56319 && nextCodeUnit >= 56320 && nextCodeUnit <= 57343;
89
- return Buffer.byteLength(delta, "utf8") - (completesSplitSurrogatePair ? 2 : 0);
90
- }
91
- function createMistralRealtimeTranscriptionSession(config) {
92
- let partialText = "";
93
- let partialBytes = 0;
94
- let hasFinalSegment = false;
95
- let terminal = false;
96
- const clearPartial = () => {
97
- partialText = "";
98
- partialBytes = 0;
99
- };
100
- const emitFinalTranscript = (text, source) => {
101
- if (!text.trim() || source === "pending" && !MISTRAL_REALTIME_SPEECH_CONTENT.test(text)) return;
102
- hasFinalSegment ||= source === "segment";
103
- config.onTranscript?.(text);
104
- };
105
- const failTerminal = (error, transport) => {
106
- if (terminal) return;
107
- terminal = true;
108
- clearPartial();
109
- transport.closeNow();
110
- try {
111
- config.onError?.(error);
112
- } catch {}
113
- };
114
- const handleEvent = (event, transport) => {
115
- if (terminal) return;
116
- if (event.type === "session.created") {
117
- clearPartial();
118
- hasFinalSegment = false;
119
- transport.sendJson({
120
- type: "session.update",
121
- session: { audio_format: {
122
- encoding: config.encoding,
123
- sample_rate: config.sampleRate
124
- } }
125
- });
126
- transport.markReady();
127
- return;
128
- }
129
- if (!transport.isReady() && event.type === "error") {
130
- transport.failConnect(new Error(readErrorDetail(event)));
131
- return;
132
- }
133
- switch (event.type) {
134
- case "transcription.text.delta":
135
- if (event.text) {
136
- const deltaBytes = measureTranscriptDeltaBytes(partialText, event.text);
137
- if (deltaBytes > MISTRAL_REALTIME_MAX_PARTIAL_TRANSCRIPT_BYTES - partialBytes) {
138
- failTerminal(/* @__PURE__ */ new Error(MISTRAL_REALTIME_PARTIAL_TRANSCRIPT_OVERFLOW_MESSAGE), transport);
139
- return;
140
- }
141
- partialText += event.text;
142
- partialBytes += deltaBytes;
143
- config.onPartial?.(partialText);
144
- }
145
- return;
146
- case "transcription.segment":
147
- if (event.text?.trim()) {
148
- emitFinalTranscript(event.text, "segment");
149
- clearPartial();
150
- }
151
- return;
152
- case "transcription.done": {
153
- terminal = true;
154
- const source = hasFinalSegment ? "pending" : "terminal";
155
- const terminalText = source === "pending" ? partialText : event.text?.trim() ? event.text : partialText;
156
- clearPartial();
157
- try {
158
- emitFinalTranscript(terminalText, source);
159
- } finally {
160
- transport.closeNow();
161
- }
162
- return;
163
- }
164
- case "error": failTerminal(new Error(readErrorDetail(event)), transport);
165
- }
166
- };
167
- return createRealtimeTranscriptionWebSocketSession({
168
- providerId: "mistral",
169
- callbacks: config,
170
- url: () => toMistralRealtimeWsUrl(config),
171
- headers: { Authorization: `Bearer ${config.apiKey}` },
172
- connectTimeoutMs: MISTRAL_REALTIME_CONNECT_TIMEOUT_MS,
173
- closeTimeoutMs: MISTRAL_REALTIME_CLOSE_TIMEOUT_MS,
174
- maxReconnectAttempts: MISTRAL_REALTIME_MAX_RECONNECT_ATTEMPTS,
175
- reconnectDelayMs: MISTRAL_REALTIME_RECONNECT_DELAY_MS,
176
- maxQueuedBytes: MISTRAL_REALTIME_MAX_QUEUED_BYTES,
177
- connectTimeoutMessage: "Mistral realtime transcription connection timeout",
178
- reconnectLimitMessage: "Mistral realtime transcription reconnect limit reached",
179
- sendAudio: (audio, transport) => {
180
- transport.sendJson({
181
- type: "input_audio.append",
182
- audio: audio.toString("base64")
183
- });
184
- },
185
- onClose: (transport) => {
186
- transport.sendJson({ type: "input_audio.flush" });
187
- transport.sendJson({ type: "input_audio.end" });
188
- },
189
- onMessage: handleEvent
190
- });
191
- }
192
4
  function buildMistralRealtimeTranscriptionProvider() {
193
- return {
194
- id: "mistral",
195
- label: "Mistral Realtime Transcription",
196
- aliases: ["mistral-realtime", "voxtral-realtime"],
197
- defaultModel: MISTRAL_REALTIME_DEFAULT_MODEL,
198
- autoSelectOrder: 45,
199
- resolveConfig: ({ rawConfig }) => normalizeProviderConfig(rawConfig),
200
- isConfigured: ({ providerConfig }) => Boolean(normalizeProviderConfig(providerConfig).apiKey || normalizeMistralApiKey(process.env.MISTRAL_API_KEY)),
201
- createSession: (req) => {
202
- const config = normalizeProviderConfig(req.providerConfig);
203
- const apiKey = config.apiKey || normalizeMistralApiKey(process.env.MISTRAL_API_KEY);
204
- if (!apiKey) throw new Error("Mistral API key missing");
205
- return createMistralRealtimeTranscriptionSession({
206
- ...req,
207
- apiKey,
208
- baseUrl: normalizeMistralRealtimeBaseUrl(config.baseUrl),
209
- model: config.model ?? MISTRAL_REALTIME_DEFAULT_MODEL,
210
- sampleRate: config.sampleRate ?? MISTRAL_REALTIME_DEFAULT_SAMPLE_RATE,
211
- encoding: config.encoding ?? MISTRAL_REALTIME_DEFAULT_ENCODING,
212
- targetStreamingDelayMs: config.targetStreamingDelayMs ?? MISTRAL_REALTIME_DEFAULT_DELAY_MS
213
- });
214
- }
215
- };
5
+ return buildMistralRealtimeTranscriptionProvider$1({ createRealtimeTranscriptionWebSocketSession });
216
6
  }
217
7
  //#endregion
218
8
  export { buildMistralRealtimeTranscriptionProvider };
@@ -1,15 +1,19 @@
1
1
  {
2
2
  "id": "mistral",
3
- "icon": "https://cdn.simpleicons.org/mistralai",
3
+ "capabilityCatalogEntry": "./dist/capability-catalog.js",
4
4
  "activation": {
5
5
  "onStartup": false
6
6
  },
7
7
  "enabledByDefault": true,
8
- "providers": ["mistral"],
8
+ "providers": [
9
+ "mistral"
10
+ ],
9
11
  "providerEndpoints": [
10
12
  {
11
13
  "endpointClass": "mistral-public",
12
- "hosts": ["api.mistral.ai"]
14
+ "hosts": [
15
+ "api.mistral.ai"
16
+ ]
13
17
  }
14
18
  ],
15
19
  "providerRequest": {
@@ -20,6 +24,9 @@
20
24
  }
21
25
  },
22
26
  "modelCatalog": {
27
+ "modelsDev": {
28
+ "mistral": "mistral"
29
+ },
23
30
  "providers": {
24
31
  "mistral": {
25
32
  "baseUrl": "https://api.mistral.ai/v1",
@@ -29,7 +36,9 @@
29
36
  {
30
37
  "id": "codestral-latest",
31
38
  "name": "Codestral (latest)",
32
- "input": ["text"],
39
+ "input": [
40
+ "text"
41
+ ],
33
42
  "contextWindow": 128000,
34
43
  "maxTokens": 4096,
35
44
  "cost": {
@@ -44,7 +53,9 @@
44
53
  "name": "Devstral 2 (latest)",
45
54
  "status": "deprecated",
46
55
  "replacedBy": "mistral-medium-3-5",
47
- "input": ["text"],
56
+ "input": [
57
+ "text"
58
+ ],
48
59
  "contextWindow": 262144,
49
60
  "maxTokens": 32768,
50
61
  "cost": {
@@ -57,7 +68,10 @@
57
68
  {
58
69
  "id": "mistral-large-latest",
59
70
  "name": "Mistral Large 3 (latest)",
60
- "input": ["text", "image"],
71
+ "input": [
72
+ "text",
73
+ "image"
74
+ ],
61
75
  "contextWindow": 262144,
62
76
  "maxTokens": 16384,
63
77
  "cost": {
@@ -72,7 +86,10 @@
72
86
  "name": "Mistral Medium 3.1",
73
87
  "status": "deprecated",
74
88
  "replacedBy": "mistral-medium-3-5",
75
- "input": ["text", "image"],
89
+ "input": [
90
+ "text",
91
+ "image"
92
+ ],
76
93
  "contextWindow": 128000,
77
94
  "maxTokens": 8192,
78
95
  "cost": {
@@ -85,7 +102,10 @@
85
102
  {
86
103
  "id": "mistral-medium-3-5",
87
104
  "name": "Mistral Medium 3.5",
88
- "input": ["text", "image"],
105
+ "input": [
106
+ "text",
107
+ "image"
108
+ ],
89
109
  "reasoning": true,
90
110
  "contextWindow": 262144,
91
111
  "maxTokens": 8192,
@@ -99,7 +119,10 @@
99
119
  {
100
120
  "id": "mistral-small-latest",
101
121
  "name": "Mistral Small 4 (latest)",
102
- "input": ["text", "image"],
122
+ "input": [
123
+ "text",
124
+ "image"
125
+ ],
103
126
  "reasoning": true,
104
127
  "contextWindow": 262144,
105
128
  "maxTokens": 16384,
@@ -113,7 +136,10 @@
113
136
  {
114
137
  "id": "mistral-small-2603",
115
138
  "name": "Mistral Small 4 (26.03)",
116
- "input": ["text", "image"],
139
+ "input": [
140
+ "text",
141
+ "image"
142
+ ],
117
143
  "reasoning": true,
118
144
  "contextWindow": 262144,
119
145
  "maxTokens": 16384,
@@ -135,7 +161,9 @@
135
161
  "providers": [
136
162
  {
137
163
  "id": "mistral",
138
- "envVars": ["MISTRAL_API_KEY"]
164
+ "envVars": [
165
+ "MISTRAL_API_KEY"
166
+ ]
139
167
  }
140
168
  ]
141
169
  },
@@ -156,13 +184,21 @@
156
184
  }
157
185
  ],
158
186
  "contracts": {
159
- "embeddingProviders": ["mistral"],
160
- "mediaUnderstandingProviders": ["mistral"],
161
- "realtimeTranscriptionProviders": ["mistral"]
187
+ "embeddingProviders": [
188
+ "mistral"
189
+ ],
190
+ "mediaUnderstandingProviders": [
191
+ "mistral"
192
+ ],
193
+ "realtimeTranscriptionProviders": [
194
+ "mistral"
195
+ ]
162
196
  },
163
197
  "mediaUnderstandingProviderMetadata": {
164
198
  "mistral": {
165
- "capabilities": ["audio"],
199
+ "capabilities": [
200
+ "audio"
201
+ ],
166
202
  "defaultModels": {
167
203
  "audio": "voxtral-mini-latest"
168
204
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openclaw/mistral-provider",
3
- "version": "2026.9.1",
3
+ "version": "2026.9.2",
4
4
  "description": "OpenClaw Mistral provider plugin",
5
5
  "repository": {
6
6
  "type": "git",
@@ -18,10 +18,10 @@
18
18
  "minHostVersion": ">=2026.7.2"
19
19
  },
20
20
  "compat": {
21
- "pluginApi": ">=2026.9.1"
21
+ "pluginApi": ">=2026.9.2"
22
22
  },
23
23
  "build": {
24
- "openclawVersion": "2026.9.1",
24
+ "openclawVersion": "2026.9.2",
25
25
  "bundledDist": false
26
26
  },
27
27
  "release": {
@@ -38,7 +38,7 @@
38
38
  "README.md"
39
39
  ],
40
40
  "peerDependencies": {
41
- "openclaw": ">=2026.9.1"
41
+ "openclaw": ">=2026.9.2"
42
42
  },
43
43
  "peerDependenciesMeta": {
44
44
  "openclaw": {