From c375cd5c6b5b55be3f06aa416c18364b2c4b7b34 Mon Sep 17 00:00:00 2001 From: Alex Yang <himself65@outlook.com> Date: Wed, 5 Jun 2024 10:23:41 -0700 Subject: [PATCH] fix: multiple tool call (#905) --- packages/core/e2e/node/openai.e2e.ts | 3 +- packages/core/e2e/node/snapshot/agent.snap | 86 +++--- .../core/e2e/node/snapshot/agent_stream.snap | 260 +++++++++++------ .../agent_with_object_function_call.snap | 28 +- .../snapshot/agent_with_object_retriever.snap | 69 ++++- .../anthropic-agent-multiple-chat.snap | 92 +++--- .../e2e/node/snapshot/anthropic-agent.snap | 271 +++++------------- .../core/e2e/node/snapshot/gpt-4-turbo.snap | 30 +- .../core/e2e/node/snapshot/llm-anthropic.snap | 211 +------------- .../snapshot/openai_agent_system_prompt.snap | 30 +- .../e2e/node/snapshot/react-agent-stream.snap | 12 +- .../core/e2e/node/snapshot/react-agent.snap | 2 +- packages/core/src/agent/anthropic.ts | 44 +-- packages/core/src/agent/openai.ts | 69 ++--- packages/core/src/llm/anthropic.ts | 36 +-- packages/core/src/llm/openai.ts | 35 +-- packages/core/src/llm/types.ts | 2 +- 17 files changed, 586 insertions(+), 694 deletions(-) diff --git a/packages/core/e2e/node/openai.e2e.ts b/packages/core/e2e/node/openai.e2e.ts index 0d18634e3..e9bfceb58 100644 --- a/packages/core/e2e/node/openai.e2e.ts +++ b/packages/core/e2e/node/openai.e2e.ts @@ -191,7 +191,8 @@ For questions about more specific sections, please use the vector_tool.`, message: "What's the summary of Alex? Does he live in Brazil based on the brief information? Return yes or no.", }); - strictEqual(mockCall.mock.callCount(), 1); + // not sure if AI calls twice("Alex", "Brazil") or once ("Alex in Brazil") or something else + ok(mockCall.mock.callCount() >= 1); consola.debug("response:", response.message.content); ok(extractText(response.message.content).toLowerCase().includes("no")); diff --git a/packages/core/e2e/node/snapshot/agent.snap b/packages/core/e2e/node/snapshot/agent.snap index 8bde56d7a..bf57c1111 100644 --- a/packages/core/e2e/node/snapshot/agent.snap +++ b/packages/core/e2e/node/snapshot/agent.snap @@ -20,21 +20,23 @@ "content": "", "role": "assistant", "options": { - "toolCall": { - "id": "call_Enrc7RhNts0qxGaEATOEhV6F", - "name": "Weather", - "input": "{\"location\":\"San Francisco\"}" - } + "toolCall": [ + { + "id": "call_sH6QfjsymHW7JFl68j8AY6xg", + "name": "Weather", + "input": "{\"location\":\"San Francisco\"}" + } + ] } }, { - "content": "35 degrees and sunny in San Francisco", "role": "user", + "content": "35 degrees and sunny in San Francisco", "options": { "toolResult": { "result": "35 degrees and sunny in San Francisco", "isError": false, - "id": "call_Enrc7RhNts0qxGaEATOEhV6F" + "id": "call_sH6QfjsymHW7JFl68j8AY6xg" } } } @@ -60,21 +62,23 @@ "content": "", "role": "assistant", "options": { - "toolCall": { - "id": "call_OYo81prrpK2RLOS9d0Y6kiXB", - "name": "unique_id", - "input": "{\"firstName\":\"Alex\",\"lastName\":\"Yang\"}" - } + "toolCall": [ + { + "id": "call_V7zs8cyDT5FqJhjwBqcCydgA", + "name": "unique_id", + "input": "{\"firstName\":\"Alex\",\"lastName\":\"Yang\"}" + } + ] } }, { - "content": "123456789", "role": "user", + "content": "123456789", "options": { "toolResult": { "result": "123456789", "isError": false, - "id": "call_OYo81prrpK2RLOS9d0Y6kiXB" + "id": "call_V7zs8cyDT5FqJhjwBqcCydgA" } } } @@ -100,21 +104,23 @@ "content": "", "role": "assistant", "options": { - "toolCall": { - "id": "call_YUXowHKwOJ6GIWV4EA59x8iF", - "name": "sumNumbers", - "input": "{\"a\":1,\"b\":1}" - } + "toolCall": [ + { + "id": "call_BrlGGU6GDGWr0hrwXt9qZKyt", + "name": "sumNumbers", + "input": "{\"a\":1,\"b\":1}" + } + ] } }, { - "content": "2", "role": "user", + "content": "2", "options": { "toolResult": { "result": "2", "isError": false, - "id": "call_YUXowHKwOJ6GIWV4EA59x8iF" + "id": "call_BrlGGU6GDGWr0hrwXt9qZKyt" } } } @@ -130,11 +136,13 @@ "content": "", "role": "assistant", "options": { - "toolCall": { - "id": "call_Enrc7RhNts0qxGaEATOEhV6F", - "name": "Weather", - "input": "{\"location\":\"San Francisco\"}" - } + "toolCall": [ + { + "id": "call_sH6QfjsymHW7JFl68j8AY6xg", + "name": "Weather", + "input": "{\"location\":\"San Francisco\"}" + } + ] } } } @@ -158,11 +166,13 @@ "content": "", "role": "assistant", "options": { - "toolCall": { - "id": "call_OYo81prrpK2RLOS9d0Y6kiXB", - "name": "unique_id", - "input": "{\"firstName\":\"Alex\",\"lastName\":\"Yang\"}" - } + "toolCall": [ + { + "id": "call_V7zs8cyDT5FqJhjwBqcCydgA", + "name": "unique_id", + "input": "{\"firstName\":\"Alex\",\"lastName\":\"Yang\"}" + } + ] } } } @@ -186,11 +196,13 @@ "content": "", "role": "assistant", "options": { - "toolCall": { - "id": "call_YUXowHKwOJ6GIWV4EA59x8iF", - "name": "sumNumbers", - "input": "{\"a\":1,\"b\":1}" - } + "toolCall": [ + { + "id": "call_BrlGGU6GDGWr0hrwXt9qZKyt", + "name": "sumNumbers", + "input": "{\"a\":1,\"b\":1}" + } + ] } } } @@ -200,7 +212,7 @@ "response": { "raw": null, "message": { - "content": "The sum of 1 + 1 is 2.", + "content": "1 + 1 is equal to 2.", "role": "assistant", "options": {} } diff --git a/packages/core/e2e/node/snapshot/agent_stream.snap b/packages/core/e2e/node/snapshot/agent_stream.snap index 70dcdf298..87fed616d 100644 --- a/packages/core/e2e/node/snapshot/agent_stream.snap +++ b/packages/core/e2e/node/snapshot/agent_stream.snap @@ -20,14 +20,21 @@ "role": "assistant", "content": "", "options": { - "toolCall": { - "name": "divideNumbers", - "id": "call_QQh8uKzGHbgBlrhqxZNoWiQT", - "input": { - "a": 16, - "b": 2 + "toolCall": [ + { + "name": "divideNumbers", + "id": "call_V4daSdWk9QeYeSMKLBisNbSf", + "input": { + "a": 16, + "b": 2 + } + }, + { + "name": "sumNumbers", + "id": "call_n2OlBxlaoeMIMVeU9DeDfiPX", + "input": "{\"a\": 8, \"b\": 20}" } - } + ] } }, { @@ -37,18 +44,7 @@ "toolResult": { "result": "8", "isError": false, - "id": "call_QQh8uKzGHbgBlrhqxZNoWiQT" - } - } - }, - { - "role": "assistant", - "content": "", - "options": { - "toolCall": { - "name": "sumNumbers", - "id": "call_C03ASSDxIFTrYmzSuTxCrItd", - "input": "{\"a\": 8, \"b\": 20}" + "id": "call_V4daSdWk9QeYeSMKLBisNbSf" } } }, @@ -59,7 +55,7 @@ "toolResult": { "result": "28", "isError": false, - "id": "call_C03ASSDxIFTrYmzSuTxCrItd" + "id": "call_n2OlBxlaoeMIMVeU9DeDfiPX" } } } @@ -75,11 +71,13 @@ "content": "", "role": "assistant", "options": { - "toolCall": { - "name": "sumNumbers", - "id": "call_C03ASSDxIFTrYmzSuTxCrItd", - "input": "{\"a\": 8, \"b\": 20}" - } + "toolCall": [ + { + "name": "sumNumbers", + "id": "call_n2OlBxlaoeMIMVeU9DeDfiPX", + "input": "{\"a\": 8, \"b\": 20}" + } + ] } } } @@ -89,7 +87,7 @@ "response": { "raw": null, "message": { - "content": "The result of dividing 16 by 2 and then adding 20 is 28.", + "content": "The result of dividing 16 by 2 is 8. When you add 20 to 8, the total is 28.", "role": "assistant", "options": {} } @@ -102,11 +100,13 @@ "chunk": { "raw": null, "options": { - "toolCall": { - "name": "divideNumbers", - "id": "call_QQh8uKzGHbgBlrhqxZNoWiQT", - "input": "" - } + "toolCall": [ + { + "name": "divideNumbers", + "id": "call_V4daSdWk9QeYeSMKLBisNbSf", + "input": "{\"a\": 16, \"b\": 2}" + } + ] }, "delta": "" } @@ -116,11 +116,13 @@ "chunk": { "raw": null, "options": { - "toolCall": { - "name": "divideNumbers", - "id": "call_QQh8uKzGHbgBlrhqxZNoWiQT", - "input": "{\"a\"" - } + "toolCall": [ + { + "name": "divideNumbers", + "id": "call_V4daSdWk9QeYeSMKLBisNbSf", + "input": "{\"a\": 16, \"b\": 2}" + } + ] }, "delta": "" } @@ -130,11 +132,13 @@ "chunk": { "raw": null, "options": { - "toolCall": { - "name": "divideNumbers", - "id": "call_QQh8uKzGHbgBlrhqxZNoWiQT", - "input": "{\"a\": 16," - } + "toolCall": [ + { + "name": "divideNumbers", + "id": "call_V4daSdWk9QeYeSMKLBisNbSf", + "input": "{\"a\": 16, \"b\": 2}" + } + ] }, "delta": "" } @@ -144,11 +148,13 @@ "chunk": { "raw": null, "options": { - "toolCall": { - "name": "divideNumbers", - "id": "call_QQh8uKzGHbgBlrhqxZNoWiQT", - "input": "{\"a\": 16, \"b\": " - } + "toolCall": [ + { + "name": "divideNumbers", + "id": "call_V4daSdWk9QeYeSMKLBisNbSf", + "input": "{\"a\": 16, \"b\": 2}" + } + ] }, "delta": "" } @@ -158,11 +164,13 @@ "chunk": { "raw": null, "options": { - "toolCall": { - "name": "divideNumbers", - "id": "call_QQh8uKzGHbgBlrhqxZNoWiQT", - "input": "{\"a\": 16, \"b\": 2}" - } + "toolCall": [ + { + "name": "divideNumbers", + "id": "call_V4daSdWk9QeYeSMKLBisNbSf", + "input": "{\"a\": 16, \"b\": 2}" + } + ] }, "delta": "" } @@ -172,14 +180,16 @@ "chunk": { "raw": null, "options": { - "toolCall": { - "name": "divideNumbers", - "id": "call_QQh8uKzGHbgBlrhqxZNoWiQT", - "input": { - "a": 16, - "b": 2 + "toolCall": [ + { + "name": "divideNumbers", + "id": "call_V4daSdWk9QeYeSMKLBisNbSf", + "input": { + "a": 16, + "b": 2 + } } - } + ] }, "delta": "" } @@ -189,11 +199,13 @@ "chunk": { "raw": null, "options": { - "toolCall": { - "name": "sumNumbers", - "id": "call_C03ASSDxIFTrYmzSuTxCrItd", - "input": "{\"a\"" - } + "toolCall": [ + { + "name": "sumNumbers", + "id": "call_n2OlBxlaoeMIMVeU9DeDfiPX", + "input": "{\"a\": 8, \"b\": 20}" + } + ] }, "delta": "" } @@ -203,11 +215,13 @@ "chunk": { "raw": null, "options": { - "toolCall": { - "name": "sumNumbers", - "id": "call_C03ASSDxIFTrYmzSuTxCrItd", - "input": "{\"a\": 8, " - } + "toolCall": [ + { + "name": "sumNumbers", + "id": "call_n2OlBxlaoeMIMVeU9DeDfiPX", + "input": "{\"a\": 8, \"b\": 20}" + } + ] }, "delta": "" } @@ -217,11 +231,13 @@ "chunk": { "raw": null, "options": { - "toolCall": { - "name": "sumNumbers", - "id": "call_C03ASSDxIFTrYmzSuTxCrItd", - "input": "{\"a\": 8, \"b\": 2" - } + "toolCall": [ + { + "name": "sumNumbers", + "id": "call_n2OlBxlaoeMIMVeU9DeDfiPX", + "input": "{\"a\": 8, \"b\": 20}" + } + ] }, "delta": "" } @@ -231,11 +247,13 @@ "chunk": { "raw": null, "options": { - "toolCall": { - "name": "sumNumbers", - "id": "call_C03ASSDxIFTrYmzSuTxCrItd", - "input": "{\"a\": 8, \"b\": 20}" - } + "toolCall": [ + { + "name": "sumNumbers", + "id": "call_n2OlBxlaoeMIMVeU9DeDfiPX", + "input": "{\"a\": 8, \"b\": 20}" + } + ] }, "delta": "" } @@ -317,7 +335,15 @@ "chunk": { "raw": null, "options": {}, - "delta": " and" + "delta": " is" + } + }, + { + "id": "PRESERVE_1", + "chunk": { + "raw": null, + "options": {}, + "delta": " " } }, { @@ -325,7 +351,7 @@ "chunk": { "raw": null, "options": {}, - "delta": " then" + "delta": "8" } }, { @@ -333,7 +359,31 @@ "chunk": { "raw": null, "options": {}, - "delta": " adding" + "delta": "." + } + }, + { + "id": "PRESERVE_1", + "chunk": { + "raw": null, + "options": {}, + "delta": " When" + } + }, + { + "id": "PRESERVE_1", + "chunk": { + "raw": null, + "options": {}, + "delta": " you" + } + }, + { + "id": "PRESERVE_1", + "chunk": { + "raw": null, + "options": {}, + "delta": " add" } }, { @@ -352,6 +402,54 @@ "delta": "20" } }, + { + "id": "PRESERVE_1", + "chunk": { + "raw": null, + "options": {}, + "delta": " to" + } + }, + { + "id": "PRESERVE_1", + "chunk": { + "raw": null, + "options": {}, + "delta": " " + } + }, + { + "id": "PRESERVE_1", + "chunk": { + "raw": null, + "options": {}, + "delta": "8" + } + }, + { + "id": "PRESERVE_1", + "chunk": { + "raw": null, + "options": {}, + "delta": "," + } + }, + { + "id": "PRESERVE_1", + "chunk": { + "raw": null, + "options": {}, + "delta": " the" + } + }, + { + "id": "PRESERVE_1", + "chunk": { + "raw": null, + "options": {}, + "delta": " total" + } + }, { "id": "PRESERVE_1", "chunk": { diff --git a/packages/core/e2e/node/snapshot/agent_with_object_function_call.snap b/packages/core/e2e/node/snapshot/agent_with_object_function_call.snap index f5d2b5b2e..f0209e8a9 100644 --- a/packages/core/e2e/node/snapshot/agent_with_object_function_call.snap +++ b/packages/core/e2e/node/snapshot/agent_with_object_function_call.snap @@ -20,16 +20,18 @@ "content": "", "role": "assistant", "options": { - "toolCall": { - "id": "call_iY0VO4HvXDT2PtR3CvRSY1uK", - "name": "get_weather", - "input": "{\"location\":\"San Francisco\"}" - } + "toolCall": [ + { + "id": "call_uERMumWlJLTO2GW93X6C2W3N", + "name": "get_weather", + "input": "{\"location\":\"San Francisco\"}" + } + ] } }, { - "content": "{\n location: San Francisco,\n temperature: 72,\n weather: cloudy,\n rain_prediction: 0.89\n}", "role": "user", + "content": "{\n location: San Francisco,\n temperature: 72,\n weather: cloudy,\n rain_prediction: 0.89\n}", "options": { "toolResult": { "result": { @@ -39,7 +41,7 @@ "rain_prediction": 0.89 }, "isError": false, - "id": "call_iY0VO4HvXDT2PtR3CvRSY1uK" + "id": "call_uERMumWlJLTO2GW93X6C2W3N" } } } @@ -55,11 +57,13 @@ "content": "", "role": "assistant", "options": { - "toolCall": { - "id": "call_iY0VO4HvXDT2PtR3CvRSY1uK", - "name": "get_weather", - "input": "{\"location\":\"San Francisco\"}" - } + "toolCall": [ + { + "id": "call_uERMumWlJLTO2GW93X6C2W3N", + "name": "get_weather", + "input": "{\"location\":\"San Francisco\"}" + } + ] } } } diff --git a/packages/core/e2e/node/snapshot/agent_with_object_retriever.snap b/packages/core/e2e/node/snapshot/agent_with_object_retriever.snap index 353cfa0d1..82c55b32a 100644 --- a/packages/core/e2e/node/snapshot/agent_with_object_retriever.snap +++ b/packages/core/e2e/node/snapshot/agent_with_object_retriever.snap @@ -24,6 +24,15 @@ }, { "id": "PRESERVE_2", + "messages": [ + { + "content": "Context information is below.\n---------------------\nAlex is a male. What's very important, Alex is not in the Brazil.\n---------------------\nGiven the context information and not prior knowledge, answer the query.\nQuery: Brazil\nAnswer:", + "role": "user" + } + ] + }, + { + "id": "PRESERVE_3", "messages": [ { "content": "Please always use the tools provided to answer a question. Do not rely on prior knowledge.", @@ -37,21 +46,39 @@ "content": "", "role": "assistant", "options": { - "toolCall": { - "id": "call_VfgZS1NPKpbtzqhM3o1Buzx8", - "name": "summary_tool", - "input": "{\"query\": \"Alex\"}" - } + "toolCall": [ + { + "id": "call_vv1XW3xv4j2us5sZOtzCU2lL", + "name": "summary_tool", + "input": "{\"query\": \"Alex\"}" + }, + { + "id": "call_V36LMHbwUJkEa20A3GoA4wIr", + "name": "summary_tool", + "input": "{\"query\": \"Brazil\"}" + } + ] } }, { + "role": "user", "content": "Alex is not in Brazil.", + "options": { + "toolResult": { + "result": "Alex is not in Brazil.", + "isError": false, + "id": "call_vv1XW3xv4j2us5sZOtzCU2lL" + } + } + }, + { "role": "user", + "content": "Alex is not in Brazil.", "options": { "toolResult": { "result": "Alex is not in Brazil.", "isError": false, - "id": "call_VfgZS1NPKpbtzqhM3o1Buzx8" + "id": "call_V36LMHbwUJkEa20A3GoA4wIr" } } } @@ -67,11 +94,18 @@ "content": "", "role": "assistant", "options": { - "toolCall": { - "id": "call_VfgZS1NPKpbtzqhM3o1Buzx8", - "name": "summary_tool", - "input": "{\"query\": \"Alex\"}" - } + "toolCall": [ + { + "id": "call_vv1XW3xv4j2us5sZOtzCU2lL", + "name": "summary_tool", + "input": "{\"query\": \"Alex\"}" + }, + { + "id": "call_V36LMHbwUJkEa20A3GoA4wIr", + "name": "summary_tool", + "input": "{\"query\": \"Brazil\"}" + } + ] } } } @@ -92,7 +126,18 @@ "response": { "raw": null, "message": { - "content": "No, Alex does not live in Brazil based on the brief information available.", + "content": "Alex is not in Brazil.", + "role": "assistant", + "options": {} + } + } + }, + { + "id": "PRESERVE_3", + "response": { + "raw": null, + "message": { + "content": "Based on the brief information, Alex is not in Brazil.", "role": "assistant", "options": {} } diff --git a/packages/core/e2e/node/snapshot/anthropic-agent-multiple-chat.snap b/packages/core/e2e/node/snapshot/anthropic-agent-multiple-chat.snap index b77cbcbe3..141f6c773 100644 --- a/packages/core/e2e/node/snapshot/anthropic-agent-multiple-chat.snap +++ b/packages/core/e2e/node/snapshot/anthropic-agent-multiple-chat.snap @@ -178,13 +178,15 @@ ], "role": "assistant", "options": { - "toolCall": { - "id": "toolu_01Gy7Gxbx7uGmjVncGH6pubL", - "name": "getWeather", - "input": { - "city": "San Francisco" + "toolCall": [ + { + "id": "toolu_017MxwaxaLYkmt4dpP5HKzFe", + "name": "getWeather", + "input": { + "city": "San Francisco" + } } - } + ] } }, { @@ -194,7 +196,7 @@ "toolResult": { "result": "The weather in San Francisco is 72 degrees", "isError": false, - "id": "toolu_01Gy7Gxbx7uGmjVncGH6pubL" + "id": "toolu_017MxwaxaLYkmt4dpP5HKzFe" } } } @@ -258,13 +260,15 @@ ], "role": "assistant", "options": { - "toolCall": { - "id": "toolu_01Gy7Gxbx7uGmjVncGH6pubL", - "name": "getWeather", - "input": { - "city": "San Francisco" + "toolCall": [ + { + "id": "toolu_017MxwaxaLYkmt4dpP5HKzFe", + "name": "getWeather", + "input": { + "city": "San Francisco" + } } - } + ] } }, { @@ -274,7 +278,7 @@ "toolResult": { "result": "The weather in San Francisco is 72 degrees", "isError": false, - "id": "toolu_01Gy7Gxbx7uGmjVncGH6pubL" + "id": "toolu_017MxwaxaLYkmt4dpP5HKzFe" } } }, @@ -352,13 +356,15 @@ ], "role": "assistant", "options": { - "toolCall": { - "id": "toolu_01Gy7Gxbx7uGmjVncGH6pubL", - "name": "getWeather", - "input": { - "city": "San Francisco" + "toolCall": [ + { + "id": "toolu_017MxwaxaLYkmt4dpP5HKzFe", + "name": "getWeather", + "input": { + "city": "San Francisco" + } } - } + ] } }, { @@ -368,7 +374,7 @@ "toolResult": { "result": "The weather in San Francisco is 72 degrees", "isError": false, - "id": "toolu_01Gy7Gxbx7uGmjVncGH6pubL" + "id": "toolu_017MxwaxaLYkmt4dpP5HKzFe" } } }, @@ -395,13 +401,15 @@ ], "role": "assistant", "options": { - "toolCall": { - "id": "toolu_01NHyahSUqrPjxQk9mvCvvGe", - "name": "getWeather", - "input": { - "city": "Shanghai" + "toolCall": [ + { + "id": "toolu_013ZnATGKhvPkt2jjxdFheDK", + "name": "getWeather", + "input": { + "city": "Shanghai" + } } - } + ] } }, { @@ -411,7 +419,7 @@ "toolResult": { "result": "The weather in Shanghai is 72 degrees", "isError": false, - "id": "toolu_01NHyahSUqrPjxQk9mvCvvGe" + "id": "toolu_013ZnATGKhvPkt2jjxdFheDK" } } } @@ -480,13 +488,15 @@ ], "role": "assistant", "options": { - "toolCall": { - "id": "toolu_01Gy7Gxbx7uGmjVncGH6pubL", - "name": "getWeather", - "input": { - "city": "San Francisco" + "toolCall": [ + { + "id": "toolu_017MxwaxaLYkmt4dpP5HKzFe", + "name": "getWeather", + "input": { + "city": "San Francisco" + } } - } + ] } } } @@ -520,13 +530,15 @@ ], "role": "assistant", "options": { - "toolCall": { - "id": "toolu_01NHyahSUqrPjxQk9mvCvvGe", - "name": "getWeather", - "input": { - "city": "Shanghai" + "toolCall": [ + { + "id": "toolu_013ZnATGKhvPkt2jjxdFheDK", + "name": "getWeather", + "input": { + "city": "Shanghai" + } } - } + ] } } } diff --git a/packages/core/e2e/node/snapshot/anthropic-agent.snap b/packages/core/e2e/node/snapshot/anthropic-agent.snap index 8d57bad7e..2763fbb13 100644 --- a/packages/core/e2e/node/snapshot/anthropic-agent.snap +++ b/packages/core/e2e/node/snapshot/anthropic-agent.snap @@ -5,8 +5,7 @@ "messages": [ { "role": "user", - "content": "What is the weather in San Francisco?", - "options": {} + "content": "What is the weather in San Francisco?" } ] }, @@ -15,25 +14,26 @@ "messages": [ { "role": "user", - "content": "What is the weather in San Francisco?", - "options": {} + "content": "What is the weather in San Francisco?" }, { "content": [ { "type": "text", - "text": "<thinking>\nThe user is asking for the weather in a specific location, San Francisco. The Weather function is the relevant tool to answer this request, as it returns weather information for a given location.\n\nThe Weather function has one required parameter:\n- location (string): The user has directly provided the location of \"San Francisco\"\n\nSince the required location parameter has been provided by the user, we have all the necessary information to call the Weather function.\n</thinking>" + "text": "<thinking>\nThe user has asked for the weather in a specific location, San Francisco. The Weather tool is the relevant function to answer this request, as it returns weather information for a given location.\n\nThe Weather tool requires a single parameter:\n- location (string, required): The user has directly provided the location as \"San Francisco\".\n\nSince the required location parameter has been provided, we have enough information to call the Weather tool.\n</thinking>" } ], "role": "assistant", "options": { - "toolCall": { - "id": "HIDDEN", - "name": "Weather", - "input": { - "location": "San Francisco" + "toolCall": [ + { + "id": "toolu_011YcKkPygw2woJZrVWRRMwH", + "name": "Weather", + "input": { + "location": "San Francisco" + } } - } + ] } }, { @@ -41,8 +41,9 @@ "role": "user", "options": { "toolResult": { + "result": "35 degrees and sunny in San Francisco", "isError": false, - "id": "HIDDEN" + "id": "toolu_011YcKkPygw2woJZrVWRRMwH" } } } @@ -53,8 +54,7 @@ "messages": [ { "role": "user", - "content": "My name is Alex Yang. What is my unique id?", - "options": {} + "content": "My name is Alex Yang. What is my unique id?" } ] }, @@ -63,26 +63,27 @@ "messages": [ { "role": "user", - "content": "My name is Alex Yang. What is my unique id?", - "options": {} + "content": "My name is Alex Yang. What is my unique id?" }, { "content": [ { "type": "text", - "text": "<thinking>\nThe unique_id function is the relevant tool to answer the user's request for their unique ID. It requires two parameters:\nfirstName: The user provided their first name, which is \"Alex\"\nlastName: The user also provided their last name, \"Yang\"\nSince the user has provided all the required parameters, we can proceed with calling the unique_id function.\n</thinking>" + "text": "<thinking>\nThe unique_id function takes firstName and lastName as required parameters. The user has provided their first name (Alex) and last name (Yang) in the request, so we have all the necessary information to call the function.\n</thinking>" } ], "role": "assistant", "options": { - "toolCall": { - "id": "HIDDEN", - "name": "unique_id", - "input": { - "firstName": "Alex", - "lastName": "Yang" + "toolCall": [ + { + "id": "toolu_012ZLUq5SWsghkXUjsEXsB1f", + "name": "unique_id", + "input": { + "firstName": "Alex", + "lastName": "Yang" + } } - } + ] } }, { @@ -90,8 +91,9 @@ "role": "user", "options": { "toolResult": { + "result": "123456789", "isError": false, - "id": "HIDDEN" + "id": "toolu_012ZLUq5SWsghkXUjsEXsB1f" } } } @@ -102,8 +104,7 @@ "messages": [ { "role": "user", - "content": "how much is 1 + 1?", - "options": {} + "content": "how much is 1 + 1?" } ] }, @@ -112,26 +113,27 @@ "messages": [ { "role": "user", - "content": "how much is 1 + 1?", - "options": {} + "content": "how much is 1 + 1?" }, { "content": [ { "type": "text", - "text": "<thinking>\nThe user is asking to sum the numbers 1 and 1. The relevant tool to use is the sumNumbers function, which takes two number parameters a and b.\nThe user has directly provided the values for the parameters:\na = 1 \nb = 1\nSince all the required parameters have been provided, we can proceed with calling the function.\n</thinking>" + "text": "<thinking>\nThe user is asking to sum the numbers 1 and 1. The relevant tool is the sumNumbers function, which takes two number parameters a and b.\nThe user has directly provided the values for the required parameters:\na = 1 \nb = 1\nSince all the required parameters are provided, we can proceed with calling the function.\n</thinking>" } ], "role": "assistant", "options": { - "toolCall": { - "id": "HIDDEN", - "name": "sumNumbers", - "input": { - "a": 1, - "b": 1 + "toolCall": [ + { + "id": "toolu_01SQD2XkaXkDNLQ2xaFzmguG", + "name": "sumNumbers", + "input": { + "a": 1, + "b": 1 + } } - } + ] } }, { @@ -139,8 +141,9 @@ "role": "user", "options": { "toolResult": { + "result": "2", "isError": false, - "id": "HIDDEN" + "id": "toolu_01SQD2XkaXkDNLQ2xaFzmguG" } } } @@ -151,48 +154,25 @@ { "id": "PRESERVE_0", "response": { - "raw": { - "id": "HIDDEN", - "type": "message", - "role": "assistant", - "model": "claude-3-opus-20240229", - "stop_sequence": null, - "usage": { - "input_tokens": 462, - "output_tokens": 147 - }, - "content": [ - { - "type": "text", - "text": "<thinking>\nThe user is asking for the weather in a specific location, San Francisco. The Weather function is the relevant tool to answer this request, as it returns weather information for a given location.\n\nThe Weather function has one required parameter:\n- location (string): The user has directly provided the location of \"San Francisco\"\n\nSince the required location parameter has been provided by the user, we have all the necessary information to call the Weather function.\n</thinking>" - }, - { - "type": "tool_use", - "id": "HIDDEN", - "name": "Weather", - "input": { - "location": "San Francisco" - } - } - ], - "stop_reason": "tool_use" - }, + "raw": null, "message": { "content": [ { "type": "text", - "text": "<thinking>\nThe user is asking for the weather in a specific location, San Francisco. The Weather function is the relevant tool to answer this request, as it returns weather information for a given location.\n\nThe Weather function has one required parameter:\n- location (string): The user has directly provided the location of \"San Francisco\"\n\nSince the required location parameter has been provided by the user, we have all the necessary information to call the Weather function.\n</thinking>" + "text": "<thinking>\nThe user has asked for the weather in a specific location, San Francisco. The Weather tool is the relevant function to answer this request, as it returns weather information for a given location.\n\nThe Weather tool requires a single parameter:\n- location (string, required): The user has directly provided the location as \"San Francisco\".\n\nSince the required location parameter has been provided, we have enough information to call the Weather tool.\n</thinking>" } ], "role": "assistant", "options": { - "toolCall": { - "id": "HIDDEN", - "name": "Weather", - "input": { - "location": "San Francisco" + "toolCall": [ + { + "id": "toolu_011YcKkPygw2woJZrVWRRMwH", + "name": "Weather", + "input": { + "location": "San Francisco" + } } - } + ] } } } @@ -200,24 +180,7 @@ { "id": "PRESERVE_1", "response": { - "raw": { - "id": "HIDDEN", - "type": "message", - "role": "assistant", - "model": "claude-3-opus-20240229", - "stop_sequence": null, - "usage": { - "input_tokens": 628, - "output_tokens": 18 - }, - "content": [ - { - "type": "text", - "text": "The current weather in San Francisco is 35 degrees and sunny." - } - ], - "stop_reason": "end_turn" - }, + "raw": null, "message": { "content": [ { @@ -233,50 +196,26 @@ { "id": "PRESERVE_2", "response": { - "raw": { - "id": "HIDDEN", - "type": "message", - "role": "assistant", - "model": "claude-3-opus-20240229", - "stop_sequence": null, - "usage": { - "input_tokens": 482, - "output_tokens": 152 - }, - "content": [ - { - "type": "text", - "text": "<thinking>\nThe unique_id function is the relevant tool to answer the user's request for their unique ID. It requires two parameters:\nfirstName: The user provided their first name, which is \"Alex\"\nlastName: The user also provided their last name, \"Yang\"\nSince the user has provided all the required parameters, we can proceed with calling the unique_id function.\n</thinking>" - }, - { - "type": "tool_use", - "id": "HIDDEN", - "name": "unique_id", - "input": { - "firstName": "Alex", - "lastName": "Yang" - } - } - ], - "stop_reason": "tool_use" - }, + "raw": null, "message": { "content": [ { "type": "text", - "text": "<thinking>\nThe unique_id function is the relevant tool to answer the user's request for their unique ID. It requires two parameters:\nfirstName: The user provided their first name, which is \"Alex\"\nlastName: The user also provided their last name, \"Yang\"\nSince the user has provided all the required parameters, we can proceed with calling the unique_id function.\n</thinking>" + "text": "<thinking>\nThe unique_id function takes firstName and lastName as required parameters. The user has provided their first name (Alex) and last name (Yang) in the request, so we have all the necessary information to call the function.\n</thinking>" } ], "role": "assistant", "options": { - "toolCall": { - "id": "HIDDEN", - "name": "unique_id", - "input": { - "firstName": "Alex", - "lastName": "Yang" + "toolCall": [ + { + "id": "toolu_012ZLUq5SWsghkXUjsEXsB1f", + "name": "unique_id", + "input": { + "firstName": "Alex", + "lastName": "Yang" + } } - } + ] } } } @@ -284,24 +223,7 @@ { "id": "PRESERVE_3", "response": { - "raw": { - "id": "HIDDEN", - "type": "message", - "role": "assistant", - "model": "claude-3-opus-20240229", - "stop_sequence": null, - "usage": { - "input_tokens": 648, - "output_tokens": 13 - }, - "content": [ - { - "type": "text", - "text": "Your unique ID is 123456789." - } - ], - "stop_reason": "end_turn" - }, + "raw": null, "message": { "content": [ { @@ -317,50 +239,26 @@ { "id": "PRESERVE_4", "response": { - "raw": { - "id": "HIDDEN", - "type": "message", - "role": "assistant", - "model": "claude-3-opus-20240229", - "stop_sequence": null, - "usage": { - "input_tokens": 498, - "output_tokens": 151 - }, - "content": [ - { - "type": "text", - "text": "<thinking>\nThe user is asking to sum the numbers 1 and 1. The relevant tool to use is the sumNumbers function, which takes two number parameters a and b.\nThe user has directly provided the values for the parameters:\na = 1 \nb = 1\nSince all the required parameters have been provided, we can proceed with calling the function.\n</thinking>" - }, - { - "type": "tool_use", - "id": "HIDDEN", - "name": "sumNumbers", - "input": { - "a": 1, - "b": 1 - } - } - ], - "stop_reason": "tool_use" - }, + "raw": null, "message": { "content": [ { "type": "text", - "text": "<thinking>\nThe user is asking to sum the numbers 1 and 1. The relevant tool to use is the sumNumbers function, which takes two number parameters a and b.\nThe user has directly provided the values for the parameters:\na = 1 \nb = 1\nSince all the required parameters have been provided, we can proceed with calling the function.\n</thinking>" + "text": "<thinking>\nThe user is asking to sum the numbers 1 and 1. The relevant tool is the sumNumbers function, which takes two number parameters a and b.\nThe user has directly provided the values for the required parameters:\na = 1 \nb = 1\nSince all the required parameters are provided, we can proceed with calling the function.\n</thinking>" } ], "role": "assistant", "options": { - "toolCall": { - "id": "HIDDEN", - "name": "sumNumbers", - "input": { - "a": 1, - "b": 1 + "toolCall": [ + { + "id": "toolu_01SQD2XkaXkDNLQ2xaFzmguG", + "name": "sumNumbers", + "input": { + "a": 1, + "b": 1 + } } - } + ] } } } @@ -368,24 +266,7 @@ { "id": "PRESERVE_5", "response": { - "raw": { - "id": "HIDDEN", - "type": "message", - "role": "assistant", - "model": "claude-3-opus-20240229", - "stop_sequence": null, - "usage": { - "input_tokens": 661, - "output_tokens": 16 - }, - "content": [ - { - "type": "text", - "text": "So 1 + 1 = 2." - } - ], - "stop_reason": "end_turn" - }, + "raw": null, "message": { "content": [ { diff --git a/packages/core/e2e/node/snapshot/gpt-4-turbo.snap b/packages/core/e2e/node/snapshot/gpt-4-turbo.snap index af377c329..951442c7b 100644 --- a/packages/core/e2e/node/snapshot/gpt-4-turbo.snap +++ b/packages/core/e2e/node/snapshot/gpt-4-turbo.snap @@ -20,21 +20,23 @@ "content": "", "role": "assistant", "options": { - "toolCall": { - "id": "call_TRC5kpzyVaV5n2R91sJsBAF1", - "name": "Weather", - "input": "{\"location\":\"San Jose\"}" - } + "toolCall": [ + { + "id": "call_Xa2Kxa2zUE073mnougPWzRlh", + "name": "Weather", + "input": "{\"location\":\"San Jose\"}" + } + ] } }, { - "content": "45 degrees and sunny in San Jose", "role": "user", + "content": "45 degrees and sunny in San Jose", "options": { "toolResult": { "result": "45 degrees and sunny in San Jose", "isError": false, - "id": "call_TRC5kpzyVaV5n2R91sJsBAF1" + "id": "call_Xa2Kxa2zUE073mnougPWzRlh" } } } @@ -50,11 +52,13 @@ "content": "", "role": "assistant", "options": { - "toolCall": { - "id": "call_TRC5kpzyVaV5n2R91sJsBAF1", - "name": "Weather", - "input": "{\"location\":\"San Jose\"}" - } + "toolCall": [ + { + "id": "call_Xa2Kxa2zUE073mnougPWzRlh", + "name": "Weather", + "input": "{\"location\":\"San Jose\"}" + } + ] } } } @@ -64,7 +68,7 @@ "response": { "raw": null, "message": { - "content": "The weather in San Jose is currently 45 degrees and sunny.", + "content": "The current weather in San Jose is 45 degrees and sunny.", "role": "assistant", "options": {} } diff --git a/packages/core/e2e/node/snapshot/llm-anthropic.snap b/packages/core/e2e/node/snapshot/llm-anthropic.snap index 9c4892063..106104b5e 100644 --- a/packages/core/e2e/node/snapshot/llm-anthropic.snap +++ b/packages/core/e2e/node/snapshot/llm-anthropic.snap @@ -24,24 +24,7 @@ { "id": "PRESERVE_0", "response": { - "raw": { - "id": "HIDDEN", - "type": "message", - "role": "assistant", - "model": "claude-3-opus-20240229", - "stop_sequence": null, - "usage": { - "input_tokens": 8, - "output_tokens": 12 - }, - "content": [ - { - "type": "text", - "text": "Hello! How can I assist you today?" - } - ], - "stop_reason": "end_turn" - }, + "raw": null, "message": { "content": "Hello! How can I assist you today?", "role": "assistant", @@ -52,116 +35,7 @@ { "id": "PRESERVE_1", "response": { - "raw": [ - { - "raw": { - "type": "content_block_delta", - "index": 0, - "delta": { - "type": "text_delta", - "text": "Hello" - } - }, - "delta": "Hello", - "options": {} - }, - { - "raw": { - "type": "content_block_delta", - "index": 0, - "delta": { - "type": "text_delta", - "text": "!" - } - }, - "delta": "!", - "options": {} - }, - { - "raw": { - "type": "content_block_delta", - "index": 0, - "delta": { - "type": "text_delta", - "text": " How" - } - }, - "delta": " How", - "options": {} - }, - { - "raw": { - "type": "content_block_delta", - "index": 0, - "delta": { - "type": "text_delta", - "text": " can" - } - }, - "delta": " can", - "options": {} - }, - { - "raw": { - "type": "content_block_delta", - "index": 0, - "delta": { - "type": "text_delta", - "text": " I" - } - }, - "delta": " I", - "options": {} - }, - { - "raw": { - "type": "content_block_delta", - "index": 0, - "delta": { - "type": "text_delta", - "text": " assist" - } - }, - "delta": " assist", - "options": {} - }, - { - "raw": { - "type": "content_block_delta", - "index": 0, - "delta": { - "type": "text_delta", - "text": " you" - } - }, - "delta": " you", - "options": {} - }, - { - "raw": { - "type": "content_block_delta", - "index": 0, - "delta": { - "type": "text_delta", - "text": " today" - } - }, - "delta": " today", - "options": {} - }, - { - "raw": { - "type": "content_block_delta", - "index": 0, - "delta": { - "type": "text_delta", - "text": "?" - } - }, - "delta": "?", - "options": {} - } - ], + "raw": null, "message": { "content": "Hello! How can I assist you today?", "role": "assistant", @@ -174,14 +48,7 @@ { "id": "PRESERVE_1", "chunk": { - "raw": { - "type": "content_block_delta", - "index": 0, - "delta": { - "type": "text_delta", - "text": "Hello" - } - }, + "raw": null, "delta": "Hello", "options": {} } @@ -189,14 +56,7 @@ { "id": "PRESERVE_1", "chunk": { - "raw": { - "type": "content_block_delta", - "index": 0, - "delta": { - "type": "text_delta", - "text": "!" - } - }, + "raw": null, "delta": "!", "options": {} } @@ -204,14 +64,7 @@ { "id": "PRESERVE_1", "chunk": { - "raw": { - "type": "content_block_delta", - "index": 0, - "delta": { - "type": "text_delta", - "text": " How" - } - }, + "raw": null, "delta": " How", "options": {} } @@ -219,14 +72,7 @@ { "id": "PRESERVE_1", "chunk": { - "raw": { - "type": "content_block_delta", - "index": 0, - "delta": { - "type": "text_delta", - "text": " can" - } - }, + "raw": null, "delta": " can", "options": {} } @@ -234,14 +80,7 @@ { "id": "PRESERVE_1", "chunk": { - "raw": { - "type": "content_block_delta", - "index": 0, - "delta": { - "type": "text_delta", - "text": " I" - } - }, + "raw": null, "delta": " I", "options": {} } @@ -249,14 +88,7 @@ { "id": "PRESERVE_1", "chunk": { - "raw": { - "type": "content_block_delta", - "index": 0, - "delta": { - "type": "text_delta", - "text": " assist" - } - }, + "raw": null, "delta": " assist", "options": {} } @@ -264,14 +96,7 @@ { "id": "PRESERVE_1", "chunk": { - "raw": { - "type": "content_block_delta", - "index": 0, - "delta": { - "type": "text_delta", - "text": " you" - } - }, + "raw": null, "delta": " you", "options": {} } @@ -279,14 +104,7 @@ { "id": "PRESERVE_1", "chunk": { - "raw": { - "type": "content_block_delta", - "index": 0, - "delta": { - "type": "text_delta", - "text": " today" - } - }, + "raw": null, "delta": " today", "options": {} } @@ -294,14 +112,7 @@ { "id": "PRESERVE_1", "chunk": { - "raw": { - "type": "content_block_delta", - "index": 0, - "delta": { - "type": "text_delta", - "text": "?" - } - }, + "raw": null, "delta": "?", "options": {} } diff --git a/packages/core/e2e/node/snapshot/openai_agent_system_prompt.snap b/packages/core/e2e/node/snapshot/openai_agent_system_prompt.snap index 751ba04c9..cfcda8739 100644 --- a/packages/core/e2e/node/snapshot/openai_agent_system_prompt.snap +++ b/packages/core/e2e/node/snapshot/openai_agent_system_prompt.snap @@ -28,21 +28,23 @@ "content": "", "role": "assistant", "options": { - "toolCall": { - "id": "call_loaXnCH3WRnbnROZzNbrRkHS", - "name": "getWeather", - "input": "{\"city\":\"San Francisco\"}" - } + "toolCall": [ + { + "id": "call_1SlugFuJ7rhwsmXd5aRnJhPe", + "name": "getWeather", + "input": "{\"city\":\"San Francisco\"}" + } + ] } }, { - "content": "The weather in San Francisco is 72 degrees", "role": "user", + "content": "The weather in San Francisco is 72 degrees", "options": { "toolResult": { "result": "The weather in San Francisco is 72 degrees", "isError": false, - "id": "call_loaXnCH3WRnbnROZzNbrRkHS" + "id": "call_1SlugFuJ7rhwsmXd5aRnJhPe" } } } @@ -58,11 +60,13 @@ "content": "", "role": "assistant", "options": { - "toolCall": { - "id": "call_loaXnCH3WRnbnROZzNbrRkHS", - "name": "getWeather", - "input": "{\"city\":\"San Francisco\"}" - } + "toolCall": [ + { + "id": "call_1SlugFuJ7rhwsmXd5aRnJhPe", + "name": "getWeather", + "input": "{\"city\":\"San Francisco\"}" + } + ] } } } @@ -72,7 +76,7 @@ "response": { "raw": null, "message": { - "content": "Arhgs! The weather in San Francisco is 72 degrees.", + "content": "Arhgs the weather in San Francisco be 72 degrees.", "role": "assistant", "options": {} } diff --git a/packages/core/e2e/node/snapshot/react-agent-stream.snap b/packages/core/e2e/node/snapshot/react-agent-stream.snap index df21c92f1..a7cf28444 100644 --- a/packages/core/e2e/node/snapshot/react-agent-stream.snap +++ b/packages/core/e2e/node/snapshot/react-agent-stream.snap @@ -41,7 +41,7 @@ "response": { "raw": null, "message": { - "content": "Thought: I need to use a tool to help me answer the question.\nAction: getWeather\nAction Input: {\"city\": \"San Francisco\"}", + "content": "Thought: I need to use a tool to help me answer the question. \nAction: getWeather\nAction Input: {\"city\": \"San Francisco\"}", "role": "assistant", "options": {} } @@ -177,7 +177,15 @@ "chunk": { "raw": null, "options": {}, - "delta": ".\n" + "delta": "." + } + }, + { + "id": "PRESERVE_0", + "chunk": { + "raw": null, + "options": {}, + "delta": " \n" } }, { diff --git a/packages/core/e2e/node/snapshot/react-agent.snap b/packages/core/e2e/node/snapshot/react-agent.snap index 290e2f877..32201c615 100644 --- a/packages/core/e2e/node/snapshot/react-agent.snap +++ b/packages/core/e2e/node/snapshot/react-agent.snap @@ -41,7 +41,7 @@ "response": { "raw": null, "message": { - "content": "Thought: I need to use a tool to help me answer the question.\nAction: getWeather\nAction Input: {\"city\": \"San Francisco\"}", + "content": "Thought: I need to use a tool to help me answer the question. \nAction: getWeather\nAction Input: {\"city\": \"San Francisco\"}", "role": "assistant", "options": {} } diff --git a/packages/core/src/agent/anthropic.ts b/packages/core/src/agent/anthropic.ts index 03ad53c99..ac415b6e9 100644 --- a/packages/core/src/agent/anthropic.ts +++ b/packages/core/src/agent/anthropic.ts @@ -93,29 +93,31 @@ export class AnthropicAgent extends AgentRunner<Anthropic> { }); if ("toolCall" in options) { const { toolCall } = options; - const targetTool = tools.find( - (tool) => tool.metadata.name === toolCall.name, - ); - const toolOutput = await callTool( - targetTool, - toolCall, - step.context.logger, - ); - step.context.store.toolOutputs.push(toolOutput); - step.context.store.messages = [ - ...step.context.store.messages, - { - content: stringifyJSONToMessageContent(toolOutput.output), - role: "user", - options: { - toolResult: { - result: toolOutput.output, - isError: toolOutput.isError, - id: toolCall.id, + for (const call of toolCall) { + const targetTool = tools.find( + (tool) => tool.metadata.name === call.name, + ); + const toolOutput = await callTool( + targetTool, + call, + step.context.logger, + ); + step.context.store.toolOutputs.push(toolOutput); + step.context.store.messages = [ + ...step.context.store.messages, + { + content: stringifyJSONToMessageContent(toolOutput.output), + role: "user", + options: { + toolResult: { + result: toolOutput.output, + isError: toolOutput.isError, + id: call.id, + }, }, }, - }, - ]; + ]; + } } }; } diff --git a/packages/core/src/agent/openai.ts b/packages/core/src/agent/openai.ts index d5d75c004..0901a614f 100644 --- a/packages/core/src/agent/openai.ts +++ b/packages/core/src/agent/openai.ts @@ -76,31 +76,34 @@ export class OpenAIAgent extends AgentRunner<OpenAI> { }); if ("toolCall" in options) { const { toolCall } = options; - const targetTool = tools.find( - (tool) => tool.metadata.name === toolCall.name, - ); - const toolOutput = await callTool( - targetTool, - toolCall, - step.context.logger, - ); - step.context.store.toolOutputs.push(toolOutput); - step.context.store.messages = [ - ...step.context.store.messages, - { - role: "user" as const, - content: stringifyJSONToMessageContent(toolOutput.output), - options: { - toolResult: { - result: toolOutput.output, - isError: toolOutput.isError, - id: toolCall.id, + for (const call of toolCall) { + const targetTool = tools.find( + (tool) => tool.metadata.name === call.name, + ); + const toolOutput = await callTool( + targetTool, + call, + step.context.logger, + ); + step.context.store.toolOutputs.push(toolOutput); + step.context.store.messages = [ + ...step.context.store.messages, + { + role: "user" as const, + content: stringifyJSONToMessageContent(toolOutput.output), + options: { + toolResult: { + result: toolOutput.output, + isError: toolOutput.isError, + id: call.id, + }, }, }, - }, - ]; + ]; + } } } else { + debugger; const responseChunkStream = new ReadableStream< ChatResponseChunk<ToolCallLLMMessageOptions> >({ @@ -135,23 +138,25 @@ export class OpenAIAgent extends AgentRunner<OpenAI> { for await (const chunk of pipStream) { if (chunk.options && "toolCall" in chunk.options) { const toolCall = chunk.options.toolCall; - toolCalls.set(toolCall.id, toolCall); + toolCall.forEach((toolCall) => { + toolCalls.set(toolCall.id, toolCall); + }); } } + step.context.store.messages = [ + ...step.context.store.messages, + { + role: "assistant" as const, + content: "", + options: { + toolCall: [...toolCalls.values()], + }, + }, + ]; for (const toolCall of toolCalls.values()) { const targetTool = tools.find( (tool) => tool.metadata.name === toolCall.name, ); - step.context.store.messages = [ - ...step.context.store.messages, - { - role: "assistant" as const, - content: "", - options: { - toolCall, - }, - }, - ]; const toolOutput = await callTool( targetTool, toolCall, diff --git a/packages/core/src/llm/anthropic.ts b/packages/core/src/llm/anthropic.ts index 78f5b7461..58c1de066 100644 --- a/packages/core/src/llm/anthropic.ts +++ b/packages/core/src/llm/anthropic.ts @@ -194,12 +194,15 @@ export class Anthropic extends ToolCallLLM<AnthropicAdditionalChatOptions> { } satisfies TextBlockParam, ] : []), - { - type: "tool_use", - id: options.toolCall.id, - name: options.toolCall.name, - input: options.toolCall.input, - } satisfies ToolUseBlockParam, + ...options.toolCall.map( + (toolCall) => + ({ + type: "tool_use", + id: toolCall.id, + name: toolCall.name, + input: toolCall.input, + }) satisfies ToolUseBlockParam, + ), ] satisfies ToolsBetaContentBlock[], } satisfies ToolsBetaMessageParam; } @@ -326,7 +329,7 @@ export class Anthropic extends ToolCallLLM<AnthropicAdditionalChatOptions> { } const response = await anthropic.beta.tools.messages.create(params); - const toolUseBlock = response.content.find( + const toolUseBlock = response.content.filter( (content): content is ToolUseBlock => content.type === "tool_use", ); @@ -340,15 +343,16 @@ export class Anthropic extends ToolCallLLM<AnthropicAdditionalChatOptions> { text: content.text, })), role: "assistant", - options: toolUseBlock - ? { - toolCall: { - id: toolUseBlock.id, - name: toolUseBlock.name, - input: toolUseBlock.input, - }, - } - : {}, + options: + toolUseBlock.length > 0 + ? { + toolCall: toolUseBlock.map((block) => ({ + id: block.id, + name: block.name, + input: block.input, + })), + } + : {}, }, }; } else { diff --git a/packages/core/src/llm/openai.ts b/packages/core/src/llm/openai.ts index 288ed4558..53f6cf7c6 100644 --- a/packages/core/src/llm/openai.ts +++ b/packages/core/src/llm/openai.ts @@ -279,19 +279,19 @@ export class OpenAI extends ToolCallLLM<OpenAIAdditionalChatOptions> { return { role: "assistant", content: extractText(message.content), - tool_calls: [ - { - id: options.toolCall.id, + tool_calls: options.toolCall.map((toolCall) => { + return { + id: toolCall.id, type: "function", function: { - name: options.toolCall.name, + name: toolCall.name, arguments: - typeof options.toolCall.input === "string" - ? options.toolCall.input - : JSON.stringify(options.toolCall.input), + typeof toolCall.input === "string" + ? toolCall.input + : JSON.stringify(toolCall.input), }, - }, - ], + }; + }), } satisfies ChatCompletionAssistantMessageParam; } else if (message.role === "user") { return { @@ -380,12 +380,13 @@ export class OpenAI extends ToolCallLLM<OpenAIAdditionalChatOptions> { role: response.choices[0].message.role, options: response.choices[0].message?.tool_calls ? { - toolCall: { - id: response.choices[0].message.tool_calls[0].id, - name: response.choices[0].message.tool_calls[0].function.name, - input: - response.choices[0].message.tool_calls[0].function.arguments, - }, + toolCall: response.choices[0].message.tool_calls.map( + (toolCall) => ({ + id: toolCall.id, + name: toolCall.function.name, + input: toolCall.function.arguments, + }), + ), } : {}, }, @@ -459,10 +460,10 @@ export class OpenAI extends ToolCallLLM<OpenAIAdditionalChatOptions> { yield { raw: part, options: shouldEmitToolCall - ? { toolCall: shouldEmitToolCall } + ? { toolCall: [shouldEmitToolCall] } : currentToolCall ? { - toolCall: currentToolCall, + toolCall: [currentToolCall], } : {}, delta: choice.delta.content ?? "", diff --git a/packages/core/src/llm/types.ts b/packages/core/src/llm/types.ts index 4060298c9..fec84cf31 100644 --- a/packages/core/src/llm/types.ts +++ b/packages/core/src/llm/types.ts @@ -212,7 +212,7 @@ export type ToolResult = { }; export type ToolCallOptions = { - toolCall: ToolCall | PartialToolCall; + toolCall: (ToolCall | PartialToolCall)[]; }; export type ToolResultOptions = { -- GitLab