diff --git a/examples/how-tos/wait-user-input.ipynb b/examples/how-tos/wait-user-input.ipynb index 3f6cb900..09270d47 100644 --- a/examples/how-tos/wait-user-input.ipynb +++ b/examples/how-tos/wait-user-input.ipynb @@ -72,35 +72,30 @@ "metadata": {}, "outputs": [], "source": [ - "import { StateGraph, START, END } from \"@langchain/langgraph\";\n", + "import { StateGraph, Annotation, START, END } from \"@langchain/langgraph\";\n", "import { MemorySaver } from \"@langchain/langgraph\";\n", "\n", - "type GraphState = {\n", - " input: string;\n", - " userFeedback: string;\n", - "}\n", + "const GraphState = Annotation.Root({\n", + " input: Annotation,\n", + " userFeedback: Annotation\n", + "});\n", "\n", - "const step1 = (state: GraphState): Partial => {\n", - " console.log(\"--- Step 1 ---\");\n", - " return state;\n", + "const step1 = (state: typeof GraphState.State) => {\n", + " console.log(\"---Step 1---\");\n", + " return state;\n", "}\n", "\n", - "const humanFeedback = (state: GraphState): Partial => {\n", - " console.log(\"--- humanFeedback ---\");\n", - " return state;\n", + "const humanFeedback = (state: typeof GraphState.State) => {\n", + " console.log(\"--- humanFeedback ---\");\n", + " return state;\n", "}\n", "\n", - "const step3 = (state: GraphState): Partial => {\n", - " console.log(\"--- Step 3 ---\");\n", - " return state;\n", + "const step3 = (state: typeof GraphState.State) => {\n", + " console.log(\"---Step 3---\");\n", + " return state;\n", "}\n", "\n", - "const builder = new StateGraph({\n", - " channels: {\n", - " input: null,\n", - " userFeedback: null,\n", - " }\n", - "})\n", + "const builder = new StateGraph(GraphState)\n", " .addNode(\"step1\", step1)\n", " .addNode(\"humanFeedback\", humanFeedback)\n", " .addNode(\"step3\", step3)\n", @@ -115,17 +110,25 @@ "\n", "// Add \n", "const graph = builder.compile({\n", - " checkpointer: memory,\n", - " interruptBefore: [\"humanFeedback\"]\n", + " checkpointer: memory,\n", + " interruptBefore: [\"humanFeedback\"]\n", "});" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 2, "id": "9e990a56", "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "image/png": "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" + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ "import * as tslab from \"tslab\";\n", "\n", @@ -146,7 +149,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 3, "id": "eb8e7d47-e7c9-4217-b72c-08394a2c4d3e", "metadata": {}, "outputs": [ @@ -155,7 +158,7 @@ "output_type": "stream", "text": [ "--- hello world ---\n", - "--- Step 1 ---\n", + "---Step 1---\n", "--- hello world ---\n", "--- GRAPH INTERRUPTED ---\n" ] @@ -170,7 +173,7 @@ "\n", "// Run the graph until the first interruption\n", "for await (const event of await graph.stream(initialInput, config)) {\n", - " console.log(`--- ${event.input} ---`);\n", + " console.log(`--- ${event.input} ---`);\n", "}\n", "\n", "// Will log when the graph is interrupted, after step 2.\n", @@ -187,7 +190,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 4, "id": "2165a1bc-1c5b-411f-9e9c-a2b9627e5d56", "metadata": {}, "outputs": [ @@ -197,35 +200,20 @@ "text": [ "--- State after update ---\n", "{\n", - " values: { input: \"hello world\", userFeedback: \"Go to step 3!!\" },\n", - " next: [ \"humanFeedback\" ],\n", - " metadata: {\n", - " source: \"update\",\n", - " step: 20,\n", - " writes: {\n", - " step1: { userFeedback: \"Go to step 3!!\", asNode: \"humanFeedback\" }\n", - " }\n", - " },\n", + " values: { input: 'hello world', userFeedback: 'Go to step 3!!' },\n", + " next: [ 'humanFeedback' ],\n", + " metadata: { source: 'update', step: 2, writes: { step1: [Object] } },\n", " config: {\n", " configurable: {\n", - " thread_id: \"1\",\n", - " checkpoint_id: \"1ef46202-3ded-6a80-8014-72aef304b6c8\"\n", + " thread_id: '1',\n", + " checkpoint_id: '1ef5e8fb-89dd-6360-8002-5ff9e3c15c57'\n", " }\n", " },\n", - " createdAt: \"2024-07-19T22:42:12.648Z\",\n", + " createdAt: '2024-08-20T01:01:24.246Z',\n", " parentConfig: undefined\n", - "}\n" + "}\n", + "[ 'humanFeedback' ]\n" ] - }, - { - "data": { - "text/plain": [ - "[ \u001b[32m\"humanFeedback\"\u001b[39m ]" - ] - }, - "execution_count": 9, - "metadata": {}, - "output_type": "execute_result" } ], "source": [ @@ -253,7 +241,7 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 5, "id": "3cca588f-e8d8-416b-aba7-0f3ae5e51598", "metadata": {}, "outputs": [ @@ -263,7 +251,7 @@ "text": [ "--- humanFeedback ---\n", "--- hello world ---\n", - "--- Step 3 ---\n", + "---Step 3---\n", "--- hello world ---\n" ] } @@ -271,7 +259,7 @@ "source": [ "// Continue the graph execution\n", "for await (const event of await graph.stream(null, config)) {\n", - " console.log(`--- ${event.input} ---`);\n", + " console.log(`--- ${event.input} ---`);\n", "}" ] }, @@ -285,19 +273,16 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 6, "id": "2b83e5ca-8497-43ca-bff7-7203e654c4d3", "metadata": {}, "outputs": [ { - "data": { - "text/plain": [ - "{ input: \u001b[32m\"hello world\"\u001b[39m, userFeedback: \u001b[32m\"Go to step 3!!\"\u001b[39m }" - ] - }, - "execution_count": 11, - "metadata": {}, - "output_type": "execute_result" + "name": "stdout", + "output_type": "stream", + "text": [ + "{ input: 'hello world', userFeedback: 'Go to step 3!!' }\n" + ] } ], "source": [ @@ -328,37 +313,38 @@ "// Set up the tool\n", "import { ChatAnthropic } from \"@langchain/anthropic\";\n", "import { tool } from \"@langchain/core/tools\";\n", - "import { StateGraph, START, END } from \"@langchain/langgraph\";\n", + "import { StateGraph, Annotation, START, END, messagesStateReducer } from \"@langchain/langgraph\";\n", "import { MemorySaver } from \"@langchain/langgraph\";\n", "import { ToolNode } from \"@langchain/langgraph/prebuilt\";\n", "import { BaseMessage, AIMessage } from \"@langchain/core/messages\";\n", "import { z } from \"zod\";\n", - "import { v4 as uuidv4 } from \"uuid\";\n", "\n", - "interface MessagesState {\n", - " messages: BaseMessage[];\n", - "}\n", + "const GraphMessagesState = Annotation.Root({\n", + " messages: Annotation({\n", + " reducer: messagesStateReducer,\n", + " }),\n", + "});\n", "\n", - "const search = tool((input) => {\n", - " return \"It's sunny in San Francisco, but you better look out if you're a Gemini 😈.\";\n", + "const search = tool((_) => {\n", + " return \"It's sunny in San Francisco, but you better look out if you're a Gemini 😈.\";\n", "}, {\n", - " name: \"search\",\n", - " description: \"Call to surf the web.\",\n", - " schema: z.string(),\n", + " name: \"search\",\n", + " description: \"Call to surf the web.\",\n", + " schema: z.string(),\n", "})\n", "\n", "const tools = [search]\n", - "const toolNode = new ToolNode(tools)\n", + "const toolNode = new ToolNode(tools)\n", "\n", "// Set up the model\n", "const model = new ChatAnthropic({ model: \"claude-3-5-sonnet-20240620\" })\n", "\n", - "const askHumanTool = tool((input: string) => {\n", - " return \"The human said XYZ\";\n", + "const askHumanTool = tool((_) => {\n", + " return \"The human said XYZ\";\n", "}, {\n", - " name: \"askHuman\",\n", - " description: \"Ask the human for input.\",\n", - " schema: z.string(),\n", + " name: \"askHuman\",\n", + " description: \"Ask the human for input.\",\n", + " schema: z.string(),\n", "});\n", "\n", "\n", @@ -367,150 +353,97 @@ "// Define nodes and conditional edges\n", "\n", "// Define the function that determines whether to continue or not\n", - "function shouldContinue(state: MessagesState): \"action\" | \"askHuman\" | typeof END {\n", - " const lastMessage = state.messages[state.messages.length - 1];\n", - " const castLastMessage = lastMessage as AIMessage;\n", - " // If there is no function call, then we finish\n", - " if (castLastMessage && !castLastMessage.tool_calls?.length) {\n", - " return END;\n", - " }\n", - " // If tool call is askHuman, we return that node\n", - " // You could also add logic here to let some system know that there's something that requires Human input\n", - " // For example, send a slack message, etc\n", - " if (castLastMessage.tool_calls?.[0]?.name === \"askHuman\") {\n", - " console.log(\"--- ASKING HUMAN ---\")\n", - " return \"askHuman\";\n", - " }\n", - " // Otherwise if it isn't, we continue with the action node\n", - " return \"action\";\n", + "function shouldContinue(state: typeof GraphMessagesState.State): \"action\" | \"askHuman\" | typeof END {\n", + " const lastMessage = state.messages[state.messages.length - 1];\n", + " const castLastMessage = lastMessage as AIMessage;\n", + " // If there is no function call, then we finish\n", + " if (castLastMessage && !castLastMessage.tool_calls?.length) {\n", + " return END;\n", + " }\n", + " // If tool call is askHuman, we return that node\n", + " // You could also add logic here to let some system know that there's something that requires Human input\n", + " // For example, send a slack message, etc\n", + " if (castLastMessage.tool_calls?.[0]?.name === \"askHuman\") {\n", + " console.log(\"--- ASKING HUMAN ---\")\n", + " return \"askHuman\";\n", + " }\n", + " // Otherwise if it isn't, we continue with the action node\n", + " return \"action\";\n", "}\n", "\n", "\n", "// Define the function that calls the model\n", - "async function callModel(state: MessagesState): Promise> {\n", - " const messages = state.messages;\n", - " const response = await modelWithTools.invoke(messages);\n", - " // We return an object with a messages property, because this will get added to the existing list\n", - " return { messages: [response] };\n", + "async function callModel(state: typeof GraphMessagesState.State): Promise> {\n", + " const messages = state.messages;\n", + " const response = await modelWithTools.invoke(messages);\n", + " // We return an object with a messages property, because this will get added to the existing list\n", + " return { messages: [response] };\n", "}\n", "\n", "\n", "// We define a fake node to ask the human\n", - "function askHuman(state: MessagesState): Partial {\n", - " return state;\n", - "}\n", - "\n", - "\n", - "type Messages = BaseMessage | BaseMessage[]\n", - "\n", - "// Define our addMessages function which will add or merge messages to state\n", - "function addMessages(left: Messages, right: Messages): BaseMessage[] {\n", - " // Coerce to list\n", - " const leftList = Array.isArray(left) ? left : [left];\n", - " const rightList = Array.isArray(right) ? right : [right];\n", - "\n", - " // Assign missing ids\n", - " leftList.forEach(m => {\n", - " if (!m.id) m.id = uuidv4();\n", - " });\n", - " rightList.forEach(m => {\n", - " if (!m.id) m.id = uuidv4();\n", - " });\n", - "\n", - " // Merge\n", - " const leftIdxById = new Map(leftList.map((m, i) => [m.id, i]));\n", - " const merged = [...leftList];\n", - " const idsToRemove = new Set();\n", - "\n", - " for (const m of rightList) {\n", - " const existingIdx = leftIdxById.get(m.id!);\n", - " if (existingIdx !== undefined) {\n", - " if (m._getType() === 'remove' && m.id) {\n", - " idsToRemove.add(m.id);\n", - " } else {\n", - " merged[existingIdx] = m;\n", - " }\n", - " } else {\n", - " if (m._getType() === 'remove') {\n", - " throw new Error(`Attempting to delete a message with an ID that doesn't exist ('${m.id}')`);\n", - " }\n", - " merged.push(m);\n", - " }\n", - " }\n", - "\n", - " return merged.filter(m => !idsToRemove.has(m.id!));\n", + "function askHuman(state: typeof GraphMessagesState.State): Partial {\n", + " return state;\n", "}\n", "\n", "// Define a new graph\n", - "const workflow = new StateGraph({\n", - " channels: {\n", - " messages: {\n", - " reducer: addMessages\n", - " },\n", - " }\n", - "})\n", - " // Define the two nodes we will cycle between\n", - " .addNode(\"agent\", callModel)\n", - " .addNode(\"action\", toolNode)\n", - " .addNode(\"askHuman\", askHuman)\n", - " // We now add a conditional edge\n", - " .addConditionalEdges(\n", - " // First, we define the start node. We use `agent`.\n", - " // This means these are the edges taken after the `agent` node is called.\n", - " \"agent\",\n", - " // Next, we pass in the function that will determine which node is called next.\n", - " shouldContinue\n", - " )\n", - " // We now add a normal edge from `action` to `agent`.\n", - " // This means that after `action` is called, `agent` node is called next.\n", - " .addEdge(\"action\", \"agent\")\n", - " // After we get back the human response, we go back to the agent\n", - " .addEdge(\"askHuman\", \"agent\")\n", - " // Set the entrypoint as `agent`\n", - " // This means that this node is the first one called\n", - " .addEdge(START, \"agent\");\n", + "const messagesWorkflow = new StateGraph(GraphMessagesState)\n", + " // Define the two nodes we will cycle between\n", + " .addNode(\"agent\", callModel)\n", + " .addNode(\"action\", toolNode)\n", + " .addNode(\"askHuman\", askHuman)\n", + " // We now add a conditional edge\n", + " .addConditionalEdges(\n", + " // First, we define the start node. We use `agent`.\n", + " // This means these are the edges taken after the `agent` node is called.\n", + " \"agent\",\n", + " // Next, we pass in the function that will determine which node is called next.\n", + " shouldContinue\n", + " )\n", + " // We now add a normal edge from `action` to `agent`.\n", + " // This means that after `action` is called, `agent` node is called next.\n", + " .addEdge(\"action\", \"agent\")\n", + " // After we get back the human response, we go back to the agent\n", + " .addEdge(\"askHuman\", \"agent\")\n", + " // Set the entrypoint as `agent`\n", + " // This means that this node is the first one called\n", + " .addEdge(START, \"agent\");\n", "\n", "\n", "// Setup memory\n", - "const memory = new MemorySaver();\n", + "const messagesMemory = new MemorySaver();\n", "\n", "// Finally, we compile it!\n", "// This compiles it into a LangChain Runnable,\n", "// meaning you can use it as you would any other runnable\n", - "const app = workflow.compile({\n", - " checkpointer: memory,\n", + "const messagesApp = messagesWorkflow.compile({\n", + " checkpointer: messagesMemory,\n", " interruptBefore: [\"askHuman\"]\n", "});" ] }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 2, "id": "4b816850", "metadata": {}, "outputs": [ { - "ename": "Error", - "evalue": "Not ready", - "output_type": "error", - "traceback": [ - "Stack trace:", - "Error: Not ready", - " at DisplayImpl.raw (file:///Users/bracesproul/Library/Caches/deno/npm/registry.npmjs.org/tslab/1.0.22/dist/public.js:83:19)", - " at DisplayImpl.png (file:///Users/bracesproul/Library/Caches/deno/npm/registry.npmjs.org/tslab/1.0.22/dist/public.js:67:14)", - " at :5:21", - " at eventLoopTick (ext:core/01_core.js:63:7)" - ] + "data": { + "image/png": "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" + }, + "metadata": {}, + "output_type": "display_data" } ], "source": [ "import * as tslab from \"tslab\";\n", "\n", - "const drawableGraph = app.getGraph();\n", - "const image = await drawableGraph.drawMermaidPng();\n", - "const arrayBuffer = await image.arrayBuffer();\n", + "const drawableGraph2 = messagesApp.getGraph();\n", + "const image2 = await drawableGraph2.drawMermaidPng();\n", + "const arrayBuffer2 = await image2.arrayBuffer();\n", "\n", - "await tslab.display.png(new Uint8Array(arrayBuffer));" + "await tslab.display.png(new Uint8Array(arrayBuffer2));" ] }, { @@ -527,7 +460,7 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 3, "id": "cfd140f0-a5a6-4697-8115-322242f197b5", "metadata": {}, "outputs": [ @@ -541,19 +474,19 @@ "================================ ai Message (1) =================================\n", "[\n", " {\n", - " type: \"text\",\n", - " text: \"Certainly! I'll use the askHuman tool to ask the user where they are, and then I'll use the search t\"... 96 more characters\n", + " type: 'text',\n", + " text: \"Certainly! I'll use the askHuman tool to ask the user about their location, and then use the search tool to look up the weather for that location. Let's start by asking the user where they are.\"\n", " },\n", " {\n", - " type: \"tool_use\",\n", - " id: \"toolu_01VF5p3e6Qyi3i2YwoTVZBGc\",\n", - " name: \"askHuman\",\n", + " type: 'tool_use',\n", + " id: 'toolu_01RN181HAAL5BcnMXkexbA1r',\n", + " name: 'askHuman',\n", " input: {\n", - " input: \"Where are you located? Please provide your city and country.\"\n", + " input: 'Where are you located? Please provide your city and country.'\n", " }\n", " }\n", "]\n", - "next: [ \"askHuman\" ]\n" + "next: [ 'askHuman' ]\n" ] } ], @@ -563,17 +496,17 @@ "const inputs = new HumanMessage(\"Use the search tool to ask the user where they are, then look up the weather there\");\n", "\n", "// Thread\n", - "const config = { configurable: { thread_id: \"3\" }, streamMode: \"values\" as const };\n", - "\n", - "for await (const event of await app.stream({\n", - " messages: [inputs]\n", - "}, config)) {\n", - " const recentMsg = event.messages[event.messages.length - 1];\n", - " console.log(`================================ ${recentMsg._getType()} Message (1) =================================`)\n", - " console.log(recentMsg.content);\n", + "const config2 = { configurable: { thread_id: \"3\" }, streamMode: \"values\" as const };\n", + "\n", + "for await (const event of await messagesApp.stream({\n", + " messages: [inputs]\n", + "}, config2)) {\n", + " const recentMsg = event.messages[event.messages.length - 1];\n", + " console.log(`================================ ${recentMsg._getType()} Message (1) =================================`)\n", + " console.log(recentMsg.content);\n", "}\n", "\n", - "console.log(\"next: \", (await app.getState(config)).next)" + "console.log(\"next: \", (await messagesApp.getState(config2)).next)" ] }, { @@ -588,7 +521,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 4, "id": "63598092-d565-4170-9773-e092d345f8c1", "metadata": {}, "outputs": [ @@ -596,39 +529,39 @@ "name": "stdout", "output_type": "stream", "text": [ - "next before update state: [ \"askHuman\" ]\n", - "next AFTER update state: [ \"askHuman\" ]\n" + "next before update state: [ 'askHuman' ]\n", + "next AFTER update state: [ 'agent' ]\n" ] } ], "source": [ "import { ToolMessage } from \"@langchain/core/messages\";\n", "\n", - "const currentState = await app.getState(config);\n", + "const currentState = await messagesApp.getState(config2);\n", "\n", "const toolCallId = currentState.values.messages[currentState.values.messages.length - 1].tool_calls[0].id;\n", "\n", "// We now create the tool call with the id and the response we want\n", "const toolMessage = new ToolMessage({\n", - " tool_call_id: toolCallId,\n", - " content: \"san francisco\"\n", + " tool_call_id: toolCallId,\n", + " content: \"san francisco\"\n", "});\n", "\n", - "console.log(\"next before update state: \", (await app.getState(config)).next)\n", + "console.log(\"next before update state: \", (await messagesApp.getState(config2)).next)\n", "\n", "// We now update the state\n", "// Notice that we are also specifying `asNode: \"askHuman\"`\n", "// This will apply this update as this node,\n", "// which will make it so that afterwards it continues as normal\n", - "await app.updateState(config, { messages: [toolMessage] });\n", + "await messagesApp.updateState(config2, { messages: [toolMessage] }, \"askHuman\");\n", "\n", "// We can check the state\n", "// We can see that the state currently has the `agent` node next\n", "// This is based on how we define our graph,\n", "// where after the `askHuman` node goes (which we just triggered)\n", "// there is an edge to the `agent` node\n", - "console.log(\"next AFTER update state: \", (await app.getState(config)).next)\n", - "// await app.getState(config)" + "console.log(\"next AFTER update state: \", (await messagesApp.getState(config2)).next)\n", + "// await messagesApp.getState(config)" ] }, { @@ -641,7 +574,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 5, "id": "a9f599b5-1a55-406b-a76b-f52b3ca06975", "metadata": {}, "outputs": [ @@ -652,21 +585,21 @@ "{\n", " messages: [\n", " HumanMessage {\n", - " \"id\": \"f89d75ad-9702-4605-a5ab-b6152bfc8c05\",\n", + " \"id\": \"a80d5763-0f27-4a00-9e54-8a239b499ea1\",\n", " \"content\": \"Use the search tool to ask the user where they are, then look up the weather there\",\n", " \"additional_kwargs\": {},\n", " \"response_metadata\": {}\n", " },\n", " AIMessage {\n", - " \"id\": \"1a7e49e0-95e8-42af-9488-316907e8bfd2\",\n", + " \"id\": \"msg_01CsrDn46VqNXrdkpVHbcMKA\",\n", " \"content\": [\n", " {\n", " \"type\": \"text\",\n", - " \"text\": \"Certainly! I'll use the askHuman tool to ask the user where they are, and then I'll use the search tool to look up the weather for that location. Let's start by asking the user for their location.\"\n", + " \"text\": \"Certainly! I'll use the askHuman tool to ask the user about their location, and then use the search tool to look up the weather for that location. Let's start by asking the user where they are.\"\n", " },\n", " {\n", " \"type\": \"tool_use\",\n", - " \"id\": \"toolu_01VF5p3e6Qyi3i2YwoTVZBGc\",\n", + " \"id\": \"toolu_01RN181HAAL5BcnMXkexbA1r\",\n", " \"name\": \"askHuman\",\n", " \"input\": {\n", " \"input\": \"Where are you located? Please provide your city and country.\"\n", @@ -674,7 +607,7 @@ " }\n", " ],\n", " \"additional_kwargs\": {\n", - " \"id\": \"msg_01BfBxARH5hF2dyP6VXzeLjt\",\n", + " \"id\": \"msg_01CsrDn46VqNXrdkpVHbcMKA\",\n", " \"type\": \"message\",\n", " \"role\": \"assistant\",\n", " \"model\": \"claude-3-5-sonnet-20240620\",\n", @@ -682,18 +615,20 @@ " \"stop_sequence\": null,\n", " \"usage\": {\n", " \"input_tokens\": 465,\n", - " \"output_tokens\": 110\n", + " \"output_tokens\": 108\n", " }\n", " },\n", " \"response_metadata\": {\n", - " \"id\": \"msg_01BfBxARH5hF2dyP6VXzeLjt\",\n", + " \"id\": \"msg_01CsrDn46VqNXrdkpVHbcMKA\",\n", " \"model\": \"claude-3-5-sonnet-20240620\",\n", " \"stop_reason\": \"tool_use\",\n", " \"stop_sequence\": null,\n", " \"usage\": {\n", " \"input_tokens\": 465,\n", - " \"output_tokens\": 110\n", - " }\n", + " \"output_tokens\": 108\n", + " },\n", + " \"type\": \"message\",\n", + " \"role\": \"assistant\"\n", " },\n", " \"tool_calls\": [\n", " {\n", @@ -701,75 +636,443 @@ " \"args\": {\n", " \"input\": \"Where are you located? Please provide your city and country.\"\n", " },\n", - " \"id\": \"toolu_01VF5p3e6Qyi3i2YwoTVZBGc\"\n", + " \"id\": \"toolu_01RN181HAAL5BcnMXkexbA1r\",\n", + " \"type\": \"tool_call\"\n", " }\n", " ],\n", " \"invalid_tool_calls\": []\n", + " },\n", + " ToolMessage {\n", + " \"id\": \"9159f841-0e15-4366-96a9-cc5ee0662da0\",\n", + " \"content\": \"san francisco\",\n", + " \"additional_kwargs\": {},\n", + " \"response_metadata\": {},\n", + " \"tool_call_id\": \"toolu_01RN181HAAL5BcnMXkexbA1r\"\n", + " },\n", + " AIMessage {\n", + " \"id\": \"msg_017hfZ8kdhX5nKD97THKWpPx\",\n", + " \"content\": [\n", + " {\n", + " \"type\": \"text\",\n", + " \"text\": \"Thank you for providing your location. Now, I'll use the search tool to look up the weather in San Francisco.\"\n", + " },\n", + " {\n", + " \"type\": \"tool_use\",\n", + " \"id\": \"toolu_01QCcxzRjojWW5JqQp7WTN82\",\n", + " \"name\": \"search\",\n", + " \"input\": {\n", + " \"input\": \"current weather in San Francisco\"\n", + " }\n", + " }\n", + " ],\n", + " \"additional_kwargs\": {\n", + " \"id\": \"msg_017hfZ8kdhX5nKD97THKWpPx\",\n", + " \"type\": \"message\",\n", + " \"role\": \"assistant\",\n", + " \"model\": \"claude-3-5-sonnet-20240620\",\n", + " \"stop_reason\": \"tool_use\",\n", + " \"stop_sequence\": null,\n", + " \"usage\": {\n", + " \"input_tokens\": 587,\n", + " \"output_tokens\": 81\n", + " }\n", + " },\n", + " \"response_metadata\": {\n", + " \"id\": \"msg_017hfZ8kdhX5nKD97THKWpPx\",\n", + " \"model\": \"claude-3-5-sonnet-20240620\",\n", + " \"stop_reason\": \"tool_use\",\n", + " \"stop_sequence\": null,\n", + " \"usage\": {\n", + " \"input_tokens\": 587,\n", + " \"output_tokens\": 81\n", + " },\n", + " \"type\": \"message\",\n", + " \"role\": \"assistant\"\n", + " },\n", + " \"tool_calls\": [\n", + " {\n", + " \"name\": \"search\",\n", + " \"args\": {\n", + " \"input\": \"current weather in San Francisco\"\n", + " },\n", + " \"id\": \"toolu_01QCcxzRjojWW5JqQp7WTN82\",\n", + " \"type\": \"tool_call\"\n", + " }\n", + " ],\n", + " \"invalid_tool_calls\": [],\n", + " \"usage_metadata\": {\n", + " \"input_tokens\": 587,\n", + " \"output_tokens\": 81,\n", + " \"total_tokens\": 668\n", + " }\n", " }\n", " ]\n", "}\n", "================================ ai Message (1) =================================\n", "[\n", " {\n", - " type: \"text\",\n", - " text: \"Certainly! I'll use the askHuman tool to ask the user where they are, and then I'll use the search t\"... 96 more characters\n", + " type: 'text',\n", + " text: \"Thank you for providing your location. Now, I'll use the search tool to look up the weather in San Francisco.\"\n", " },\n", " {\n", - " type: \"tool_use\",\n", - " id: \"toolu_01VF5p3e6Qyi3i2YwoTVZBGc\",\n", - " name: \"askHuman\",\n", - " input: {\n", - " input: \"Where are you located? Please provide your city and country.\"\n", - " }\n", + " type: 'tool_use',\n", + " id: 'toolu_01QCcxzRjojWW5JqQp7WTN82',\n", + " name: 'search',\n", + " input: { input: 'current weather in San Francisco' }\n", " }\n", - "]\n" - ] - }, - { - "ename": "Error", - "evalue": "400 {\"type\":\"error\",\"error\":{\"type\":\"invalid_request_error\",\"message\":\"Your API request included an `assistant` message in the final position, which would pre-fill the `assistant` response. When using tools, pre-filling the `assistant` response is not supported.\"}}", - "output_type": "error", - "traceback": [ - "Stack trace:", - "Error: 400 {\"type\":\"error\",\"error\":{\"type\":\"invalid_request_error\",\"message\":\"Your API request included an `assistant` message in the final position, which would pre-fill the `assistant` response. When using tools, pre-filling the `assistant` response is not supported.\"}}", - " at Function.generate (file:///Users/bracesproul/Library/Caches/deno/npm/registry.npmjs.org/@anthropic-ai/sdk/0.21.0/error.mjs:36:20)", - " at Anthropic.makeStatusError (file:///Users/bracesproul/Library/Caches/deno/npm/registry.npmjs.org/@anthropic-ai/sdk/0.21.0/core.mjs:256:25)", - " at Anthropic.makeRequest (file:///Users/bracesproul/Library/Caches/deno/npm/registry.npmjs.org/@anthropic-ai/sdk/0.21.0/core.mjs:299:30)", - " at eventLoopTick (ext:core/01_core.js:63:7)", - " at async RetryOperation._fn (file:///Users/bracesproul/Library/Caches/deno/npm/registry.npmjs.org/p-retry/4.6.2/index.js:50:12)" + "]\n", + "{\n", + " messages: [\n", + " HumanMessage {\n", + " \"id\": \"a80d5763-0f27-4a00-9e54-8a239b499ea1\",\n", + " \"content\": \"Use the search tool to ask the user where they are, then look up the weather there\",\n", + " \"additional_kwargs\": {},\n", + " \"response_metadata\": {}\n", + " },\n", + " AIMessage {\n", + " \"id\": \"msg_01CsrDn46VqNXrdkpVHbcMKA\",\n", + " \"content\": [\n", + " {\n", + " \"type\": \"text\",\n", + " \"text\": \"Certainly! I'll use the askHuman tool to ask the user about their location, and then use the search tool to look up the weather for that location. Let's start by asking the user where they are.\"\n", + " },\n", + " {\n", + " \"type\": \"tool_use\",\n", + " \"id\": \"toolu_01RN181HAAL5BcnMXkexbA1r\",\n", + " \"name\": \"askHuman\",\n", + " \"input\": {\n", + " \"input\": \"Where are you located? Please provide your city and country.\"\n", + " }\n", + " }\n", + " ],\n", + " \"additional_kwargs\": {\n", + " \"id\": \"msg_01CsrDn46VqNXrdkpVHbcMKA\",\n", + " \"type\": \"message\",\n", + " \"role\": \"assistant\",\n", + " \"model\": \"claude-3-5-sonnet-20240620\",\n", + " \"stop_reason\": \"tool_use\",\n", + " \"stop_sequence\": null,\n", + " \"usage\": {\n", + " \"input_tokens\": 465,\n", + " \"output_tokens\": 108\n", + " }\n", + " },\n", + " \"response_metadata\": {\n", + " \"id\": \"msg_01CsrDn46VqNXrdkpVHbcMKA\",\n", + " \"model\": \"claude-3-5-sonnet-20240620\",\n", + " \"stop_reason\": \"tool_use\",\n", + " \"stop_sequence\": null,\n", + " \"usage\": {\n", + " \"input_tokens\": 465,\n", + " \"output_tokens\": 108\n", + " },\n", + " \"type\": \"message\",\n", + " \"role\": \"assistant\"\n", + " },\n", + " \"tool_calls\": [\n", + " {\n", + " \"name\": \"askHuman\",\n", + " \"args\": {\n", + " \"input\": \"Where are you located? Please provide your city and country.\"\n", + " },\n", + " \"id\": \"toolu_01RN181HAAL5BcnMXkexbA1r\",\n", + " \"type\": \"tool_call\"\n", + " }\n", + " ],\n", + " \"invalid_tool_calls\": []\n", + " },\n", + " ToolMessage {\n", + " \"id\": \"9159f841-0e15-4366-96a9-cc5ee0662da0\",\n", + " \"content\": \"san francisco\",\n", + " \"additional_kwargs\": {},\n", + " \"response_metadata\": {},\n", + " \"tool_call_id\": \"toolu_01RN181HAAL5BcnMXkexbA1r\"\n", + " },\n", + " AIMessage {\n", + " \"id\": \"msg_017hfZ8kdhX5nKD97THKWpPx\",\n", + " \"content\": [\n", + " {\n", + " \"type\": \"text\",\n", + " \"text\": \"Thank you for providing your location. Now, I'll use the search tool to look up the weather in San Francisco.\"\n", + " },\n", + " {\n", + " \"type\": \"tool_use\",\n", + " \"id\": \"toolu_01QCcxzRjojWW5JqQp7WTN82\",\n", + " \"name\": \"search\",\n", + " \"input\": {\n", + " \"input\": \"current weather in San Francisco\"\n", + " }\n", + " }\n", + " ],\n", + " \"additional_kwargs\": {\n", + " \"id\": \"msg_017hfZ8kdhX5nKD97THKWpPx\",\n", + " \"type\": \"message\",\n", + " \"role\": \"assistant\",\n", + " \"model\": \"claude-3-5-sonnet-20240620\",\n", + " \"stop_reason\": \"tool_use\",\n", + " \"stop_sequence\": null,\n", + " \"usage\": {\n", + " \"input_tokens\": 587,\n", + " \"output_tokens\": 81\n", + " }\n", + " },\n", + " \"response_metadata\": {\n", + " \"id\": \"msg_017hfZ8kdhX5nKD97THKWpPx\",\n", + " \"model\": \"claude-3-5-sonnet-20240620\",\n", + " \"stop_reason\": \"tool_use\",\n", + " \"stop_sequence\": null,\n", + " \"usage\": {\n", + " \"input_tokens\": 587,\n", + " \"output_tokens\": 81\n", + " },\n", + " \"type\": \"message\",\n", + " \"role\": \"assistant\"\n", + " },\n", + " \"tool_calls\": [\n", + " {\n", + " \"name\": \"search\",\n", + " \"args\": {\n", + " \"input\": \"current weather in San Francisco\"\n", + " },\n", + " \"id\": \"toolu_01QCcxzRjojWW5JqQp7WTN82\",\n", + " \"type\": \"tool_call\"\n", + " }\n", + " ],\n", + " \"invalid_tool_calls\": [],\n", + " \"usage_metadata\": {\n", + " \"input_tokens\": 587,\n", + " \"output_tokens\": 81,\n", + " \"total_tokens\": 668\n", + " }\n", + " },\n", + " ToolMessage {\n", + " \"id\": \"0bf52bcd-ffbd-4f82-9ee1-7ba2108f0d27\",\n", + " \"content\": \"It's sunny in San Francisco, but you better look out if you're a Gemini 😈.\",\n", + " \"name\": \"search\",\n", + " \"additional_kwargs\": {},\n", + " \"response_metadata\": {},\n", + " \"tool_call_id\": \"toolu_01QCcxzRjojWW5JqQp7WTN82\"\n", + " }\n", + " ]\n", + "}\n", + "================================ tool Message (1) =================================\n", + "{\n", + " name: 'search',\n", + " content: \"It's sunny in San Francisco, but you better look out if you're a Gemini 😈.\"\n", + "}\n", + "{\n", + " messages: [\n", + " HumanMessage {\n", + " \"id\": \"a80d5763-0f27-4a00-9e54-8a239b499ea1\",\n", + " \"content\": \"Use the search tool to ask the user where they are, then look up the weather there\",\n", + " \"additional_kwargs\": {},\n", + " \"response_metadata\": {}\n", + " },\n", + " AIMessage {\n", + " \"id\": \"msg_01CsrDn46VqNXrdkpVHbcMKA\",\n", + " \"content\": [\n", + " {\n", + " \"type\": \"text\",\n", + " \"text\": \"Certainly! I'll use the askHuman tool to ask the user about their location, and then use the search tool to look up the weather for that location. Let's start by asking the user where they are.\"\n", + " },\n", + " {\n", + " \"type\": \"tool_use\",\n", + " \"id\": \"toolu_01RN181HAAL5BcnMXkexbA1r\",\n", + " \"name\": \"askHuman\",\n", + " \"input\": {\n", + " \"input\": \"Where are you located? Please provide your city and country.\"\n", + " }\n", + " }\n", + " ],\n", + " \"additional_kwargs\": {\n", + " \"id\": \"msg_01CsrDn46VqNXrdkpVHbcMKA\",\n", + " \"type\": \"message\",\n", + " \"role\": \"assistant\",\n", + " \"model\": \"claude-3-5-sonnet-20240620\",\n", + " \"stop_reason\": \"tool_use\",\n", + " \"stop_sequence\": null,\n", + " \"usage\": {\n", + " \"input_tokens\": 465,\n", + " \"output_tokens\": 108\n", + " }\n", + " },\n", + " \"response_metadata\": {\n", + " \"id\": \"msg_01CsrDn46VqNXrdkpVHbcMKA\",\n", + " \"model\": \"claude-3-5-sonnet-20240620\",\n", + " \"stop_reason\": \"tool_use\",\n", + " \"stop_sequence\": null,\n", + " \"usage\": {\n", + " \"input_tokens\": 465,\n", + " \"output_tokens\": 108\n", + " },\n", + " \"type\": \"message\",\n", + " \"role\": \"assistant\"\n", + " },\n", + " \"tool_calls\": [\n", + " {\n", + " \"name\": \"askHuman\",\n", + " \"args\": {\n", + " \"input\": \"Where are you located? Please provide your city and country.\"\n", + " },\n", + " \"id\": \"toolu_01RN181HAAL5BcnMXkexbA1r\",\n", + " \"type\": \"tool_call\"\n", + " }\n", + " ],\n", + " \"invalid_tool_calls\": []\n", + " },\n", + " ToolMessage {\n", + " \"id\": \"9159f841-0e15-4366-96a9-cc5ee0662da0\",\n", + " \"content\": \"san francisco\",\n", + " \"additional_kwargs\": {},\n", + " \"response_metadata\": {},\n", + " \"tool_call_id\": \"toolu_01RN181HAAL5BcnMXkexbA1r\"\n", + " },\n", + " AIMessage {\n", + " \"id\": \"msg_017hfZ8kdhX5nKD97THKWpPx\",\n", + " \"content\": [\n", + " {\n", + " \"type\": \"text\",\n", + " \"text\": \"Thank you for providing your location. Now, I'll use the search tool to look up the weather in San Francisco.\"\n", + " },\n", + " {\n", + " \"type\": \"tool_use\",\n", + " \"id\": \"toolu_01QCcxzRjojWW5JqQp7WTN82\",\n", + " \"name\": \"search\",\n", + " \"input\": {\n", + " \"input\": \"current weather in San Francisco\"\n", + " }\n", + " }\n", + " ],\n", + " \"additional_kwargs\": {\n", + " \"id\": \"msg_017hfZ8kdhX5nKD97THKWpPx\",\n", + " \"type\": \"message\",\n", + " \"role\": \"assistant\",\n", + " \"model\": \"claude-3-5-sonnet-20240620\",\n", + " \"stop_reason\": \"tool_use\",\n", + " \"stop_sequence\": null,\n", + " \"usage\": {\n", + " \"input_tokens\": 587,\n", + " \"output_tokens\": 81\n", + " }\n", + " },\n", + " \"response_metadata\": {\n", + " \"id\": \"msg_017hfZ8kdhX5nKD97THKWpPx\",\n", + " \"model\": \"claude-3-5-sonnet-20240620\",\n", + " \"stop_reason\": \"tool_use\",\n", + " \"stop_sequence\": null,\n", + " \"usage\": {\n", + " \"input_tokens\": 587,\n", + " \"output_tokens\": 81\n", + " },\n", + " \"type\": \"message\",\n", + " \"role\": \"assistant\"\n", + " },\n", + " \"tool_calls\": [\n", + " {\n", + " \"name\": \"search\",\n", + " \"args\": {\n", + " \"input\": \"current weather in San Francisco\"\n", + " },\n", + " \"id\": \"toolu_01QCcxzRjojWW5JqQp7WTN82\",\n", + " \"type\": \"tool_call\"\n", + " }\n", + " ],\n", + " \"invalid_tool_calls\": [],\n", + " \"usage_metadata\": {\n", + " \"input_tokens\": 587,\n", + " \"output_tokens\": 81,\n", + " \"total_tokens\": 668\n", + " }\n", + " },\n", + " ToolMessage {\n", + " \"id\": \"0bf52bcd-ffbd-4f82-9ee1-7ba2108f0d27\",\n", + " \"content\": \"It's sunny in San Francisco, but you better look out if you're a Gemini 😈.\",\n", + " \"name\": \"search\",\n", + " \"additional_kwargs\": {},\n", + " \"response_metadata\": {},\n", + " \"tool_call_id\": \"toolu_01QCcxzRjojWW5JqQp7WTN82\"\n", + " },\n", + " AIMessage {\n", + " \"id\": \"msg_01NuhYbiu36DSgW7brfoKMr8\",\n", + " \"content\": \"Based on the search results, I can provide you with information about the current weather in San Francisco:\\n\\nThe weather in San Francisco is currently sunny. \\n\\nIt's worth noting that the search result included an unusual comment about Geminis, which doesn't seem directly related to the weather. If you'd like more detailed weather information, such as temperature, humidity, or forecast, please let me know, and I can perform another search for more specific weather data.\\n\\nIs there anything else you'd like to know about the weather in San Francisco or any other information you need?\",\n", + " \"additional_kwargs\": {\n", + " \"id\": \"msg_01NuhYbiu36DSgW7brfoKMr8\",\n", + " \"type\": \"message\",\n", + " \"role\": \"assistant\",\n", + " \"model\": \"claude-3-5-sonnet-20240620\",\n", + " \"stop_reason\": \"end_turn\",\n", + " \"stop_sequence\": null,\n", + " \"usage\": {\n", + " \"input_tokens\": 701,\n", + " \"output_tokens\": 121\n", + " }\n", + " },\n", + " \"response_metadata\": {\n", + " \"id\": \"msg_01NuhYbiu36DSgW7brfoKMr8\",\n", + " \"model\": \"claude-3-5-sonnet-20240620\",\n", + " \"stop_reason\": \"end_turn\",\n", + " \"stop_sequence\": null,\n", + " \"usage\": {\n", + " \"input_tokens\": 701,\n", + " \"output_tokens\": 121\n", + " },\n", + " \"type\": \"message\",\n", + " \"role\": \"assistant\"\n", + " },\n", + " \"tool_calls\": [],\n", + " \"invalid_tool_calls\": [],\n", + " \"usage_metadata\": {\n", + " \"input_tokens\": 701,\n", + " \"output_tokens\": 121,\n", + " \"total_tokens\": 822\n", + " }\n", + " }\n", + " ]\n", + "}\n", + "================================ ai Message (1) =================================\n", + "Based on the search results, I can provide you with information about the current weather in San Francisco:\n", + "\n", + "The weather in San Francisco is currently sunny. \n", + "\n", + "It's worth noting that the search result included an unusual comment about Geminis, which doesn't seem directly related to the weather. If you'd like more detailed weather information, such as temperature, humidity, or forecast, please let me know, and I can perform another search for more specific weather data.\n", + "\n", + "Is there anything else you'd like to know about the weather in San Francisco or any other information you need?\n" ] } ], "source": [ - "for await (const event of await app.stream(null, config)) {\n", - " console.log(event)\n", - " const recentMsg = event.messages[event.messages.length - 1];\n", - " console.log(`================================ ${recentMsg._getType()} Message (1) =================================`)\n", - " if (recentMsg._getType() === \"tool\") {\n", - " console.log({\n", - " name: recentMsg.name,\n", - " content: recentMsg.content\n", - " })\n", - " } else if (recentMsg._getType() === \"ai\") {\n", - " console.log(recentMsg.content)\n", - " }\n", + "for await (const event of await messagesApp.stream(null, config2)) {\n", + " console.log(event)\n", + " const recentMsg = event.messages[event.messages.length - 1];\n", + " console.log(`================================ ${recentMsg._getType()} Message (1) =================================`)\n", + " if (recentMsg._getType() === \"tool\") {\n", + " console.log({\n", + " name: recentMsg.name,\n", + " content: recentMsg.content\n", + " })\n", + " } else if (recentMsg._getType() === \"ai\") {\n", + " console.log(recentMsg.content)\n", + " }\n", "}" ] } ], "metadata": { "kernelspec": { - "display_name": "Deno", + "display_name": "TypeScript", "language": "typescript", - "name": "deno" + "name": "tslab" }, "language_info": { + "codemirror_mode": { + "mode": "typescript", + "name": "javascript", + "typescript": true + }, "file_extension": ".ts", - "mimetype": "text/x.typescript", + "mimetype": "text/typescript", "name": "typescript", - "nb_converter": "script", - "pygments_lexer": "typescript", - "version": "5.3.3" + "version": "3.7.2" } }, "nbformat": 4,