rswarm
rswarm is a Rust library for agent-style LLM workflows: multi-turn conversations, function and tool calling, streaming responses, XML-defined execution steps, persistence, guardrails, and event hooks.
The current workspace passes:
cargo fmt --all --checkcargo clippy --all-targets --all-features -- -D warningscargo doc --no-deps --all-featurescargo test --workspace --all-features
What It Covers
- Agent construction with static or dynamic instructions
- Multi-turn
Swarm::run(...)conversations - Function calling with serial or parallel tool execution
- Streaming responses through
rswarm::stream::Streamer - Structured response checks and JSON Schema-backed tool argument validation
- SQLite and PostgreSQL persistence backends
- Event subscribers, circuit breakers, escalation, and guardrails
- In-memory semantic memory plus feature-gated vector backends
Installation
Add the library:
For most applications you will also want:
If you plan to use the streaming API shown below:
Optional feature flags:
postgres: PostgreSQL persistencepostgres-tls: PostgreSQL persistence with rustls + native rootsmetrics-export: Prometheus metrics exporterotel: OpenTelemetry tracing exportsqlite-vec: reserved feature; adapter currently returns a configuration errorqdrant: reserved feature; adapter currently returns a configuration error
Example:
Configuration
Environment variables:
OPENAI_API_KEY: required unless passed directly toSwarm::builder().with_api_key(...)OPENAI_API_URL: optional override for the chat-completions endpoint
Default API URL:
https://api.openai.com/v1/chat/completions
The examples crate also uses:
OPENAI_MODEL: optional, defaults togpt-4o
Quick Start
Swarm::run(...) requires a non-empty message history. Use the message constructors instead of struct literals.
use ;
async
Defining Agents
Create agents with Agent::new(...) and then opt into additional behavior with builder-style methods:
use ;
let agent = new?
.with_function_call_policy
.with_tool_call_execution
.with_capabilities;
Relevant agent APIs:
with_functions(...)with_function_call_policy(...)with_tool_call_execution(...)with_expected_response_fields(...)with_capabilities(...)
Instruction modes:
Instructions::Text(String)Instructions::Function(Arc<dyn Fn(ContextVariables) -> String + Send + Sync>)
Function Calling
AgentFunction is the main application-level tool/function abstraction used during Swarm::run(...).
use ;
use json;
use Arc;
let weather = new?
.with_description
.with_parameters_schema?;
let agent = new?
.with_functions
.with_function_call_policy
.with_tool_call_execution;
Notes:
- Parameter schemas must be JSON Schema objects with root
"type": "object". - Tool arguments are validated with
jsonschema, not a hand-rolled subset. accepts_context_variables = truepasses validated arguments into the handler asContextVariables.ToolCallExecution::Serialthreads context updates from one call into the next.ToolCallExecution::Parallelexecutes calls independently and preserves per-tool success/failure reporting.
Low-Level Tool API
If you want a lower-level tool abstraction outside the AgentFunction flow, the crate also exposes:
ToolClosureToolToolRegistryInvocationArgsToolSchemaToolCallSpec
Use this layer when you want explicit tool registration/execution without relying on AgentFunction.
Messages
Use constructors instead of field access:
use ;
let user = user?;
let assistant = assistant?;
let function = function?;
let tool_result = tool_result?;
let tool_call = new?;
let assistant_with_tools = assistant_tool_calls?;
Important message constraints:
Swarm::run(...)rejects an emptymessagesvector- assistant messages must contain exactly one of
content,function_call, ortool_calls - tool messages must include
tool_call_id
Streaming
Use rswarm::stream::Streamer for incremental output:
use StreamExt;
use ;
let agent = new?;
let swarm = builder
.with_api_key
.with_agent
.build?;
let streamer = new;
let history = vec!;
let mut stream = streamer.stream_chat;
while let Some = stream.next.await
Structured Responses
If you expect a JSON-shaped answer, you can require fields up front:
use ;
let agent = new?
.with_expected_response_fields?;
XML-Defined Execution Steps
rswarm can extract and execute XML-defined steps embedded in the instruction text. The Swarm::run(...) path handles parsing and execution automatically.
Example shape:
Summarize the request.
Continue until the task is complete.
See rswarm_examples/prompt.txt for a real example.
Persistence
SQLite:
use ;
let store = open?;
let swarm = builder
.with_api_key
.with_persistence_backend
.build?;
PostgreSQL:
use PostgresStore;
// Localhost / Unix-socket only, because this path uses NoTls.
let local_store = connect.await?;
For remote PostgreSQL, use TLS:
use PostgresStore;
let store = connect_with_native_roots
.await?;
This helper requires the postgres-tls feature.
Persistence backends cover sessions, events, checkpoints, and memory records.
Semantic Memory
Available today:
InMemoryVectorStoreRetrievalPolicyVectorMemory
Current status of feature-gated adapters:
sqlite-vec: feature exists, persistent adapter currently returns a configuration errorqdrant: feature exists, adapter currently returns a configuration error
Use InMemoryVectorStore for development and small deployments until those adapters land.
Events, Guardrails, and Runtime Controls
The builder supports:
with_subscriber(...)with_runtime_limits(...)with_content_policy(...)with_injection_policy(...)with_redaction_policy(...)with_redaction_threshold(...)with_escalation_config(...)with_provider_circuit_breaker(...)with_tool_circuit_breaker(...)
These are useful for observability, compliance, and production hardening.
Examples
Runnable example crate:
The example crate uses dotenvy, reads rswarm_examples/prompt.txt, and requires a local Chrome/Chromium install for the docs browser tool.
See:
Development Workflow
Useful commands:
Current Caveats
Swarm::run(...)requires at least one input message- the vector database adapters behind
sqlite-vecandqdrantare not implemented yet AgentandMessageuse constructors/builders; their internal fields are not public API- remote PostgreSQL connections should use TLS helpers, not
PostgresStore::connect(...)
License
Licensed under either:
- MIT
- Apache-2.0