@ai-sdk%2Fmistral@3.0.51.patch 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709
  1. diff --git a/dist/index.d.mts b/dist/index.d.mts
  2. index 1bde0b9f8cbe6771a52c1041095c9dddfe8e5b6c..0ca2ffb2a0c9327aed5ddcf0004500dc8b42569f 100644
  3. --- a/dist/index.d.mts
  4. +++ b/dist/index.d.mts
  5. @@ -14,6 +14,7 @@ declare const mistralLanguageModelOptions: z.ZodObject<{
  6. none: "none";
  7. high: "high";
  8. }>>;
  9. + promptCacheKey: z.ZodOptional<z.ZodString>;
  10. }, z.core.$strip>;
  11. type MistralLanguageModelOptions = z.infer<typeof mistralLanguageModelOptions>;
  12. diff --git a/dist/index.d.ts b/dist/index.d.ts
  13. index 1bde0b9f8cbe6771a52c1041095c9dddfe8e5b6c..0ca2ffb2a0c9327aed5ddcf0004500dc8b42569f 100644
  14. --- a/dist/index.d.ts
  15. +++ b/dist/index.d.ts
  16. @@ -14,6 +14,7 @@ declare const mistralLanguageModelOptions: z.ZodObject<{
  17. none: "none";
  18. high: "high";
  19. }>>;
  20. + promptCacheKey: z.ZodOptional<z.ZodString>;
  21. }, z.core.$strip>;
  22. type MistralLanguageModelOptions = z.infer<typeof mistralLanguageModelOptions>;
  23. diff --git a/dist/index.js b/dist/index.js
  24. index d3f904c12a1d582cc7b9e9a2d30273e1a8505b28..267f34e20ea392b7a85ad5259d72d50605a6f971 100644
  25. --- a/dist/index.js
  26. +++ b/dist/index.js
  27. @@ -128,11 +128,14 @@ function convertToMistralChatMessages(prompt) {
  28. }
  29. case "assistant": {
  30. let text = "";
  31. + const structuredContent = [];
  32. + let hasNativeReasoning = false;
  33. const toolCalls = [];
  34. for (const part of content) {
  35. switch (part.type) {
  36. case "text": {
  37. text += part.text;
  38. + structuredContent.push({ type: "text", text: part.text });
  39. break;
  40. }
  41. case "tool-call": {
  42. @@ -148,6 +151,13 @@ function convertToMistralChatMessages(prompt) {
  43. }
  44. case "reasoning": {
  45. text += part.text;
  46. + const native = part.providerOptions?.mistral?.thinking;
  47. + if (native?.type === "thinking") {
  48. + hasNativeReasoning = true;
  49. + structuredContent.push(native);
  50. + break;
  51. + }
  52. + structuredContent.push({ type: "text", text: part.text });
  53. break;
  54. }
  55. default: {
  56. @@ -159,7 +169,7 @@ function convertToMistralChatMessages(prompt) {
  57. }
  58. messages.push({
  59. role: "assistant",
  60. - content: text,
  61. + content: hasNativeReasoning ? structuredContent : text,
  62. prefix: isLastMessage ? true : void 0,
  63. tool_calls: toolCalls.length > 0 ? toolCalls : void 0
  64. });
  65. @@ -268,7 +278,8 @@ var mistralLanguageModelOptions = import_v4.z.object({
  66. * - `'high'`: Enable reasoning
  67. * - `'none'`: Disable reasoning
  68. */
  69. - reasoningEffort: import_v4.z.enum(["high", "none"]).optional()
  70. + reasoningEffort: import_v4.z.enum(["high", "none"]).optional(),
  71. + promptCacheKey: import_v4.z.string().optional()
  72. });
  73. // src/mistral-error.ts
  74. @@ -407,6 +418,7 @@ var MistralChatLanguageModel = class {
  75. stop: stopSequences,
  76. random_seed: seed,
  77. reasoning_effort: options.reasoningEffort,
  78. + prompt_cache_key: options.promptCacheKey,
  79. // response format:
  80. response_format: (responseFormat == null ? void 0 : responseFormat.type) === "json" ? structuredOutputs && (responseFormat == null ? void 0 : responseFormat.schema) != null ? {
  81. type: "json_schema",
  82. @@ -465,9 +477,11 @@ var MistralChatLanguageModel = class {
  83. for (const part of choice.message.content) {
  84. if (part.type === "thinking") {
  85. const reasoningText = extractReasoningContent(part.thinking);
  86. - if (reasoningText.length > 0) {
  87. - content.push({ type: "reasoning", text: reasoningText });
  88. - }
  89. + content.push({
  90. + type: "reasoning",
  91. + text: reasoningText,
  92. + providerMetadata: { mistral: { thinking: part } }
  93. + });
  94. } else if (part.type === "text") {
  95. if (part.text.length > 0) {
  96. content.push({ type: "text", text: part.text });
  97. @@ -528,6 +542,7 @@ var MistralChatLanguageModel = class {
  98. let isFirstChunk = true;
  99. let activeText = false;
  100. let activeReasoningId = null;
  101. + let activeThinking = null;
  102. const generateId2 = this.generateId;
  103. return {
  104. stream: response.pipeThrough(
  105. @@ -561,18 +576,19 @@ var MistralChatLanguageModel = class {
  106. for (const part of delta.content) {
  107. if (part.type === "thinking") {
  108. const reasoningDelta = extractReasoningContent(part.thinking);
  109. - if (reasoningDelta.length > 0) {
  110. - if (activeReasoningId == null) {
  111. - if (activeText) {
  112. - controller.enqueue({ type: "text-end", id: "0" });
  113. - activeText = false;
  114. - }
  115. - activeReasoningId = generateId2();
  116. - controller.enqueue({
  117. - type: "reasoning-start",
  118. - id: activeReasoningId
  119. - });
  120. + activeThinking = mergeThinking(activeThinking, part);
  121. + if (activeReasoningId == null) {
  122. + if (activeText) {
  123. + controller.enqueue({ type: "text-end", id: "0" });
  124. + activeText = false;
  125. }
  126. + activeReasoningId = generateId2();
  127. + controller.enqueue({
  128. + type: "reasoning-start",
  129. + id: activeReasoningId
  130. + });
  131. + }
  132. + if (reasoningDelta.length > 0) {
  133. controller.enqueue({
  134. type: "reasoning-delta",
  135. id: activeReasoningId,
  136. @@ -587,9 +603,11 @@ var MistralChatLanguageModel = class {
  137. if (activeReasoningId != null) {
  138. controller.enqueue({
  139. type: "reasoning-end",
  140. - id: activeReasoningId
  141. + id: activeReasoningId,
  142. + providerMetadata: { mistral: { thinking: activeThinking } }
  143. });
  144. activeReasoningId = null;
  145. + activeThinking = null;
  146. }
  147. controller.enqueue({ type: "text-start", id: "0" });
  148. activeText = true;
  149. @@ -638,7 +656,8 @@ var MistralChatLanguageModel = class {
  150. if (activeReasoningId != null) {
  151. controller.enqueue({
  152. type: "reasoning-end",
  153. - id: activeReasoningId
  154. + id: activeReasoningId,
  155. + providerMetadata: { mistral: { thinking: activeThinking } }
  156. });
  157. }
  158. if (activeText) {
  159. @@ -660,6 +679,13 @@ var MistralChatLanguageModel = class {
  160. function extractReasoningContent(thinking) {
  161. return thinking.filter((chunk) => chunk.type === "text").map((chunk) => chunk.text).join("");
  162. }
  163. +function mergeThinking(current, next) {
  164. + if (current === null) return { ...next, thinking: [...next.thinking] };
  165. + current.thinking.push(...next.thinking);
  166. + if (next.closed !== void 0) current.closed = next.closed;
  167. + if (next.signature !== void 0) current.signature = next.signature;
  168. + return current;
  169. +}
  170. function extractTextContent(content) {
  171. if (typeof content === "string") {
  172. return content;
  173. @@ -686,6 +712,30 @@ function extractTextContent(content) {
  174. }
  175. return textContent.length ? textContent.join("") : void 0;
  176. }
  177. +var mistralThinkingContentSchema = import_v43.z.discriminatedUnion("type", [
  178. + import_v43.z.object({
  179. + type: import_v43.z.literal("text"),
  180. + text: import_v43.z.string()
  181. + }),
  182. + import_v43.z.object({
  183. + type: import_v43.z.literal("tool_reference"),
  184. + tool: import_v43.z.string(),
  185. + title: import_v43.z.string(),
  186. + url: import_v43.z.string().nullish(),
  187. + favicon: import_v43.z.string().nullish(),
  188. + description: import_v43.z.string().nullish()
  189. + }),
  190. + import_v43.z.object({
  191. + type: import_v43.z.literal("reference"),
  192. + reference_ids: import_v43.z.array(import_v43.z.union([import_v43.z.string(), import_v43.z.number().int()]))
  193. + })
  194. +]);
  195. +var mistralThinkChunkSchema = import_v43.z.object({
  196. + type: import_v43.z.literal("thinking"),
  197. + thinking: import_v43.z.array(mistralThinkingContentSchema),
  198. + closed: import_v43.z.boolean().optional(),
  199. + signature: import_v43.z.string().nullish()
  200. +});
  201. var mistralContentSchema = import_v43.z.union([
  202. import_v43.z.string(),
  203. import_v43.z.array(
  204. @@ -708,15 +758,7 @@ var mistralContentSchema = import_v43.z.union([
  205. type: import_v43.z.literal("reference"),
  206. reference_ids: import_v43.z.array(import_v43.z.union([import_v43.z.string(), import_v43.z.number()]))
  207. }),
  208. - import_v43.z.object({
  209. - type: import_v43.z.literal("thinking"),
  210. - thinking: import_v43.z.array(
  211. - import_v43.z.object({
  212. - type: import_v43.z.literal("text"),
  213. - text: import_v43.z.string()
  214. - })
  215. - )
  216. - })
  217. + mistralThinkChunkSchema
  218. ])
  219. )
  220. ]).nullish();
  221. diff --git a/dist/index.mjs b/dist/index.mjs
  222. index d2eff622c1b84a96bdeb4012cb0206a33012a04d..3bff11ddd6136ada45809568828cbc8f2493a42a 100644
  223. --- a/dist/index.mjs
  224. +++ b/dist/index.mjs
  225. @@ -116,11 +116,14 @@ function convertToMistralChatMessages(prompt) {
  226. }
  227. case "assistant": {
  228. let text = "";
  229. + const structuredContent = [];
  230. + let hasNativeReasoning = false;
  231. const toolCalls = [];
  232. for (const part of content) {
  233. switch (part.type) {
  234. case "text": {
  235. text += part.text;
  236. + structuredContent.push({ type: "text", text: part.text });
  237. break;
  238. }
  239. case "tool-call": {
  240. @@ -136,6 +139,13 @@ function convertToMistralChatMessages(prompt) {
  241. }
  242. case "reasoning": {
  243. text += part.text;
  244. + const native = part.providerOptions?.mistral?.thinking;
  245. + if (native?.type === "thinking") {
  246. + hasNativeReasoning = true;
  247. + structuredContent.push(native);
  248. + break;
  249. + }
  250. + structuredContent.push({ type: "text", text: part.text });
  251. break;
  252. }
  253. default: {
  254. @@ -147,7 +157,7 @@ function convertToMistralChatMessages(prompt) {
  255. }
  256. messages.push({
  257. role: "assistant",
  258. - content: text,
  259. + content: hasNativeReasoning ? structuredContent : text,
  260. prefix: isLastMessage ? true : void 0,
  261. tool_calls: toolCalls.length > 0 ? toolCalls : void 0
  262. });
  263. @@ -256,7 +266,8 @@ var mistralLanguageModelOptions = z.object({
  264. * - `'high'`: Enable reasoning
  265. * - `'none'`: Disable reasoning
  266. */
  267. - reasoningEffort: z.enum(["high", "none"]).optional()
  268. + reasoningEffort: z.enum(["high", "none"]).optional(),
  269. + promptCacheKey: z.string().optional()
  270. });
  271. // src/mistral-error.ts
  272. @@ -397,6 +408,7 @@ var MistralChatLanguageModel = class {
  273. stop: stopSequences,
  274. random_seed: seed,
  275. reasoning_effort: options.reasoningEffort,
  276. + prompt_cache_key: options.promptCacheKey,
  277. // response format:
  278. response_format: (responseFormat == null ? void 0 : responseFormat.type) === "json" ? structuredOutputs && (responseFormat == null ? void 0 : responseFormat.schema) != null ? {
  279. type: "json_schema",
  280. @@ -455,9 +467,11 @@ var MistralChatLanguageModel = class {
  281. for (const part of choice.message.content) {
  282. if (part.type === "thinking") {
  283. const reasoningText = extractReasoningContent(part.thinking);
  284. - if (reasoningText.length > 0) {
  285. - content.push({ type: "reasoning", text: reasoningText });
  286. - }
  287. + content.push({
  288. + type: "reasoning",
  289. + text: reasoningText,
  290. + providerMetadata: { mistral: { thinking: part } }
  291. + });
  292. } else if (part.type === "text") {
  293. if (part.text.length > 0) {
  294. content.push({ type: "text", text: part.text });
  295. @@ -518,6 +532,7 @@ var MistralChatLanguageModel = class {
  296. let isFirstChunk = true;
  297. let activeText = false;
  298. let activeReasoningId = null;
  299. + let activeThinking = null;
  300. const generateId2 = this.generateId;
  301. return {
  302. stream: response.pipeThrough(
  303. @@ -551,18 +566,19 @@ var MistralChatLanguageModel = class {
  304. for (const part of delta.content) {
  305. if (part.type === "thinking") {
  306. const reasoningDelta = extractReasoningContent(part.thinking);
  307. - if (reasoningDelta.length > 0) {
  308. - if (activeReasoningId == null) {
  309. - if (activeText) {
  310. - controller.enqueue({ type: "text-end", id: "0" });
  311. - activeText = false;
  312. - }
  313. - activeReasoningId = generateId2();
  314. - controller.enqueue({
  315. - type: "reasoning-start",
  316. - id: activeReasoningId
  317. - });
  318. + activeThinking = mergeThinking(activeThinking, part);
  319. + if (activeReasoningId == null) {
  320. + if (activeText) {
  321. + controller.enqueue({ type: "text-end", id: "0" });
  322. + activeText = false;
  323. }
  324. + activeReasoningId = generateId2();
  325. + controller.enqueue({
  326. + type: "reasoning-start",
  327. + id: activeReasoningId
  328. + });
  329. + }
  330. + if (reasoningDelta.length > 0) {
  331. controller.enqueue({
  332. type: "reasoning-delta",
  333. id: activeReasoningId,
  334. @@ -577,9 +593,11 @@ var MistralChatLanguageModel = class {
  335. if (activeReasoningId != null) {
  336. controller.enqueue({
  337. type: "reasoning-end",
  338. - id: activeReasoningId
  339. + id: activeReasoningId,
  340. + providerMetadata: { mistral: { thinking: activeThinking } }
  341. });
  342. activeReasoningId = null;
  343. + activeThinking = null;
  344. }
  345. controller.enqueue({ type: "text-start", id: "0" });
  346. activeText = true;
  347. @@ -628,7 +646,8 @@ var MistralChatLanguageModel = class {
  348. if (activeReasoningId != null) {
  349. controller.enqueue({
  350. type: "reasoning-end",
  351. - id: activeReasoningId
  352. + id: activeReasoningId,
  353. + providerMetadata: { mistral: { thinking: activeThinking } }
  354. });
  355. }
  356. if (activeText) {
  357. @@ -650,6 +669,13 @@ var MistralChatLanguageModel = class {
  358. function extractReasoningContent(thinking) {
  359. return thinking.filter((chunk) => chunk.type === "text").map((chunk) => chunk.text).join("");
  360. }
  361. +function mergeThinking(current, next) {
  362. + if (current === null) return { ...next, thinking: [...next.thinking] };
  363. + current.thinking.push(...next.thinking);
  364. + if (next.closed !== void 0) current.closed = next.closed;
  365. + if (next.signature !== void 0) current.signature = next.signature;
  366. + return current;
  367. +}
  368. function extractTextContent(content) {
  369. if (typeof content === "string") {
  370. return content;
  371. @@ -676,6 +702,30 @@ function extractTextContent(content) {
  372. }
  373. return textContent.length ? textContent.join("") : void 0;
  374. }
  375. +var mistralThinkingContentSchema = z3.discriminatedUnion("type", [
  376. + z3.object({
  377. + type: z3.literal("text"),
  378. + text: z3.string()
  379. + }),
  380. + z3.object({
  381. + type: z3.literal("tool_reference"),
  382. + tool: z3.string(),
  383. + title: z3.string(),
  384. + url: z3.string().nullish(),
  385. + favicon: z3.string().nullish(),
  386. + description: z3.string().nullish()
  387. + }),
  388. + z3.object({
  389. + type: z3.literal("reference"),
  390. + reference_ids: z3.array(z3.union([z3.string(), z3.number().int()]))
  391. + })
  392. +]);
  393. +var mistralThinkChunkSchema = z3.object({
  394. + type: z3.literal("thinking"),
  395. + thinking: z3.array(mistralThinkingContentSchema),
  396. + closed: z3.boolean().optional(),
  397. + signature: z3.string().nullish()
  398. +});
  399. var mistralContentSchema = z3.union([
  400. z3.string(),
  401. z3.array(
  402. @@ -698,15 +748,7 @@ var mistralContentSchema = z3.union([
  403. type: z3.literal("reference"),
  404. reference_ids: z3.array(z3.union([z3.string(), z3.number()]))
  405. }),
  406. - z3.object({
  407. - type: z3.literal("thinking"),
  408. - thinking: z3.array(
  409. - z3.object({
  410. - type: z3.literal("text"),
  411. - text: z3.string()
  412. - })
  413. - )
  414. - })
  415. + mistralThinkChunkSchema
  416. ])
  417. )
  418. ]).nullish();
  419. diff --git a/src/convert-to-mistral-chat-messages.ts b/src/convert-to-mistral-chat-messages.ts
  420. index 3c6914f8da615d7517bc43dd56198298d0a50247..8cd6f4c7577f746ef41e8a0aee682234c473667a 100644
  421. --- a/src/convert-to-mistral-chat-messages.ts
  422. +++ b/src/convert-to-mistral-chat-messages.ts
  423. @@ -3,7 +3,11 @@ import {
  424. type LanguageModelV3DataContent,
  425. type LanguageModelV3Prompt,
  426. } from '@ai-sdk/provider';
  427. -import type { MistralPrompt } from './mistral-chat-prompt';
  428. +import type {
  429. + MistralAssistantMessageContent,
  430. + MistralPrompt,
  431. + MistralThinkChunk,
  432. +} from './mistral-chat-prompt';
  433. import { convertToBase64 } from '@ai-sdk/provider-utils';
  434. function formatFileUrl({
  435. @@ -76,6 +80,8 @@ export function convertToMistralChatMessages(
  436. case 'assistant': {
  437. let text = '';
  438. + const structuredContent: Array<MistralAssistantMessageContent> = [];
  439. + let hasNativeReasoning = false;
  440. const toolCalls: Array<{
  441. id: string;
  442. type: 'function';
  443. @@ -86,6 +92,7 @@ export function convertToMistralChatMessages(
  444. switch (part.type) {
  445. case 'text': {
  446. text += part.text;
  447. + structuredContent.push({ type: 'text', text: part.text });
  448. break;
  449. }
  450. case 'tool-call': {
  451. @@ -101,6 +108,14 @@ export function convertToMistralChatMessages(
  452. }
  453. case 'reasoning': {
  454. text += part.text;
  455. + const native = part.providerOptions?.mistral
  456. + ?.thinking as MistralThinkChunk | undefined;
  457. + if (native?.type === 'thinking') {
  458. + hasNativeReasoning = true;
  459. + structuredContent.push(native);
  460. + break;
  461. + }
  462. + structuredContent.push({ type: 'text', text: part.text });
  463. break;
  464. }
  465. default: {
  466. @@ -113,7 +128,7 @@ export function convertToMistralChatMessages(
  467. messages.push({
  468. role: 'assistant',
  469. - content: text,
  470. + content: hasNativeReasoning ? structuredContent : text,
  471. prefix: isLastMessage ? true : undefined,
  472. tool_calls: toolCalls.length > 0 ? toolCalls : undefined,
  473. });
  474. diff --git a/src/mistral-chat-language-model.ts b/src/mistral-chat-language-model.ts
  475. index 7e4a7ab552f1b41b7074e1b3cada8a51d791268d..847d26f9dfe03572a969a122f8c96b8bbfda8066 100644
  476. --- a/src/mistral-chat-language-model.ts
  477. +++ b/src/mistral-chat-language-model.ts
  478. @@ -122,6 +122,7 @@ export class MistralChatLanguageModel implements LanguageModelV3 {
  479. stop: stopSequences,
  480. random_seed: seed,
  481. reasoning_effort: options.reasoningEffort,
  482. + prompt_cache_key: options.promptCacheKey,
  483. // response format:
  484. response_format:
  485. @@ -201,9 +202,11 @@ export class MistralChatLanguageModel implements LanguageModelV3 {
  486. for (const part of choice.message.content) {
  487. if (part.type === 'thinking') {
  488. const reasoningText = extractReasoningContent(part.thinking);
  489. - if (reasoningText.length > 0) {
  490. - content.push({ type: 'reasoning', text: reasoningText });
  491. - }
  492. + content.push({
  493. + type: 'reasoning',
  494. + text: reasoningText,
  495. + providerMetadata: { mistral: { thinking: part } },
  496. + });
  497. } else if (part.type === 'text') {
  498. if (part.text.length > 0) {
  499. content.push({ type: 'text', text: part.text });
  500. @@ -278,6 +281,7 @@ export class MistralChatLanguageModel implements LanguageModelV3 {
  501. let isFirstChunk = true;
  502. let activeText = false;
  503. let activeReasoningId: string | null = null;
  504. + let activeThinking: z.infer<typeof mistralThinkChunkSchema> | null = null;
  505. const generateId = this.generateId;
  506. @@ -326,20 +330,21 @@ export class MistralChatLanguageModel implements LanguageModelV3 {
  507. for (const part of delta.content) {
  508. if (part.type === 'thinking') {
  509. const reasoningDelta = extractReasoningContent(part.thinking);
  510. - if (reasoningDelta.length > 0) {
  511. - if (activeReasoningId == null) {
  512. - // end any active text before starting reasoning
  513. - if (activeText) {
  514. - controller.enqueue({ type: 'text-end', id: '0' });
  515. - activeText = false;
  516. - }
  517. -
  518. - activeReasoningId = generateId();
  519. - controller.enqueue({
  520. - type: 'reasoning-start',
  521. - id: activeReasoningId,
  522. - });
  523. + activeThinking = mergeThinking(activeThinking, part);
  524. + if (activeReasoningId == null) {
  525. + // end any active text before starting reasoning
  526. + if (activeText) {
  527. + controller.enqueue({ type: 'text-end', id: '0' });
  528. + activeText = false;
  529. }
  530. +
  531. + activeReasoningId = generateId();
  532. + controller.enqueue({
  533. + type: 'reasoning-start',
  534. + id: activeReasoningId,
  535. + });
  536. + }
  537. + if (reasoningDelta.length > 0) {
  538. controller.enqueue({
  539. type: 'reasoning-delta',
  540. id: activeReasoningId,
  541. @@ -357,8 +362,12 @@ export class MistralChatLanguageModel implements LanguageModelV3 {
  542. controller.enqueue({
  543. type: 'reasoning-end',
  544. id: activeReasoningId,
  545. + providerMetadata: {
  546. + mistral: { thinking: activeThinking },
  547. + },
  548. });
  549. activeReasoningId = null;
  550. + activeThinking = null;
  551. }
  552. controller.enqueue({ type: 'text-start', id: '0' });
  553. activeText = true;
  554. @@ -416,6 +425,9 @@ export class MistralChatLanguageModel implements LanguageModelV3 {
  555. controller.enqueue({
  556. type: 'reasoning-end',
  557. id: activeReasoningId,
  558. + providerMetadata: {
  559. + mistral: { thinking: activeThinking },
  560. + },
  561. });
  562. }
  563. if (activeText) {
  564. @@ -437,7 +449,7 @@ export class MistralChatLanguageModel implements LanguageModelV3 {
  565. }
  566. function extractReasoningContent(
  567. - thinking: Array<{ type: string; text: string }>,
  568. + thinking: Array<z.infer<typeof mistralThinkingContentSchema>>,
  569. ) {
  570. return thinking
  571. .filter(chunk => chunk.type === 'text')
  572. @@ -445,6 +457,17 @@ function extractReasoningContent(
  573. .join('');
  574. }
  575. +function mergeThinking(
  576. + current: z.infer<typeof mistralThinkChunkSchema> | null,
  577. + next: z.infer<typeof mistralThinkChunkSchema>,
  578. +) {
  579. + if (current === null) return { ...next, thinking: [...next.thinking] };
  580. + current.thinking.push(...next.thinking);
  581. + if (next.closed !== undefined) current.closed = next.closed;
  582. + if (next.signature !== undefined) current.signature = next.signature;
  583. + return current;
  584. +}
  585. +
  586. function extractTextContent(content: z.infer<typeof mistralContentSchema>) {
  587. if (typeof content === 'string') {
  588. return content;
  589. @@ -478,6 +501,32 @@ function extractTextContent(content: z.infer<typeof mistralContentSchema>) {
  590. return textContent.length ? textContent.join('') : undefined;
  591. }
  592. +const mistralThinkingContentSchema = z.discriminatedUnion('type', [
  593. + z.object({
  594. + type: z.literal('text'),
  595. + text: z.string(),
  596. + }),
  597. + z.object({
  598. + type: z.literal('tool_reference'),
  599. + tool: z.string(),
  600. + title: z.string(),
  601. + url: z.string().nullish(),
  602. + favicon: z.string().nullish(),
  603. + description: z.string().nullish(),
  604. + }),
  605. + z.object({
  606. + type: z.literal('reference'),
  607. + reference_ids: z.array(z.union([z.string(), z.number().int()])),
  608. + }),
  609. +]);
  610. +
  611. +const mistralThinkChunkSchema = z.object({
  612. + type: z.literal('thinking'),
  613. + thinking: z.array(mistralThinkingContentSchema),
  614. + closed: z.boolean().optional(),
  615. + signature: z.string().nullish(),
  616. +});
  617. +
  618. const mistralContentSchema = z
  619. .union([
  620. z.string(),
  621. @@ -501,15 +550,7 @@ const mistralContentSchema = z
  622. type: z.literal('reference'),
  623. reference_ids: z.array(z.union([z.string(), z.number()])),
  624. }),
  625. - z.object({
  626. - type: z.literal('thinking'),
  627. - thinking: z.array(
  628. - z.object({
  629. - type: z.literal('text'),
  630. - text: z.string(),
  631. - }),
  632. - ),
  633. - }),
  634. + mistralThinkChunkSchema,
  635. ]),
  636. ),
  637. ])
  638. diff --git a/src/mistral-chat-options.ts b/src/mistral-chat-options.ts
  639. index 54b29c08517d348995b6ca093b11160e453d5c8b..de30c3e7d924889339e38b1067cb26e9ada05d11 100644
  640. --- a/src/mistral-chat-options.ts
  641. +++ b/src/mistral-chat-options.ts
  642. @@ -64,6 +64,11 @@ export const mistralLanguageModelOptions = z.object({
  643. * - `'none'`: Disable reasoning
  644. */
  645. reasoningEffort: z.enum(['high', 'none']).optional(),
  646. +
  647. + /**
  648. + * A stable identifier used to route requests with shared prompt prefixes.
  649. + */
  650. + promptCacheKey: z.string().optional(),
  651. });
  652. export type MistralLanguageModelOptions = z.infer<
  653. diff --git a/src/mistral-chat-prompt.ts b/src/mistral-chat-prompt.ts
  654. index 13f1dced55ac4be084128127a57fbdd58115bc28..172b11dde3dd326c2f3befd99237474ed8c79285 100644
  655. --- a/src/mistral-chat-prompt.ts
  656. +++ b/src/mistral-chat-prompt.ts
  657. @@ -23,7 +23,7 @@ export type MistralUserMessageContent =
  658. export interface MistralAssistantMessage {
  659. role: 'assistant';
  660. - content: string;
  661. + content: string | Array<MistralAssistantMessageContent>;
  662. prefix?: boolean;
  663. tool_calls?: Array<{
  664. id: string;
  665. @@ -32,6 +32,29 @@ export interface MistralAssistantMessage {
  666. }>;
  667. }
  668. +export type MistralAssistantMessageContent =
  669. + | { type: 'text'; text: string }
  670. + | MistralThinkChunk;
  671. +
  672. +export type MistralThinkChunk = {
  673. + type: 'thinking';
  674. + thinking: Array<MistralThinkingContent>;
  675. + closed?: boolean;
  676. + signature?: string | null;
  677. +};
  678. +
  679. +export type MistralThinkingContent =
  680. + | { type: 'text'; text: string }
  681. + | {
  682. + type: 'tool_reference';
  683. + tool: string;
  684. + title: string;
  685. + url?: string | null;
  686. + favicon?: string | null;
  687. + description?: string | null;
  688. + }
  689. + | { type: 'reference'; reference_ids: Array<string | number> };
  690. +
  691. export interface MistralToolMessage {
  692. role: 'tool';
  693. name: string;