mirror of
https://github.com/anomalyco/opencode.git
synced 2026-05-24 05:35:15 +00:00
test: add regression test for tool_use ids were found without tool_result blocks immediately after
This commit is contained in:
parent
1f279cd2c8
commit
c4d804fb48
1 changed files with 264 additions and 1 deletions
|
|
@ -13,7 +13,7 @@ import { ProviderID, ModelID } from "../../src/provider/schema"
|
|||
import { Filesystem } from "../../src/util/filesystem"
|
||||
import { tmpdir } from "../fixture/fixture"
|
||||
import type { Agent } from "../../src/agent/agent"
|
||||
import type { MessageV2 } from "../../src/session/message-v2"
|
||||
import { MessageV2 } from "../../src/session/message-v2"
|
||||
import { SessionID, MessageID } from "../../src/session/schema"
|
||||
import { AppRuntime } from "../../src/effect/app-runtime"
|
||||
|
||||
|
|
@ -909,6 +909,269 @@ describe("session.llm.stream", () => {
|
|||
})
|
||||
})
|
||||
|
||||
test("sends anthropic tool_use blocks with tool_result immediately after them", async () => {
|
||||
const server = state.server
|
||||
if (!server) {
|
||||
throw new Error("Server not initialized")
|
||||
}
|
||||
|
||||
const source = await loadFixture("anthropic", "claude-opus-4-6")
|
||||
const model = source.model
|
||||
const chunks = [
|
||||
{
|
||||
type: "message_start",
|
||||
message: {
|
||||
id: "msg-tool-order",
|
||||
model: model.id,
|
||||
usage: {
|
||||
input_tokens: 3,
|
||||
cache_creation_input_tokens: null,
|
||||
cache_read_input_tokens: null,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "content_block_start",
|
||||
index: 0,
|
||||
content_block: { type: "text", text: "" },
|
||||
},
|
||||
{
|
||||
type: "content_block_delta",
|
||||
index: 0,
|
||||
delta: { type: "text_delta", text: "ok" },
|
||||
},
|
||||
{ type: "content_block_stop", index: 0 },
|
||||
{
|
||||
type: "message_delta",
|
||||
delta: { stop_reason: "end_turn", stop_sequence: null, container: null },
|
||||
usage: {
|
||||
input_tokens: 3,
|
||||
output_tokens: 2,
|
||||
cache_creation_input_tokens: null,
|
||||
cache_read_input_tokens: null,
|
||||
},
|
||||
},
|
||||
{ type: "message_stop" },
|
||||
]
|
||||
const request = waitRequest("/messages", createEventResponse(chunks))
|
||||
|
||||
await using tmp = await tmpdir({
|
||||
init: async (dir) => {
|
||||
await Bun.write(
|
||||
path.join(dir, "opencode.json"),
|
||||
JSON.stringify({
|
||||
$schema: "https://opencode.ai/config.json",
|
||||
enabled_providers: ["anthropic"],
|
||||
provider: {
|
||||
anthropic: {
|
||||
name: "Anthropic",
|
||||
env: ["ANTHROPIC_API_KEY"],
|
||||
npm: "@ai-sdk/anthropic",
|
||||
api: "https://api.anthropic.com/v1",
|
||||
models: {
|
||||
[model.id]: model,
|
||||
},
|
||||
options: {
|
||||
apiKey: "test-anthropic-key",
|
||||
baseURL: `${server.url.origin}/v1`,
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
)
|
||||
},
|
||||
})
|
||||
|
||||
await Instance.provide({
|
||||
directory: tmp.path,
|
||||
fn: async () => {
|
||||
const resolved = await getModel(ProviderID.make("anthropic"), ModelID.make(model.id))
|
||||
const sessionID = SessionID.make("session-test-anthropic-tools")
|
||||
const agent = {
|
||||
name: "test",
|
||||
mode: "primary",
|
||||
options: {},
|
||||
permission: [{ permission: "*", pattern: "*", action: "allow" }],
|
||||
} satisfies Agent.Info
|
||||
const user = {
|
||||
id: MessageID.make("user-anthropic-tools"),
|
||||
sessionID,
|
||||
role: "user",
|
||||
time: { created: Date.now() },
|
||||
agent: agent.name,
|
||||
model: { providerID: ProviderID.make("anthropic"), modelID: resolved.id, variant: "max" },
|
||||
} satisfies MessageV2.User
|
||||
|
||||
const input = [
|
||||
{
|
||||
info: {
|
||||
id: "msg_user",
|
||||
sessionID,
|
||||
role: "user",
|
||||
time: { created: 1 },
|
||||
agent: "gentleman",
|
||||
model: { providerID: "anthropic", modelID: "claude-opus-4-6", variant: "max" },
|
||||
},
|
||||
parts: [
|
||||
{
|
||||
id: "p_user",
|
||||
sessionID,
|
||||
messageID: "msg_user",
|
||||
type: "text",
|
||||
text: "Can you check whether there are any PDF files in my home directory?",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
info: {
|
||||
id: "msg_call",
|
||||
sessionID,
|
||||
parentID: "msg_user",
|
||||
role: "assistant",
|
||||
mode: "gentleman",
|
||||
agent: "gentleman",
|
||||
variant: "max",
|
||||
path: { cwd: "/root", root: "/" },
|
||||
cost: 0,
|
||||
tokens: { input: 0, output: 0, reasoning: 0, cache: { read: 0, write: 0 } },
|
||||
modelID: "claude-opus-4-6",
|
||||
providerID: "anthropic",
|
||||
time: { created: 2, completed: 3 },
|
||||
finish: "tool-calls",
|
||||
},
|
||||
parts: [
|
||||
{
|
||||
id: "p_step",
|
||||
sessionID,
|
||||
messageID: "msg_call",
|
||||
type: "step-start",
|
||||
},
|
||||
{
|
||||
id: "p_read",
|
||||
sessionID,
|
||||
messageID: "msg_call",
|
||||
type: "tool",
|
||||
tool: "read",
|
||||
callID: "toolu_01N8mDEzG8DSTs7UPHFtmgCT",
|
||||
state: {
|
||||
status: "completed",
|
||||
input: { filePath: "/root" },
|
||||
output: "<path>/root</path>",
|
||||
metadata: {},
|
||||
title: "root",
|
||||
time: { start: 10, end: 11 },
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "p_glob",
|
||||
sessionID,
|
||||
messageID: "msg_call",
|
||||
type: "tool",
|
||||
tool: "glob",
|
||||
callID: "toolu_01APxrADs7VozN8uWzw9WwHr",
|
||||
state: {
|
||||
status: "completed",
|
||||
input: { pattern: "**/*.pdf", path: "/root" },
|
||||
output: "No files found",
|
||||
metadata: {},
|
||||
title: "root",
|
||||
time: { start: 12, end: 13 },
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "p_text",
|
||||
sessionID,
|
||||
messageID: "msg_call",
|
||||
type: "text",
|
||||
text: "I checked your home directory and looked for PDF files.",
|
||||
time: { start: 14, end: 15 },
|
||||
},
|
||||
],
|
||||
},
|
||||
] as any[]
|
||||
|
||||
await drain({
|
||||
user,
|
||||
sessionID,
|
||||
model: resolved,
|
||||
agent,
|
||||
system: [],
|
||||
messages: await MessageV2.toModelMessages(input as any, resolved),
|
||||
tools: {
|
||||
read: tool({
|
||||
description: "Stub read tool",
|
||||
inputSchema: z.object({
|
||||
filePath: z.string(),
|
||||
}),
|
||||
execute: async () => ({ output: "stub" }),
|
||||
}),
|
||||
glob: tool({
|
||||
description: "Stub glob tool",
|
||||
inputSchema: z.object({
|
||||
pattern: z.string(),
|
||||
path: z.string().optional(),
|
||||
}),
|
||||
execute: async () => ({ output: "stub" }),
|
||||
}),
|
||||
},
|
||||
})
|
||||
|
||||
const capture = await request
|
||||
const body = capture.body
|
||||
|
||||
expect(capture.url.pathname.endsWith("/messages")).toBe(true)
|
||||
expect(body.messages).toStrictEqual([
|
||||
{
|
||||
role: "user",
|
||||
content: [{ type: "text", text: "Can you check whether there are any PDF files in my home directory?" }],
|
||||
},
|
||||
{
|
||||
role: "assistant",
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: "I checked your home directory and looked for PDF files.",
|
||||
cache_control: {
|
||||
type: "ephemeral",
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "tool_use",
|
||||
id: "toolu_01N8mDEzG8DSTs7UPHFtmgCT",
|
||||
name: "read",
|
||||
input: { filePath: "/root" },
|
||||
},
|
||||
{
|
||||
type: "tool_use",
|
||||
id: "toolu_01APxrADs7VozN8uWzw9WwHr",
|
||||
name: "glob",
|
||||
input: { pattern: "**/*.pdf", path: "/root" },
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
role: "user",
|
||||
content: [
|
||||
{
|
||||
type: "tool_result",
|
||||
tool_use_id: "toolu_01N8mDEzG8DSTs7UPHFtmgCT",
|
||||
content: "<path>/root</path>",
|
||||
},
|
||||
{
|
||||
type: "tool_result",
|
||||
tool_use_id: "toolu_01APxrADs7VozN8uWzw9WwHr",
|
||||
content: "No files found",
|
||||
cache_control: {
|
||||
type: "ephemeral",
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
])
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
test("sends Google API payload for Gemini models", async () => {
|
||||
const server = state.server
|
||||
if (!server) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue