Trustcall

0.0.39 · active · verified Sun Apr 12

Trustcall is a Python library that provides tenacious and trustworthy tool calling capabilities built on LangGraph. It addresses common challenges with Large Language Models (LLMs) in generating and updating complex, nested JSON schemas by employing a 'patch-don't-post' methodology. This approach enables faster, cheaper, and more resilient structured output generation, as well as accurate updates to existing schemas without information loss. The library is actively developed, with its current version being 0.0.39, and new minor versions are released regularly.

Warnings

Install

Imports

Quickstart

This quickstart demonstrates how to use `trustcall.create_extractor` to define a Pydantic schema for structured data extraction. It shows how Trustcall automatically handles validation errors by re-prompting the LLM to generate JSON patches, ensuring the output conforms to the schema. It also includes an example of updating an existing Pydantic model with new information using the `existing` parameter.

import os
from typing import List
from langchain_fireworks import ChatFireworks
from pydantic.v1 import BaseModel, Field, validator
from trustcall import create_extractor

# Ensure FIREWORKS_API_KEY is set in your environment variables
# os.environ["FIREWORKS_API_KEY"] = os.environ.get('FIREWORKS_API_KEY', 'YOUR_FIREWORKS_API_KEY')

class Preferences(BaseModel):
    foods: List[str] = Field(description="Favorite foods")

    @validator("foods")
    def at_least_three_foods(cls, v):
        # This validator serves as a demonstration of error recovery
        if len(v) < 3:
            raise ValueError("Must have at least three favorite foods")
        return v

llm = ChatFireworks(model="accounts/fireworks/models/firefunction-v2")
extractor = create_extractor(llm, tools=[Preferences], tool_choice="Preferences")

# Example 1: Initial extraction with validation error, Trustcall recovers
res = extractor.invoke({"messages": [("user", "I like apple pie and ice cream.")]})
msg = res["messages"][-1]
print("Initial extraction (recovered):", msg.tool_calls)
# Expected output for foods list is now >= 3 items due to recovery

# Example 2: Updating an existing schema
class UserProfile(BaseModel):
    name: str
    hobbies: List[str]

existing_profile = UserProfile(name="Alice", hobbies=["reading", "hiking"])

update_extractor = create_extractor(
    llm, 
    tools=[UserProfile], 
    tool_choice="UserProfile"
)

updated_res = update_extractor.invoke({
    "messages": [("user", "My new hobby is painting.")], 
    "existing": {"UserProfile": existing_profile}
})

updated_msg = updated_res["messages"][-1]
print("\nUpdated profile:", updated_msg.tool_calls)

view raw JSON →