hinthornw / trustcall

Tenacious tool calling built on LangGraph
MIT License
178 stars 17 forks source link

Unexpected GraphRecursionError #10

Closed Tesla2000 closed 3 weeks ago

Tesla2000 commented 4 weeks ago

Steps to reproduce: Run this test:

from __future__ import annotations

from typing import Optional

from unittest import TestCase

from dotenv import load_dotenv
from langchain_openai import ChatOpenAI
from pydantic import BaseModel
from trustcall import create_extractor

class TrustcallTest(TestCase):
    def test_trustcall(self):
        load_dotenv()
        class UserModel(BaseModel):
            age: Optional[float] = None
            assumed_income_growth_rate_annual: Optional[float] = None
        patch_data_extractor = create_extractor(
            ChatOpenAI(temperature=0.0, model_name="gpt-4o-mini"),
            tools=[UserModel],
            tool_choice=UserModel.__name__,
        )
        patch_data_extractor.invoke({"messages": [{"role": "user", "content": "I am 46"}], "existing": {"age": 45.0}})

Expected behavior: New user age is extracted or Schema age could not not be found for existing payload 45.0 is raised, message is not noticable with a lot of log messages and the exception doesn't provide a way to identify the issue Actual behaviour: Schema age could not not be found for existing payload 45.0 is printed 25 time before GraphRecursionError

hinthornw commented 3 weeks ago

Hi there! Thank you for your patience while I was away!

The reason you have an error is the 'existing' arg expects the format:

{ "<schema name>": {<existing object>}

NOT

{<existing object>}

The following works just fine for me:

from __future__ import annotations

from typing import Optional
from unittest import TestCase

from dotenv import load_dotenv
from langchain_openai import ChatOpenAI
from pydantic import BaseModel
from trustcall import create_extractor

class UserModel(BaseModel):
    age: Optional[float] = None
    assumed_income_growth_rate_annual: Optional[float] = None

patch_data_extractor = create_extractor(
    ChatOpenAI(temperature=0.0, model_name="gpt-4o-mini"),
    tools=[UserModel],
    tool_choice=UserModel.__name__,
)
patch_data_extractor.invoke(
    {
        "messages": [{"role": "user", "content": "I am 46"}],
        "existing": {"UserModel": {"age": 45.0}},
    }
)
hinthornw commented 3 weeks ago

Added a new version that does validation of the "existing" dict to fail early with a better error message.