langchain-ai / langgraph

Build resilient language agents as graphs.
https://langchain-ai.github.io/langgraph/
MIT License
4.28k stars 645 forks source link

Stream_events call fails with custom RawToolMessage class #621

Open meliascosta opened 1 month ago

meliascosta commented 1 month ago

Checked other resources

Example Code

tools = [create_plot, query_data]
tools_by_name = {tool.name: tool for tool in tools}

class RawToolMessage(ToolMessage):
    """
    Customized Tool message that lets us pass around the raw tool outputs (along with string contents for passing back to the model).
    """

    raw: dict
    """Arbitrary (non-string) tool outputs. Won't be sent to model."""
    tool_name: str
    """Name of tool that generated output."""

def execute_tools(state: State, config) -> dict:
    """
    Execute the called tools
    """
    messages = []

    last_ai_msg = [msg for msg in state["messages"] if isinstance(msg, AIMessage)][-1]
    for tool_call in last_ai_msg.tool_calls:
        try:
            result = tools_by_name[tool_call["name"]].invoke(tool_call["args"], config)
        except Exception as e:
            messages.append(
                ToolMessage(
                    content=f"Error: {repr(e)}\n please fix your mistakes.",
                    tool_call_id=tool_call["id"],
                    tool_name=tool_call["name"],
                )
            )
            continue

        if tool_call["name"] == "create_plot":
            result_repr = f"Created a plot with title: {result["plot_echarts_option"]["title"]["text"]}"
        elif tool_call["name"] == "query_data":
            result_repr = str({k: (v if k!= "data" else v[:5]) for k, v in result.items()})
        messages.append(
            RawToolMessage(
                result_repr,
                raw=result,
                tool_call_id=tool_call["id"],
                tool_name=tool_call["name"],
            )
        )
    return {"messages": messages}

Error Message and Stack Trace (if applicable)

Here is the complete trace but the error is actually happening before:

Error in LogStreamCallbackHandler.on_llm_end callback: ValueError("Trying to deserialize something that cannot be deserialized in current version of langchain-core: ('langchain', 'schema', 'messages', 'RawToolMessage')")

---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
File ~/.cache/pypoetry/virtualenvs/cc-llm-engine-uD5CZwxz-py3.12/lib/python3.12/site-packages/jsonpointer.py:288, in JsonPointer.walk(self, doc, part)
    [287](https://vscode-remote+wsl-002bubuntu.vscode-resource.vscode-cdn.net/home/mec/personal/cc/repos/cc-llm-engine/notebooks/~/.cache/pypoetry/virtualenvs/cc-llm-engine-uD5CZwxz-py3.12/lib/python3.12/site-packages/jsonpointer.py:287) try:
--> [288](https://vscode-remote+wsl-002bubuntu.vscode-resource.vscode-cdn.net/home/mec/personal/cc/repos/cc-llm-engine/notebooks/~/.cache/pypoetry/virtualenvs/cc-llm-engine-uD5CZwxz-py3.12/lib/python3.12/site-packages/jsonpointer.py:288)     return doc[part]
    [290](https://vscode-remote+wsl-002bubuntu.vscode-resource.vscode-cdn.net/home/mec/personal/cc/repos/cc-llm-engine/notebooks/~/.cache/pypoetry/virtualenvs/cc-llm-engine-uD5CZwxz-py3.12/lib/python3.12/site-packages/jsonpointer.py:290) except KeyError:

KeyError: 'AzureChatOpenAI:4'

During handling of the above exception, another exception occurred:

JsonPointerException                      Traceback (most recent call last)
Cell In[4], [line 1](vscode-notebook-cell:?execution_count=4&line=1)
----> [1](vscode-notebook-cell:?execution_count=4&line=1) async for x in app.astream_events({"messages": [HumanMessage("show my top 5 vendors by expense in a pie chart")]}, config={"configurable": {"db_schema": "greenlanterneastllc_test"}}, version="v1"):
      [2](vscode-notebook-cell:?execution_count=4&line=2)     print(x, flush=True)

File ~/.cache/pypoetry/virtualenvs/cc-llm-engine-uD5CZwxz-py3.12/lib/python3.12/site-packages/langchain_core/runnables/base.py:1145, in Runnable.astream_events(self, input, config, version, include_names, include_types, include_tags, exclude_names, exclude_types, exclude_tags, **kwargs)
   [1134](https://vscode-remote+wsl-002bubuntu.vscode-resource.vscode-cdn.net/home/mec/personal/cc/repos/cc-llm-engine/notebooks/~/.cache/pypoetry/virtualenvs/cc-llm-engine-uD5CZwxz-py3.12/lib/python3.12/site-packages/langchain_core/runnables/base.py:1134) # Ignoring mypy complaint about too many different union combinations
   [1135](https://vscode-remote+wsl-002bubuntu.vscode-resource.vscode-cdn.net/home/mec/personal/cc/repos/cc-llm-engine/notebooks/~/.cache/pypoetry/virtualenvs/cc-llm-engine-uD5CZwxz-py3.12/lib/python3.12/site-packages/langchain_core/runnables/base.py:1135) # This arises because many of the argument types are unions
   [1136](https://vscode-remote+wsl-002bubuntu.vscode-resource.vscode-cdn.net/home/mec/personal/cc/repos/cc-llm-engine/notebooks/~/.cache/pypoetry/virtualenvs/cc-llm-engine-uD5CZwxz-py3.12/lib/python3.12/site-packages/langchain_core/runnables/base.py:1136) async for log in _astream_log_implementation(  # type: ignore[misc]
   [1137](https://vscode-remote+wsl-002bubuntu.vscode-resource.vscode-cdn.net/home/mec/personal/cc/repos/cc-llm-engine/notebooks/~/.cache/pypoetry/virtualenvs/cc-llm-engine-uD5CZwxz-py3.12/lib/python3.12/site-packages/langchain_core/runnables/base.py:1137)     self,
   [1138](https://vscode-remote+wsl-002bubuntu.vscode-resource.vscode-cdn.net/home/mec/personal/cc/repos/cc-llm-engine/notebooks/~/.cache/pypoetry/virtualenvs/cc-llm-engine-uD5CZwxz-py3.12/lib/python3.12/site-packages/langchain_core/runnables/base.py:1138)     input,
   (...)
   [1143](https://vscode-remote+wsl-002bubuntu.vscode-resource.vscode-cdn.net/home/mec/personal/cc/repos/cc-llm-engine/notebooks/~/.cache/pypoetry/virtualenvs/cc-llm-engine-uD5CZwxz-py3.12/lib/python3.12/site-packages/langchain_core/runnables/base.py:1143)     **kwargs,
   [1144](https://vscode-remote+wsl-002bubuntu.vscode-resource.vscode-cdn.net/home/mec/personal/cc/repos/cc-llm-engine/notebooks/~/.cache/pypoetry/virtualenvs/cc-llm-engine-uD5CZwxz-py3.12/lib/python3.12/site-packages/langchain_core/runnables/base.py:1144) ):
-> [1145](https://vscode-remote+wsl-002bubuntu.vscode-resource.vscode-cdn.net/home/mec/personal/cc/repos/cc-llm-engine/notebooks/~/.cache/pypoetry/virtualenvs/cc-llm-engine-uD5CZwxz-py3.12/lib/python3.12/site-packages/langchain_core/runnables/base.py:1145)     run_log = run_log + log
   [1147](https://vscode-remote+wsl-002bubuntu.vscode-resource.vscode-cdn.net/home/mec/personal/cc/repos/cc-llm-engine/notebooks/~/.cache/pypoetry/virtualenvs/cc-llm-engine-uD5CZwxz-py3.12/lib/python3.12/site-packages/langchain_core/runnables/base.py:1147)     if not encountered_start_event:
   [1148](https://vscode-remote+wsl-002bubuntu.vscode-resource.vscode-cdn.net/home/mec/personal/cc/repos/cc-llm-engine/notebooks/~/.cache/pypoetry/virtualenvs/cc-llm-engine-uD5CZwxz-py3.12/lib/python3.12/site-packages/langchain_core/runnables/base.py:1148)         # Yield the start event for the root runnable.
   [1149](https://vscode-remote+wsl-002bubuntu.vscode-resource.vscode-cdn.net/home/mec/personal/cc/repos/cc-llm-engine/notebooks/~/.cache/pypoetry/virtualenvs/cc-llm-engine-uD5CZwxz-py3.12/lib/python3.12/site-packages/langchain_core/runnables/base.py:1149)         encountered_start_event = True

File ~/.cache/pypoetry/virtualenvs/cc-llm-engine-uD5CZwxz-py3.12/lib/python3.12/site-packages/langchain_core/tracers/log_stream.py:135, in RunLog.__add__(self, other)
    [133](https://vscode-remote+wsl-002bubuntu.vscode-resource.vscode-cdn.net/home/mec/personal/cc/repos/cc-llm-engine/notebooks/~/.cache/pypoetry/virtualenvs/cc-llm-engine-uD5CZwxz-py3.12/lib/python3.12/site-packages/langchain_core/tracers/log_stream.py:133) if type(other) == RunLogPatch:
    [134](https://vscode-remote+wsl-002bubuntu.vscode-resource.vscode-cdn.net/home/mec/personal/cc/repos/cc-llm-engine/notebooks/~/.cache/pypoetry/virtualenvs/cc-llm-engine-uD5CZwxz-py3.12/lib/python3.12/site-packages/langchain_core/tracers/log_stream.py:134)     ops = self.ops + other.ops
--> [135](https://vscode-remote+wsl-002bubuntu.vscode-resource.vscode-cdn.net/home/mec/personal/cc/repos/cc-llm-engine/notebooks/~/.cache/pypoetry/virtualenvs/cc-llm-engine-uD5CZwxz-py3.12/lib/python3.12/site-packages/langchain_core/tracers/log_stream.py:135)     state = jsonpatch.apply_patch(self.state, other.ops)
    [136](https://vscode-remote+wsl-002bubuntu.vscode-resource.vscode-cdn.net/home/mec/personal/cc/repos/cc-llm-engine/notebooks/~/.cache/pypoetry/virtualenvs/cc-llm-engine-uD5CZwxz-py3.12/lib/python3.12/site-packages/langchain_core/tracers/log_stream.py:136)     return RunLog(*ops, state=state)
    [138](https://vscode-remote+wsl-002bubuntu.vscode-resource.vscode-cdn.net/home/mec/personal/cc/repos/cc-llm-engine/notebooks/~/.cache/pypoetry/virtualenvs/cc-llm-engine-uD5CZwxz-py3.12/lib/python3.12/site-packages/langchain_core/tracers/log_stream.py:138) raise TypeError(
    [139](https://vscode-remote+wsl-002bubuntu.vscode-resource.vscode-cdn.net/home/mec/personal/cc/repos/cc-llm-engine/notebooks/~/.cache/pypoetry/virtualenvs/cc-llm-engine-uD5CZwxz-py3.12/lib/python3.12/site-packages/langchain_core/tracers/log_stream.py:139)     f"unsupported operand type(s) for +: '{type(self)}' and '{type(other)}'"
    [140](https://vscode-remote+wsl-002bubuntu.vscode-resource.vscode-cdn.net/home/mec/personal/cc/repos/cc-llm-engine/notebooks/~/.cache/pypoetry/virtualenvs/cc-llm-engine-uD5CZwxz-py3.12/lib/python3.12/site-packages/langchain_core/tracers/log_stream.py:140) )

File ~/.cache/pypoetry/virtualenvs/cc-llm-engine-uD5CZwxz-py3.12/lib/python3.12/site-packages/jsonpatch.py:157, in apply_patch(doc, patch, in_place, pointer_cls)
    [155](https://vscode-remote+wsl-002bubuntu.vscode-resource.vscode-cdn.net/home/mec/personal/cc/repos/cc-llm-engine/notebooks/~/.cache/pypoetry/virtualenvs/cc-llm-engine-uD5CZwxz-py3.12/lib/python3.12/site-packages/jsonpatch.py:155) else:
    [156](https://vscode-remote+wsl-002bubuntu.vscode-resource.vscode-cdn.net/home/mec/personal/cc/repos/cc-llm-engine/notebooks/~/.cache/pypoetry/virtualenvs/cc-llm-engine-uD5CZwxz-py3.12/lib/python3.12/site-packages/jsonpatch.py:156)     patch = JsonPatch(patch, pointer_cls=pointer_cls)
--> [157](https://vscode-remote+wsl-002bubuntu.vscode-resource.vscode-cdn.net/home/mec/personal/cc/repos/cc-llm-engine/notebooks/~/.cache/pypoetry/virtualenvs/cc-llm-engine-uD5CZwxz-py3.12/lib/python3.12/site-packages/jsonpatch.py:157) return patch.apply(doc, in_place)

File ~/.cache/pypoetry/virtualenvs/cc-llm-engine-uD5CZwxz-py3.12/lib/python3.12/site-packages/jsonpatch.py:692, in JsonPatch.apply(self, obj, in_place)
    [689](https://vscode-remote+wsl-002bubuntu.vscode-resource.vscode-cdn.net/home/mec/personal/cc/repos/cc-llm-engine/notebooks/~/.cache/pypoetry/virtualenvs/cc-llm-engine-uD5CZwxz-py3.12/lib/python3.12/site-packages/jsonpatch.py:689)     obj = copy.deepcopy(obj)
    [691](https://vscode-remote+wsl-002bubuntu.vscode-resource.vscode-cdn.net/home/mec/personal/cc/repos/cc-llm-engine/notebooks/~/.cache/pypoetry/virtualenvs/cc-llm-engine-uD5CZwxz-py3.12/lib/python3.12/site-packages/jsonpatch.py:691) for operation in self._ops:
--> [692](https://vscode-remote+wsl-002bubuntu.vscode-resource.vscode-cdn.net/home/mec/personal/cc/repos/cc-llm-engine/notebooks/~/.cache/pypoetry/virtualenvs/cc-llm-engine-uD5CZwxz-py3.12/lib/python3.12/site-packages/jsonpatch.py:692)     obj = operation.apply(obj)
    [694](https://vscode-remote+wsl-002bubuntu.vscode-resource.vscode-cdn.net/home/mec/personal/cc/repos/cc-llm-engine/notebooks/~/.cache/pypoetry/virtualenvs/cc-llm-engine-uD5CZwxz-py3.12/lib/python3.12/site-packages/jsonpatch.py:694) return obj

File ~/.cache/pypoetry/virtualenvs/cc-llm-engine-uD5CZwxz-py3.12/lib/python3.12/site-packages/jsonpatch.py:282, in AddOperation.apply(self, obj)
    [278](https://vscode-remote+wsl-002bubuntu.vscode-resource.vscode-cdn.net/home/mec/personal/cc/repos/cc-llm-engine/notebooks/~/.cache/pypoetry/virtualenvs/cc-llm-engine-uD5CZwxz-py3.12/lib/python3.12/site-packages/jsonpatch.py:278) except KeyError as ex:
    [279](https://vscode-remote+wsl-002bubuntu.vscode-resource.vscode-cdn.net/home/mec/personal/cc/repos/cc-llm-engine/notebooks/~/.cache/pypoetry/virtualenvs/cc-llm-engine-uD5CZwxz-py3.12/lib/python3.12/site-packages/jsonpatch.py:279)     raise InvalidJsonPatch(
    [280](https://vscode-remote+wsl-002bubuntu.vscode-resource.vscode-cdn.net/home/mec/personal/cc/repos/cc-llm-engine/notebooks/~/.cache/pypoetry/virtualenvs/cc-llm-engine-uD5CZwxz-py3.12/lib/python3.12/site-packages/jsonpatch.py:280)         "The operation does not contain a 'value' member")
--> [282](https://vscode-remote+wsl-002bubuntu.vscode-resource.vscode-cdn.net/home/mec/personal/cc/repos/cc-llm-engine/notebooks/~/.cache/pypoetry/virtualenvs/cc-llm-engine-uD5CZwxz-py3.12/lib/python3.12/site-packages/jsonpatch.py:282) subobj, part = self.pointer.to_last(obj)
    [284](https://vscode-remote+wsl-002bubuntu.vscode-resource.vscode-cdn.net/home/mec/personal/cc/repos/cc-llm-engine/notebooks/~/.cache/pypoetry/virtualenvs/cc-llm-engine-uD5CZwxz-py3.12/lib/python3.12/site-packages/jsonpatch.py:284) if isinstance(subobj, MutableSequence):
    [285](https://vscode-remote+wsl-002bubuntu.vscode-resource.vscode-cdn.net/home/mec/personal/cc/repos/cc-llm-engine/notebooks/~/.cache/pypoetry/virtualenvs/cc-llm-engine-uD5CZwxz-py3.12/lib/python3.12/site-packages/jsonpatch.py:285)     if part == '-':

File ~/.cache/pypoetry/virtualenvs/cc-llm-engine-uD5CZwxz-py3.12/lib/python3.12/site-packages/jsonpointer.py:196, in JsonPointer.to_last(self, doc)
    [193](https://vscode-remote+wsl-002bubuntu.vscode-resource.vscode-cdn.net/home/mec/personal/cc/repos/cc-llm-engine/notebooks/~/.cache/pypoetry/virtualenvs/cc-llm-engine-uD5CZwxz-py3.12/lib/python3.12/site-packages/jsonpointer.py:193)     return doc, None
    [195](https://vscode-remote+wsl-002bubuntu.vscode-resource.vscode-cdn.net/home/mec/personal/cc/repos/cc-llm-engine/notebooks/~/.cache/pypoetry/virtualenvs/cc-llm-engine-uD5CZwxz-py3.12/lib/python3.12/site-packages/jsonpointer.py:195) for part in self.parts[:-1]:
--> [196](https://vscode-remote+wsl-002bubuntu.vscode-resource.vscode-cdn.net/home/mec/personal/cc/repos/cc-llm-engine/notebooks/~/.cache/pypoetry/virtualenvs/cc-llm-engine-uD5CZwxz-py3.12/lib/python3.12/site-packages/jsonpointer.py:196)     doc = self.walk(doc, part)
    [198](https://vscode-remote+wsl-002bubuntu.vscode-resource.vscode-cdn.net/home/mec/personal/cc/repos/cc-llm-engine/notebooks/~/.cache/pypoetry/virtualenvs/cc-llm-engine-uD5CZwxz-py3.12/lib/python3.12/site-packages/jsonpointer.py:198) return doc, JsonPointer.get_part(doc, self.parts[-1])

File ~/.cache/pypoetry/virtualenvs/cc-llm-engine-uD5CZwxz-py3.12/lib/python3.12/site-packages/jsonpointer.py:291, in JsonPointer.walk(self, doc, part)
    [288](https://vscode-remote+wsl-002bubuntu.vscode-resource.vscode-cdn.net/home/mec/personal/cc/repos/cc-llm-engine/notebooks/~/.cache/pypoetry/virtualenvs/cc-llm-engine-uD5CZwxz-py3.12/lib/python3.12/site-packages/jsonpointer.py:288)     return doc[part]
    [290](https://vscode-remote+wsl-002bubuntu.vscode-resource.vscode-cdn.net/home/mec/personal/cc/repos/cc-llm-engine/notebooks/~/.cache/pypoetry/virtualenvs/cc-llm-engine-uD5CZwxz-py3.12/lib/python3.12/site-packages/jsonpointer.py:290) except KeyError:
--> [291](https://vscode-remote+wsl-002bubuntu.vscode-resource.vscode-cdn.net/home/mec/personal/cc/repos/cc-llm-engine/notebooks/~/.cache/pypoetry/virtualenvs/cc-llm-engine-uD5CZwxz-py3.12/lib/python3.12/site-packages/jsonpointer.py:291)     raise JsonPointerException("member '%s' not found in %s" % (part, doc))

JsonPointerException: member 'AzureChatOpenAI:4' not found in { [[deleted private data]] }

Description

I'm trying to stream_events in a graph that uses a custom RawToolMessage class as described in this example notebook. If I use the .invoke method like in the original notebook it works OK, but when I call .stream_events I get an error:

Error in LogStreamCallbackHandler.on_llm_end callback: ValueError("Trying to deserialize something that cannot be deserialized in current version of langchain-core: ('langchain', 'schema', 'messages', 'RawToolMessage')")

System Info

System Information

OS: Linux OS Version: #1 SMP Thu Jan 11 04:09:03 UTC 2024 Python Version: 3.12.3 (main, May 23 2024, 22:52:35) [GCC 9.4.0]

Package Information

langchain_core: 0.1.52 langchain: 0.1.20 langchain_community: 0.0.38 langsmith: 0.1.62 langchain_openai: 0.1.7 langchain_postgres: 0.0.6 langchain_text_splitters: 0.0.2 langgraph: 0.0.46

Packages not installed (Not Necessarily a Problem)

The following packages were not found:

langserve

baskaryan commented 1 month ago

hm im not able to reproduce by just calling astream_events on the azure container apps graph. could you provide a full langraph graph to reproduce?

could you also try updating your packages

pip install -U langchain langchain-community langchain-openai langchain-postgres langgraph

would also be helpful to know what the outputs of the create_plot and query_data tools are

meliascosta commented 1 month ago

Thanks for the reply! I thought I had updated my packages but it turns out there was a dependency holding everything down. Just updated, new versions listed below. The problem persists.

System Information

OS: Linux OS Version: #1 SMP Thu Jan 11 04:09:03 UTC 2024 Python Version: 3.12.3 (main, May 23 2024, 22:52:35) [GCC 9.4.0]

Package Information

langchain_core: 0.2.5 langchain: 0.2.3 langchain_community: 0.2.4 langsmith: 0.1.75 langchain_openai: 0.1.8 langchain_postgres: 0.0.6 langchain_text_splitters: 0.2.1 langgraph: 0.0.65

I can confirm the azure container apps graph runs ok. But my code still fails. I've hardcoded the tool outputs and the repr on the RawToolMessage and it still fails. Replacing with a regular ToolMessage makes it work again.

Pasting the whole code for the graph here:

from datetime import datetime
from typing import Annotated, Literal, Optional

from loguru import logger as log
from langchain_core.messages import SystemMessage, ToolMessage, AIMessage
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_core.runnables import (
    RunnableConfig,
    RunnableLambda,
    ensure_config,
)
from langchain_core.tools import tool
from langgraph.graph import StateGraph
from langgraph.graph.message import AnyMessage, add_messages
from langgraph.prebuilt import ToolNode, tools_condition
from pydantic import BaseModel
from typing_extensions import TypedDict

from twill_llm_engine.llms import model_selector

def update_dialog_stack(left: list[str], right: Optional[str]) -> list[str]:
    """Push or pop the state."""
    if right is None:
        return left
    if right == "pop":
        return left[:-1]
    return left + [right]

class Widget(BaseModel):
    name: str
    type: str
    data: dict
    echarts_option: dict
    sql_query: str

class State(TypedDict):
    messages: Annotated[list[AnyMessage], add_messages]
    widgets: dict
    user_info: str

    dialog_state: Annotated[
        list[
            Literal[
                "assistant",
                "generate_chart",
            ]
        ],
        update_dialog_stack,
    ]

@tool
def create_plot(nl_query: str):
    "Creates a plot from a natural language query. The natural language query should be as descriptive as possible."
    config = ensure_config()
    # output = plot_chain.invoke({"nl_query": nl_query}, config=config)
    output = {"a": "b"}
    return output

@tool
def query_data(nl_query: str):
    "Brings information from database to answer a natural language query. The natural language query should be as descriptive as possible."
    config = ensure_config()
    # output = sql_agent.invoke({"nl_query": nl_query}, config=config)
    output = {"a": "b"}
    return output

primary_assistant_prompt = ChatPromptTemplate.from_messages(
    [
        SystemMessage(
            "You are a helpful accounting and finance assistant. "
            " Use the provided tools to query the database or create a plot. "
            " If you want to create a plot or chart no need to query the database first, the plot tool already takes care of that. "
            " If a search comes up empty, expand your search before giving up."
            "\nCurrent time: {time}.",
        ),
        MessagesPlaceholder("messages"),
    ]
).partial(time=datetime.now())

tools = [create_plot, query_data]
tools_by_name = {tool.name: tool for tool in tools}

class RawToolMessage(ToolMessage):
    """
    Customized Tool message that lets us pass around the raw tool outputs (along with string contents for passing back to the model).
    """

    raw: dict
    """Arbitrary (non-string) tool outputs. Won't be sent to model."""
    tool_name: str
    """Name of tool that generated output."""

def execute_tools(state: State, config) -> dict:
    """
    Execute the called tools
    """
    messages = []

    last_ai_msg = [
        msg for msg in state["messages"] if isinstance(msg, AIMessage)
    ][-1]
    for tool_call in last_ai_msg.tool_calls:
        try:
            result = tools_by_name[tool_call["name"]].invoke(
                tool_call["args"], config
            )
        except Exception as e:
            messages.append(
                ToolMessage(
                    content=f"Error: {repr(e)}\n please fix your mistakes.",
                    tool_call_id=tool_call["id"],
                    tool_name=tool_call["name"],
                )
            )
            continue

        # if tool_call["name"] == "create_plot":
        #     result_repr = f"Created a plot with title: {result["plot_echarts_option"]["title"]["text"]}"
        # elif tool_call["name"] == "query_data":
        #     result_repr = str({k: (v if k!= "data" else v[:5]) for k, v in result.items()})
        log.info(result)
        # messages.append(ToolMessage("something", tool_call_id=tool_call["id"]))
        messages.append(
            RawToolMessage(
                "something",
                raw={"a": "B"},
                tool_call_id=tool_call["id"],
                tool_name=tool_call["name"],
            )
        )
    return {"messages": messages}

def call_model(state: State, config: RunnableConfig):
    configurable = config["configurable"]
    model = model_selector[configurable.get("main_model", "gpt3.5")]
    assistant_runnable = primary_assistant_prompt | model.bind_tools(tools)

    while True:
        result = assistant_runnable.invoke(state)
        # If the LLM happens to return an empty response, we will re-prompt it
        # for an actual response.
        if not result.tool_calls and (
            not result.content
            or isinstance(result.content, list)
            and not result.content[0].get("text")
        ):
            messages = state["messages"] + [
                ("user", "Respond with a real output.")
            ]
            state = {**state, "messages": messages}
        else:
            break
    return {"messages": result}

builder = StateGraph(State)

# Define nodes: these do the work
builder.add_node("assistant", call_model)
builder.add_node("tools", execute_tools)
# Define edges: these determine how the control flow moves
builder.set_entry_point("assistant")
builder.add_conditional_edges(
    "assistant",
    tools_condition,
)
builder.add_edge("tools", "assistant")

app = builder.compile()

Thanks again!

meliascosta commented 4 weeks ago

Wasn't able to figure out what was wrong but switching to stream_events "v2" resolved the issue