Tenacious & trustworthy tool calling built on LangGraph.
Project description
trustcall
Tenacious tool calling built on LangGraph.
Uses patch-based extraction for:
- Faster & cheaper generation of structured output.
- Resilient retrying of validation errors, even for complex, nested schemas.
- Acccurate updates to existing schemas, avoiding undesired deletions.
Works flexibly across a number of common LLM workflows:
- Extraction
- LLM routing
- Multi-step agent tool use
and more!
Examples
First, install:
pip install -U trustcall langchain-fireworks
Then set up your schema:
from typing import List
from langchain_fireworks import ChatFireworks
from pydantic.v1 import BaseModel, Field, validator
from trustcall import create_extractor
class Preferences(BaseModel):
foods: List[str] = Field(description="Favorite foods")
@validator("foods")
def at_least_three_foods(cls, v):
# Just a silly example to show how it can recover from a
# validation error.
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")
res = extractor.invoke({"messages": [("user", "I like apple pie and ice cream.")]})
msg = res["messages"][-1]
print(msg.tool_calls)
print(res["responses"])
# [{'id': 'call_pBrHTBNHNLnGCv7UBKBJz6xf', 'name': 'Preferences', 'args': {'foods': ['apple pie', 'ice cream', 'pizza', 'sushi']}}]
# [Preferences(foods=['apple pie', 'ice cream', 'pizza', 'sushi'])]
Since the extractor also returns the chat message (with validated and cleaned tools), you can easiliy use the abstraction for conversational agent applications:
import operator
from datetime import datetime
from typing import List
import pytz
from langchain_fireworks import ChatFireworks
from langgraph.checkpoint.memory import MemorySaver
from langgraph.graph import START, StateGraph
from langgraph.prebuilt import ToolNode, tools_condition
from pydantic.v1 import BaseModel, Field, validator
from trustcall import create_extractor
from typing_extensions import Annotated, TypedDict
class Preferences(BaseModel):
foods: List[str] = Field(description="Favorite foods")
@validator("foods")
def at_least_three_foods(cls, v):
if len(v) < 3:
raise ValueError("Must have at least three favorite foods")
return v
llm = ChatFireworks(model="accounts/fireworks/models/firefunction-v2")
def save_user_information(preferences: Preferences):
"""Save user information to a database."""
return "User information saved"
def lookup_time(tz: str) -> str:
"""Lookup the current time in a given timezone."""
try:
# Convert the timezone string to a timezone object
timezone = pytz.timezone(tz)
# Get the current time in the given timezone
tm = datetime.now(timezone)
return f"The current time in {tz} is {tm.strftime('%H:%M:%S')}"
except pytz.UnknownTimeZoneError:
return f"Unknown timezone: {tz}"
agent = create_extractor(llm, tools=[save_user_information, lookup_time])
class State(TypedDict):
messages: Annotated[list, operator.add]
builder = StateGraph(State)
builder.add_node("agent", agent)
builder.add_node("tools", ToolNode([save_user_information, lookup_time]))
builder.add_edge("tools", "agent")
builder.add_edge(START, "agent")
builder.add_conditional_edges("agent", tools_condition)
graph = builder.compile(checkpointer=MemorySaver())
config = {"configurable": {"thread_id": "1234"}}
res = graph.invoke({"messages": [("user", "Hi there!")]}, config)
res["messages"][-1].pretty_print()
# ================================== Ai Message ==================================
# I'm happy to help you with any questions or tasks you have. What's on your mind today?
res = graph.invoke(
{"messages": [("user", "Curious; what's the time in denver right now?")]}, config
)
res["messages"][-1].pretty_print()
# ================================== Ai Message ==================================
# The current time in Denver is 00:57:25.
res = graph.invoke(
{
"messages": [
("user", "Did you know my favorite foods are spinach and potatoes?")
]
},
config,
)
res["messages"][-1].pretty_print()
# ================================== Ai Message ==================================
# I've saved your favorite foods, spinach and potatoes.
If you check out the last call in that conversation, you can see that the agent initially generated an invalid tool call, but our validation was able to fix up the output before passing the payload on to our tools.
These are just a couple examples to highlight what you can accomplish with trustcall
.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
File details
Details for the file trustcall-0.0.1.tar.gz
.
File metadata
- Download URL: trustcall-0.0.1.tar.gz
- Upload date:
- Size: 8.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: poetry/1.8.3 CPython/3.11.2 Darwin/23.4.0
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | 7a722630b2ad24ec96929400cb309510ca66ee0dd21c39078292762db5c78316 |
|
MD5 | c2963f9eca1eb4fe986db73b2fa7dcbe |
|
BLAKE2b-256 | b5ce587122c0f54cf07d7b0057a9993e163e952c41b32770f4c625e8d3732379 |
File details
Details for the file trustcall-0.0.1-py3-none-any.whl
.
File metadata
- Download URL: trustcall-0.0.1-py3-none-any.whl
- Upload date:
- Size: 9.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: poetry/1.8.3 CPython/3.11.2 Darwin/23.4.0
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | 93ead5236a81b4a4e4ba6391cc04f53531a875198d8292d586e737375b0553b2 |
|
MD5 | 08997f550ebad6e0053549fb12a9b85c |
|
BLAKE2b-256 | 4041f2e7cdcf5b76897dfe10430ccb7c5daf2e1dae206110715a6189a0bc262f |