Expand description
A modular framework for building AI agents in Rust.
Polaris is an ECS-inspired runtime for composing AI agents as directed graphs of systems. It provides layered abstractions — from low-level dependency-injected systems, through graph-based execution, up to session management and HTTP serving.
§Why Polaris
Building performant AI agents is a design problem. The bottleneck is not compute, APIs, or infrastructure — it is discovering how an agent should behave for a given use case, and being able to change that behavior quickly when it turns out to be wrong.
Polaris provides composable primitives without prescribing how they should be assembled. There is no default execution loop. Agent behavior is constructed from small, replaceable parts, and the framework imposes no opinion on the result.
§Architecture
Polaris is organized into three layers. Lower layers are fixed primitives; upper layers are swappable.
| Layer | Name | Modules | Scope |
|---|---|---|---|
| 1 | System Framework | system | Systems, resources, plugins, server |
| 2 | Graph Execution | graph, agent | Directed-graph model, agent trait |
| 3 | Plugins | tools, models, plugins, sessions, app, shell, dashboard | LLM providers, tools, HTTP, sessions, dashboard contributions |
Layer 1 provides the ECS-inspired primitives: systems as pure async
functions, resources as shared state, dependency injection via typed
parameters, plugins as the unit of composition, and the
Server runtime.
Layer 2 defines how agents are structured: a directed graph of nodes
(computation, control flow) connected by edges (sequential, conditional,
parallel, looping). The Agent trait packages a behavior
pattern as a reusable graph builder.
Layer 3 delivers every optional capability through plugins: LLM providers, tool registries, session management, HTTP serving, and more. Every component is replaceable.
§Quick Start
use polaris_ai::prelude::*;
use polaris_ai::system::system;
use polaris_ai::system::server::Server;
use polaris_ai::plugins::MinimalPlugins;
use polaris_ai::graph::GraphExecutor;
#[system]
async fn greet() -> String {
"Hello from Polaris!".to_string()
}
let mut server = Server::new();
server.add_plugins(MinimalPlugins.build());
server.finish().await;
let graph = {
let mut g = Graph::new();
g.add_system(greet);
g
};
let mut ctx = server.create_context();
let executor = GraphExecutor::new();
let result = executor.execute(&graph, &mut ctx, None, None).await?;
let output = result.output::<String>();§Core Concepts
§Systems and Resources
A system is a pure async function that declares its dependencies as
typed parameters. The #[system] macro generates the boilerplate:
#[system]
async fn reason(llm: Res<LlmClient>, memory: Res<Memory>) -> ReasoningResult {
// Access resources, produce output
ReasoningResult { action: "search".into() }
}Resources are how agents get capabilities. An LLM provider, a tool
registry, a memory backend — each exists as a resource in the
SystemContext:
| Parameter | Resolution | Access | Use for |
|---|---|---|---|
Res<T> | Hierarchy (local → parent → global) | Immutable | Config, registries, per-request input |
ResMut<T> | Current context only | Exclusive | Accumulated state (conversation history) |
Out<T> | Previous system output | Immutable | System-to-system data handoff |
ErrOut<T> | Error-edge output | Immutable | Error context in handler subgraphs |
§Graphs
Agent logic is expressed as a directed graph of systems and control flow:
let mut graph = Graph::new();
graph
.add_system(reason)
.add_conditional_branch::<ReasoningResult, _, _, _>(
"needs_tool",
|r| r.needs_tool,
|g| { g.add_system(execute_tool); },
|g| { g.add_system(respond); },
);Node types: System, Decision, Switch, Parallel, Loop, Scope.
Edge types: Sequential, Conditional, Parallel, LoopBack, Error, Timeout.
The graph’s full topology is inspectable, validated before execution, and
restructured by rewiring edges. See the graph module for the full API.
§Plugins
Every capability is delivered through plugins registered at startup:
let mut server = Server::new();
server
.add_plugins(DefaultPlugins::new().build())
.add_plugins(ToolsPlugin)
.add_plugins(SessionsPlugin::new(Arc::new(InMemoryStore::new())));Plugins have a lifecycle: build() → ready() → update() → cleanup().
Dependencies are declared and resolved automatically. See the system
module for the Plugin trait and the plugins module for built-in
plugin groups.
§Data Flow Patterns
Choosing the right parameter type is critical for correct data flow:
| Pattern | Use | Avoid |
|---|---|---|
| Step A’s result feeds step B | Out<T> — A returns T, B declares Out<T> | ResMut<SharedState> with Option fields |
| Immutable per-request input | Res<T> via ctx.insert(T) in setup closure | ResMut<WorkingState> with .input.clone() |
| Accumulated state (history, counters) | ResMut<T> — local resource | Out<T> — outputs are per-system |
| Shared server-wide config | Res<T> — global resource | ResMut<T> — compile error on globals |
| Error context in handler | ErrOut<CaughtError> | Custom ResMut<LastError> |
§Data Lifetimes
| Data lives… | Mechanism |
|---|---|
| Server lifetime | GlobalResource + Res<T> |
| Session lifetime | LocalResource inserted at session creation |
| Single turn | LocalResource inserted in process_turn_with |
| Between two systems | Return value + Out<T> |
| Error handler subgraph | ErrOut<CaughtError> |
§Common Integration Patterns
| Goal | Pattern | Entry point |
|---|---|---|
| Run one-shot agent | sessions.run_oneshot(&agent_type, |ctx| { ... }) | sessions |
| Multi-turn with cleanup | sessions.scoped_session(&agent_type, |ctx| { ... }) | sessions |
| Execute agent from HTTP | HttpRouter::add_routes_with → SessionsAPI → HttpIOProvider | app |
| Register HTTP routes | server.api::<HttpRouter>().add_routes(router) (stateless) / add_routes_with(|server| ...) (stateful) | app |
| Add tools for LLM | #[tool] macro + ToolRegistry | tools |
| Add model provider | Implement LlmProvider + register via plugin | models |
| Handle system errors | Fallible system + error edge + ErrOut<CaughtError> | graph |
| Schedule plugin updates | tick_schedules() + server.tick::<S>() | system |
§Crate Organisation
| Module | Crate | Purpose |
|---|---|---|
system | polaris_system | ECS-inspired systems, resources, and plugins |
graph | polaris_graph | Directed-graph execution primitives |
agent | polaris_agent | Agent trait for reusable behavior patterns |
tools | polaris_tools | Tool framework for LLM-callable functions |
models | polaris_models / polaris_model_providers | Model registry and provider implementations |
plugins | polaris_core_plugins | Core infrastructure plugins (time, tracing, persistence) |
sessions | polaris_sessions | Session management and orchestration |
shell | polaris_shell | Shell command execution with permission model |
app | polaris_app | HTTP server runtime with plugin integration |
dashboard | polaris_dashboard | Cross-plugin dashboard contribution registry |
§Exploration Map
| If you want to find… | Start here |
|---|---|
| System primitives, resources, and plugin lifecycle | system |
| Graph nodes, edges, execution, hooks, and middleware | graph |
| LLM providers and provider plugins | models |
| Core infrastructure plugins and observability | plugins |
| Session lifecycle, persistence, and HTTP session routes | sessions |
| Feature-gated exports and which module owns them | Feature Export Map |
§Feature Flags
Every workspace feature that downstream consumers might want is surfaced
through polaris-ai directly, so consumers don’t need to depend on the
inner crates (polaris_app, polaris_core_plugins, etc.). All features
are opt-in except file-store, which is on by default to preserve the
historical session-store behavior — set default-features = false to opt
out. Features that would otherwise be ambiguous at the top level are
prefixed with the sub-crate’s short name (e.g. sessions-http); features
that are already unambiguous keep their original name (e.g. anthropic).
§Model Providers
| Feature | Exported item | Find it under |
|---|---|---|
anthropic | models::AnthropicPlugin | models |
openai | models::OpenAiPlugin | models |
bedrock | models::BedrockPlugin | models |
§Observability
| Feature | Exported item | Effect |
|---|---|---|
graph-tracing | No new public type | Extends plugins::TracingPlugin with graph-execution spans |
models-tracing | No new public type | Extends plugins::TracingPlugin to decorate model providers |
tools-tracing | No new public type | Extends plugins::TracingPlugin to decorate tools |
otel | plugins::OpenTelemetryPlugin | Adds OTLP export via the tracing subscriber and switches HTTP request spans in app to OTel HTTP semantic-convention field names |
§Tokenization
| Feature | Exported item | Effect |
|---|---|---|
tiktoken | models::tokenizer::TiktokenCounter and models::tokenizer::EncodingFamily | Enables tiktoken-backed counting and models::TokenizerPlugin::default |
§Sessions
| Feature | Exported item | Find it under |
|---|---|---|
sessions-http | sessions::HttpPlugin and sessions::http | sessions |
file-store (default) | sessions::FileStore | sessions |
§HTTP App Runtime
| Feature | Exported item | Effect |
|---|---|---|
ws | app::WsRouter | Adds the WebSocket router and the dashboard event-stream surface |
§Testing
| Feature | Exported item | Effect |
|---|---|---|
test-utils | plugins::MockClock, plugins::MockIOProvider | Mocks for clock and user IO; intended for downstream dev-dependencies |
§Feature Coverage Map
Use this table when the question is “what does feature X expose,
modify, or wire up at runtime?”
| Feature | Adds public items | Also changes | Runtime surface |
|---|---|---|---|
anthropic | models::anthropic, models::AnthropicPlugin | Makes the anthropic/... provider family available through models::ModelRegistry once registered | models::AnthropicPlugin registers the Anthropic provider |
openai | models::openai, models::OpenAiPlugin | Makes the openai/... provider family available through models::ModelRegistry once registered | models::OpenAiPlugin registers the OpenAI provider |
bedrock | models::bedrock, models::BedrockPlugin | Makes the bedrock/... provider family available through models::ModelRegistry once registered | models::BedrockPlugin registers the Bedrock provider |
graph-tracing | No new public item | Extends plugins::TracingPlugin only; no separate GraphTracingPlugin exists | plugins::TracingPlugin registers graph middleware through graph::MiddlewareAPI |
models-tracing | No new public item | Extends plugins::TracingPlugin only | plugins::TracingPlugin decorates the global models::ModelRegistry |
tools-tracing | No new public item | Extends plugins::TracingPlugin only | plugins::TracingPlugin decorates the global tools::ToolRegistry |
otel | plugins::OpenTelemetryPlugin | Integrates with plugins::TracingPlugin / plugins::TracingLayersApi and switches HTTP request spans in app to OTel HTTP semantic-convention field names | plugins::OpenTelemetryPlugin pushes an OTLP export layer into the tracing subscriber; app::AppPlugin emits spans with http.request.method / url.path / http.response.status_code fields (plus otel.name / otel.kind) instead of the polaris.http.* defaults. Does not extract W3C traceparent headers — incoming requests start a fresh trace. |
tiktoken | models::tokenizer::TiktokenCounter, models::tokenizer::EncodingFamily | Adds Default for models::TokenizerPlugin and changes what models::TokenizerPlugin::default builds | models::TokenizerPlugin::default registers a global models::Tokenizer backed by models::tokenizer::TiktokenCounter |
sessions-http | sessions::http, sessions::HttpPlugin, sessions::http::models | Adds request/response model types and HTTP-facing session APIs under sessions | sessions::HttpPlugin registers routes through app::HttpRouter and depends on app::AppPlugin + sessions::SessionsPlugin |
file-store (default) | sessions::FileStore | Pulls tokio/fs into the dep graph | Lets sessions::SessionsPlugin use a filesystem-backed sessions::SessionStore |
ws | app::WsRouter | Enables axum/ws and gates WebSocket route registration on app::AppPlugin | app::AppPlugin mounts the app::WsRouter; required by the dashboard event stream |
test-utils | plugins::MockClock, plugins::MockIOProvider | None at runtime | Provides mocks for downstream test suites that exercise plugins::TimePlugin / plugins::IOProvider |
Modules§
- agent
- Agent trait for defining reusable behavior patterns.
- app
- HTTP server runtime with plugin-based route composition.
- dashboard
dashboard-registry - Cross-plugin contribution registry for dashboards and observability UIs.
- graph
- Directed-graph execution primitives for agent behavior.
- models
- Model registry and LLM provider implementations.
- plugins
- Core infrastructure plugins for the Polaris runtime, plus a catalog of every plugin shipped by this workspace.
- prelude
- Re-export all common types for easy access.
- sessions
- Session management and agent orchestration.
- shell
- Shell command execution with a layered permission model.
- system
- ECS-inspired systems, resources, plugins, and the server runtime.
- tools
- Tool framework for LLM-callable functions.