test(core): cover Anthropic partial recovery

This commit is contained in:
kitlangton
2026-08-22 01:52:57 +00:00
committed by Hona
parent aa2e22b62d
commit d44b793a91
3 changed files with 193 additions and 0 deletions
@@ -0,0 +1,35 @@
{
"version": 1,
"metadata": {
"tags": [
"prefix:anthropic-recovery",
"provider:anthropic",
"protocol:anthropic-messages",
"tool",
"recovery"
],
"name": "anthropic-recovery/accepts-a-synthetic-settled-tool-call-followed-by-continuation",
"recordedAt": "2026-08-22T01:52:23.916Z"
},
"interactions": [
{
"transport": "http",
"request": {
"method": "POST",
"url": "https://opencode.ai/zen/v1/messages",
"headers": {
"anthropic-version": "2023-06-01",
"content-type": "application/json"
},
"body": "{\"model\":\"claude-sonnet-4-6\",\"messages\":[{\"role\":\"user\",\"content\":[{\"type\":\"text\",\"text\":\"Look up the weather in Paris.\"}]},{\"role\":\"assistant\",\"content\":[{\"type\":\"text\",\"text\":\"I should use the weather tool.\"},{\"type\":\"tool_use\",\"id\":\"call_weather\",\"name\":\"lookup_weather\",\"input\":{\"city\":\"Paris\"}}]},{\"role\":\"user\",\"content\":[{\"type\":\"tool_result\",\"tool_use_id\":\"call_weather\",\"content\":\"sunny\"}]},{\"role\":\"user\",\"content\":[{\"type\":\"text\",\"text\":\"The previous response was interrupted. Continue from where you left off without repeating completed content.\",\"cache_control\":{\"type\":\"ephemeral\"}}]}],\"tools\":[{\"name\":\"lookup_weather\",\"description\":\"Return benign weather data for a city.\",\"input_schema\":{\"type\":\"object\",\"properties\":{\"city\":{\"type\":\"string\"}},\"required\":[\"city\"]},\"cache_control\":{\"type\":\"ephemeral\"}}],\"tool_choice\":{\"type\":\"none\"},\"stream\":true,\"max_tokens\":128,\"temperature\":0}"
},
"response": {
"status": 200,
"headers": {
"content-type": "text/event-stream; charset=utf-8"
},
"body": "event: message_start\ndata: {\"type\":\"message_start\",\"message\":{\"model\":\"claude-sonnet-4-6\",\"id\":\"msg_011CeGx6WkAQP5ULyRRbRwRw\",\"type\":\"message\",\"role\":\"assistant\",\"content\":[],\"stop_reason\":null,\"stop_sequence\":null,\"stop_details\":null,\"usage\":{\"input_tokens\":664,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"cache_creation\":{\"ephemeral_5m_input_tokens\":0,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":1,\"service_tier\":\"standard\",\"inference_geo\":\"global\"}} }\n\nevent: content_block_start\ndata: {\"type\":\"content_block_start\",\"index\":0,\"content_block\":{\"type\":\"text\",\"text\":\"\"} }\n\nevent: ping\ndata: {\"type\": \"ping\"}\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"The\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" weather in Paris is currently **sunny**! It looks like a great day to enjoy the city. ☀️\"} }\n\nevent: content_block_stop\ndata: {\"type\":\"content_block_stop\",\"index\":0 }\n\nevent: message_delta\ndata: {\"type\":\"message_delta\",\"delta\":{\"stop_reason\":\"end_turn\",\"stop_sequence\":null,\"stop_details\":null},\"usage\":{\"input_tokens\":664,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"output_tokens\":28} }\n\nevent: message_stop\ndata: {\"type\":\"message_stop\" }\n\nevent: ping\ndata: {\"type\":\"ping\",\"cost\":\"0\"}\n\n"
}
}
]
}
@@ -0,0 +1,59 @@
import { describe, expect } from "bun:test"
import { Effect } from "effect"
import { LLM, Message, ToolCallPart, ToolDefinition } from "../../src/index.js"
import { configure } from "../../src/providers/anthropic.js"
import { LLMClient } from "../../src/route.js"
import { recordedTests } from "../recorded-test.js"
const model = configure({
apiKey: process.env.ZEN_TEST_API_KEY ?? "fixture",
baseURL: "https://opencode.ai/zen/v1",
}).model("claude-sonnet-4-6")
const continuation =
"The previous response was interrupted. Continue from where you left off without repeating completed content."
const lookup = ToolDefinition.make({
name: "lookup_weather",
description: "Return benign weather data for a city.",
inputSchema: {
type: "object",
properties: { city: { type: "string" } },
required: ["city"],
},
})
const recorded = recordedTests({
prefix: "anthropic-recovery",
provider: "anthropic",
protocol: "anthropic-messages",
requires: ["ZEN_TEST_API_KEY"],
options: { redact: { allowRequestHeaders: ["anthropic-version"] } },
})
describe("Anthropic interrupted recovery recorded", () => {
recorded.effect.with(
"accepts a synthetic settled tool call followed by continuation",
{ tags: ["tool", "recovery"] },
() =>
Effect.gen(function* () {
const response = yield* LLMClient.generate(
LLM.request({
id: "recorded_anthropic_interrupted_tool_recovery",
model,
messages: [
Message.user("Look up the weather in Paris."),
Message.assistant([
{ type: "text", text: "I should use the weather tool." },
ToolCallPart.make({ id: "call_weather", name: "lookup_weather", input: { city: "Paris" } }),
]),
Message.tool({ id: "call_weather", name: "lookup_weather", result: "sunny", resultType: "text" }),
Message.user(continuation),
],
tools: [lookup],
toolChoice: "none",
generation: { maxTokens: 128, temperature: 0 },
}),
)
expect(response.text.trim().length).toBeGreaterThan(0)
}),
)
})
@@ -0,0 +1,99 @@
import { expect, test } from "bun:test"
import { LLM } from "@opencode-ai/ai"
import { configure } from "@opencode-ai/ai/providers/anthropic"
import { compileRequest } from "@opencode-ai/ai/route/client"
import { Agent } from "@opencode-ai/core/agent"
import { Model } from "@opencode-ai/core/model"
import { Provider } from "@opencode-ai/core/provider"
import { SessionMessage } from "@opencode-ai/core/session/message"
import { toLLMMessages } from "@opencode-ai/core/session/runner/to-llm-message"
import { DateTime, Effect } from "effect"
const created = DateTime.makeUnsafe(0)
const anthropic = Model.Ref.make({
id: Model.ID.make("claude-sonnet-4-6"),
providerID: Provider.ID.make("anthropic"),
})
const continuation =
"The previous response was interrupted. Continue from where you left off without repeating completed content."
test("failed Anthropic tool history replays through the public request boundary", async () => {
const messages = toLLMMessages(
[
SessionMessage.User.make({
id: SessionMessage.ID.make("msg_user"),
type: "user",
text: "Look up the weather.",
time: { created },
}),
SessionMessage.Assistant.make({
id: SessionMessage.ID.make("msg_failed"),
type: "assistant",
agent: Agent.defaultID,
model: anthropic,
content: [
SessionMessage.AssistantReasoning.make({
type: "reasoning",
text: "I should use the weather tool.",
state: { signature: "signed-reasoning" },
}),
SessionMessage.AssistantTool.make({
type: "tool",
id: "call_weather",
name: "lookup_weather",
executed: false,
state: SessionMessage.ToolStateCompleted.make({
status: "completed",
input: { city: "Paris" },
content: [{ type: "text", text: "sunny" }],
}),
time: { created, completed: created },
}),
],
finish: "error",
error: { type: "provider.internal", message: "Provider stream failed" },
time: { created, completed: created },
}),
SessionMessage.Synthetic.make({
id: SessionMessage.ID.make("msg_continue"),
type: "synthetic",
text: continuation,
time: { created },
}),
],
anthropic,
)
const prepared = await Effect.runPromise(
compileRequest(
LLM.request({
model: configure({ apiKey: "test" }).model("claude-sonnet-4-6"),
messages,
}),
),
)
expect(messages).toMatchObject([
{ role: "user" },
{
role: "assistant",
content: [
{ type: "text", text: "I should use the weather tool." },
{ type: "tool-call", id: "call_weather", providerMetadata: undefined },
],
},
{ role: "tool", content: [{ type: "tool-result", id: "call_weather" }] },
{ role: "user", content: [{ type: "text", text: continuation }] },
])
expect(prepared.body.messages).toMatchObject([
{ role: "user", content: [{ type: "text", text: "Look up the weather." }] },
{
role: "assistant",
content: [
{ type: "text", text: "I should use the weather tool." },
{ type: "tool_use", id: "call_weather", name: "lookup_weather", input: { city: "Paris" } },
],
},
{ role: "user", content: [{ type: "tool_result", tool_use_id: "call_weather", content: "sunny" }] },
{ role: "user", content: [{ type: "text", text: continuation }] },
])
})