






























@@ -407,6 +407,113 @@ describe("anthropic transport stream", () => {
407407);
408408});
409409410+it.each([
411+["empty", ""],
412+["whitespace-only", " \n\t "],
413+["invalid-surrogate-only", String.fromCharCode(0xd83d)],
414+])("replaces %s text-only tool results with a non-empty payload", async (_label, text) => {
415+await runTransportStream(
416+makeAnthropicTransportModel(),
417+{
418+messages: [
419+{
420+role: "assistant",
421+provider: "anthropic",
422+api: "anthropic-messages",
423+model: "claude-sonnet-4-6",
424+stopReason: "toolUse",
425+timestamp: 0,
426+content: [{ type: "toolCall", id: "tool_1", name: "quiet", arguments: {} }],
427+},
428+{
429+role: "toolResult",
430+toolCallId: "tool_1",
431+content: [{ type: "text", text }],
432+isError: false,
433+},
434+],
435+} as AnthropicStreamContext,
436+{
437+apiKey: "sk-ant-api",
438+} as AnthropicStreamOptions,
439+);
440+441+expect(latestAnthropicRequest().payload.messages).toEqual(
442+expect.arrayContaining([
443+expect.objectContaining({
444+role: "user",
445+content: expect.arrayContaining([
446+expect.objectContaining({
447+type: "tool_result",
448+tool_use_id: "tool_1",
449+content: "(no output)",
450+is_error: false,
451+}),
452+]),
453+}),
454+]),
455+);
456+});
457+458+it("drops empty text blocks from image tool results before Anthropic payloads", async () => {
459+const imageData = Buffer.from("image").toString("base64");
460+461+await runTransportStream(
462+makeAnthropicTransportModel({ id: "claude-sonnet-4-6" }),
463+{
464+messages: [
465+{
466+role: "assistant",
467+provider: "anthropic",
468+api: "anthropic-messages",
469+model: "claude-sonnet-4-6",
470+stopReason: "toolUse",
471+timestamp: 0,
472+content: [{ type: "toolCall", id: "tool_1", name: "screenshot", arguments: {} }],
473+},
474+{
475+role: "toolResult",
476+toolCallId: "tool_1",
477+content: [
478+{ type: "text", text: "" },
479+{ type: "image", data: imageData, mimeType: "image/png" },
480+],
481+isError: false,
482+},
483+],
484+} as AnthropicStreamContext,
485+{
486+apiKey: "sk-ant-api",
487+} as AnthropicStreamOptions,
488+);
489+490+expect(latestAnthropicRequest().payload.messages).toEqual(
491+expect.arrayContaining([
492+expect.objectContaining({
493+role: "user",
494+content: expect.arrayContaining([
495+expect.objectContaining({
496+type: "tool_result",
497+tool_use_id: "tool_1",
498+content: [
499+{ type: "text", text: "(see attached image)" },
500+{
501+type: "image",
502+source: {
503+type: "base64",
504+media_type: "image/png",
505+data: imageData,
506+},
507+},
508+],
509+is_error: false,
510+}),
511+]),
512+}),
513+]),
514+);
515+});
516+410517it("maps adaptive thinking effort for Claude 4.6 transport runs", async () => {
411518const model = makeAnthropicTransportModel({
412519id: "claude-opus-4-6",
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。