Skip to content

Commit

Permalink
fix(anthropic): Fixed streaming tool calls with Anthropic (#7081)
Browse files Browse the repository at this point in the history
  • Loading branch information
bracesproul authored Oct 27, 2024
1 parent f75e99b commit 660edbd
Show file tree
Hide file tree
Showing 3 changed files with 18 additions and 62 deletions.
6 changes: 2 additions & 4 deletions libs/langchain-anthropic/src/chat_models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ import type {

import { isLangChainTool } from "@langchain/core/utils/function_calling";
import { AnthropicToolsOutputParser } from "./output_parsers.js";
import { extractToolCallChunk, handleToolChoice } from "./utils/tools.js";
import { handleToolChoice } from "./utils/tools.js";
import { _convertMessagesToAnthropicPayload } from "./utils/message_inputs.js";
import {
_makeMessageChunkFromAnthropicEvent,
Expand Down Expand Up @@ -815,16 +815,14 @@ export class ChatAnthropicMessages<

const { chunk } = result;

const newToolCallChunk = extractToolCallChunk(chunk);

// Extract the text content token for text field and runManager.
const token = extractToken(chunk);
const generationChunk = new ChatGenerationChunk({
message: new AIMessageChunk({
// Just yield chunk as it is and tool_use will be concat by BaseChatModel._generateUncached().
content: chunk.content,
additional_kwargs: chunk.additional_kwargs,
tool_call_chunks: newToolCallChunk ? [newToolCallChunk] : undefined,
tool_call_chunks: chunk.tool_call_chunks,
usage_metadata: shouldStreamUsage ? chunk.usage_metadata : undefined,
response_metadata: chunk.response_metadata,
id: chunk.id,
Expand Down
16 changes: 16 additions & 0 deletions libs/langchain-anthropic/src/utils/message_outputs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ export function _makeMessageChunkFromAnthropicEvent(
data.type === "content_block_start" &&
data.content_block.type === "tool_use"
) {
const toolCallContentBlock =
data.content_block as Anthropic.Messages.ToolUseBlock;
return {
chunk: new AIMessageChunk({
content: fields.coerceContentToString
Expand All @@ -72,6 +74,14 @@ export function _makeMessageChunkFromAnthropicEvent(
},
],
additional_kwargs: {},
tool_call_chunks: [
{
id: toolCallContentBlock.id,
index: data.index,
name: toolCallContentBlock.name,
args: "",
},
],
}),
};
} else if (
Expand Down Expand Up @@ -110,6 +120,12 @@ export function _makeMessageChunkFromAnthropicEvent(
},
],
additional_kwargs: {},
tool_call_chunks: [
{
index: data.index,
args: data.delta.partial_json,
},
],
}),
};
} else if (
Expand Down
58 changes: 0 additions & 58 deletions libs/langchain-anthropic/src/utils/tools.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
import type { MessageCreateParams } from "@anthropic-ai/sdk/resources/index.mjs";
import { AIMessageChunk } from "@langchain/core/messages";
import { ToolCallChunk } from "@langchain/core/messages/tool";
import { AnthropicToolChoice } from "../types.js";

export function handleToolChoice(
Expand Down Expand Up @@ -29,59 +27,3 @@ export function handleToolChoice(
return toolChoice;
}
}

export function extractToolCallChunk(
chunk: AIMessageChunk
): ToolCallChunk | undefined {
let newToolCallChunk: ToolCallChunk | undefined;

// Initial chunk for tool calls from anthropic contains identifying information like ID and name.
// This chunk does not contain any input JSON.
const toolUseChunks = Array.isArray(chunk.content)
? chunk.content.find((c) => c.type === "tool_use")
: undefined;
if (
toolUseChunks &&
"index" in toolUseChunks &&
"name" in toolUseChunks &&
"id" in toolUseChunks
) {
newToolCallChunk = {
args: "",
id: toolUseChunks.id,
name: toolUseChunks.name,
index: toolUseChunks.index,
type: "tool_call_chunk",
};
}

// Chunks after the initial chunk only contain the index and partial JSON.
const inputJsonDeltaChunks = Array.isArray(chunk.content)
? chunk.content.find((c) => c.type === "input_json_delta")
: undefined;
if (
inputJsonDeltaChunks &&
"index" in inputJsonDeltaChunks &&
"input" in inputJsonDeltaChunks
) {
if (typeof inputJsonDeltaChunks.input === "string") {
newToolCallChunk = {
id: inputJsonDeltaChunks.id,
name: inputJsonDeltaChunks.name,
args: inputJsonDeltaChunks.input,
index: inputJsonDeltaChunks.index,
type: "tool_call_chunk",
};
} else {
newToolCallChunk = {
id: inputJsonDeltaChunks.id,
name: inputJsonDeltaChunks.name,
args: JSON.stringify(inputJsonDeltaChunks.input, null, 2),
index: inputJsonDeltaChunks.index,
type: "tool_call_chunk",
};
}
}

return newToolCallChunk;
}

0 comments on commit 660edbd

Please sign in to comment.