Skip to main content

Tessaract

A provider-agnostic SDK for building AI agents natively.

Tessaract exists to make it straightforward to build agents that work across multiple providers. It is an unopinionated portal to multi-model intelligence giving you model diversity and full control over how you compose models in your applications.

Today, Tessaract supports OpenAI's Responses API, including streaming, reasoning, and tool calling. More provider adapters are planned.

More concretely, Tessaract exposes a canonical model of the LLM ecosystem: requests, responses, content, streaming events, tools, usage, errors and reasoning. You write an agent loop once against Tessaract's types, and a provider adapter translates to and from each vendor's native API.

Status: OpenAI is the only supported provider today. Anthropic support is in progress.


Contents


Features

  • Provider prefixes. Register a provider under a prefix and address models as "<prefix>/<model>", e.g. "oai/gpt-5.6-luna".
  • Canonical tools. Define function tools once with FunctionTool / InputSchema / Property, including nested objects, arrays, enums and nullable fields. The adapter emits the provider's native schema.
  • Canonical reasoning. Control effort and summaries with ReasoningOptions; read reasoning back as ReasoningOutputItems.
  • Typed output. Responses contain AssistantMessage, ReasoningOutputItem, FunctionCallOutputItem and ProviderOutputItem objects. Function-call arguments are already parsed into a dict.
  • Canonical streaming events. Text, reasoning and tool-argument deltas are normalized into typed events (text.delta, reasoning_summary.delta, response.completed, …).
  • Lossless multi-turn history. Every output item keeps its provider-native raw payload, so you can append a response's output straight back into the conversation history.
  • Escape hatches everywhere. request_options passes any native request parameter through, FunctionTool.provider_options adds native tool fields, and raw_response / raw_event expose the original SDK objects.

Installation

Tessaract requires Python 3.11+. Install the openai extra to use the currently supported provider.

With pip:

python -m pip install "tessaract[openai]"

With uv:

uv add "tessaract[openai]"

Quickstart

Set your OpenAI API key:

export OPENAI_API_KEY="your-api-key"

Then send your first request:

from tessaract import OpenAIProvider, Tessaract

client = Tessaract(providers={"oai": OpenAIProvider()})
response = client.send(model="oai/gpt-5.6-luna", input="Say hello in five words.")

print(response.output_text)

OpenAIProvider reads OPENAI_API_KEY from the environment. The oai prefix is your choice; Tessaract sends the model name after / to OpenAI. See the getting started guide for configuration and multi-turn conversations.


Build an agent with reasoning and tool calling

An agent is a loop:

  1. Send the conversation history, plus the tools the model may use.
  2. Append the model's output (reasoning, messages and function calls) to the history.
  3. If the model asked for function calls, run them and append a FunctionToolResult for each one.
  4. Repeat until the model replies without calling a tool.
import json
import os
from datetime import datetime, timezone

from tessaract import (
    FunctionTool,
    FunctionToolResult,
    InputSchema,
    OpenAIProvider,
    Property,
    ReasoningOptions,
    Tessaract,
    UserMessage,
)

# 1. Plain Python functions the agent can call
def get_time() -> str:
    return datetime.now(timezone.utc).isoformat()

def get_weather(city: str) -> str:
    temps = {"paris": "19C", "amsterdam": "20C"}
    return json.dumps({"temperature": temps.get(city.lower(), "unknown")})

FUNCTIONS = {"get_time": get_time, "get_weather": get_weather}

# 2. Describe them to the model
tools = [
    FunctionTool(
        name="get_time",
        description="Get the current UTC time as an ISO-8601 string.",
        strict=False,
    ),
    FunctionTool(
        name="get_weather",
        description="Get the current temperature for a city.",
        input_schema=InputSchema(
            properties={
                "city": Property(type="string", description="City name, e.g. Paris."),
            },
            required=["city"],
            additionalProperties=False,
        ),
    ),
]

client = Tessaract(
    providers={"oai": OpenAIProvider(api_key=os.environ["OPENAI_API_KEY"])}
)

# 3. The agent loop
def run_agent(history: list, user_text: str) -> str:
    history.append(UserMessage(content=user_text))

    while True:
        response = client.send(
            model="oai/gpt-5.6-luna",
            input=history,
            tools=tools,
            reasoning=ReasoningOptions(effort="medium", summary="auto"),
            request_options={"instructions": "You are a concise, helpful assistant."},
        )

        # Keep reasoning, messages and calls in the history for the next turn
        history.extend(response.output)

        for item in response.output:
            if item.type == "reasoning" and item.text:
                print(f"[thinking] {item.text}")

        calls = [item for item in response.output if item.type == "function_call"]
        if not calls:
            return response.output_text

        for call in calls:
            result = FUNCTIONS[call.name](**call.arguments)  # arguments is already a dict
            history.append(FunctionToolResult(call_id=call.call_id, result=result))


history: list = []
print(run_agent(history, "What's the weather in Paris, and what time is it?"))

The step-by-step walkthrough is in the building an agent guide, with runnable versions in examples/.


Streaming

Pass stream=True to get an iterator of canonical events. The final event is response.completed, which carries the same Response object that a non-streaming call returns, so the agent loop doesn't change:

completed = None

for event in client.send(
    model="oai/gpt-5.6-luna",
    input=history,
    tools=tools,
    stream=True,
    reasoning=ReasoningOptions(effort="medium", summary="auto"),
):
    if event.type == "reasoning.started":
        print("\n[thinking] ", end="")
    elif event.type == "reasoning_summary.delta":
        print(event.delta, end="", flush=True)
    elif event.type == "text.delta":
        print(event.delta, end="", flush=True)
    elif event.type == "response.completed":
        completed = event.response

history.extend(completed.output)

See the streaming guide for the full event list.


Documentation

Guide What it covers
Getting started Installing, configuring providers, sending your first request, and building multi-turn history
Building an agent A step-by-step tutorial for an agent with reasoning and tool calling, both synchronous and streaming
Tool calling FunctionTool, InputSchema, Property, strict mode, tool results and provider_options
Reasoning ReasoningOptions, effort levels, summaries, and reading and preserving reasoning items
Streaming Every canonical stream event, and how to build a streaming agent loop
API reference Every public class, field and method
Architecture Canonical types, adapters and providers, and how to add a new provider

Project layout

src/tessaract/
├── client.py                    # Tessaract client: routes requests to adapters
├── providers/
│   ├── provider.py              # Provider base dataclass
│   └── openai_provider.py       # OpenAIProvider (wraps openai.OpenAI)
├── adapters/
│   ├── adapter.py               # Adapter base class + protocols
│   └── openai/openai_adapter.py # Canonical <-> OpenAI Responses API translation
├── tools/function.py            # FunctionTool, InputSchema, Property
└── types/
    ├── input_types.py           # UserMessage, FunctionToolResult
    ├── output_types.py          # AssistantMessage, ReasoningOutputItem, FunctionCallOutputItem, ...
    ├── request.py               # Request, ReasoningOptions
    ├── response.py              # Response, ResponseStatus, ResponseError
    └── streaming/event_types.py # Canonical stream events

Status and roadmap

Working today (OpenAI):

  • Synchronous and streaming requests via the Responses API
  • Multi-turn conversations
  • Function tools with parallel calls
  • Reasoning effort and summaries
  • Pass-through request options

Planned:

  • Anthropic provider and adapter
  • Google GenAI provider and adapter
  • Token usage and finish details on Response
  • TestProvider for CI and testing without making live API calls
  • built-in utils: agent loop helper, tool schema autowriter
  • Token counting

Metadata

Release files for tessaract 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for tessaract 0.1.0
File Size Uploaded
tessaract-0.1.0.tar.gz 12.5 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for tessaract 0.1.0
File Interpreter ABI Platform
tessaract-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 31.1 kB

Release files / tessaract-0.1.0.tar.gz

Download URL tessaract-0.1.0.tar.gz
Size 12.5 kB
Tags Source
SHA-256 checksum
How to use checksums
4c29dd7daf84cc60a2f6cced5705dc1b1d7f9c797765d59d97377ee52fe2ebc7
BLAKE2b-256 checksum
How to use checksums
a7ed92d6a6471366132fe16fdd27dcf31bcb88fce4db03659b9ede4ae9815764
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 26, 2026.

Transparency log

Release files / tessaract-0.1.0-py3-none-any.whl

Download URL tessaract-0.1.0-py3-none-any.whl
Size 18.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
33fba3f49247982b26750cce08c332a55834b31fb3bd231b854ca7b0958a9b9d
BLAKE2b-256 checksum
How to use checksums
c64d2d312defa390fb91210ad19a5fa5cb0585a6636305ca92d3ebccde967847
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 26, 2026.

Transparency log

Release history Release notifications | RSS feed

0.2.1

2 release files

0.2.0

2 release files

0.1.2

2 release files

0.1.1

2 release files

This release

0.1.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page