Skip to content

Commit c497efe

Browse files
authored
fix: remove duplicate tools in LG integration (#401)
* fix: remove duplicate tools in langgraph integrations
1 parent eae2316 commit c497efe

File tree

6 files changed

+48
-29
lines changed

6 files changed

+48
-29
lines changed

typescript-sdk/integrations/langgraph/examples/python/poetry.lock

Lines changed: 8 additions & 8 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

typescript-sdk/integrations/langgraph/examples/python/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ langchain-experimental = ">=0.0.11"
2121
langchain-google-genai = ">=2.1.9"
2222
langchain-openai = ">=0.0.1"
2323
langgraph = "^0.6.1"
24-
ag-ui-langgraph = { version = "0.0.12a3", extras = ["fastapi"] }
24+
ag-ui-langgraph = { version = "0.0.14a0", extras = ["fastapi"] }
2525
python-dotenv = "^1.0.0"
2626
fastapi = "^0.115.12"
2727

typescript-sdk/integrations/langgraph/python/ag_ui_langgraph/agent.py

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -446,12 +446,26 @@ def langgraph_default_merge_state(self, state: State, messages: List[BaseMessage
446446
else:
447447
tools_as_dicts.append(tool)
448448

449+
all_tools = [*state.get("tools", []), *tools_as_dicts]
450+
451+
# Remove duplicates based on tool name
452+
seen_names = set()
453+
unique_tools = []
454+
for tool in all_tools:
455+
tool_name = tool.get("name") if isinstance(tool, dict) else getattr(tool, "name", None)
456+
if tool_name and tool_name not in seen_names:
457+
seen_names.add(tool_name)
458+
unique_tools.append(tool)
459+
elif not tool_name:
460+
# Keep tools without names (shouldn't happen, but just in case)
461+
unique_tools.append(tool)
462+
449463
return {
450464
**state,
451465
"messages": new_messages,
452-
"tools": [*state.get("tools", []), *tools_as_dicts],
466+
"tools": unique_tools,
453467
"ag-ui": {
454-
"tools": [*state.get("tools", []), *tools_as_dicts],
468+
"tools": unique_tools,
455469
"context": input.context or []
456470
}
457471
}

typescript-sdk/integrations/langgraph/python/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name = "ag-ui-langgraph"
3-
version = "0.0.13"
3+
version = "0.0.14-alpha.0"
44
description = "Implementation of the AG-UI protocol for LangGraph."
55
authors = ["Ran Shem Tov <[email protected]>"]
66
readme = "README.md"

typescript-sdk/integrations/langgraph/src/agent.ts

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ import {
2525
PredictStateTool,
2626
LangGraphReasoning,
2727
StateEnrichment,
28-
LangGraphTool,
28+
LangGraphToolWithName,
2929
} from "./types";
3030
import {
3131
AbstractAgent,
@@ -1003,21 +1003,25 @@ export class LangGraphAgent extends AbstractAgent {
10031003

10041004
const newMessages = messages.filter((message) => !existingMessageIds.has(message.id));
10051005

1006-
const langGraphTools: LangGraphTool[] = [...(state.tools ?? []), ...(input.tools ?? [])].map((tool) => {
1007-
if (tool.type) {
1008-
return tool;
1006+
const langGraphTools: LangGraphToolWithName[] = [...(state.tools ?? []), ...(input.tools ?? [])].reduce((acc, tool) => {
1007+
let mappedTool = tool;
1008+
if (!tool.type) {
1009+
mappedTool = {
1010+
type: "function",
1011+
name: tool.name,
1012+
function: {
1013+
name: tool.name,
1014+
description: tool.description,
1015+
parameters: tool.parameters,
1016+
},
1017+
}
10091018
}
10101019

1011-
return {
1012-
type: "function",
1013-
name: tool.name,
1014-
function: {
1015-
name: tool.name,
1016-
description: tool.description,
1017-
parameters: tool.parameters,
1018-
},
1019-
};
1020-
});
1020+
// Verify no duplicated
1021+
if (acc.find((t: LangGraphToolWithName) => (t.name === mappedTool.name) || t.function.name === mappedTool.function.name)) return acc;
1022+
1023+
return [...acc, mappedTool];
1024+
}, []);
10211025

10221026
return {
10231027
...state,

typescript-sdk/integrations/langgraph/src/types.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,9 @@ export enum LangGraphEventTypes {
1515
OnInterrupt = "on_interrupt",
1616
}
1717

18-
export type LangGraphTool = {
18+
export type LangGraphToolWithName = {
1919
type: "function";
20+
name?: string;
2021
function: {
2122
name: string;
2223
description: string;
@@ -29,9 +30,9 @@ export type State<TDefinedState = Record<string, any>> = {
2930
} & Record<string, any>;
3031
export interface StateEnrichment {
3132
messages: LangGraphMessage[];
32-
tools: LangGraphTool[];
33+
tools: LangGraphToolWithName[];
3334
'ag-ui': {
34-
tools: LangGraphTool[];
35+
tools: LangGraphToolWithName[];
3536
context: RunAgentInput['context']
3637
}
3738
}

0 commit comments

Comments
 (0)