Skip to main content
Fabro can be used as a Rust SDK with two primary entry points:
  • fabro-agent — a full AI coding agent with tool use, sandboxed execution, event streaming, and context management. Use this when you want to build an agent that can read files, run commands, and interact with a codebase.
  • fabro-llm — a standalone LLM client for multi-provider completions, streaming, and tool execution loops. Use this when you want direct control over LLM calls without the agent layer.
Both crates can be used independently of Fabro’s workflow engine.

Agent (fabro-agent)

The fabro-agent crate provides a session-based AI agent that runs an LLM with tool use in a sandboxed environment. The agent loop streams LLM responses, executes tool calls (shell, read_file, write_file, edit_file, glob, grep, web_fetch, web_search), feeds results back, and repeats until the model responds with text or hits a safety limit.
Cargo.toml

Quick start

Session

Session is the core type. It holds the LLM client, a provider profile, a sandbox, and configuration. The main loop lives inside process_input(). Constructor:
Lifecycle methods: Inspection: Steering:

SessionOptions

All fields are public. Key settings with their defaults:

Sandbox

The Sandbox trait abstracts where tools execute — local filesystem, Docker container, SSH remote, or a cloud sandbox. All tool operations go through this interface.
Built-in implementations: The DaytonaSandbox implementation (feature-gated: daytona) runs inside a Daytona cloud sandbox.

Provider profiles

The AgentProfile trait encapsulates LLM-specific system prompts, tool definitions, and capability metadata. It controls how the agent presents itself to the model.
Built-in profiles: AnthropicProfile, OpenAiProfile, GeminiProfile.

Events

All operations emit AgentEvent values through a tokio broadcast channel. Subscribe before calling process_input().
Key AgentEvent variants:

Tool hooks

Implement ToolHookCallback to intercept tool calls for approval, logging, or transformation:
Pass hooks via SessionOptions:
For simple sync approval, use ToolApprovalAdapter to wrap a closure:

Error handling

All fallible Session methods return Result<T, AgentError>:

LLM client (fabro-llm)

The fabro-llm crate is a standalone Rust library for calling LLM providers. It provides a unified client that routes requests to Anthropic, OpenAI, Gemini, and other providers, with built-in streaming, tool execution, retries, and middleware. You can use it independently of Fabro’s workflow engine — add it as a dependency in any Rust project.
Cargo.toml

Quick start

The simplest path is an environment-backed CredentialSource, an explicit Arc<Catalog>, then Client::from_source(&source, catalog). That keeps credential and model resolution explicit while still auto-reading environment variables such as ANTHROPIC_API_KEY, OPENAI_API_KEY, and GEMINI_API_KEY.

Client

Client is the core type that holds provider adapters and middleware. It routes each request to the appropriate provider.

Creating from a credential source

For env-backed usage, EnvCredentialSource checks for API key environment variables and registers adapters for each provider found: The first provider registered becomes the default. Provider base URLs come from the model catalog. For vault-backed usage inside Fabro, use fabro_auth::VaultCredentialSource instead. The built-in Modal definition reads two proxy-token headers from the vault, so EnvCredentialSource does not configure it automatically. For direct SDK use, enable Modal and set its endpoint URL in the catalog:
Then read the two environment variables explicitly and create a typed credential after constructing catalog from those settings:

Creating manually

Low-level calls

For direct control without the tool loop, use complete() and stream() on the client:

High-level generation

The generate() function wraps the client with automatic tool execution loops, retries, and timeouts. It is the recommended entry point for most use cases.

Basic completion

Multi-turn conversations

Use .messages() instead of .prompt() to pass a full conversation history:
You cannot use both .prompt() and .messages() on the same request — this returns SdkError::Configuration.

GenerateParams reference

GenerateResult

GenerateResult dereferences to Response, so you can call response methods directly:

Tools

Tools let the model call functions during generation. There are two kinds:
  • Active tools have an execute handler — Fabro runs them automatically and feeds results back to the model.
  • Passive tools have no handler — Fabro returns the tool calls to you in the response.

Defining an active tool

Using tools with generate

The generate() function loops automatically: the model calls tools, Fabro executes them, feeds results back, and repeats until the model stops or max_tool_rounds is reached.

Tool choice

Control how the model selects tools:

Passive tools

Passive tools let you handle execution yourself:

Streaming

Text stream

For simple cases where you only need the text deltas:

Full event stream

For fine-grained control, consume StreamEvent variants directly:

StreamEvent variants

Structured output

Generate typed JSON objects that conform to a JSON Schema:

Middleware

Middleware intercepts requests and responses for logging, caching, or transformation:
Add middleware to the client:

Model catalog

The crate embeds a catalog of known models with metadata:
See Models for the full catalog table.

Error handling

All fallible operations return Result<T, SdkError>. The error type classifies failures to enable retry and failover decisions:

Error classification

Every SdkError exposes classification methods:

Provider error kinds

Retries

The generate() function retries automatically based on max_retries (default: 2). For low-level use, the retry function wraps any async operation:
Retry only fires when error.retryable() returns true and respects Retry-After headers.

Cancellation

Pass a CancellationToken to interrupt long-running generation:

Provider adapters

Each provider has a dedicated adapter. All adapters implement the ProviderAdapter trait and are interchangeable. All adapters support .with_base_url() for proxies or custom endpoints. OpenAiAdapter also supports .with_org_id() and .with_project_id().

Custom provider

Implement the ProviderAdapter trait to add a new provider:
Register it on the client: