Metadata-Version: 2.4
Name: truffle-kit
Version: 0.1.0
Summary: A lightweight Python framework for building single and multi-agent LLM applications.
Project-URL: Homepage, https://github.com/dsoderman/truffle-kit
Project-URL: Repository, https://github.com/dsoderman/truffle-kit
Project-URL: Issues, https://github.com/dsoderman/truffle-kit/issues
Author-email: Daniel Soderman <daniel.soderman1997@gmail.com>
License: MIT
License-File: LICENSE
Keywords: agents,ai,llm,multi-agent,openai,tool-calling
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.11
Requires-Dist: openai>=1.0.0
Provides-Extra: dev
Requires-Dist: pytest; extra == 'dev'
Provides-Extra: postgres
Requires-Dist: psycopg2-binary>=2.9; extra == 'postgres'
Description-Content-Type: text/markdown

<p align="left">
  <img src="assets/truffle-logo-wide.png" alt="Truffle" width="400"/>
</p>

A lightweight Python framework for building single and multi-agent LLM applications.

```bash
pip install truffle-kit
```

```python
import truffle
```

<p>
  <a href="#overview">Overview</a> &nbsp;·&nbsp;
  <a href="#quick-start">Quick Start</a> &nbsp;·&nbsp;
  <a href="#the-tool-decorator">@tool</a> &nbsp;·&nbsp;
  <a href="#memory-strategies">Memory</a> &nbsp;·&nbsp;
  <a href="#multi-agent-with-orchestrator">Multi-Agent</a> &nbsp;·&nbsp;
  <a href="#contrib">Contrib</a> &nbsp;·&nbsp;
  <a href="#api-reference">API Reference</a>
</p>

---

## Overview

Truffle gives you the building blocks to compose agents — each with their own tools, memory, and responsibilities — and wire them together under an orchestrator that routes tasks automatically.

- `@tool` — decorate any function to make it available to an agent. Schema is generated automatically from type hints and docstrings.
- `Agent` — an LLM agent with a tool-calling loop, pluggable memory, and a status callback.
- `Orchestrator` — wraps multiple agents and auto-generates handoff tools so the LLM can delegate tasks by name.
- Memory strategies — choose how each agent manages its conversation history.

Works with any OpenAI-compatible client (OpenAI, Azure OpenAI, etc).

---

## Quick start

```python
import asyncio
from openai import OpenAI
from truffle import Agent, tool, SlidingWindowMemory

client = OpenAI()

@tool
def get_weather(location: str) -> str:
    """Get the current weather for a location."""
    return f"Sunny and 22°C in {location}."

agent = Agent(
    name="assistant",
    instructions="You are a helpful assistant.",
    client=client,
    model="gpt-4o",
    tool_schemas=[get_weather.schema],
    tool_registry={"get_weather": get_weather},
    memory=SlidingWindowMemory(max_messages=20),
    on_status=lambda tool_name: print(f"calling {tool_name}..."),
)

async def main():
    reply = await agent.run("What's the weather in Tokyo?")
    print(reply)

asyncio.run(main())
```

---

## The @tool decorator

Decorate any function with `@tool` and Truffle generates the OpenAI function schema from its type hints and docstring — no JSON required.

```python
from truffle import tool

@tool
def convert_to_usd(amount: float, from_currency: str) -> str:
    """Convert an amount from a given currency to USD."""
    ...

# Schema is available at:
print(convert_to_usd.schema)
```

Supported types: `str`, `int`, `float`, `bool`, `list`, `dict`, and `Optional[X]`.

---

## Memory strategies

Pass a memory strategy to any `Agent` or `Orchestrator` to control how conversation history is managed.

### UnlimitedMemory (default)

Keeps every message. No truncation.

```python
from truffle import UnlimitedMemory

Agent(..., memory=UnlimitedMemory())
```

### SlidingWindowMemory

Keeps the system prompt and the last `max_messages` messages. Oldest messages are dropped silently.

```python
from truffle import SlidingWindowMemory

Agent(..., memory=SlidingWindowMemory(max_messages=20))
```

### SummarizingMemory

Once the conversation exceeds `threshold` messages, the oldest ones (beyond `keep_recent`) are summarized into a single condensed message using the LLM. Falls back to silent truncation if no client is provided.

```python
from truffle import SummarizingMemory

Agent(..., memory=SummarizingMemory(
    threshold=30,
    keep_recent=10,
    client=client,
    model="gpt-4o",
))
```

---

## Multi-agent with Orchestrator

`Orchestrator` takes a list of agents and automatically creates a `handoff_to_<name>` tool for each one. The orchestrator's LLM decides which agent to delegate to based on the task.

```python
from truffle import Agent, Orchestrator, tool, SlidingWindowMemory

@tool
def add_item(name: str, quantity: int) -> str:
    """Add an item to the inventory."""
    ...

@tool
def get_weather(location: str) -> str:
    """Get the weather for a location."""
    ...

inventory_agent = Agent(
    name="inventory",
    instructions="You manage inventory.",
    client=client,
    model="gpt-4o",
    tool_schemas=[add_item.schema],
    tool_registry={"add_item": add_item},
    memory=SlidingWindowMemory(max_messages=20),
)

geo_agent = Agent(
    name="geo",
    instructions="You handle weather and geography.",
    client=client,
    model="gpt-4o",
    tool_schemas=[get_weather.schema],
    tool_registry={"get_weather": get_weather},
    memory=SlidingWindowMemory(max_messages=20),
)

orchestrator = Orchestrator(
    name="orchestrator",
    instructions="Route tasks to the right agent.",
    client=client,
    model="gpt-4o",
    agents=[inventory_agent, geo_agent],
)

reply = await orchestrator.run("Add 5 health potions to the inventory.")
```

---

## Contrib

Pre-built agents you can drop in and use immediately.

### PostgresAgent

A read-only SQL agent for PostgreSQL. Automatically connects to the database, fetches the full schema, and injects it into the system prompt — no manual setup required.

```bash
pip install truffle-kit[postgres]
```

Add the following to your `.env` file:

```env
PG_HOST=localhost
PG_PORT=5432
PG_DATABASE=mydb
PG_USER=myuser
PG_PASSWORD=mypassword
```

```python
import os
from dotenv import load_dotenv
from truffle.contrib.sql import PostgresAgent

load_dotenv()

agent = PostgresAgent(
    client=openai_client,
    model="gpt-4o",
    host=os.environ["PG_HOST"],
    port=int(os.environ["PG_PORT"]),
    database=os.environ["PG_DATABASE"],
    user=os.environ["PG_USER"],
    password=os.environ["PG_PASSWORD"],
)

reply = await agent.run("How many users signed up last month?")
```

Supports all standard `Agent` parameters (`memory`, `on_status`, etc.) and can be passed directly to an `Orchestrator` as a sub-agent.

#### Modes

| Mode | Allowed | Blocked |
|---|---|---|
| `"default"` | `SELECT` | Everything else |
| `"go_bananas"` | `SELECT`, `INSERT`, `UPDATE`, `DELETE` | `DROP`, `ALTER`, `CREATE`, and all privilege/execution vectors |

```python
# Read-only (default)
agent = PostgresAgent(..., mode="default")

# Full read/write access, no structural changes
agent = PostgresAgent(..., mode="go_bananas")
```

---

## API reference

### `Agent`

| Parameter       | Type                    | Description                                       |
| --------------- | ----------------------- | ------------------------------------------------- |
| `name`          | `str`                   | Agent name                                        |
| `instructions`  | `str`                   | System prompt                                     |
| `client`        | OpenAI client           | Any OpenAI-compatible client                      |
| `model`         | `str`                   | Model name                                        |
| `tool_schemas`  | `list[dict]`            | List of tool schemas (use `.schema` from `@tool`) |
| `tool_registry` | `dict[str, Callable]`   | Maps tool name to function                        |
| `memory`        | `MemoryStrategy`        | Memory strategy (default: `UnlimitedMemory`)      |
| `on_thinking`   | `Callable[[], None]`    | Called before each LLM inference (default: `loading_status`) |
| `on_status`     | `Callable[[str], None]` | Called with the tool name on each tool call       |

### `Orchestrator`

Same as `Agent` except `tool_schemas` and `tool_registry` are replaced by:

| Parameter | Type          | Description                 |
| --------- | ------------- | --------------------------- |
| `agents`  | `list[Agent]` | Sub-agents to route between |

### `agent.run(prompt)`

Runs the agent loop and returns the final reply as a string.

### `agent.clear_memory()`

Resets memory to the system prompt only. Also triggered by sending `"/clear"` as the prompt.
