fix(ts-oss/anthropic): find the text block in no-tools responses (#6506)
This commit is contained in:
@@ -100,12 +100,16 @@ export class AnthropicLLM implements LLM {
|
|||||||
return { content, role: "assistant", toolCalls };
|
return { content, role: "assistant", toolCalls };
|
||||||
}
|
}
|
||||||
|
|
||||||
const firstBlock = response.content[0];
|
// Thinking-enabled responses put a thinking block before the text block,
|
||||||
if (firstBlock.type === "text") {
|
// and a response can carry no text block at all, so find the text block
|
||||||
return firstBlock.text;
|
// like the tools branch above instead of indexing content[0]. Mirrors the
|
||||||
} else {
|
// Python provider (#6481).
|
||||||
throw new Error("Unexpected response type from Anthropic API");
|
for (const block of response.content) {
|
||||||
|
if (block.type === "text") {
|
||||||
|
return block.text;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
async generateChat(messages: Message[]): Promise<LLMResponse> {
|
async generateChat(messages: Message[]): Promise<LLMResponse> {
|
||||||
|
|||||||
@@ -72,6 +72,37 @@ describe("AnthropicLLM (unit)", () => {
|
|||||||
expect(callArgs.tool_choice).toBeUndefined();
|
expect(callArgs.tool_choice).toBeUndefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Regression: thinking-enabled models emit a thinking block before the text
|
||||||
|
// block. Indexing content[0] threw "Unexpected response type"; the text block
|
||||||
|
// must be found by type instead (TS parity with #6481).
|
||||||
|
it("returns the text block when a thinking block precedes it (no tools)", async () => {
|
||||||
|
mockCreate.mockResolvedValueOnce({
|
||||||
|
content: [
|
||||||
|
{ type: "thinking", thinking: "Let me reason about this." },
|
||||||
|
{ type: "text", text: '{"facts": ["fact1"]}' },
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
const llm = new AnthropicLLM({ apiKey: "test-key" });
|
||||||
|
const result = await llm.generateResponse([
|
||||||
|
{ role: "user", content: "Hello" },
|
||||||
|
]);
|
||||||
|
|
||||||
|
expect(result).toBe('{"facts": ["fact1"]}');
|
||||||
|
});
|
||||||
|
|
||||||
|
// A response carrying no text block at all must resolve to "" rather than throw.
|
||||||
|
it("returns an empty string when no text block is present (no tools)", async () => {
|
||||||
|
mockCreate.mockResolvedValueOnce({
|
||||||
|
content: [{ type: "thinking", thinking: "Thinking only." }],
|
||||||
|
});
|
||||||
|
|
||||||
|
const llm = new AnthropicLLM({ apiKey: "test-key" });
|
||||||
|
await expect(
|
||||||
|
llm.generateResponse([{ role: "user", content: "Hello" }]),
|
||||||
|
).resolves.toBe("");
|
||||||
|
});
|
||||||
|
|
||||||
// Bug #1 regression: bare string "auto" must NOT be sent; object form required
|
// Bug #1 regression: bare string "auto" must NOT be sent; object form required
|
||||||
it("forwards tool_choice as { type: 'auto' } (not bare string) when tools are provided", async () => {
|
it("forwards tool_choice as { type: 'auto' } (not bare string) when tools are provided", async () => {
|
||||||
mockCreate.mockResolvedValueOnce({
|
mockCreate.mockResolvedValueOnce({
|
||||||
|
|||||||
Reference in New Issue
Block a user