{ "cells": [ { "cell_type": "code", "execution_count": 20, "metadata": { "execution": { "iopub.execute_input": "2025-01-29T20:09:11.440091Z", "iopub.status.busy": "2025-01-29T20:09:11.439766Z", "iopub.status.idle": "2025-01-29T20:09:11.751153Z", "shell.execute_reply": "2025-01-29T20:09:11.750263Z", "shell.execute_reply.started": "2025-01-29T20:09:11.440060Z" }, "id": "xaiioUQni_ga", "trusted": true }, "outputs": [], "source": [ "import os\n", "\n", "from typing import Annotated, Dict, Any\n", "from typing_extensions import TypedDict\n", "from datetime import date\n", "\n", "from langgraph.graph.message import add_messages\n", "from langgraph.graph import StateGraph, START, END\n", "# from langchain_google_genai import ChatGoogleGenerativeAI\n", "# from langchain_openai import ChatOpenAI\n", "from langchain_ollama import ChatOllama\n", "\n", "from IPython.display import Image, display\n", "from pprint import pprint\n", "from langchain_core.messages.ai import AIMessage\n", "from typing import Literal\n", "from langchain_core.tools import tool\n", "from langgraph.prebuilt import ToolNode" ] }, { "cell_type": "code", "execution_count": 21, "metadata": {}, "outputs": [], "source": [ "\n", "def setup_ollama_api():\n", " os.environ[\"OLLAMA_HOST\"] = \"http://141.211.127.171\"" ] }, { "cell_type": "code", "execution_count": 22, "metadata": { "execution": { "iopub.execute_input": "2025-01-29T20:09:11.753044Z", "iopub.status.busy": "2025-01-29T20:09:11.752582Z", "iopub.status.idle": "2025-01-29T20:09:11.760099Z", "shell.execute_reply": "2025-01-29T20:09:11.759040Z", "shell.execute_reply.started": "2025-01-29T20:09:11.752997Z" }, "id": "2RJQRlfVjqkJ", "trusted": true }, "outputs": [], "source": [ "# setup_openai_api()\n", "# llm = ChatOpenAI(temperature=0)\n", "\n", "setup_ollama_api()\n", "llm = ChatOllama(model=\"llama3.2:latest\", temperature=0)\n" ] }, { "cell_type": "code", "execution_count": 23, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "AIMessage(content='How can I assist you today?', additional_kwargs={}, response_metadata={'model': 'llama3.2:latest', 'created_at': '2025-01-31T20:57:47.70065Z', 'done': True, 'done_reason': 'stop', 'total_duration': 220359333, 'load_duration': 37094541, 'prompt_eval_count': 26, 'prompt_eval_duration': 130000000, 'eval_count': 8, 'eval_duration': 51000000, 'message': Message(role='assistant', content='', images=None, tool_calls=None)}, id='run-a10ebcbb-65e5-4909-a173-66134bd953cf-0', usage_metadata={'input_tokens': 26, 'output_tokens': 8, 'total_tokens': 34})" ] }, "execution_count": 23, "metadata": {}, "output_type": "execute_result" } ], "source": [ "llm.invoke(\"Hello\")" ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "execution": { "iopub.execute_input": "2025-01-29T20:09:11.763927Z", "iopub.status.busy": "2025-01-29T20:09:11.763466Z", "iopub.status.idle": "2025-01-29T20:09:11.779944Z", "shell.execute_reply": "2025-01-29T20:09:11.778675Z", "shell.execute_reply.started": "2025-01-29T20:09:11.763894Z" }, "id": "2RJQRlfVjqkJ", "trusted": true }, "outputs": [], "source": [ "class PainLevels(TypedDict):\n", " left_head: int\n", " right_head: int\n", " left_arm: int\n", " left_hand: int\n", " right_arm: int\n", " right_hand: int\n", " left_body_trunk: int\n", " right_body_trunk: int\n", " left_leg: int\n", " left_foot: int\n", " right_leg: int\n", " right_foot: int\n", "\n", "class Surgery(TypedDict):\n", " surgery_name: str\n", " time: date\n", "\n", "class PatientID(TypedDict):\n", " name: str\n", " DOB: date\n", " gender: str\n", " contact: str\n", " emergency_contact: str\n", "\n", "class MainSymptom(TypedDict):\n", " main_symptom: str\n", " length: str\n", "\n", "class Pain(TypedDict):\n", " painlevel: PainLevels\n", " pain_description: str\n", " start_time: date\n", " radiation: bool\n", " triggers: str\n", " symptom: str\n", "\n", "class MedicalHistory(TypedDict):\n", " medical_condition: str\n", " first_time: date\n", " surgery_history: list[Surgery]\n", " medication: str\n", " allergy: str\n", "\n", "class FamilyHistory(TypedDict):\n", " family_history: str\n", "\n", "class SocialHistory(TypedDict):\n", " occupation: str\n", " smoke: bool\n", " alcohol: bool\n", " drug: bool\n", " support_system: str\n", " living_condition: str\n", "\n", "class ReviewSystem(TypedDict):\n", " weight_change: str\n", " fever: bool\n", " chill: bool\n", " night_sweats: bool\n", " sleep: str\n", " gastrointestinal: str\n", " urinary: str\n", "\n", "class PainManagement(TypedDict):\n", " pain_medication: str\n", " specialist: bool\n", " other_therapy: str\n", " effectiveness: bool\n", "\n", "class Functional(TypedDict):\n", " life_quality: str\n", " limit_activity: str\n", " mood: str\n", "\n", "class Plan(TypedDict):\n", " goal: str\n", " expectation: str\n", " alternative_treatment_illness: str\n", "\n", "class PatientData(TypedDict):\n", " ID: PatientID\n", " main: MainSymptom\n", " \"\"\"pain: Pain\n", " medical_hist: MedicalHistory\n", " family_hist: FamilyHistory\n", " social_hist: SocialHistory\n", " review_system: ReviewSystem\n", " pain_manage: PainManagement\n", " functional: Functional\n", " plan: Plan\"\"\"\n", "\n", "class DataState(TypedDict):\n", " \"\"\"State representing the patient's data status and conversation.\"\"\"\n", " messages: Annotated[list, add_messages]\n", " data: Dict[str, PatientData]\n", " finished: bool" ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "execution": { "iopub.execute_input": "2025-01-29T20:09:11.781576Z", "iopub.status.busy": "2025-01-29T20:09:11.781212Z", "iopub.status.idle": "2025-01-29T20:09:11.800825Z", "shell.execute_reply": "2025-01-29T20:09:11.799561Z", "shell.execute_reply.started": "2025-01-29T20:09:11.781544Z" }, "id": "2RJQRlfVjqkJ", "trusted": true }, "outputs": [], "source": [ "# The system instruction defines how the chatbot is expected to behave and includes\n", "# rules for when to call different functions, as well as rules for the conversation, such\n", "# as tone and what is permitted for discussion.\n", "MEDICAL_INTAKE_SYSINT = (\n", " \"system\",\n", " \"\"\"You are MedAssist, an intelligent medical intake system designed to gather comprehensive patient information. You guide patients through a structured data collection process while maintaining a supportive and professional demeanor.\n", " \n", " Before collecting any data, always use get_empty_datadict to create a new empty data dictionary for a new patient. Then use the following steps to collect data from a patient.\n", " \n", " Primary Data Collection Areas:\n", " 1. Patient Identification\n", " - Basic information (name, DOB, gender, contact)\n", " - Emergency contact information\n", "\n", " 2. Main Symptom Assessment\n", " - Primary complaint\n", " - Duration of symptoms\n", "\n", " 3. Pain Assessment\n", " - Pain location using body mapping (head, arms, hands, trunk, legs, feet)\n", " - Pain intensity (0-10 scale for each location)\n", " - Pain characteristics and patterns\n", " - Onset time\n", " - Radiation patterns\n", " - Triggering factors\n", " - Associated symptoms\n", "\n", " 4. Medical History\n", " - Existing medical conditions\n", " - First occurrence date\n", " - Surgical history with dates\n", " - Current medications\n", " - Allergies\n", "\n", " 5. Background Information\n", " - Family medical history\n", " - Social history (occupation, lifestyle factors)\n", " - Living conditions and support system\n", "\n", " 6. System Review\n", " - Recent health changes\n", " - Sleep patterns\n", " - Gastrointestinal and urinary function\n", " - Constitutional symptoms (fever, chills, night sweats)\n", "\n", " 7. Pain Management History\n", " - Current pain medications\n", " - Specialist consultations\n", " - Alternative therapies\n", " - Treatment effectiveness\n", "\n", " 8. Functional Assessment\n", " - Impact on quality of life\n", " - Activity limitations\n", " - Mood and emotional state\n", "\n", " 9. Treatment Planning\n", " - Treatment goals\n", " - Patient expectations\n", " - Alternative treatment considerations\n", "\n", " Data Management Commands:\n", " - Use get_data to review current information\n", " - Use add_to_data to append new information\n", " - Use clear_data to reset the current session\n", " - Use confirm_data to verify information with the patient\n", " - Use insert_data to finalize the record\n", "\n", " Guidelines:\n", " 1. Always introduce yourself and explain the intake process\n", " 2. Collect information systematically but adapt to the patient's natural flow of conversation\n", " 3. If patient starts with a specific concern, begin there but ensure all sections are eventually completed\n", " 4. Use conversational prompts to gather missing information\n", " 5. Validate pain levels on a 0-10 scale for each body location\n", " 6. Regularly summarize collected information for patient verification\n", " 7. Show empathy while maintaining professional boundaries\n", " 8. Focus on medical data collection while acknowledging patient concerns\n", " 9. Always confirm complete data set before finalizing\n", " 10. Thank the patient and provide clear closure when finished\n", "\n", " Remember:\n", " - Maintain medical privacy and confidentiality\n", " - Stay within scope of data collection\n", " - Be patient and clear in communication\n", " - Double-check all information before final submission\n", " - Adapt language to patient's comprehension level\n", " - Document 'unknown' or 'not applicable' when appropriate\n", "\n", " Always confirm_data with the patient before calling save_data, and address any corrections needed. Once save_data is complete, provide a summary and conclude the session.\"\"\"\n", ")\n", "\n", "# This is the message with which the system opens the conversation.\n", "WELCOME_MSG = \"Welcome to the Paintrek world. I am a health assistant, an interactive clinical recording system. I will ask you questions about your pain and related symptoms and record your responses. I will then store this information securely. At any time, you can type `q` to quit.\"" ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "execution": { "iopub.execute_input": "2025-01-29T20:09:11.828125Z", "iopub.status.busy": "2025-01-29T20:09:11.827744Z", "iopub.status.idle": "2025-01-29T20:09:11.835672Z", "shell.execute_reply": "2025-01-29T20:09:11.834403Z", "shell.execute_reply.started": "2025-01-29T20:09:11.828093Z" }, "id": "SWXwd1ITUSPF", "trusted": true }, "outputs": [], "source": [ "def human_node(state: DataState) -> DataState:\n", " \"\"\"Display the last model message to the user, and receive the user's input.\"\"\"\n", " last_msg = state[\"messages\"][-1]\n", " print(\"Model:\", last_msg.content)\n", "\n", " user_input = input(\"User: \")\n", "\n", " # If it looks like the user is trying to quit, flag the conversation\n", " # as over.\n", " if user_input in {\"q\", \"quit\", \"exit\", \"goodbye\"}:\n", " state[\"finished\"] = True\n", "\n", " return state | {\"messages\": [(\"user\", user_input)]}\n", "\n", "\n", "def maybe_exit_human_node(state: DataState) -> Literal[\"chatbot_healthassistant\", \"__end__\"]:\n", " \"\"\"Route to the chatbot, unless it looks like the user is exiting.\"\"\"\n", " if state.get(\"finished\", False):\n", " return END\n", " else:\n", " return \"chatbot_healthassistant\"\n", " \n" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [], "source": [ "@tool\n", "def get_empty_datadict(state: DataState) -> DataState:\n", " \"\"\"Before collecting data, get a empty data dictionary for a new patient\"\"\"\n", " # Note that this is just hard-coded text, but you could connect this to a live stock\n", " # database, or you could use Gemini's multi-modal capabilities and take live photos of\n", " # your cafe's chalk menu or the products on the counter and assmble them into an input.\n", " state[\"data\"]= {\n", " \"patient_1\": {\n", " \"data_1\": { # Placeholder patient ID, can be replaced dynamically\n", " \"ID\": {\n", " \"name\": \"\",\n", " \"DOB\": date(1900, 1, 1), # Default placeholder date\n", " \"gender\": \"\",\n", " \"contact\": \"\",\n", " \"emergency_contact\": \"\"\n", " },\n", " \"main\": {\n", " \"main_symptom\": \"\",\n", " \"length\": \"\"\n", " }\n", " }\n", " }\n", " }\n", " return state\n" ] }, { "cell_type": "code", "execution_count": 8, "metadata": { "execution": { "iopub.execute_input": "2025-01-29T20:09:11.858218Z", "iopub.status.busy": "2025-01-29T20:09:11.857696Z", "iopub.status.idle": "2025-01-29T20:09:11.903753Z", "shell.execute_reply": "2025-01-29T20:09:11.902647Z", "shell.execute_reply.started": "2025-01-29T20:09:11.858152Z" }, "id": "jqsLovPBQe0I", "trusted": true }, "outputs": [], "source": [ "from collections.abc import Iterable\n", "from random import randint\n", "\n", "from langgraph.prebuilt import InjectedState\n", "from langchain_core.messages.tool import ToolMessage\n", "\n", "# These functions have no body; LangGraph does not allow @tools to update\n", "# the conversation state, so you will implement a separate node to handle\n", "# state updates. Using @tools is still very convenient for defining the tool\n", "# schema, so empty functions have been defined that will be bound to the LLM\n", "# but their implementation is deferred to the order_node.\n", "\n", "\n", "@tool\n", "def patient_id(name: str, DOB: str, gender: str, contact: str, emergency_contact: str) -> str:\n", " \"\"\"Collecting basic patient identification information including:\n", " - Basic information (name, DOB, gender, contact details)\n", " - Emergency contact information\n", "\n", " Returns:\n", " The updated data with the patient ID information added.\n", " \"\"\"\n", "\n", "@tool\n", "def symptom(main_symptom: str, length: str) -> str:\n", " \"\"\"Collecting patient's main symptom assessment including:\n", " - Primary symptoms\n", " - Duration of the symptoms\n", "\n", " Returns:\n", " The updated data with the patient's symptom information added.\n", " \"\"\"\n", "\n", "\n", "@tool\n", "def confirm_data() -> str:\n", " \"\"\"Asks the patient if the data intake is correct.\n", "\n", " Returns:\n", " The user's free-text response.\n", " \"\"\"\n", "\n", "\n", "@tool\n", "def get_data() -> str:\n", " \"\"\"Returns the users data so far. One item per line.\"\"\"\n", "\n", "\n", "@tool\n", "def clear_data():\n", " \"\"\"Removes all items from the user's order.\"\"\"\n", "\n", "\n", "@tool\n", "def save_data() -> int:\n", " \"\"\"Send the data into database.\n", "\n", " Returns:\n", " The status of data saving, finished.\n", " \"\"\"\n", "\n", "\n", "def data_node(state: DataState) -> DataState:\n", " \"\"\"The ordering node. This is where the dataintake is manipulated.\"\"\"\n", " tool_msg = state.get(\"messages\", [])[-1]\n", " data = state.get(\"data\", [])\n", " outbound_msgs = []\n", " data_saved = False\n", "\n", " for tool_call in tool_msg.tool_calls:\n", "\n", " if tool_call[\"name\"] == \"patient_id\":\n", "\n", " # Each order item is just a string. This is where it assembled as \"drink (modifiers, ...)\".\n", " patient_name = tool_call[\"args\"][\"name\"]\n", " patient_DOB = tool_call[\"args\"][\"DOB\"]\n", " patient_gender = tool_call[\"args\"][\"gender\"]\n", " patient_contact = tool_call[\"args\"][\"contact\"]\n", " patient_emergency_contact = tool_call[\"args\"][\"emergency_contact\"]\n", "\n", " data[\"ID\"][\"name\"]=patient_name\n", " data[\"ID\"][\"DOB\"]=patient_DOB\n", " data[\"ID\"][\"gender\"]=patient_gender\n", " data[\"ID\"][\"contact\"]=patient_contact\n", " data[\"ID\"][\"emergency_contact\"]=patient_emergency_contact\n", " \n", " response = \"\\n\".join(data)\n", "\n", " if tool_call[\"name\"] == \"symptom\":\n", "\n", " # Each order item is just a string. This is where it assembled as \"drink (modifiers, ...)\".\n", " main_symptom = tool_call[\"args\"][\"main_symptom\"]\n", " symptom_length = tool_call[\"args\"][\"length\"]\n", "\n", " data[\"symptom\"][\"main_symptom\"]=main_symptom\n", " data[\"symptom\"][\"symptom_length\"]=symptom_length\n", " response = \"\\n\".join(data)\n", "\n", " elif tool_call[\"name\"] == \"confirm_data\":\n", "\n", " # We could entrust the LLM to do order confirmation, but it is a good practice to\n", " # show the user the exact data that comprises their order so that what they confirm\n", " # precisely matches the order that goes to the kitchen - avoiding hallucination\n", " # or reality skew.\n", "\n", " # In a real scenario, this is where you would connect your POS screen to show the\n", " # order to the user.\n", "\n", " print(\"Your input data:\")\n", " if not data:\n", " print(\" (no items)\")\n", "\n", " for data in data:\n", " print(f\" {data}\")\n", "\n", " response = input(\"Is this correct? \")\n", "\n", " elif tool_call[\"name\"] == \"get_data\":\n", "\n", " response = \"\\n\".join(data) if data else \"(no data)\"\n", "\n", " elif tool_call[\"name\"] == \"clear_data\":\n", "\n", " data.clear()\n", " response = None\n", "\n", " elif tool_call[\"name\"] == \"save_data\":\n", "\n", " #order_text = \"\\n\".join(order)\n", " print(\"Saving the data!\")\n", " #print(order_text)\n", "\n", " # TODO(you!): Implement cafe.\n", " data_saved = True\n", " # response = randint(1, 5) # ETA in minutes\n", "\n", " else:\n", " raise NotImplementedError(f'Unknown tool call: {tool_call[\"name\"]}')\n", "\n", " # Record the tool results as tool messages.\n", " outbound_msgs.append(\n", " ToolMessage(\n", " content=response,\n", " name=tool_call[\"name\"],\n", " tool_call_id=tool_call[\"id\"],\n", " )\n", " )\n", "\n", " return {\"messages\": outbound_msgs, \"data\": data, \"finished\": data_saved}\n", "\n", "def chatbot_with_tools(state: DataState) -> DataState:\n", " \"\"\"The chatbot with tools. A simple wrapper around the model's own chat interface.\"\"\"\n", " defaults = {\"order\": [], \"finished\": False}\n", "\n", " if state[\"messages\"]:\n", " new_output = llm_with_tools.invoke([MEDICAL_INTAKE_SYSINT] + state[\"messages\"])\n", " else:\n", " new_output = AIMessage(content=WELCOME_MSG)\n", "\n", " # Set up some defaults if not already set, then pass through the provided state,\n", " # overriding only the \"messages\" field.\n", " return defaults | state | {\"messages\": [new_output]}\n", "\n", "\n", "def maybe_route_to_tools(state: DataState) -> str:\n", " \"\"\"Route between chat and tool nodes if a tool call is made.\"\"\"\n", " if not (msgs := state.get(\"messages\", [])):\n", " raise ValueError(f\"No messages found when parsing state: {state}\")\n", "\n", " msg = msgs[-1]\n", "\n", " if state.get(\"finished\", False):\n", " # When an order is placed, exit the app. The system instruction indicates\n", " # that the chatbot should say thanks and goodbye at this point, so we can exit\n", " # cleanly.\n", " return END\n", "\n", " elif hasattr(msg, \"tool_calls\") and len(msg.tool_calls) > 0:\n", " # Route to `tools` node for any automated tool calls first.\n", " if any(\n", " tool[\"name\"] for tool in msg.tool_calls\n", " ):\n", " return \"datacreation\"\n", " else:\n", " return \"documenting\"\n", "\n", " else:\n", " return \"patient\"" ] }, { "cell_type": "code", "execution_count": 9, "metadata": { "execution": { "iopub.execute_input": "2025-01-29T20:09:11.906458Z", "iopub.status.busy": "2025-01-29T20:09:11.905241Z", "iopub.status.idle": "2025-01-29T20:09:11.994921Z", "shell.execute_reply": "2025-01-29T20:09:11.993761Z", "shell.execute_reply.started": "2025-01-29T20:09:11.906419Z" }, "id": "9rqkQzlZxrzp", "trusted": true }, "outputs": [ { "data": { "image/png": "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", "text/plain": [ "" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# Auto-tools will be invoked automatically by the ToolNode\n", "auto_tools = [get_empty_datadict]\n", "tool_node = ToolNode(auto_tools)\n", "\n", "# Order-tools will be handled by the order node.\n", "intake_tools = [patient_id, symptom, confirm_data, get_data, clear_data, save_data]\n", "\n", "# The LLM needs to know about all of the tools, so specify everything here.\n", "llm_with_tools = llm.bind_tools(auto_tools + intake_tools)\n", "\n", "\n", "graph_builder = StateGraph(DataState)\n", "\n", "# Nodes\n", "graph_builder.add_node(\"chatbot_healthassistant\", chatbot_with_tools)\n", "graph_builder.add_node(\"patient\", human_node)\n", "graph_builder.add_node(\"datacreation\", tool_node)\n", "graph_builder.add_node(\"documenting\", data_node)\n", "\n", "# Chatbot -> {ordering, tools, human, END}\n", "graph_builder.add_conditional_edges(\"chatbot_healthassistant\", maybe_route_to_tools)\n", "# Human -> {chatbot, END}\n", "graph_builder.add_conditional_edges(\"patient\", maybe_exit_human_node)\n", "# TestCase_Paintrek\n", "# Tools (both kinds) always route back to chat afterwards.\n", "graph_builder.add_edge(\"datacreation\", \"chatbot_healthassistant\")\n", "graph_builder.add_edge(\"documenting\", \"chatbot_healthassistant\")\n", "\n", "graph_builder.add_edge(START, \"chatbot_healthassistant\")\n", "graph_with_order_tools = graph_builder.compile()\n", "\n", "Image(graph_with_order_tools.get_graph().draw_mermaid_png())" ] }, { "cell_type": "markdown", "metadata": { "id": "G0SVsDu4gD_T" }, "source": [ "Now run the complete ordering system graph.\n", "\n", "**You must uncomment the `.invoke(...)` line to run this step.**" ] }, { "cell_type": "code", "execution_count": 25, "metadata": { "execution": { "iopub.execute_input": "2025-01-29T20:09:38.185616Z", "iopub.status.busy": "2025-01-29T20:09:38.185131Z", "iopub.status.idle": "2025-01-29T20:10:08.474591Z", "shell.execute_reply": "2025-01-29T20:10:08.472926Z", "shell.execute_reply.started": "2025-01-29T20:09:38.185577Z" }, "id": "NCRSgaBUfIHF", "trusted": true }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Model: Welcome to the Paintrek world. I am a health assistant, an interactive clinical recording system. I will ask you questions about your pain and related symptoms and record your responses. I will then store this information securely. At any time, you can type `q` to quit.\n" ] }, { "ename": "ConnectError", "evalue": "[Errno 111] Connection refused", "output_type": "error", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mConnectError\u001b[0m Traceback (most recent call last)", "File \u001b[0;32m~/miniconda3/envs/paintrekbot/lib/python3.12/site-packages/httpx/_transports/default.py:101\u001b[0m, in \u001b[0;36mmap_httpcore_exceptions\u001b[0;34m()\u001b[0m\n\u001b[1;32m 100\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[0;32m--> 101\u001b[0m \u001b[38;5;28;01myield\u001b[39;00m\n\u001b[1;32m 102\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mException\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m exc:\n", "File \u001b[0;32m~/miniconda3/envs/paintrekbot/lib/python3.12/site-packages/httpx/_transports/default.py:250\u001b[0m, in \u001b[0;36mHTTPTransport.handle_request\u001b[0;34m(self, request)\u001b[0m\n\u001b[1;32m 249\u001b[0m \u001b[38;5;28;01mwith\u001b[39;00m map_httpcore_exceptions():\n\u001b[0;32m--> 250\u001b[0m resp \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_pool\u001b[38;5;241m.\u001b[39mhandle_request(req)\n\u001b[1;32m 252\u001b[0m \u001b[38;5;28;01massert\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(resp\u001b[38;5;241m.\u001b[39mstream, typing\u001b[38;5;241m.\u001b[39mIterable)\n", "File \u001b[0;32m~/miniconda3/envs/paintrekbot/lib/python3.12/site-packages/httpcore/_sync/connection_pool.py:256\u001b[0m, in \u001b[0;36mConnectionPool.handle_request\u001b[0;34m(self, request)\u001b[0m\n\u001b[1;32m 255\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_close_connections(closing)\n\u001b[0;32m--> 256\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m exc \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m\n\u001b[1;32m 258\u001b[0m \u001b[38;5;66;03m# Return the response. Note that in this case we still have to manage\u001b[39;00m\n\u001b[1;32m 259\u001b[0m \u001b[38;5;66;03m# the point at which the response is closed.\u001b[39;00m\n", "File \u001b[0;32m~/miniconda3/envs/paintrekbot/lib/python3.12/site-packages/httpcore/_sync/connection_pool.py:236\u001b[0m, in \u001b[0;36mConnectionPool.handle_request\u001b[0;34m(self, request)\u001b[0m\n\u001b[1;32m 234\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[1;32m 235\u001b[0m \u001b[38;5;66;03m# Send the request on the assigned connection.\u001b[39;00m\n\u001b[0;32m--> 236\u001b[0m response \u001b[38;5;241m=\u001b[39m connection\u001b[38;5;241m.\u001b[39mhandle_request(\n\u001b[1;32m 237\u001b[0m pool_request\u001b[38;5;241m.\u001b[39mrequest\n\u001b[1;32m 238\u001b[0m )\n\u001b[1;32m 239\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m ConnectionNotAvailable:\n\u001b[1;32m 240\u001b[0m \u001b[38;5;66;03m# In some cases a connection may initially be available to\u001b[39;00m\n\u001b[1;32m 241\u001b[0m \u001b[38;5;66;03m# handle a request, but then become unavailable.\u001b[39;00m\n\u001b[1;32m 242\u001b[0m \u001b[38;5;66;03m#\u001b[39;00m\n\u001b[1;32m 243\u001b[0m \u001b[38;5;66;03m# In this case we clear the connection and try again.\u001b[39;00m\n", "File \u001b[0;32m~/miniconda3/envs/paintrekbot/lib/python3.12/site-packages/httpcore/_sync/connection.py:101\u001b[0m, in \u001b[0;36mHTTPConnection.handle_request\u001b[0;34m(self, request)\u001b[0m\n\u001b[1;32m 100\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_connect_failed \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;01mTrue\u001b[39;00m\n\u001b[0;32m--> 101\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m exc\n\u001b[1;32m 103\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_connection\u001b[38;5;241m.\u001b[39mhandle_request(request)\n", "File \u001b[0;32m~/miniconda3/envs/paintrekbot/lib/python3.12/site-packages/httpcore/_sync/connection.py:78\u001b[0m, in \u001b[0;36mHTTPConnection.handle_request\u001b[0;34m(self, request)\u001b[0m\n\u001b[1;32m 77\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_connection \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[0;32m---> 78\u001b[0m stream \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_connect(request)\n\u001b[1;32m 80\u001b[0m ssl_object \u001b[38;5;241m=\u001b[39m stream\u001b[38;5;241m.\u001b[39mget_extra_info(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mssl_object\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n", "File \u001b[0;32m~/miniconda3/envs/paintrekbot/lib/python3.12/site-packages/httpcore/_sync/connection.py:124\u001b[0m, in \u001b[0;36mHTTPConnection._connect\u001b[0;34m(self, request)\u001b[0m\n\u001b[1;32m 123\u001b[0m \u001b[38;5;28;01mwith\u001b[39;00m Trace(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mconnect_tcp\u001b[39m\u001b[38;5;124m\"\u001b[39m, logger, request, kwargs) \u001b[38;5;28;01mas\u001b[39;00m trace:\n\u001b[0;32m--> 124\u001b[0m stream \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_network_backend\u001b[38;5;241m.\u001b[39mconnect_tcp(\u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs)\n\u001b[1;32m 125\u001b[0m trace\u001b[38;5;241m.\u001b[39mreturn_value \u001b[38;5;241m=\u001b[39m stream\n", "File \u001b[0;32m~/miniconda3/envs/paintrekbot/lib/python3.12/site-packages/httpcore/_backends/sync.py:207\u001b[0m, in \u001b[0;36mSyncBackend.connect_tcp\u001b[0;34m(self, host, port, timeout, local_address, socket_options)\u001b[0m\n\u001b[1;32m 202\u001b[0m exc_map: ExceptionMapping \u001b[38;5;241m=\u001b[39m {\n\u001b[1;32m 203\u001b[0m socket\u001b[38;5;241m.\u001b[39mtimeout: ConnectTimeout,\n\u001b[1;32m 204\u001b[0m \u001b[38;5;167;01mOSError\u001b[39;00m: ConnectError,\n\u001b[1;32m 205\u001b[0m }\n\u001b[0;32m--> 207\u001b[0m \u001b[38;5;28;01mwith\u001b[39;00m map_exceptions(exc_map):\n\u001b[1;32m 208\u001b[0m sock \u001b[38;5;241m=\u001b[39m socket\u001b[38;5;241m.\u001b[39mcreate_connection(\n\u001b[1;32m 209\u001b[0m address,\n\u001b[1;32m 210\u001b[0m timeout,\n\u001b[1;32m 211\u001b[0m source_address\u001b[38;5;241m=\u001b[39msource_address,\n\u001b[1;32m 212\u001b[0m )\n", "File \u001b[0;32m~/miniconda3/envs/paintrekbot/lib/python3.12/contextlib.py:158\u001b[0m, in \u001b[0;36m_GeneratorContextManager.__exit__\u001b[0;34m(self, typ, value, traceback)\u001b[0m\n\u001b[1;32m 157\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[0;32m--> 158\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mgen\u001b[38;5;241m.\u001b[39mthrow(value)\n\u001b[1;32m 159\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mStopIteration\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m exc:\n\u001b[1;32m 160\u001b[0m \u001b[38;5;66;03m# Suppress StopIteration *unless* it's the same exception that\u001b[39;00m\n\u001b[1;32m 161\u001b[0m \u001b[38;5;66;03m# was passed to throw(). This prevents a StopIteration\u001b[39;00m\n\u001b[1;32m 162\u001b[0m \u001b[38;5;66;03m# raised inside the \"with\" statement from being suppressed.\u001b[39;00m\n", "File \u001b[0;32m~/miniconda3/envs/paintrekbot/lib/python3.12/site-packages/httpcore/_exceptions.py:14\u001b[0m, in \u001b[0;36mmap_exceptions\u001b[0;34m(map)\u001b[0m\n\u001b[1;32m 13\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(exc, from_exc):\n\u001b[0;32m---> 14\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m to_exc(exc) \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;21;01mexc\u001b[39;00m\n\u001b[1;32m 15\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m\n", "\u001b[0;31mConnectError\u001b[0m: [Errno 111] Connection refused", "\nThe above exception was the direct cause of the following exception:\n", "\u001b[0;31mConnectError\u001b[0m Traceback (most recent call last)", "Cell \u001b[0;32mIn[25], line 6\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[38;5;66;03m# The default recursion limit for traversing nodes is 25 - setting it higher\u001b[39;00m\n\u001b[1;32m 2\u001b[0m \u001b[38;5;66;03m# means you can try a more complex order with multiple steps and round-trips.\u001b[39;00m\n\u001b[1;32m 3\u001b[0m \u001b[38;5;66;03m# config = {\"recursion_limit\": 500}\u001b[39;00m\n\u001b[1;32m 4\u001b[0m \n\u001b[1;32m 5\u001b[0m \u001b[38;5;66;03m# Uncomment this line to execute the graph:\u001b[39;00m\n\u001b[0;32m----> 6\u001b[0m state \u001b[38;5;241m=\u001b[39m graph_with_order_tools\u001b[38;5;241m.\u001b[39minvoke({\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mmessages\u001b[39m\u001b[38;5;124m\"\u001b[39m: []})\n", "File \u001b[0;32m~/miniconda3/envs/paintrekbot/lib/python3.12/site-packages/langgraph/pregel/__init__.py:1961\u001b[0m, in \u001b[0;36mPregel.invoke\u001b[0;34m(self, input, config, stream_mode, output_keys, interrupt_before, interrupt_after, debug, **kwargs)\u001b[0m\n\u001b[1;32m 1959\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[1;32m 1960\u001b[0m chunks \u001b[38;5;241m=\u001b[39m []\n\u001b[0;32m-> 1961\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m chunk \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mstream(\n\u001b[1;32m 1962\u001b[0m \u001b[38;5;28minput\u001b[39m,\n\u001b[1;32m 1963\u001b[0m config,\n\u001b[1;32m 1964\u001b[0m stream_mode\u001b[38;5;241m=\u001b[39mstream_mode,\n\u001b[1;32m 1965\u001b[0m output_keys\u001b[38;5;241m=\u001b[39moutput_keys,\n\u001b[1;32m 1966\u001b[0m interrupt_before\u001b[38;5;241m=\u001b[39minterrupt_before,\n\u001b[1;32m 1967\u001b[0m interrupt_after\u001b[38;5;241m=\u001b[39minterrupt_after,\n\u001b[1;32m 1968\u001b[0m debug\u001b[38;5;241m=\u001b[39mdebug,\n\u001b[1;32m 1969\u001b[0m \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs,\n\u001b[1;32m 1970\u001b[0m ):\n\u001b[1;32m 1971\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m stream_mode \u001b[38;5;241m==\u001b[39m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mvalues\u001b[39m\u001b[38;5;124m\"\u001b[39m:\n\u001b[1;32m 1972\u001b[0m latest \u001b[38;5;241m=\u001b[39m chunk\n", "File \u001b[0;32m~/miniconda3/envs/paintrekbot/lib/python3.12/site-packages/langgraph/pregel/__init__.py:1670\u001b[0m, in \u001b[0;36mPregel.stream\u001b[0;34m(self, input, config, stream_mode, output_keys, interrupt_before, interrupt_after, debug, subgraphs)\u001b[0m\n\u001b[1;32m 1664\u001b[0m \u001b[38;5;66;03m# Similarly to Bulk Synchronous Parallel / Pregel model\u001b[39;00m\n\u001b[1;32m 1665\u001b[0m \u001b[38;5;66;03m# computation proceeds in steps, while there are channel updates.\u001b[39;00m\n\u001b[1;32m 1666\u001b[0m \u001b[38;5;66;03m# Channel updates from step N are only visible in step N+1\u001b[39;00m\n\u001b[1;32m 1667\u001b[0m \u001b[38;5;66;03m# channels are guaranteed to be immutable for the duration of the step,\u001b[39;00m\n\u001b[1;32m 1668\u001b[0m \u001b[38;5;66;03m# with channel updates applied only at the transition between steps.\u001b[39;00m\n\u001b[1;32m 1669\u001b[0m \u001b[38;5;28;01mwhile\u001b[39;00m loop\u001b[38;5;241m.\u001b[39mtick(input_keys\u001b[38;5;241m=\u001b[39m\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39minput_channels):\n\u001b[0;32m-> 1670\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m _ \u001b[38;5;129;01min\u001b[39;00m runner\u001b[38;5;241m.\u001b[39mtick(\n\u001b[1;32m 1671\u001b[0m loop\u001b[38;5;241m.\u001b[39mtasks\u001b[38;5;241m.\u001b[39mvalues(),\n\u001b[1;32m 1672\u001b[0m timeout\u001b[38;5;241m=\u001b[39m\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mstep_timeout,\n\u001b[1;32m 1673\u001b[0m retry_policy\u001b[38;5;241m=\u001b[39m\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mretry_policy,\n\u001b[1;32m 1674\u001b[0m get_waiter\u001b[38;5;241m=\u001b[39mget_waiter,\n\u001b[1;32m 1675\u001b[0m ):\n\u001b[1;32m 1676\u001b[0m \u001b[38;5;66;03m# emit output\u001b[39;00m\n\u001b[1;32m 1677\u001b[0m \u001b[38;5;28;01myield from\u001b[39;00m output()\n\u001b[1;32m 1678\u001b[0m \u001b[38;5;66;03m# emit output\u001b[39;00m\n", "File \u001b[0;32m~/miniconda3/envs/paintrekbot/lib/python3.12/site-packages/langgraph/pregel/runner.py:230\u001b[0m, in \u001b[0;36mPregelRunner.tick\u001b[0;34m(self, tasks, reraise, timeout, retry_policy, get_waiter)\u001b[0m\n\u001b[1;32m 228\u001b[0m t \u001b[38;5;241m=\u001b[39m tasks[\u001b[38;5;241m0\u001b[39m]\n\u001b[1;32m 229\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[0;32m--> 230\u001b[0m run_with_retry(\n\u001b[1;32m 231\u001b[0m t,\n\u001b[1;32m 232\u001b[0m retry_policy,\n\u001b[1;32m 233\u001b[0m configurable\u001b[38;5;241m=\u001b[39m{\n\u001b[1;32m 234\u001b[0m CONFIG_KEY_SEND: partial(writer, t),\n\u001b[1;32m 235\u001b[0m CONFIG_KEY_CALL: partial(call, t),\n\u001b[1;32m 236\u001b[0m },\n\u001b[1;32m 237\u001b[0m )\n\u001b[1;32m 238\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mcommit(t, \u001b[38;5;28;01mNone\u001b[39;00m)\n\u001b[1;32m 239\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mException\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m exc:\n", "File \u001b[0;32m~/miniconda3/envs/paintrekbot/lib/python3.12/site-packages/langgraph/pregel/retry.py:40\u001b[0m, in \u001b[0;36mrun_with_retry\u001b[0;34m(task, retry_policy, configurable)\u001b[0m\n\u001b[1;32m 38\u001b[0m task\u001b[38;5;241m.\u001b[39mwrites\u001b[38;5;241m.\u001b[39mclear()\n\u001b[1;32m 39\u001b[0m \u001b[38;5;66;03m# run the task\u001b[39;00m\n\u001b[0;32m---> 40\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m task\u001b[38;5;241m.\u001b[39mproc\u001b[38;5;241m.\u001b[39minvoke(task\u001b[38;5;241m.\u001b[39minput, config)\n\u001b[1;32m 41\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m ParentCommand \u001b[38;5;28;01mas\u001b[39;00m exc:\n\u001b[1;32m 42\u001b[0m ns: \u001b[38;5;28mstr\u001b[39m \u001b[38;5;241m=\u001b[39m config[CONF][CONFIG_KEY_CHECKPOINT_NS]\n", "File \u001b[0;32m~/miniconda3/envs/paintrekbot/lib/python3.12/site-packages/langgraph/utils/runnable.py:462\u001b[0m, in \u001b[0;36mRunnableSeq.invoke\u001b[0;34m(self, input, config, **kwargs)\u001b[0m\n\u001b[1;32m 458\u001b[0m config \u001b[38;5;241m=\u001b[39m patch_config(\n\u001b[1;32m 459\u001b[0m config, callbacks\u001b[38;5;241m=\u001b[39mrun_manager\u001b[38;5;241m.\u001b[39mget_child(\u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mseq:step:\u001b[39m\u001b[38;5;132;01m{\u001b[39;00mi\u001b[38;5;250m \u001b[39m\u001b[38;5;241m+\u001b[39m\u001b[38;5;250m \u001b[39m\u001b[38;5;241m1\u001b[39m\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m\"\u001b[39m)\n\u001b[1;32m 460\u001b[0m )\n\u001b[1;32m 461\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m i \u001b[38;5;241m==\u001b[39m \u001b[38;5;241m0\u001b[39m:\n\u001b[0;32m--> 462\u001b[0m \u001b[38;5;28minput\u001b[39m \u001b[38;5;241m=\u001b[39m step\u001b[38;5;241m.\u001b[39minvoke(\u001b[38;5;28minput\u001b[39m, config, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs)\n\u001b[1;32m 463\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[1;32m 464\u001b[0m \u001b[38;5;28minput\u001b[39m \u001b[38;5;241m=\u001b[39m step\u001b[38;5;241m.\u001b[39minvoke(\u001b[38;5;28minput\u001b[39m, config)\n", "File \u001b[0;32m~/miniconda3/envs/paintrekbot/lib/python3.12/site-packages/langgraph/utils/runnable.py:226\u001b[0m, in \u001b[0;36mRunnableCallable.invoke\u001b[0;34m(self, input, config, **kwargs)\u001b[0m\n\u001b[1;32m 224\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[1;32m 225\u001b[0m context\u001b[38;5;241m.\u001b[39mrun(_set_config_context, config)\n\u001b[0;32m--> 226\u001b[0m ret \u001b[38;5;241m=\u001b[39m context\u001b[38;5;241m.\u001b[39mrun(\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mfunc, \u001b[38;5;241m*\u001b[39margs, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs)\n\u001b[1;32m 227\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(ret, Runnable) \u001b[38;5;129;01mand\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mrecurse:\n\u001b[1;32m 228\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m ret\u001b[38;5;241m.\u001b[39minvoke(\u001b[38;5;28minput\u001b[39m, config)\n", "Cell \u001b[0;32mIn[8], line 156\u001b[0m, in \u001b[0;36mchatbot_with_tools\u001b[0;34m(state)\u001b[0m\n\u001b[1;32m 153\u001b[0m defaults \u001b[38;5;241m=\u001b[39m {\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124morder\u001b[39m\u001b[38;5;124m\"\u001b[39m: [], \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mfinished\u001b[39m\u001b[38;5;124m\"\u001b[39m: \u001b[38;5;28;01mFalse\u001b[39;00m}\n\u001b[1;32m 155\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m state[\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mmessages\u001b[39m\u001b[38;5;124m\"\u001b[39m]:\n\u001b[0;32m--> 156\u001b[0m new_output \u001b[38;5;241m=\u001b[39m llm_with_tools\u001b[38;5;241m.\u001b[39minvoke([MEDICAL_INTAKE_SYSINT] \u001b[38;5;241m+\u001b[39m state[\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mmessages\u001b[39m\u001b[38;5;124m\"\u001b[39m])\n\u001b[1;32m 157\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[1;32m 158\u001b[0m new_output \u001b[38;5;241m=\u001b[39m AIMessage(content\u001b[38;5;241m=\u001b[39mWELCOME_MSG)\n", "File \u001b[0;32m~/miniconda3/envs/paintrekbot/lib/python3.12/site-packages/langchain_core/runnables/base.py:5352\u001b[0m, in \u001b[0;36mRunnableBindingBase.invoke\u001b[0;34m(self, input, config, **kwargs)\u001b[0m\n\u001b[1;32m 5346\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21minvoke\u001b[39m(\n\u001b[1;32m 5347\u001b[0m \u001b[38;5;28mself\u001b[39m,\n\u001b[1;32m 5348\u001b[0m \u001b[38;5;28minput\u001b[39m: Input,\n\u001b[1;32m 5349\u001b[0m config: Optional[RunnableConfig] \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;01mNone\u001b[39;00m,\n\u001b[1;32m 5350\u001b[0m \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs: Optional[Any],\n\u001b[1;32m 5351\u001b[0m ) \u001b[38;5;241m-\u001b[39m\u001b[38;5;241m>\u001b[39m Output:\n\u001b[0;32m-> 5352\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mbound\u001b[38;5;241m.\u001b[39minvoke(\n\u001b[1;32m 5353\u001b[0m \u001b[38;5;28minput\u001b[39m,\n\u001b[1;32m 5354\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_merge_configs(config),\n\u001b[1;32m 5355\u001b[0m \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39m{\u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39m\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mkwargs, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs},\n\u001b[1;32m 5356\u001b[0m )\n", "File \u001b[0;32m~/miniconda3/envs/paintrekbot/lib/python3.12/site-packages/langchain_core/language_models/chat_models.py:284\u001b[0m, in \u001b[0;36mBaseChatModel.invoke\u001b[0;34m(self, input, config, stop, **kwargs)\u001b[0m\n\u001b[1;32m 273\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21minvoke\u001b[39m(\n\u001b[1;32m 274\u001b[0m \u001b[38;5;28mself\u001b[39m,\n\u001b[1;32m 275\u001b[0m \u001b[38;5;28minput\u001b[39m: LanguageModelInput,\n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 279\u001b[0m \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs: Any,\n\u001b[1;32m 280\u001b[0m ) \u001b[38;5;241m-\u001b[39m\u001b[38;5;241m>\u001b[39m BaseMessage:\n\u001b[1;32m 281\u001b[0m config \u001b[38;5;241m=\u001b[39m ensure_config(config)\n\u001b[1;32m 282\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m cast(\n\u001b[1;32m 283\u001b[0m ChatGeneration,\n\u001b[0;32m--> 284\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mgenerate_prompt(\n\u001b[1;32m 285\u001b[0m [\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_convert_input(\u001b[38;5;28minput\u001b[39m)],\n\u001b[1;32m 286\u001b[0m stop\u001b[38;5;241m=\u001b[39mstop,\n\u001b[1;32m 287\u001b[0m callbacks\u001b[38;5;241m=\u001b[39mconfig\u001b[38;5;241m.\u001b[39mget(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mcallbacks\u001b[39m\u001b[38;5;124m\"\u001b[39m),\n\u001b[1;32m 288\u001b[0m tags\u001b[38;5;241m=\u001b[39mconfig\u001b[38;5;241m.\u001b[39mget(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mtags\u001b[39m\u001b[38;5;124m\"\u001b[39m),\n\u001b[1;32m 289\u001b[0m metadata\u001b[38;5;241m=\u001b[39mconfig\u001b[38;5;241m.\u001b[39mget(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mmetadata\u001b[39m\u001b[38;5;124m\"\u001b[39m),\n\u001b[1;32m 290\u001b[0m run_name\u001b[38;5;241m=\u001b[39mconfig\u001b[38;5;241m.\u001b[39mget(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mrun_name\u001b[39m\u001b[38;5;124m\"\u001b[39m),\n\u001b[1;32m 291\u001b[0m run_id\u001b[38;5;241m=\u001b[39mconfig\u001b[38;5;241m.\u001b[39mpop(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mrun_id\u001b[39m\u001b[38;5;124m\"\u001b[39m, \u001b[38;5;28;01mNone\u001b[39;00m),\n\u001b[1;32m 292\u001b[0m \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs,\n\u001b[1;32m 293\u001b[0m )\u001b[38;5;241m.\u001b[39mgenerations[\u001b[38;5;241m0\u001b[39m][\u001b[38;5;241m0\u001b[39m],\n\u001b[1;32m 294\u001b[0m )\u001b[38;5;241m.\u001b[39mmessage\n", "File \u001b[0;32m~/miniconda3/envs/paintrekbot/lib/python3.12/site-packages/langchain_core/language_models/chat_models.py:860\u001b[0m, in \u001b[0;36mBaseChatModel.generate_prompt\u001b[0;34m(self, prompts, stop, callbacks, **kwargs)\u001b[0m\n\u001b[1;32m 852\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mgenerate_prompt\u001b[39m(\n\u001b[1;32m 853\u001b[0m \u001b[38;5;28mself\u001b[39m,\n\u001b[1;32m 854\u001b[0m prompts: \u001b[38;5;28mlist\u001b[39m[PromptValue],\n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 857\u001b[0m \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs: Any,\n\u001b[1;32m 858\u001b[0m ) \u001b[38;5;241m-\u001b[39m\u001b[38;5;241m>\u001b[39m LLMResult:\n\u001b[1;32m 859\u001b[0m prompt_messages \u001b[38;5;241m=\u001b[39m [p\u001b[38;5;241m.\u001b[39mto_messages() \u001b[38;5;28;01mfor\u001b[39;00m p \u001b[38;5;129;01min\u001b[39;00m prompts]\n\u001b[0;32m--> 860\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mgenerate(prompt_messages, stop\u001b[38;5;241m=\u001b[39mstop, callbacks\u001b[38;5;241m=\u001b[39mcallbacks, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs)\n", "File \u001b[0;32m~/miniconda3/envs/paintrekbot/lib/python3.12/site-packages/langchain_core/language_models/chat_models.py:690\u001b[0m, in \u001b[0;36mBaseChatModel.generate\u001b[0;34m(self, messages, stop, callbacks, tags, metadata, run_name, run_id, **kwargs)\u001b[0m\n\u001b[1;32m 687\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m i, m \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28menumerate\u001b[39m(messages):\n\u001b[1;32m 688\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[1;32m 689\u001b[0m results\u001b[38;5;241m.\u001b[39mappend(\n\u001b[0;32m--> 690\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_generate_with_cache(\n\u001b[1;32m 691\u001b[0m m,\n\u001b[1;32m 692\u001b[0m stop\u001b[38;5;241m=\u001b[39mstop,\n\u001b[1;32m 693\u001b[0m run_manager\u001b[38;5;241m=\u001b[39mrun_managers[i] \u001b[38;5;28;01mif\u001b[39;00m run_managers \u001b[38;5;28;01melse\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m,\n\u001b[1;32m 694\u001b[0m \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs,\n\u001b[1;32m 695\u001b[0m )\n\u001b[1;32m 696\u001b[0m )\n\u001b[1;32m 697\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mBaseException\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m e:\n\u001b[1;32m 698\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m run_managers:\n", "File \u001b[0;32m~/miniconda3/envs/paintrekbot/lib/python3.12/site-packages/langchain_core/language_models/chat_models.py:925\u001b[0m, in \u001b[0;36mBaseChatModel._generate_with_cache\u001b[0;34m(self, messages, stop, run_manager, **kwargs)\u001b[0m\n\u001b[1;32m 923\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[1;32m 924\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m inspect\u001b[38;5;241m.\u001b[39msignature(\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_generate)\u001b[38;5;241m.\u001b[39mparameters\u001b[38;5;241m.\u001b[39mget(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mrun_manager\u001b[39m\u001b[38;5;124m\"\u001b[39m):\n\u001b[0;32m--> 925\u001b[0m result \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_generate(\n\u001b[1;32m 926\u001b[0m messages, stop\u001b[38;5;241m=\u001b[39mstop, run_manager\u001b[38;5;241m=\u001b[39mrun_manager, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs\n\u001b[1;32m 927\u001b[0m )\n\u001b[1;32m 928\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[1;32m 929\u001b[0m result \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_generate(messages, stop\u001b[38;5;241m=\u001b[39mstop, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs)\n", "File \u001b[0;32m~/miniconda3/envs/paintrekbot/lib/python3.12/site-packages/langchain_ollama/chat_models.py:701\u001b[0m, in \u001b[0;36mChatOllama._generate\u001b[0;34m(self, messages, stop, run_manager, **kwargs)\u001b[0m\n\u001b[1;32m 694\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21m_generate\u001b[39m(\n\u001b[1;32m 695\u001b[0m \u001b[38;5;28mself\u001b[39m,\n\u001b[1;32m 696\u001b[0m messages: List[BaseMessage],\n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 699\u001b[0m \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs: Any,\n\u001b[1;32m 700\u001b[0m ) \u001b[38;5;241m-\u001b[39m\u001b[38;5;241m>\u001b[39m ChatResult:\n\u001b[0;32m--> 701\u001b[0m final_chunk \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_chat_stream_with_aggregation(\n\u001b[1;32m 702\u001b[0m messages, stop, run_manager, verbose\u001b[38;5;241m=\u001b[39m\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mverbose, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs\n\u001b[1;32m 703\u001b[0m )\n\u001b[1;32m 704\u001b[0m generation_info \u001b[38;5;241m=\u001b[39m final_chunk\u001b[38;5;241m.\u001b[39mgeneration_info\n\u001b[1;32m 705\u001b[0m chat_generation \u001b[38;5;241m=\u001b[39m ChatGeneration(\n\u001b[1;32m 706\u001b[0m message\u001b[38;5;241m=\u001b[39mAIMessage(\n\u001b[1;32m 707\u001b[0m content\u001b[38;5;241m=\u001b[39mfinal_chunk\u001b[38;5;241m.\u001b[39mtext,\n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 711\u001b[0m generation_info\u001b[38;5;241m=\u001b[39mgeneration_info,\n\u001b[1;32m 712\u001b[0m )\n", "File \u001b[0;32m~/miniconda3/envs/paintrekbot/lib/python3.12/site-packages/langchain_ollama/chat_models.py:602\u001b[0m, in \u001b[0;36mChatOllama._chat_stream_with_aggregation\u001b[0;34m(self, messages, stop, run_manager, verbose, **kwargs)\u001b[0m\n\u001b[1;32m 593\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21m_chat_stream_with_aggregation\u001b[39m(\n\u001b[1;32m 594\u001b[0m \u001b[38;5;28mself\u001b[39m,\n\u001b[1;32m 595\u001b[0m messages: List[BaseMessage],\n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 599\u001b[0m \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs: Any,\n\u001b[1;32m 600\u001b[0m ) \u001b[38;5;241m-\u001b[39m\u001b[38;5;241m>\u001b[39m ChatGenerationChunk:\n\u001b[1;32m 601\u001b[0m final_chunk \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;01mNone\u001b[39;00m\n\u001b[0;32m--> 602\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m stream_resp \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_create_chat_stream(messages, stop, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs):\n\u001b[1;32m 603\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(stream_resp, \u001b[38;5;28mstr\u001b[39m):\n\u001b[1;32m 604\u001b[0m chunk \u001b[38;5;241m=\u001b[39m ChatGenerationChunk(\n\u001b[1;32m 605\u001b[0m message\u001b[38;5;241m=\u001b[39mAIMessageChunk(\n\u001b[1;32m 606\u001b[0m content\u001b[38;5;241m=\u001b[39m(\n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 619\u001b[0m ),\n\u001b[1;32m 620\u001b[0m )\n", "File \u001b[0;32m~/miniconda3/envs/paintrekbot/lib/python3.12/site-packages/langchain_ollama/chat_models.py:589\u001b[0m, in \u001b[0;36mChatOllama._create_chat_stream\u001b[0;34m(self, messages, stop, **kwargs)\u001b[0m\n\u001b[1;32m 586\u001b[0m chat_params \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_chat_params(messages, stop, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs)\n\u001b[1;32m 588\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m chat_params[\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mstream\u001b[39m\u001b[38;5;124m\"\u001b[39m]:\n\u001b[0;32m--> 589\u001b[0m \u001b[38;5;28;01myield from\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_client\u001b[38;5;241m.\u001b[39mchat(\u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mchat_params)\n\u001b[1;32m 590\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[1;32m 591\u001b[0m \u001b[38;5;28;01myield\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_client\u001b[38;5;241m.\u001b[39mchat(\u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mchat_params)\n", "File \u001b[0;32m~/miniconda3/envs/paintrekbot/lib/python3.12/site-packages/ollama/_client.py:163\u001b[0m, in \u001b[0;36mClient._request..inner\u001b[0;34m()\u001b[0m\n\u001b[1;32m 162\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21minner\u001b[39m():\n\u001b[0;32m--> 163\u001b[0m \u001b[38;5;28;01mwith\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_client\u001b[38;5;241m.\u001b[39mstream(\u001b[38;5;241m*\u001b[39margs, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs) \u001b[38;5;28;01mas\u001b[39;00m r:\n\u001b[1;32m 164\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[1;32m 165\u001b[0m r\u001b[38;5;241m.\u001b[39mraise_for_status()\n", "File \u001b[0;32m~/miniconda3/envs/paintrekbot/lib/python3.12/contextlib.py:137\u001b[0m, in \u001b[0;36m_GeneratorContextManager.__enter__\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 135\u001b[0m \u001b[38;5;28;01mdel\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39margs, \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mkwds, \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mfunc\n\u001b[1;32m 136\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[0;32m--> 137\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mnext\u001b[39m(\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mgen)\n\u001b[1;32m 138\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mStopIteration\u001b[39;00m:\n\u001b[1;32m 139\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mRuntimeError\u001b[39;00m(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mgenerator didn\u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mt yield\u001b[39m\u001b[38;5;124m\"\u001b[39m) \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m\n", "File \u001b[0;32m~/miniconda3/envs/paintrekbot/lib/python3.12/site-packages/httpx/_client.py:868\u001b[0m, in \u001b[0;36mClient.stream\u001b[0;34m(self, method, url, content, data, files, json, params, headers, cookies, auth, follow_redirects, timeout, extensions)\u001b[0m\n\u001b[1;32m 845\u001b[0m \u001b[38;5;250m\u001b[39m\u001b[38;5;124;03m\"\"\"\u001b[39;00m\n\u001b[1;32m 846\u001b[0m \u001b[38;5;124;03mAlternative to `httpx.request()` that streams the response body\u001b[39;00m\n\u001b[1;32m 847\u001b[0m \u001b[38;5;124;03minstead of loading it into memory at once.\u001b[39;00m\n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 853\u001b[0m \u001b[38;5;124;03m[0]: /quickstart#streaming-responses\u001b[39;00m\n\u001b[1;32m 854\u001b[0m \u001b[38;5;124;03m\"\"\"\u001b[39;00m\n\u001b[1;32m 855\u001b[0m request \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mbuild_request(\n\u001b[1;32m 856\u001b[0m method\u001b[38;5;241m=\u001b[39mmethod,\n\u001b[1;32m 857\u001b[0m url\u001b[38;5;241m=\u001b[39murl,\n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 866\u001b[0m extensions\u001b[38;5;241m=\u001b[39mextensions,\n\u001b[1;32m 867\u001b[0m )\n\u001b[0;32m--> 868\u001b[0m response \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39msend(\n\u001b[1;32m 869\u001b[0m request\u001b[38;5;241m=\u001b[39mrequest,\n\u001b[1;32m 870\u001b[0m auth\u001b[38;5;241m=\u001b[39mauth,\n\u001b[1;32m 871\u001b[0m follow_redirects\u001b[38;5;241m=\u001b[39mfollow_redirects,\n\u001b[1;32m 872\u001b[0m stream\u001b[38;5;241m=\u001b[39m\u001b[38;5;28;01mTrue\u001b[39;00m,\n\u001b[1;32m 873\u001b[0m )\n\u001b[1;32m 874\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[1;32m 875\u001b[0m \u001b[38;5;28;01myield\u001b[39;00m response\n", "File \u001b[0;32m~/miniconda3/envs/paintrekbot/lib/python3.12/site-packages/httpx/_client.py:914\u001b[0m, in \u001b[0;36mClient.send\u001b[0;34m(self, request, stream, auth, follow_redirects)\u001b[0m\n\u001b[1;32m 910\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_set_timeout(request)\n\u001b[1;32m 912\u001b[0m auth \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_build_request_auth(request, auth)\n\u001b[0;32m--> 914\u001b[0m response \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_send_handling_auth(\n\u001b[1;32m 915\u001b[0m request,\n\u001b[1;32m 916\u001b[0m auth\u001b[38;5;241m=\u001b[39mauth,\n\u001b[1;32m 917\u001b[0m follow_redirects\u001b[38;5;241m=\u001b[39mfollow_redirects,\n\u001b[1;32m 918\u001b[0m history\u001b[38;5;241m=\u001b[39m[],\n\u001b[1;32m 919\u001b[0m )\n\u001b[1;32m 920\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[1;32m 921\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m stream:\n", "File \u001b[0;32m~/miniconda3/envs/paintrekbot/lib/python3.12/site-packages/httpx/_client.py:942\u001b[0m, in \u001b[0;36mClient._send_handling_auth\u001b[0;34m(self, request, auth, follow_redirects, history)\u001b[0m\n\u001b[1;32m 939\u001b[0m request \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mnext\u001b[39m(auth_flow)\n\u001b[1;32m 941\u001b[0m \u001b[38;5;28;01mwhile\u001b[39;00m \u001b[38;5;28;01mTrue\u001b[39;00m:\n\u001b[0;32m--> 942\u001b[0m response \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_send_handling_redirects(\n\u001b[1;32m 943\u001b[0m request,\n\u001b[1;32m 944\u001b[0m follow_redirects\u001b[38;5;241m=\u001b[39mfollow_redirects,\n\u001b[1;32m 945\u001b[0m history\u001b[38;5;241m=\u001b[39mhistory,\n\u001b[1;32m 946\u001b[0m )\n\u001b[1;32m 947\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[1;32m 948\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n", "File \u001b[0;32m~/miniconda3/envs/paintrekbot/lib/python3.12/site-packages/httpx/_client.py:979\u001b[0m, in \u001b[0;36mClient._send_handling_redirects\u001b[0;34m(self, request, follow_redirects, history)\u001b[0m\n\u001b[1;32m 976\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m hook \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_event_hooks[\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mrequest\u001b[39m\u001b[38;5;124m\"\u001b[39m]:\n\u001b[1;32m 977\u001b[0m hook(request)\n\u001b[0;32m--> 979\u001b[0m response \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_send_single_request(request)\n\u001b[1;32m 980\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[1;32m 981\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m hook \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_event_hooks[\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mresponse\u001b[39m\u001b[38;5;124m\"\u001b[39m]:\n", "File \u001b[0;32m~/miniconda3/envs/paintrekbot/lib/python3.12/site-packages/httpx/_client.py:1014\u001b[0m, in \u001b[0;36mClient._send_single_request\u001b[0;34m(self, request)\u001b[0m\n\u001b[1;32m 1009\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mRuntimeError\u001b[39;00m(\n\u001b[1;32m 1010\u001b[0m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mAttempted to send an async request with a sync Client instance.\u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m 1011\u001b[0m )\n\u001b[1;32m 1013\u001b[0m \u001b[38;5;28;01mwith\u001b[39;00m request_context(request\u001b[38;5;241m=\u001b[39mrequest):\n\u001b[0;32m-> 1014\u001b[0m response \u001b[38;5;241m=\u001b[39m transport\u001b[38;5;241m.\u001b[39mhandle_request(request)\n\u001b[1;32m 1016\u001b[0m \u001b[38;5;28;01massert\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(response\u001b[38;5;241m.\u001b[39mstream, SyncByteStream)\n\u001b[1;32m 1018\u001b[0m response\u001b[38;5;241m.\u001b[39mrequest \u001b[38;5;241m=\u001b[39m request\n", "File \u001b[0;32m~/miniconda3/envs/paintrekbot/lib/python3.12/site-packages/httpx/_transports/default.py:249\u001b[0m, in \u001b[0;36mHTTPTransport.handle_request\u001b[0;34m(self, request)\u001b[0m\n\u001b[1;32m 235\u001b[0m \u001b[38;5;28;01mimport\u001b[39;00m \u001b[38;5;21;01mhttpcore\u001b[39;00m\n\u001b[1;32m 237\u001b[0m req \u001b[38;5;241m=\u001b[39m httpcore\u001b[38;5;241m.\u001b[39mRequest(\n\u001b[1;32m 238\u001b[0m method\u001b[38;5;241m=\u001b[39mrequest\u001b[38;5;241m.\u001b[39mmethod,\n\u001b[1;32m 239\u001b[0m url\u001b[38;5;241m=\u001b[39mhttpcore\u001b[38;5;241m.\u001b[39mURL(\n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 247\u001b[0m extensions\u001b[38;5;241m=\u001b[39mrequest\u001b[38;5;241m.\u001b[39mextensions,\n\u001b[1;32m 248\u001b[0m )\n\u001b[0;32m--> 249\u001b[0m \u001b[38;5;28;01mwith\u001b[39;00m map_httpcore_exceptions():\n\u001b[1;32m 250\u001b[0m resp \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_pool\u001b[38;5;241m.\u001b[39mhandle_request(req)\n\u001b[1;32m 252\u001b[0m \u001b[38;5;28;01massert\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(resp\u001b[38;5;241m.\u001b[39mstream, typing\u001b[38;5;241m.\u001b[39mIterable)\n", "File \u001b[0;32m~/miniconda3/envs/paintrekbot/lib/python3.12/contextlib.py:158\u001b[0m, in \u001b[0;36m_GeneratorContextManager.__exit__\u001b[0;34m(self, typ, value, traceback)\u001b[0m\n\u001b[1;32m 156\u001b[0m value \u001b[38;5;241m=\u001b[39m typ()\n\u001b[1;32m 157\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[0;32m--> 158\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mgen\u001b[38;5;241m.\u001b[39mthrow(value)\n\u001b[1;32m 159\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mStopIteration\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m exc:\n\u001b[1;32m 160\u001b[0m \u001b[38;5;66;03m# Suppress StopIteration *unless* it's the same exception that\u001b[39;00m\n\u001b[1;32m 161\u001b[0m \u001b[38;5;66;03m# was passed to throw(). This prevents a StopIteration\u001b[39;00m\n\u001b[1;32m 162\u001b[0m \u001b[38;5;66;03m# raised inside the \"with\" statement from being suppressed.\u001b[39;00m\n\u001b[1;32m 163\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m exc \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m value\n", "File \u001b[0;32m~/miniconda3/envs/paintrekbot/lib/python3.12/site-packages/httpx/_transports/default.py:118\u001b[0m, in \u001b[0;36mmap_httpcore_exceptions\u001b[0;34m()\u001b[0m\n\u001b[1;32m 115\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m\n\u001b[1;32m 117\u001b[0m message \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mstr\u001b[39m(exc)\n\u001b[0;32m--> 118\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m mapped_exc(message) \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;21;01mexc\u001b[39;00m\n", "\u001b[0;31mConnectError\u001b[0m: [Errno 111] Connection refused", "\u001b[0mDuring task with name 'chatbot_healthassistant' and id '0532b9be-af05-b133-af5f-0e24ecfcbbb0'" ] } ], "source": [ "# The default recursion limit for traversing nodes is 25 - setting it higher\n", "# means you can try a more complex order with multiple steps and round-trips.\n", "# config = {\"recursion_limit\": 500}\n", "\n", "# Uncomment this line to execute the graph:\n", "state = graph_with_order_tools.invoke({\"messages\": []})\n", "\n", "# pprint(state)" ] }, { "cell_type": "code", "execution_count": 23, "metadata": { "execution": { "iopub.execute_input": "2024-11-26T20:49:57.557546Z", "iopub.status.busy": "2024-11-26T20:49:57.557070Z", "iopub.status.idle": "2024-11-26T20:49:57.565305Z", "shell.execute_reply": "2024-11-26T20:49:57.563903Z", "shell.execute_reply.started": "2024-11-26T20:49:57.557497Z" }, "id": "n4jUJCr3fJpy", "trusted": true }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "dict_keys(['messages', 'finished'])\n" ] } ], "source": [ "# Uncomment this once you have run the graph from the previous cell.\n", "pprint(state.keys())" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "trusted": true }, "outputs": [], "source": [] } ], "metadata": { "colab": { "name": "day-3-building-an-agent-with-langgraph.ipynb", "toc_visible": true }, "kaggle": { "accelerator": "none", "dataSources": [], "dockerImageVersionId": 30786, "isGpuEnabled": false, "isInternetEnabled": true, "language": "python", "sourceType": "notebook" }, "kernelspec": { "display_name": "paintrekbot", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.12.8" } }, "nbformat": 4, "nbformat_minor": 4 }