mirror of
https://github.com/anomalyco/opencode.git
synced 2026-09-28 13:33:35 +08:00
+29



![opencode-agent[bot] <opencode-agent[bot]@users.noreply.github.com>](/assets/img/avatar_default.png)


![opencode-agent[bot] <219766164+opencode-agent[bot]@users.noreply.github.com>](/assets/img/avatar_default.png)

![opencode-agent[bot]](/assets/img/avatar_default.png)
opencode-agent[bot]
Aiden Cline
opencode-agent[bot] <219766164+opencode-agent[bot]@users.noreply.github.com>
Dax Raad
Dax
opencode-agent[bot] <opencode-agent[bot]@users.noreply.github.com>
Aiden Cline
Nabs
usrnk1
Brendan Allan
Aarav Sareen
Brendan Allan
Victor Navarro
Vladimir Glafirov
AidenGeunGeun
Mark
Aiden Cline
Adam
opencode
Luke Parker
David Hill
Jay
Jay
BB84
Dustin Deus
Frank
Jack
Sebastian
Jérôme Benoit
Test User
Simon Klee
Rahul A Mistry
Qiping Li
liqiping
OpeOginni
Matthias Reso
tobwen
Daniel Polito
opencode
02f2725154
Co-authored-by: Aiden Cline <rekram1-node@users.noreply.github.com> Co-authored-by: opencode-agent[bot] <219766164+opencode-agent[bot]@users.noreply.github.com> Co-authored-by: Dax Raad <d@ironbay.co> Co-authored-by: Dax <mail@thdxr.com> Co-authored-by: opencode-agent[bot] <opencode-agent[bot]@users.noreply.github.com> Co-authored-by: Aiden Cline <63023139+rekram1-node@users.noreply.github.com> Co-authored-by: Nabs <nabil@instafork.com> Co-authored-by: usrnk1 <7547651+usrnk1@users.noreply.github.com> Co-authored-by: Brendan Allan <14191578+Brendonovich@users.noreply.github.com> Co-authored-by: Aarav Sareen <96787824+arvsrn@users.noreply.github.com> Co-authored-by: Brendan Allan <git@brendonovich.dev> Co-authored-by: Victor Navarro <vn4varro@gmail.com> Co-authored-by: Vladimir Glafirov <vglafirov@gitlab.com> Co-authored-by: AidenGeunGeun <eastlandwyvern@gmail.com> Co-authored-by: Mark <geraint0923@users.noreply.github.com> Co-authored-by: Aiden Cline <aidenpcline@gmail.com> Co-authored-by: Adam <2363879+adamdotdevin@users.noreply.github.com> Co-authored-by: opencode <opencode@sst.dev> Co-authored-by: Luke Parker <10430890+Hona@users.noreply.github.com> Co-authored-by: David Hill <1879069+iamdavidhill@users.noreply.github.com> Co-authored-by: Jay <air@live.ca> Co-authored-by: Jay <53023+jayair@users.noreply.github.com> Co-authored-by: BB84 <110078428+BB-84C@users.noreply.github.com> Co-authored-by: Dustin Deus <deusdustin@gmail.com> Co-authored-by: Frank <frank@anoma.ly> Co-authored-by: Jack <jack@anoma.ly> Co-authored-by: Sebastian <hasta84@gmail.com> Co-authored-by: Jérôme Benoit <jerome.benoit@sap.com> Co-authored-by: Test User <test@test.com> Co-authored-by: Simon Klee <hello@simonklee.dk> Co-authored-by: Rahul A Mistry <149420892+ProdigyRahul@users.noreply.github.com> Co-authored-by: Qiping Li <liqiping1991@gmail.com> Co-authored-by: liqiping <liqiping@msh.team> Co-authored-by: OpeOginni <107570612+OpeOginni@users.noreply.github.com> Co-authored-by: Matthias Reso <13337103+mreso@users.noreply.github.com> Co-authored-by: tobwen <1864057+tobwen@users.noreply.github.com> Co-authored-by: Daniel Polito <danielbpolito@gmail.com> Co-authored-by: opencode <noreply@opencode.ai>
710 lines
27 KiB
Diff
710 lines
27 KiB
Diff
diff --git a/dist/index.d.mts b/dist/index.d.mts
|
|
index 1bde0b9f8cbe6771a52c1041095c9dddfe8e5b6c..0ca2ffb2a0c9327aed5ddcf0004500dc8b42569f 100644
|
|
--- a/dist/index.d.mts
|
|
+++ b/dist/index.d.mts
|
|
@@ -14,6 +14,7 @@ declare const mistralLanguageModelOptions: z.ZodObject<{
|
|
none: "none";
|
|
high: "high";
|
|
}>>;
|
|
+ promptCacheKey: z.ZodOptional<z.ZodString>;
|
|
}, z.core.$strip>;
|
|
type MistralLanguageModelOptions = z.infer<typeof mistralLanguageModelOptions>;
|
|
|
|
diff --git a/dist/index.d.ts b/dist/index.d.ts
|
|
index 1bde0b9f8cbe6771a52c1041095c9dddfe8e5b6c..0ca2ffb2a0c9327aed5ddcf0004500dc8b42569f 100644
|
|
--- a/dist/index.d.ts
|
|
+++ b/dist/index.d.ts
|
|
@@ -14,6 +14,7 @@ declare const mistralLanguageModelOptions: z.ZodObject<{
|
|
none: "none";
|
|
high: "high";
|
|
}>>;
|
|
+ promptCacheKey: z.ZodOptional<z.ZodString>;
|
|
}, z.core.$strip>;
|
|
type MistralLanguageModelOptions = z.infer<typeof mistralLanguageModelOptions>;
|
|
|
|
diff --git a/dist/index.js b/dist/index.js
|
|
index d3f904c12a1d582cc7b9e9a2d30273e1a8505b28..267f34e20ea392b7a85ad5259d72d50605a6f971 100644
|
|
--- a/dist/index.js
|
|
+++ b/dist/index.js
|
|
@@ -128,11 +128,14 @@ function convertToMistralChatMessages(prompt) {
|
|
}
|
|
case "assistant": {
|
|
let text = "";
|
|
+ const structuredContent = [];
|
|
+ let hasNativeReasoning = false;
|
|
const toolCalls = [];
|
|
for (const part of content) {
|
|
switch (part.type) {
|
|
case "text": {
|
|
text += part.text;
|
|
+ structuredContent.push({ type: "text", text: part.text });
|
|
break;
|
|
}
|
|
case "tool-call": {
|
|
@@ -148,6 +151,13 @@ function convertToMistralChatMessages(prompt) {
|
|
}
|
|
case "reasoning": {
|
|
text += part.text;
|
|
+ const native = part.providerOptions?.mistral?.thinking;
|
|
+ if (native?.type === "thinking") {
|
|
+ hasNativeReasoning = true;
|
|
+ structuredContent.push(native);
|
|
+ break;
|
|
+ }
|
|
+ structuredContent.push({ type: "text", text: part.text });
|
|
break;
|
|
}
|
|
default: {
|
|
@@ -159,7 +169,7 @@ function convertToMistralChatMessages(prompt) {
|
|
}
|
|
messages.push({
|
|
role: "assistant",
|
|
- content: text,
|
|
+ content: hasNativeReasoning ? structuredContent : text,
|
|
prefix: isLastMessage ? true : void 0,
|
|
tool_calls: toolCalls.length > 0 ? toolCalls : void 0
|
|
});
|
|
@@ -268,7 +278,8 @@ var mistralLanguageModelOptions = import_v4.z.object({
|
|
* - `'high'`: Enable reasoning
|
|
* - `'none'`: Disable reasoning
|
|
*/
|
|
- reasoningEffort: import_v4.z.enum(["high", "none"]).optional()
|
|
+ reasoningEffort: import_v4.z.enum(["high", "none"]).optional(),
|
|
+ promptCacheKey: import_v4.z.string().optional()
|
|
});
|
|
|
|
// src/mistral-error.ts
|
|
@@ -407,6 +418,7 @@ var MistralChatLanguageModel = class {
|
|
stop: stopSequences,
|
|
random_seed: seed,
|
|
reasoning_effort: options.reasoningEffort,
|
|
+ prompt_cache_key: options.promptCacheKey,
|
|
// response format:
|
|
response_format: (responseFormat == null ? void 0 : responseFormat.type) === "json" ? structuredOutputs && (responseFormat == null ? void 0 : responseFormat.schema) != null ? {
|
|
type: "json_schema",
|
|
@@ -465,9 +477,11 @@ var MistralChatLanguageModel = class {
|
|
for (const part of choice.message.content) {
|
|
if (part.type === "thinking") {
|
|
const reasoningText = extractReasoningContent(part.thinking);
|
|
- if (reasoningText.length > 0) {
|
|
- content.push({ type: "reasoning", text: reasoningText });
|
|
- }
|
|
+ content.push({
|
|
+ type: "reasoning",
|
|
+ text: reasoningText,
|
|
+ providerMetadata: { mistral: { thinking: part } }
|
|
+ });
|
|
} else if (part.type === "text") {
|
|
if (part.text.length > 0) {
|
|
content.push({ type: "text", text: part.text });
|
|
@@ -528,6 +542,7 @@ var MistralChatLanguageModel = class {
|
|
let isFirstChunk = true;
|
|
let activeText = false;
|
|
let activeReasoningId = null;
|
|
+ let activeThinking = null;
|
|
const generateId2 = this.generateId;
|
|
return {
|
|
stream: response.pipeThrough(
|
|
@@ -561,18 +576,19 @@ var MistralChatLanguageModel = class {
|
|
for (const part of delta.content) {
|
|
if (part.type === "thinking") {
|
|
const reasoningDelta = extractReasoningContent(part.thinking);
|
|
- if (reasoningDelta.length > 0) {
|
|
- if (activeReasoningId == null) {
|
|
- if (activeText) {
|
|
- controller.enqueue({ type: "text-end", id: "0" });
|
|
- activeText = false;
|
|
- }
|
|
- activeReasoningId = generateId2();
|
|
- controller.enqueue({
|
|
- type: "reasoning-start",
|
|
- id: activeReasoningId
|
|
- });
|
|
+ activeThinking = mergeThinking(activeThinking, part);
|
|
+ if (activeReasoningId == null) {
|
|
+ if (activeText) {
|
|
+ controller.enqueue({ type: "text-end", id: "0" });
|
|
+ activeText = false;
|
|
}
|
|
+ activeReasoningId = generateId2();
|
|
+ controller.enqueue({
|
|
+ type: "reasoning-start",
|
|
+ id: activeReasoningId
|
|
+ });
|
|
+ }
|
|
+ if (reasoningDelta.length > 0) {
|
|
controller.enqueue({
|
|
type: "reasoning-delta",
|
|
id: activeReasoningId,
|
|
@@ -587,9 +603,11 @@ var MistralChatLanguageModel = class {
|
|
if (activeReasoningId != null) {
|
|
controller.enqueue({
|
|
type: "reasoning-end",
|
|
- id: activeReasoningId
|
|
+ id: activeReasoningId,
|
|
+ providerMetadata: { mistral: { thinking: activeThinking } }
|
|
});
|
|
activeReasoningId = null;
|
|
+ activeThinking = null;
|
|
}
|
|
controller.enqueue({ type: "text-start", id: "0" });
|
|
activeText = true;
|
|
@@ -638,7 +656,8 @@ var MistralChatLanguageModel = class {
|
|
if (activeReasoningId != null) {
|
|
controller.enqueue({
|
|
type: "reasoning-end",
|
|
- id: activeReasoningId
|
|
+ id: activeReasoningId,
|
|
+ providerMetadata: { mistral: { thinking: activeThinking } }
|
|
});
|
|
}
|
|
if (activeText) {
|
|
@@ -660,6 +679,13 @@ var MistralChatLanguageModel = class {
|
|
function extractReasoningContent(thinking) {
|
|
return thinking.filter((chunk) => chunk.type === "text").map((chunk) => chunk.text).join("");
|
|
}
|
|
+function mergeThinking(current, next) {
|
|
+ if (current === null) return { ...next, thinking: [...next.thinking] };
|
|
+ current.thinking.push(...next.thinking);
|
|
+ if (next.closed !== void 0) current.closed = next.closed;
|
|
+ if (next.signature !== void 0) current.signature = next.signature;
|
|
+ return current;
|
|
+}
|
|
function extractTextContent(content) {
|
|
if (typeof content === "string") {
|
|
return content;
|
|
@@ -686,6 +712,30 @@ function extractTextContent(content) {
|
|
}
|
|
return textContent.length ? textContent.join("") : void 0;
|
|
}
|
|
+var mistralThinkingContentSchema = import_v43.z.discriminatedUnion("type", [
|
|
+ import_v43.z.object({
|
|
+ type: import_v43.z.literal("text"),
|
|
+ text: import_v43.z.string()
|
|
+ }),
|
|
+ import_v43.z.object({
|
|
+ type: import_v43.z.literal("tool_reference"),
|
|
+ tool: import_v43.z.string(),
|
|
+ title: import_v43.z.string(),
|
|
+ url: import_v43.z.string().nullish(),
|
|
+ favicon: import_v43.z.string().nullish(),
|
|
+ description: import_v43.z.string().nullish()
|
|
+ }),
|
|
+ import_v43.z.object({
|
|
+ type: import_v43.z.literal("reference"),
|
|
+ reference_ids: import_v43.z.array(import_v43.z.union([import_v43.z.string(), import_v43.z.number().int()]))
|
|
+ })
|
|
+]);
|
|
+var mistralThinkChunkSchema = import_v43.z.object({
|
|
+ type: import_v43.z.literal("thinking"),
|
|
+ thinking: import_v43.z.array(mistralThinkingContentSchema),
|
|
+ closed: import_v43.z.boolean().optional(),
|
|
+ signature: import_v43.z.string().nullish()
|
|
+});
|
|
var mistralContentSchema = import_v43.z.union([
|
|
import_v43.z.string(),
|
|
import_v43.z.array(
|
|
@@ -708,15 +758,7 @@ var mistralContentSchema = import_v43.z.union([
|
|
type: import_v43.z.literal("reference"),
|
|
reference_ids: import_v43.z.array(import_v43.z.union([import_v43.z.string(), import_v43.z.number()]))
|
|
}),
|
|
- import_v43.z.object({
|
|
- type: import_v43.z.literal("thinking"),
|
|
- thinking: import_v43.z.array(
|
|
- import_v43.z.object({
|
|
- type: import_v43.z.literal("text"),
|
|
- text: import_v43.z.string()
|
|
- })
|
|
- )
|
|
- })
|
|
+ mistralThinkChunkSchema
|
|
])
|
|
)
|
|
]).nullish();
|
|
diff --git a/dist/index.mjs b/dist/index.mjs
|
|
index d2eff622c1b84a96bdeb4012cb0206a33012a04d..3bff11ddd6136ada45809568828cbc8f2493a42a 100644
|
|
--- a/dist/index.mjs
|
|
+++ b/dist/index.mjs
|
|
@@ -116,11 +116,14 @@ function convertToMistralChatMessages(prompt) {
|
|
}
|
|
case "assistant": {
|
|
let text = "";
|
|
+ const structuredContent = [];
|
|
+ let hasNativeReasoning = false;
|
|
const toolCalls = [];
|
|
for (const part of content) {
|
|
switch (part.type) {
|
|
case "text": {
|
|
text += part.text;
|
|
+ structuredContent.push({ type: "text", text: part.text });
|
|
break;
|
|
}
|
|
case "tool-call": {
|
|
@@ -136,6 +139,13 @@ function convertToMistralChatMessages(prompt) {
|
|
}
|
|
case "reasoning": {
|
|
text += part.text;
|
|
+ const native = part.providerOptions?.mistral?.thinking;
|
|
+ if (native?.type === "thinking") {
|
|
+ hasNativeReasoning = true;
|
|
+ structuredContent.push(native);
|
|
+ break;
|
|
+ }
|
|
+ structuredContent.push({ type: "text", text: part.text });
|
|
break;
|
|
}
|
|
default: {
|
|
@@ -147,7 +157,7 @@ function convertToMistralChatMessages(prompt) {
|
|
}
|
|
messages.push({
|
|
role: "assistant",
|
|
- content: text,
|
|
+ content: hasNativeReasoning ? structuredContent : text,
|
|
prefix: isLastMessage ? true : void 0,
|
|
tool_calls: toolCalls.length > 0 ? toolCalls : void 0
|
|
});
|
|
@@ -256,7 +266,8 @@ var mistralLanguageModelOptions = z.object({
|
|
* - `'high'`: Enable reasoning
|
|
* - `'none'`: Disable reasoning
|
|
*/
|
|
- reasoningEffort: z.enum(["high", "none"]).optional()
|
|
+ reasoningEffort: z.enum(["high", "none"]).optional(),
|
|
+ promptCacheKey: z.string().optional()
|
|
});
|
|
|
|
// src/mistral-error.ts
|
|
@@ -397,6 +408,7 @@ var MistralChatLanguageModel = class {
|
|
stop: stopSequences,
|
|
random_seed: seed,
|
|
reasoning_effort: options.reasoningEffort,
|
|
+ prompt_cache_key: options.promptCacheKey,
|
|
// response format:
|
|
response_format: (responseFormat == null ? void 0 : responseFormat.type) === "json" ? structuredOutputs && (responseFormat == null ? void 0 : responseFormat.schema) != null ? {
|
|
type: "json_schema",
|
|
@@ -455,9 +467,11 @@ var MistralChatLanguageModel = class {
|
|
for (const part of choice.message.content) {
|
|
if (part.type === "thinking") {
|
|
const reasoningText = extractReasoningContent(part.thinking);
|
|
- if (reasoningText.length > 0) {
|
|
- content.push({ type: "reasoning", text: reasoningText });
|
|
- }
|
|
+ content.push({
|
|
+ type: "reasoning",
|
|
+ text: reasoningText,
|
|
+ providerMetadata: { mistral: { thinking: part } }
|
|
+ });
|
|
} else if (part.type === "text") {
|
|
if (part.text.length > 0) {
|
|
content.push({ type: "text", text: part.text });
|
|
@@ -518,6 +532,7 @@ var MistralChatLanguageModel = class {
|
|
let isFirstChunk = true;
|
|
let activeText = false;
|
|
let activeReasoningId = null;
|
|
+ let activeThinking = null;
|
|
const generateId2 = this.generateId;
|
|
return {
|
|
stream: response.pipeThrough(
|
|
@@ -551,18 +566,19 @@ var MistralChatLanguageModel = class {
|
|
for (const part of delta.content) {
|
|
if (part.type === "thinking") {
|
|
const reasoningDelta = extractReasoningContent(part.thinking);
|
|
- if (reasoningDelta.length > 0) {
|
|
- if (activeReasoningId == null) {
|
|
- if (activeText) {
|
|
- controller.enqueue({ type: "text-end", id: "0" });
|
|
- activeText = false;
|
|
- }
|
|
- activeReasoningId = generateId2();
|
|
- controller.enqueue({
|
|
- type: "reasoning-start",
|
|
- id: activeReasoningId
|
|
- });
|
|
+ activeThinking = mergeThinking(activeThinking, part);
|
|
+ if (activeReasoningId == null) {
|
|
+ if (activeText) {
|
|
+ controller.enqueue({ type: "text-end", id: "0" });
|
|
+ activeText = false;
|
|
}
|
|
+ activeReasoningId = generateId2();
|
|
+ controller.enqueue({
|
|
+ type: "reasoning-start",
|
|
+ id: activeReasoningId
|
|
+ });
|
|
+ }
|
|
+ if (reasoningDelta.length > 0) {
|
|
controller.enqueue({
|
|
type: "reasoning-delta",
|
|
id: activeReasoningId,
|
|
@@ -577,9 +593,11 @@ var MistralChatLanguageModel = class {
|
|
if (activeReasoningId != null) {
|
|
controller.enqueue({
|
|
type: "reasoning-end",
|
|
- id: activeReasoningId
|
|
+ id: activeReasoningId,
|
|
+ providerMetadata: { mistral: { thinking: activeThinking } }
|
|
});
|
|
activeReasoningId = null;
|
|
+ activeThinking = null;
|
|
}
|
|
controller.enqueue({ type: "text-start", id: "0" });
|
|
activeText = true;
|
|
@@ -628,7 +646,8 @@ var MistralChatLanguageModel = class {
|
|
if (activeReasoningId != null) {
|
|
controller.enqueue({
|
|
type: "reasoning-end",
|
|
- id: activeReasoningId
|
|
+ id: activeReasoningId,
|
|
+ providerMetadata: { mistral: { thinking: activeThinking } }
|
|
});
|
|
}
|
|
if (activeText) {
|
|
@@ -650,6 +669,13 @@ var MistralChatLanguageModel = class {
|
|
function extractReasoningContent(thinking) {
|
|
return thinking.filter((chunk) => chunk.type === "text").map((chunk) => chunk.text).join("");
|
|
}
|
|
+function mergeThinking(current, next) {
|
|
+ if (current === null) return { ...next, thinking: [...next.thinking] };
|
|
+ current.thinking.push(...next.thinking);
|
|
+ if (next.closed !== void 0) current.closed = next.closed;
|
|
+ if (next.signature !== void 0) current.signature = next.signature;
|
|
+ return current;
|
|
+}
|
|
function extractTextContent(content) {
|
|
if (typeof content === "string") {
|
|
return content;
|
|
@@ -676,6 +702,30 @@ function extractTextContent(content) {
|
|
}
|
|
return textContent.length ? textContent.join("") : void 0;
|
|
}
|
|
+var mistralThinkingContentSchema = z3.discriminatedUnion("type", [
|
|
+ z3.object({
|
|
+ type: z3.literal("text"),
|
|
+ text: z3.string()
|
|
+ }),
|
|
+ z3.object({
|
|
+ type: z3.literal("tool_reference"),
|
|
+ tool: z3.string(),
|
|
+ title: z3.string(),
|
|
+ url: z3.string().nullish(),
|
|
+ favicon: z3.string().nullish(),
|
|
+ description: z3.string().nullish()
|
|
+ }),
|
|
+ z3.object({
|
|
+ type: z3.literal("reference"),
|
|
+ reference_ids: z3.array(z3.union([z3.string(), z3.number().int()]))
|
|
+ })
|
|
+]);
|
|
+var mistralThinkChunkSchema = z3.object({
|
|
+ type: z3.literal("thinking"),
|
|
+ thinking: z3.array(mistralThinkingContentSchema),
|
|
+ closed: z3.boolean().optional(),
|
|
+ signature: z3.string().nullish()
|
|
+});
|
|
var mistralContentSchema = z3.union([
|
|
z3.string(),
|
|
z3.array(
|
|
@@ -698,15 +748,7 @@ var mistralContentSchema = z3.union([
|
|
type: z3.literal("reference"),
|
|
reference_ids: z3.array(z3.union([z3.string(), z3.number()]))
|
|
}),
|
|
- z3.object({
|
|
- type: z3.literal("thinking"),
|
|
- thinking: z3.array(
|
|
- z3.object({
|
|
- type: z3.literal("text"),
|
|
- text: z3.string()
|
|
- })
|
|
- )
|
|
- })
|
|
+ mistralThinkChunkSchema
|
|
])
|
|
)
|
|
]).nullish();
|
|
diff --git a/src/convert-to-mistral-chat-messages.ts b/src/convert-to-mistral-chat-messages.ts
|
|
index 3c6914f8da615d7517bc43dd56198298d0a50247..8cd6f4c7577f746ef41e8a0aee682234c473667a 100644
|
|
--- a/src/convert-to-mistral-chat-messages.ts
|
|
+++ b/src/convert-to-mistral-chat-messages.ts
|
|
@@ -3,7 +3,11 @@ import {
|
|
type LanguageModelV3DataContent,
|
|
type LanguageModelV3Prompt,
|
|
} from '@ai-sdk/provider';
|
|
-import type { MistralPrompt } from './mistral-chat-prompt';
|
|
+import type {
|
|
+ MistralAssistantMessageContent,
|
|
+ MistralPrompt,
|
|
+ MistralThinkChunk,
|
|
+} from './mistral-chat-prompt';
|
|
import { convertToBase64 } from '@ai-sdk/provider-utils';
|
|
|
|
function formatFileUrl({
|
|
@@ -76,6 +80,8 @@ export function convertToMistralChatMessages(
|
|
|
|
case 'assistant': {
|
|
let text = '';
|
|
+ const structuredContent: Array<MistralAssistantMessageContent> = [];
|
|
+ let hasNativeReasoning = false;
|
|
const toolCalls: Array<{
|
|
id: string;
|
|
type: 'function';
|
|
@@ -86,6 +92,7 @@ export function convertToMistralChatMessages(
|
|
switch (part.type) {
|
|
case 'text': {
|
|
text += part.text;
|
|
+ structuredContent.push({ type: 'text', text: part.text });
|
|
break;
|
|
}
|
|
case 'tool-call': {
|
|
@@ -101,6 +108,14 @@ export function convertToMistralChatMessages(
|
|
}
|
|
case 'reasoning': {
|
|
text += part.text;
|
|
+ const native = part.providerOptions?.mistral
|
|
+ ?.thinking as MistralThinkChunk | undefined;
|
|
+ if (native?.type === 'thinking') {
|
|
+ hasNativeReasoning = true;
|
|
+ structuredContent.push(native);
|
|
+ break;
|
|
+ }
|
|
+ structuredContent.push({ type: 'text', text: part.text });
|
|
break;
|
|
}
|
|
default: {
|
|
@@ -113,7 +128,7 @@ export function convertToMistralChatMessages(
|
|
|
|
messages.push({
|
|
role: 'assistant',
|
|
- content: text,
|
|
+ content: hasNativeReasoning ? structuredContent : text,
|
|
prefix: isLastMessage ? true : undefined,
|
|
tool_calls: toolCalls.length > 0 ? toolCalls : undefined,
|
|
});
|
|
diff --git a/src/mistral-chat-language-model.ts b/src/mistral-chat-language-model.ts
|
|
index 7e4a7ab552f1b41b7074e1b3cada8a51d791268d..847d26f9dfe03572a969a122f8c96b8bbfda8066 100644
|
|
--- a/src/mistral-chat-language-model.ts
|
|
+++ b/src/mistral-chat-language-model.ts
|
|
@@ -122,6 +122,7 @@ export class MistralChatLanguageModel implements LanguageModelV3 {
|
|
stop: stopSequences,
|
|
random_seed: seed,
|
|
reasoning_effort: options.reasoningEffort,
|
|
+ prompt_cache_key: options.promptCacheKey,
|
|
|
|
// response format:
|
|
response_format:
|
|
@@ -201,9 +202,11 @@ export class MistralChatLanguageModel implements LanguageModelV3 {
|
|
for (const part of choice.message.content) {
|
|
if (part.type === 'thinking') {
|
|
const reasoningText = extractReasoningContent(part.thinking);
|
|
- if (reasoningText.length > 0) {
|
|
- content.push({ type: 'reasoning', text: reasoningText });
|
|
- }
|
|
+ content.push({
|
|
+ type: 'reasoning',
|
|
+ text: reasoningText,
|
|
+ providerMetadata: { mistral: { thinking: part } },
|
|
+ });
|
|
} else if (part.type === 'text') {
|
|
if (part.text.length > 0) {
|
|
content.push({ type: 'text', text: part.text });
|
|
@@ -278,6 +281,7 @@ export class MistralChatLanguageModel implements LanguageModelV3 {
|
|
let isFirstChunk = true;
|
|
let activeText = false;
|
|
let activeReasoningId: string | null = null;
|
|
+ let activeThinking: z.infer<typeof mistralThinkChunkSchema> | null = null;
|
|
|
|
const generateId = this.generateId;
|
|
|
|
@@ -326,20 +330,21 @@ export class MistralChatLanguageModel implements LanguageModelV3 {
|
|
for (const part of delta.content) {
|
|
if (part.type === 'thinking') {
|
|
const reasoningDelta = extractReasoningContent(part.thinking);
|
|
- if (reasoningDelta.length > 0) {
|
|
- if (activeReasoningId == null) {
|
|
- // end any active text before starting reasoning
|
|
- if (activeText) {
|
|
- controller.enqueue({ type: 'text-end', id: '0' });
|
|
- activeText = false;
|
|
- }
|
|
-
|
|
- activeReasoningId = generateId();
|
|
- controller.enqueue({
|
|
- type: 'reasoning-start',
|
|
- id: activeReasoningId,
|
|
- });
|
|
+ activeThinking = mergeThinking(activeThinking, part);
|
|
+ if (activeReasoningId == null) {
|
|
+ // end any active text before starting reasoning
|
|
+ if (activeText) {
|
|
+ controller.enqueue({ type: 'text-end', id: '0' });
|
|
+ activeText = false;
|
|
}
|
|
+
|
|
+ activeReasoningId = generateId();
|
|
+ controller.enqueue({
|
|
+ type: 'reasoning-start',
|
|
+ id: activeReasoningId,
|
|
+ });
|
|
+ }
|
|
+ if (reasoningDelta.length > 0) {
|
|
controller.enqueue({
|
|
type: 'reasoning-delta',
|
|
id: activeReasoningId,
|
|
@@ -357,8 +362,12 @@ export class MistralChatLanguageModel implements LanguageModelV3 {
|
|
controller.enqueue({
|
|
type: 'reasoning-end',
|
|
id: activeReasoningId,
|
|
+ providerMetadata: {
|
|
+ mistral: { thinking: activeThinking },
|
|
+ },
|
|
});
|
|
activeReasoningId = null;
|
|
+ activeThinking = null;
|
|
}
|
|
controller.enqueue({ type: 'text-start', id: '0' });
|
|
activeText = true;
|
|
@@ -416,6 +425,9 @@ export class MistralChatLanguageModel implements LanguageModelV3 {
|
|
controller.enqueue({
|
|
type: 'reasoning-end',
|
|
id: activeReasoningId,
|
|
+ providerMetadata: {
|
|
+ mistral: { thinking: activeThinking },
|
|
+ },
|
|
});
|
|
}
|
|
if (activeText) {
|
|
@@ -437,7 +449,7 @@ export class MistralChatLanguageModel implements LanguageModelV3 {
|
|
}
|
|
|
|
function extractReasoningContent(
|
|
- thinking: Array<{ type: string; text: string }>,
|
|
+ thinking: Array<z.infer<typeof mistralThinkingContentSchema>>,
|
|
) {
|
|
return thinking
|
|
.filter(chunk => chunk.type === 'text')
|
|
@@ -445,6 +457,17 @@ function extractReasoningContent(
|
|
.join('');
|
|
}
|
|
|
|
+function mergeThinking(
|
|
+ current: z.infer<typeof mistralThinkChunkSchema> | null,
|
|
+ next: z.infer<typeof mistralThinkChunkSchema>,
|
|
+) {
|
|
+ if (current === null) return { ...next, thinking: [...next.thinking] };
|
|
+ current.thinking.push(...next.thinking);
|
|
+ if (next.closed !== undefined) current.closed = next.closed;
|
|
+ if (next.signature !== undefined) current.signature = next.signature;
|
|
+ return current;
|
|
+}
|
|
+
|
|
function extractTextContent(content: z.infer<typeof mistralContentSchema>) {
|
|
if (typeof content === 'string') {
|
|
return content;
|
|
@@ -478,6 +501,32 @@ function extractTextContent(content: z.infer<typeof mistralContentSchema>) {
|
|
return textContent.length ? textContent.join('') : undefined;
|
|
}
|
|
|
|
+const mistralThinkingContentSchema = z.discriminatedUnion('type', [
|
|
+ z.object({
|
|
+ type: z.literal('text'),
|
|
+ text: z.string(),
|
|
+ }),
|
|
+ z.object({
|
|
+ type: z.literal('tool_reference'),
|
|
+ tool: z.string(),
|
|
+ title: z.string(),
|
|
+ url: z.string().nullish(),
|
|
+ favicon: z.string().nullish(),
|
|
+ description: z.string().nullish(),
|
|
+ }),
|
|
+ z.object({
|
|
+ type: z.literal('reference'),
|
|
+ reference_ids: z.array(z.union([z.string(), z.number().int()])),
|
|
+ }),
|
|
+]);
|
|
+
|
|
+const mistralThinkChunkSchema = z.object({
|
|
+ type: z.literal('thinking'),
|
|
+ thinking: z.array(mistralThinkingContentSchema),
|
|
+ closed: z.boolean().optional(),
|
|
+ signature: z.string().nullish(),
|
|
+});
|
|
+
|
|
const mistralContentSchema = z
|
|
.union([
|
|
z.string(),
|
|
@@ -501,15 +550,7 @@ const mistralContentSchema = z
|
|
type: z.literal('reference'),
|
|
reference_ids: z.array(z.union([z.string(), z.number()])),
|
|
}),
|
|
- z.object({
|
|
- type: z.literal('thinking'),
|
|
- thinking: z.array(
|
|
- z.object({
|
|
- type: z.literal('text'),
|
|
- text: z.string(),
|
|
- }),
|
|
- ),
|
|
- }),
|
|
+ mistralThinkChunkSchema,
|
|
]),
|
|
),
|
|
])
|
|
diff --git a/src/mistral-chat-options.ts b/src/mistral-chat-options.ts
|
|
index 54b29c08517d348995b6ca093b11160e453d5c8b..de30c3e7d924889339e38b1067cb26e9ada05d11 100644
|
|
--- a/src/mistral-chat-options.ts
|
|
+++ b/src/mistral-chat-options.ts
|
|
@@ -64,6 +64,11 @@ export const mistralLanguageModelOptions = z.object({
|
|
* - `'none'`: Disable reasoning
|
|
*/
|
|
reasoningEffort: z.enum(['high', 'none']).optional(),
|
|
+
|
|
+ /**
|
|
+ * A stable identifier used to route requests with shared prompt prefixes.
|
|
+ */
|
|
+ promptCacheKey: z.string().optional(),
|
|
});
|
|
|
|
export type MistralLanguageModelOptions = z.infer<
|
|
diff --git a/src/mistral-chat-prompt.ts b/src/mistral-chat-prompt.ts
|
|
index 13f1dced55ac4be084128127a57fbdd58115bc28..172b11dde3dd326c2f3befd99237474ed8c79285 100644
|
|
--- a/src/mistral-chat-prompt.ts
|
|
+++ b/src/mistral-chat-prompt.ts
|
|
@@ -23,7 +23,7 @@ export type MistralUserMessageContent =
|
|
|
|
export interface MistralAssistantMessage {
|
|
role: 'assistant';
|
|
- content: string;
|
|
+ content: string | Array<MistralAssistantMessageContent>;
|
|
prefix?: boolean;
|
|
tool_calls?: Array<{
|
|
id: string;
|
|
@@ -32,6 +32,29 @@ export interface MistralAssistantMessage {
|
|
}>;
|
|
}
|
|
|
|
+export type MistralAssistantMessageContent =
|
|
+ | { type: 'text'; text: string }
|
|
+ | MistralThinkChunk;
|
|
+
|
|
+export type MistralThinkChunk = {
|
|
+ type: 'thinking';
|
|
+ thinking: Array<MistralThinkingContent>;
|
|
+ closed?: boolean;
|
|
+ signature?: string | null;
|
|
+};
|
|
+
|
|
+export type MistralThinkingContent =
|
|
+ | { type: 'text'; text: string }
|
|
+ | {
|
|
+ type: 'tool_reference';
|
|
+ tool: string;
|
|
+ title: string;
|
|
+ url?: string | null;
|
|
+ favicon?: string | null;
|
|
+ description?: string | null;
|
|
+ }
|
|
+ | { type: 'reference'; reference_ids: Array<string | number> };
|
|
+
|
|
export interface MistralToolMessage {
|
|
role: 'tool';
|
|
name: string;
|