diff --git a/packages/langchain-agents/src/__tests__/handler.test.ts b/packages/langchain-agents/src/__tests__/handler.test.ts index c499bd7b..2a0fb025 100644 --- a/packages/langchain-agents/src/__tests__/handler.test.ts +++ b/packages/langchain-agents/src/__tests__/handler.test.ts @@ -1016,7 +1016,6 @@ describe('model source', () => { expect(MockChatOpenAI).toHaveBeenCalledWith({ temperature: 0.2, max_tokens: 512, - tools: ['openai-tool'], model: 'gpt-4o', }); expect(mockCreateAgent).toHaveBeenCalledWith(expect.objectContaining({ model: constructed })); diff --git a/packages/langchain-agents/src/__tests__/native-graph.test.ts b/packages/langchain-agents/src/__tests__/native-graph.test.ts index b5cbc390..26b57a7e 100644 --- a/packages/langchain-agents/src/__tests__/native-graph.test.ts +++ b/packages/langchain-agents/src/__tests__/native-graph.test.ts @@ -180,7 +180,11 @@ describe('toLangGraph', () => { it('spreads model.parameters into the default ChatOpenAI constructor', async () => { const root = makeNode('root', '', []); - root.config.model.parameters = { temperature: 0.2, max_tokens: 512 }; + root.config.model.parameters = { + temperature: 0.2, + max_tokens: 512, + tools: [{ name: 'openai-tool' }], + }; const def = makeGraphDef([root], {}, 'root'); MockChatOpenAI.mockClear(); await toLangGraph(Promise.resolve(def)).invoke('hi'); diff --git a/packages/langchain-agents/src/handler.ts b/packages/langchain-agents/src/handler.ts index 98650611..7ca58b7f 100644 --- a/packages/langchain-agents/src/handler.ts +++ b/packages/langchain-agents/src/handler.ts @@ -263,7 +263,8 @@ function modelConstructorArgs(config: AiConfigRep, fallbackName: string): Record const parameters = { ...(config.model?.parameters && typeof config.model.parameters === 'object' ? config.model.parameters : {}), }; - if ((config.provider?.name ?? '').toLowerCase() === 'bedrock') delete parameters.tools; + // Tools are bound from config.tools. A tools key here is forwarded raw and rejected. + delete parameters.tools; return { ...parameters, model: resolvedModelName(config, fallbackName) }; } diff --git a/packages/langchain-agents/src/native-graph.ts b/packages/langchain-agents/src/native-graph.ts index d58f9869..c24e43f9 100644 --- a/packages/langchain-agents/src/native-graph.ts +++ b/packages/langchain-agents/src/native-graph.ts @@ -142,13 +142,19 @@ export const toLangGraph = ( const toolHandlers = opts?.toolHandlers ?? {}; const modelFactory = opts?.modelFactory ?? - ((node) => - new ChatOpenAI({ + ((node) => { + const parameters = { ...(node.config.model.parameters && typeof node.config.model.parameters === 'object' ? node.config.model.parameters : {}), + }; + // Tools are bound from the node config. A tools key here is forwarded raw and rejected. + delete parameters.tools; + return new ChatOpenAI({ + ...parameters, model: node.config.model.name, - })); + }); + }); const ldContext = opts?.context; return trace.getTracer('@launchdarkly/ai-langchain-agents').startActiveSpan('launchdarkly.graph', async (span) => { diff --git a/packages/langchain-messages/src/__tests__/handler.test.ts b/packages/langchain-messages/src/__tests__/handler.test.ts index 15853414..97a1934a 100644 --- a/packages/langchain-messages/src/__tests__/handler.test.ts +++ b/packages/langchain-messages/src/__tests__/handler.test.ts @@ -1291,7 +1291,6 @@ describe('model source', () => { expect(ChatOpenAI).toHaveBeenCalledWith({ temperature: 0.2, max_tokens: 512, - tools: ['openai-tool'], model: 'gpt-4o', }); }); diff --git a/packages/langchain-messages/src/handler.ts b/packages/langchain-messages/src/handler.ts index 6abb89d0..0a7e82b6 100644 --- a/packages/langchain-messages/src/handler.ts +++ b/packages/langchain-messages/src/handler.ts @@ -173,7 +173,8 @@ function modelConstructorArgs(config: AiConfigRep, fallbackName: string): Record const parameters = { ...(config.model?.parameters && typeof config.model.parameters === 'object' ? config.model.parameters : {}), }; - if ((config.provider?.name ?? '').toLowerCase() === 'bedrock') delete parameters.tools; + // Tools are bound from config.tools. A tools key here is forwarded raw and rejected. + delete parameters.tools; // Name from the config always wins over a colliding `model` key in the parameter bag. return { ...parameters, model: resolvedModelName(config, fallbackName) }; } @@ -184,7 +185,7 @@ function modelConstructorArgs(config: AiConfigRep, fallbackName: string): Record * Otherwise, the provider and model name from the AI config are used to * instantiate the appropriate model via a dynamic import, so that neither * @langchain/openai, @langchain/anthropic, nor @langchain/aws is a hard - * dependency. Parameters are passed through unchanged. + * dependency. Parameters are passed through, except `tools`, which is bound separately. */ async function resolveBaseModel(config: AiConfigRep, llm?: LangChainModelSource): Promise { const invocation = configForModelCall(config);