mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-26 07:15:14 +00:00
fix: replay Xiaomi Anthropic reasoning blocks
This commit is contained in:
@@ -33,6 +33,7 @@ Docs: https://docs.openclaw.ai
|
||||
|
||||
### Fixes
|
||||
|
||||
- Providers/Xiaomi: replay MiMo Anthropic-compatible `reasoning_content` as provider-required thinking blocks even when OpenClaw thinking is disabled, fixing follow-up tool turns for `mimo-v2-flash`. Fixes #83407. Thanks @Xgenious7.
|
||||
- Agents/exec approvals: forward approval-runtime credentials on agent-owned Gateway approval calls so approved async commands complete through the existing runtime path instead of stalling on unauthenticated follow-up calls. Thanks @IWhatsskill, @Patrick-Erichsen, and @jesse-merhi.
|
||||
- Gateway/skills: preflight remote macOS skill-bin refreshes with a WebSocket connectivity check so stale node sessions skip quickly instead of logging slow `system.which` timeout warnings.
|
||||
- GitHub Copilot: drop unsafe native Responses reasoning replay items with non-replayable IDs before dispatch, preventing affected Copilot sessions from failing with `invalid_request_body`. Fixes #83220. Thanks @galiniliev.
|
||||
|
||||
@@ -1027,7 +1027,7 @@ describe("anthropic transport stream", () => {
|
||||
]);
|
||||
});
|
||||
|
||||
it("backfills empty reasoning_content for compatible Anthropic tool-use replays", async () => {
|
||||
it("backfills empty reasoning_content thinking blocks for compatible Anthropic tool-use replays", async () => {
|
||||
await runTransportStream(
|
||||
makeAnthropicTransportModel({
|
||||
id: "mimo-v2.6-pro",
|
||||
@@ -1066,13 +1066,69 @@ describe("anthropic transport stream", () => {
|
||||
latestAnthropicRequest().payload.messages,
|
||||
(record) => record.role === "assistant",
|
||||
);
|
||||
expect(assistantMessage.reasoning_content).toBe("");
|
||||
expect(assistantMessage).not.toHaveProperty("reasoning_content");
|
||||
expect(assistantMessage.content).toEqual([
|
||||
{
|
||||
type: "thinking",
|
||||
thinking: "",
|
||||
signature: "reasoning_content",
|
||||
},
|
||||
{ type: "tool_use", id: "call_1", name: "lookup", input: {} },
|
||||
]);
|
||||
});
|
||||
|
||||
it("backfills empty reasoning_content for compatible Anthropic text replays", async () => {
|
||||
it("backfills MiMo v2-flash tool-use replay when OpenClaw thinking is off", async () => {
|
||||
await runTransportStream(
|
||||
makeAnthropicTransportModel({
|
||||
id: "mimo-v2-flash",
|
||||
name: "MiMo V2 Flash",
|
||||
provider: "xiaomi",
|
||||
baseUrl: "https://api.xiaomimimo.com/anthropic",
|
||||
reasoning: false,
|
||||
}),
|
||||
{
|
||||
messages: [
|
||||
{ role: "user", content: "look this up" },
|
||||
{
|
||||
role: "assistant",
|
||||
provider: "xiaomi",
|
||||
api: "anthropic-messages",
|
||||
model: "mimo-v2-flash",
|
||||
stopReason: "toolUse",
|
||||
timestamp: 0,
|
||||
content: [{ type: "toolCall", id: "call_1", name: "lookup", arguments: {} }],
|
||||
},
|
||||
{
|
||||
role: "toolResult",
|
||||
toolCallId: "call_1",
|
||||
content: [{ type: "text", text: "found" }],
|
||||
isError: false,
|
||||
},
|
||||
{ role: "user", content: "continue" },
|
||||
],
|
||||
} as AnthropicStreamContext,
|
||||
{
|
||||
apiKey: "sk-xiaomi-test",
|
||||
} as AnthropicStreamOptions,
|
||||
);
|
||||
|
||||
const assistantMessage = findRecord(
|
||||
latestAnthropicRequest().payload.messages,
|
||||
(record) => record.role === "assistant",
|
||||
);
|
||||
expect(latestAnthropicRequest().payload).not.toHaveProperty("thinking");
|
||||
expect(assistantMessage).not.toHaveProperty("reasoning_content");
|
||||
expect(assistantMessage.content).toEqual([
|
||||
{
|
||||
type: "thinking",
|
||||
thinking: "",
|
||||
signature: "reasoning_content",
|
||||
},
|
||||
{ type: "tool_use", id: "call_1", name: "lookup", input: {} },
|
||||
]);
|
||||
});
|
||||
|
||||
it("backfills empty reasoning_content thinking blocks for compatible Anthropic text replays", async () => {
|
||||
await runTransportStream(
|
||||
makeAnthropicTransportModel({
|
||||
id: "mimo-v2.6-pro",
|
||||
@@ -1105,8 +1161,15 @@ describe("anthropic transport stream", () => {
|
||||
latestAnthropicRequest().payload.messages,
|
||||
(record) => record.role === "assistant",
|
||||
);
|
||||
expect(assistantMessage.reasoning_content).toBe("");
|
||||
expect(assistantMessage.content).toEqual([{ type: "text", text: "Hello!" }]);
|
||||
expect(assistantMessage).not.toHaveProperty("reasoning_content");
|
||||
expect(assistantMessage.content).toEqual([
|
||||
{
|
||||
type: "thinking",
|
||||
thinking: "",
|
||||
signature: "reasoning_content",
|
||||
},
|
||||
{ type: "text", text: "Hello!" },
|
||||
]);
|
||||
});
|
||||
|
||||
it("does not backfill reasoning_content for generic Anthropic-compatible tool-use replays", async () => {
|
||||
@@ -1154,7 +1217,7 @@ describe("anthropic transport stream", () => {
|
||||
]);
|
||||
});
|
||||
|
||||
it("does not replay reasoning_content when compatible Anthropic thinking is disabled", async () => {
|
||||
it("replays observed reasoning_content for compatible Anthropic routes when thinking is disabled", async () => {
|
||||
await runTransportStream(
|
||||
makeAnthropicTransportModel({
|
||||
id: "mimo-v2.6-pro",
|
||||
@@ -1194,8 +1257,15 @@ describe("anthropic transport stream", () => {
|
||||
(record) => record.role === "assistant",
|
||||
);
|
||||
expect(latestAnthropicRequest().payload.thinking).toEqual({ type: "disabled" });
|
||||
expect(assistantMessage).not.toHaveProperty("reasoning_content");
|
||||
expect(assistantMessage.content).toEqual([{ type: "text", text: "Hello!" }]);
|
||||
expect(assistantMessage.reasoning_content).toBe("Need to answer politely.");
|
||||
expect(assistantMessage.content).toEqual([
|
||||
{
|
||||
type: "thinking",
|
||||
thinking: "Need to answer politely.",
|
||||
signature: "reasoning_content",
|
||||
},
|
||||
{ type: "text", text: "Hello!" },
|
||||
]);
|
||||
});
|
||||
|
||||
it("does not replay synthetic reasoning_content to native Anthropic models", async () => {
|
||||
|
||||
@@ -411,7 +411,11 @@ function convertAnthropicMessages(
|
||||
if (reasoningContent.length > 0) {
|
||||
assistantMsg.reasoning_content = reasoningContent.join("\n");
|
||||
} else if (allowReasoningContentReplay) {
|
||||
assistantMsg.reasoning_content = "";
|
||||
blocks.unshift({
|
||||
type: "thinking",
|
||||
thinking: "",
|
||||
signature: "reasoning_content",
|
||||
});
|
||||
}
|
||||
params.push(assistantMsg);
|
||||
}
|
||||
@@ -788,8 +792,7 @@ function buildAnthropicParams(
|
||||
model: model.id,
|
||||
messages: ensureNonEmptyAnthropicMessages(
|
||||
convertAnthropicMessages(context.messages, model, isOAuthToken, {
|
||||
allowReasoningContentReplay:
|
||||
supportsReasoningContentReplay(model) && options?.thinkingEnabled === true,
|
||||
allowReasoningContentReplay: supportsReasoningContentReplay(model),
|
||||
}),
|
||||
),
|
||||
max_tokens: maxTokens,
|
||||
@@ -946,8 +949,7 @@ export function createAnthropicMessagesTransportStreamFn(): StreamFn {
|
||||
);
|
||||
stream.push({ type: "start", partial: output as never });
|
||||
const blocks = output.content;
|
||||
const allowReasoningContentReplay =
|
||||
supportsReasoningContentReplay(model) && transportOptions.thinkingEnabled === true;
|
||||
const allowReasoningContentReplay = supportsReasoningContentReplay(model);
|
||||
const reasoningContentThinkingBlocks = new Map<number, number>();
|
||||
const reasoningContentTextBlocks = new Map<number, number>();
|
||||
const eventIndexKey = (eventIndex: unknown) =>
|
||||
|
||||
Reference in New Issue
Block a user