Skip to content

Commit

Permalink
community[patch]: Fix streaming bedrock tool calls (#6506)
Browse files Browse the repository at this point in the history
* community[patch]: Fix streaming bedrock tool calls

* remove anthropic tools field
  • Loading branch information
bracesproul authored Aug 12, 2024
1 parent 4645f29 commit 61de639
Show file tree
Hide file tree
Showing 2 changed files with 72 additions and 8 deletions.
17 changes: 9 additions & 8 deletions libs/langchain-community/src/chat_models/bedrock/web.ts
Original file line number Diff line number Diff line change
Expand Up @@ -509,8 +509,6 @@ export class BedrockChat
streamProcessingMode: "SYNCHRONOUS" | "ASYNCHRONOUS";
};

protected _anthropicTools?: AnthropicTool[];

get lc_aliases(): Record<string, string> {
return {
model: "model_id",
Expand Down Expand Up @@ -599,9 +597,8 @@ export class BedrockChat
);
}

const callOptionTools = formatTools(options?.tools ?? []);
return {
tools: [...(this._anthropicTools ?? []), ...callOptionTools],
tools: options?.tools ? formatTools(options.tools) : undefined,
temperature: this.temperature,
max_tokens: this.maxTokens,
stop: options?.stop ?? this.stopSequences,
Expand Down Expand Up @@ -813,7 +810,7 @@ export class BedrockChat
provider === "meta" ||
provider === "mistral"
) {
const toolsInParams = !_toolsInParams(options);
const toolsInParams = _toolsInParams(options);
const reader = response.body?.getReader();
const decoder = new TextDecoder();
for await (const chunk of this._readChunks(reader)) {
Expand All @@ -840,7 +837,10 @@ export class BedrockChat
provider,
chunkResult,
{
coerceContentToString: toolsInParams,
// Content should _ONLY_ be coerced if tools are not in params
// If they are, we need content to be of type MessageTypeComplex
// so the tools can be passed through.
coerceContentToString: !toolsInParams,
}
);
if (chunk === undefined) {
Expand Down Expand Up @@ -962,8 +962,9 @@ export class BedrockChat
"Currently, tool calling through Bedrock is only supported for Anthropic models."
);
}
this._anthropicTools = formatTools(tools);
return this;
return this.bind({
tools: formatTools(tools),
});
}
}

Expand Down
63 changes: 63 additions & 0 deletions libs/langchain-standard-tests/src/integration_tests/chat_models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1537,6 +1537,62 @@ Extraction path: {extractionPath}`,
expect(result.tool_calls?.[0].args).toHaveProperty("location");
}

/**
* Tests the chat model's ability to stream responses while using tools.
* This test verifies that the model can:
* 1. Correctly bind a tool defined using StructuredToolParams
* 2. Stream a response for a prompt that should trigger the use of the bound tool
* 3. Generate a streamed response that includes appropriate tool calls
*
* The test uses a simple weather tool to simulate a scenario where the model
* needs to make a tool call to retrieve weather information in a streaming context.
*
* It ensures that the model can correctly interpret the tool's schema,
* make the appropriate tool call, and include the required arguments
* while streaming the response.
*
* @param {any | undefined} callOptions Optional call options to pass to the model.
* These options will be applied to the model at runtime.
*/
async testStreamTools(callOptions?: any) {
// Skip the test if the model doesn't support tool calling
if (!this.chatModelHasToolCalling) {
console.log("Test requires tool calling. Skipping...");
return;
}

const model = new this.Cls(this.constructorArgs);
if (!model.bindTools) {
throw new Error(
"bindTools undefined. Cannot test OpenAI formatted tool calls."
);
}

const tool: StructuredToolParams = {
name: "get_current_weather",
description: "Get the current weather in a given location",
schema: z.object({
location: z.string().describe("The city name, e.g. San Francisco"),
}),
};
const modelWithTools = model.bindTools([tool]);

const prompt = "What's the weather like in San Francisco today?";
const stream = await modelWithTools.stream(prompt, callOptions);
let full: AIMessageChunk | undefined;
for await (const chunk of stream) {
full = !full ? chunk : concat(full, chunk);
}
expect(full).toBeDefined();
if (!full) return;

// Expect at least one tool call, allow multiple.
expect(full.tool_calls?.length).toBeGreaterThanOrEqual(1);

expect(full.tool_calls?.[0].name).toBe(tool.name);
expect(full.tool_calls?.[0].args).toHaveProperty("location");
}

/**
* Run all unit tests for the chat model.
* Each test is wrapped in a try/catch block to prevent the entire test suite from failing.
Expand Down Expand Up @@ -1692,6 +1748,13 @@ Extraction path: {extractionPath}`,
);
}

try {
await this.testStreamTools();
} catch (e: any) {
allTestsPassed = false;
console.error("testStreamTools failed", e.message);
}

return allTestsPassed;
}
}

0 comments on commit 61de639

Please sign in to comment.