TinyAgents is a small, provider-neutral agent harness for Rust, plus a durable
typed state-graph runtime. It takes its shape from
LangChain (models, tools, middleware, structured
output, streaming, usage/cost) and
LangGraph (START/END, nodes,
conditional edges, channels/reducers, checkpoints, interrupts, subgraphs, time
travel) — rebuilt as ordinary, typed Rust with no hidden magic.
It is for Rust services that need to call models and tools in a loop, want that loop to be resumable and inspectable, and would rather not carry a Python runtime or a framework's DSL to get there.
TinyAgents is a Cargo workspace, not one crate. Depend on the pieces you need:
tinyagents-harness— provider-neutral model calls, typed tools, middleware, structured output, streaming, usage/cost accounting, retries, caching, and a Claude Code CLI model adapter with stream-json, session, authentication, and MCP endpoint support. Features:sqlite,tools,multimodal,tracing.tinyagents-graph— a LangGraph-style durable, typed state graph:START/END, nodes, conditional edges,Sendfanout, reducers/channels, checkpoints, interrupts, subgraphs, and time travel. Features:sqlite,tracing.tinyagents-registry— a named capability catalog (models, tools, agents, graphs, and routers), plus an offline model price/capability catalog.tinyagents-session— a SQLite-backed store for session history, messages, tool calls, cost, and run lineage.tinyagents-definition— the host-owned agent definition vocabulary: identity, description, declared model/tools/delegates, and a read-only catalogue seam. Authorization, prompt construction, and execution stay with the host and harness.tinyagents-runtime— host-neutral stateful turns over the harness and append-only transcript seam; hosts retain policy, prompt composition, authorization, and durable-dialect conversion.tinyagents-orchestration— host-neutral composition of durable multi-agent work (teams and workflows) over the graph, harness, and session layers; depends one-way on those crates and stays host-free.tinyagents-integration-tests— cross-crate tests and the runnable examples referenced below (not published, workspace-internal).
None of the crates are published to crates.io (publish = false in every
Cargo.toml), so add them as git or path dependencies:
[dependencies]
tinyagents-harness = { git = "https://github.com/tinyhumansai/tinyagents", package = "tinyagents-harness" }
tinyagents-graph = { git = "https://github.com/tinyhumansai/tinyagents", package = "tinyagents-graph" }
tinyagents-registry = { git = "https://github.com/tinyhumansai/tinyagents", package = "tinyagents-registry" }
# The code samples below build `Message` and provider types directly from
# TinyInference, the message/model crate TinyAgents is built on. It is a
# separate git dependency, not re-exported by the crates above.
tinyinference-llm = { git = "https://github.com/tinyhumansai/tinyinference", package = "tinyinference-llm" }A minimal typed graph — a whole-state agent/tool loop (trimmed from
examples/basic_graph.rs):
use tinyagents_graph::*;
use tinyinference_llm::message::Message;
#[derive(Clone, Debug)]
struct AgentState {
messages: Vec<Message>,
needs_tool: bool,
}
let graph = GraphBuilder::<AgentState, AgentState>::overwrite()
.add_node("agent", |mut state: AgentState, _ctx: NodeContext| async move {
state.messages.push(Message::assistant("checking the local tool"));
Ok(NodeResult::Update(state))
})
.add_node("tool", |mut state: AgentState, _ctx: NodeContext| async move {
state.messages.push(Message::tool("echo", "tool result"));
state.needs_tool = false;
Ok(NodeResult::Update(state))
})
.set_entry("agent")
.add_conditional_edges(
"agent",
|state: &AgentState| if state.needs_tool { "tool".to_string() } else { "done".to_string() },
[("tool", "tool"), ("done", END)],
)
.add_edge("tool", "agent")
.compile()?;
let run = graph.run(AgentState { messages: vec![], needs_tool: true }).await?;Run it for real:
git clone git@github.com:tinyhumansai/tinyagents.git
cd tinyagents
cargo run -p tinyagents-integration-tests --example basic_graphA one-shot model call through the harness (export OPENAI_API_KEY=... then
cargo run -p tinyagents-integration-tests --example openai_chat):
use std::sync::Arc;
use tinyagents_harness::runtime::AgentHarness;
use tinyinference_llm::message::Message;
use tinyinference_llm::providers::openai::OpenAiModel;
let model = OpenAiModel::from_env()?;
let mut harness: AgentHarness<()> = AgentHarness::new();
harness.register_model("openai", Arc::new(model)).set_default_model("openai");
let run = harness
.invoke_default(&(), vec![Message::user("What is a Rust trait?")])
.await?;
println!("{}", run.text().unwrap_or_default());tinyagents-graph is a durable, typed state graph modeled on LangGraph:
START/END markers, nodes, static and conditional edges, Command-based
routing, Send fanout, reducers over named channels, checkpointing (with an
optional sqlite backend), interrupts, streaming events, topology export, and
replay/time travel across superstep boundaries. A node can embed another
compiled graph as a subgraph, so a whole workflow can appear as a single step
inside a larger one.
tinyagents-harness runs the model/tool agent loop: provider-neutral model
calls, typed tool definitions, middleware, structured output, streaming,
usage and cost accounting, retries and limits, response caching, and a testkit
for exercising the loop without a live provider. Memory, workspace lifecycle,
authorization, and persistence policy stay in the host and can wrap a complete
run with AgentMiddleware.
tinyagents-orchestration owns child-agent composition. SubAgentTool is a
typed parent-context dispatcher: it starts a child in the background and
returns a stable job id immediately. SubAgentJobsTool queries job
status/results and SubAgentMessageTool sends messages to a live job;
hosts register these control tools over the same explicitly shared
SubAgentJobRegistry. SubAgentSession covers retained post-completion
conversations, while SubagentDriver coordinates durable lifecycle
preparation, execution, pause, resume, and persistence. Teams and workflow DAGs
are intentionally outside this focused crate.
tinyagents-runtime owns mutable model history for one host-owned
conversation, a stable prompt prefix, a frozen tool declaration snapshot, and
the sequencing around one append-only transcript commit. A host supplies the
driver, its lossless transcript codec, and lifecycle hooks. On a driver error,
the runtime can commit recoverable logical history with an interrupted,
display-only partial in the same history operation; model-context replay omits
that partial. A codec can also derive TurnUsage from its explicit host
context after the driver runs; that usage is attached to the same atomic
append's final assistant row for both success and recoverable partials. A
post-commit hook observes durable successes but cannot change their result.
See the runtime module.
tinyagents-registry is a name-addressable catalog of models, tools, agents,
graphs, and routers. Application code resolves capabilities by name against it
rather than holding direct handles.
Every provider speaks the OpenAI Chat Completions wire format, so one adapter
reaches all of them; only the base URL and model differ. Built-in presets:
OpenAI, Anthropic (via its OpenAI-compatible endpoint), DeepSeek, Groq, xAI,
OpenRouter, Together, Mistral, and Ollama (local). Any other OpenAI-compatible
endpoint works by base URL — see providers.env.example
for the full list and configuration format.
All live in
crates/tinyagents-integration-tests/examples/:
basic_graph,complex_graph,durable_graph,resilient_graph— a minimal typed graph, then conditional routing/fanout, checkpoint/resume/time-travel, and node-level retry.agent_loop_tools— the agent/tool loop the harness runs.orchestrator_subagents— an orchestrator agent that resolves and calls sub-agents by name from the registry.goals_and_todos— a durable goal driving a task-board kanban on one thread.openai_chat,openai_tools,openai_structured,openai_graph_agent— provider-backed chat, tool calling, structured output, and a graph-driven agent (all needOPENAI_API_KEY).subconscious_loop— an offline, testable autonomous closed-loop harness (see its own README).
docs/spec/README.md— architecture specification.- Wiki — Harness, Graph Runtime, Registry, Providers, Quick Start, Examples, Development.
cargo fmt --check
cargo clippy --workspace --all-targets -- -D warnings
cargo build --workspace --all-targets
cargo test --workspacecargo test never touches the network. To check the providers you hold keys
for — a chat call, a streaming call, and a tool call each, reported as a
provider | PASS/FAIL(reason) | latency(ms) table:
cp providers.env.example providers.env # fill in the keys you have; blank => skipped
PROVIDER_MATRIX=1 cargo test -p tinyagents-integration-tests --test live_provider_matrix -- --nocaptureDialling is opt-in through PROVIDER_MATRIX=1, so a bare cargo test stays
offline even with a fully configured providers.env. providers.env is
gitignored — never commit real keys.
Read CONTRIBUTING.md before opening a pull request.
TinyAgents is licensed under GPL-3.0-only.
