stasis-rs 0.9.2

Durable AI orchestration framework with runtime jobs, lineage, and memory integration
docs.rs failed to build stasis-rs-0.9.2
Please check the build logs for more information.
See Builds for ideas on how to fix a failed build, or Metadata for how to configure docs.rs builds.
If you believe this is docs.rs' fault, open an issue.
Visit the last successful build: stasis-rs-0.9.0

Stasis


Stasis is a Rust framework for building long-running AI systems that behave like distributed applications.

It provides durable runtime orchestration, background workflows, recurring scheduling, multi-agent coordination, memory integration, and cluster-aware control plane primitives while still scaling down cleanly to simple chat-style agent execution.

Unlike prompt orchestration frameworks focused primarily on request/response composition, Stasis is designed for operational reliability:

  • durable execution
  • queue ownership and worker coordination
  • endpoint routing
  • runtime observability
  • scheduling and retries
  • typed tool contracts
  • distributed runtime control

Quick Start

use stasis::sdk_prelude::{InvokeAgentRequest, InMemoryAgentRepository, RegisterAgentRequest, StasisSdk};
use stasis::sdk_prelude_ext::GenaiLlmGateway;

#[tokio::main]
async fn main() -> stasis::domain::errors::Result<()> {
    let repo = InMemoryAgentRepository::default();
    let llm = GenaiLlmGateway::from_env();
    let sdk = StasisSdk::new(repo, llm);

    sdk.register_agent(RegisterAgentRequest {
        id: "planner".into(),
        name: "Planner".into(),
        system_prompt: "Break tasks into steps".into(),
    })
    .await?;

    let out = sdk
        .invoke_agent(InvokeAgentRequest {
            agent_id: "planner".into(),
            user_prompt: "Plan a sprint kickoff".into(),
        })
        .await?;

    println!("{}", out.completion);
    Ok(())
}

In this example:

  • Agents are stored in-memory.
  • Prompt execution uses the configured provider gateway from environment variables.
  • Runtime state is local and ephemeral.
  • No external infrastructure is required.
  • This is StasisSdk-only mode (the simplest operating model).

For a deterministic local smoke test (no provider dependency), use examples/simple_agent.rs.

Prelude tiers:

  • stasis::prelude: minimal, stable default imports.
  • stasis::prelude_ext: extended runtime/memory/control-plane imports.
  • stasis::sdk_prelude: minimal SDK-first imports for app code.

To use a real provider via genai, set a provider key (for example OPENAI_API_KEY) and optionally configure model/provider routing:

export STASIS_LLM_PROVIDER=openai
export STASIS_LLM_MODEL=gpt-4o-mini

Or copy .env.example to .env and call stasis::config_prelude::bootstrap() at process start (the dashboard does this automatically). See docs-book/src/environment-configuration.md for Vault/file secret mounts.

You can also set a Stasis-scoped fallback key:

export STASIS_LLM_API_KEY=...

Provider-specific overrides are supported:

  • STASIS_OPENAI_API_KEY
  • STASIS_ANTHROPIC_API_KEY
  • STASIS_OLLAMA_API_KEY

Runtime examples are available in examples.

Package note: the crates.io package is stasis-rs while Rust imports use stasis.

Tool Macro (Signature-Driven)

StasisTool can be generated from a typed async function using #[stasis_tool(...)]:

use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use stasis::domain::errors::Result;
use stasis::stasis_tool;

#[derive(Debug, Clone, Deserialize, JsonSchema)]
struct SearchInput {
    query: String,
}

#[derive(Debug, Clone, Serialize, JsonSchema)]
struct SearchOutput {
    summary: String,
}

#[stasis_tool(
    name = "search_docs",
    description = "Searches internal docs",
    output_schema = true
)]
async fn search_docs(input: SearchInput) -> Result<SearchOutput> {
    Ok(SearchOutput {
        summary: format!("query={}", input.query),
    })
}

// Generated symbols:
// - struct SearchDocsTool;
// - fn search_docs_tool() -> SearchDocsTool;

This avoids repetitive manual trait implementations while preserving strict JSON-schema-based validation.

Macro contract:

  • Function must be async and take exactly one typed input argument.
  • Return type must be Result<OutputType>.
  • Input type must implement Deserialize + JsonSchema.
  • Output type must implement Serialize.
  • When output_schema = true, output type must also implement JsonSchema.

Production-focused entry points:

Embedded Dashboard

You can embed the dashboard into your existing Axum app behind an optional feature flag.

Main View

Grapheme Workflow Builder

Job Scheduler

Enable feature:

cargo add stasis-rs --features dashboard-embedded

Mount dashboard routes in your app code:

use std::sync::Arc;

use axum::Router;
use stasis::dashboard::{DashboardRouterExt, RuntimeDashboardQueryService};

fn app(service: Arc<RuntimeDashboardQueryService>) -> Router {
    Router::new().add_dashboard_with(service, |state| {
        state
            .with_action_auth_bearer_token("replace-me")
            .with_action_required_role("scheduler.admin")
    })
}

The standalone stasis_dashboard binary remains available for separate operations workflows.

Dashboard runtime backend selection (for stasis_dashboard):

  • STASIS_DASHBOARD_RUNTIME_BACKEND=in-memory|surreal-mem|surreal-ws|surreal-kv
  • STASIS_DASHBOARD_SURREAL_NAMESPACE (default: stasis)
  • STASIS_DASHBOARD_SURREAL_DATABASE (default: runtime)
  • STASIS_DASHBOARD_SURREAL_USERNAME / STASIS_DASHBOARD_SURREAL_PASSWORD (optional; root sign-in for remote Surreal backends)
  • STASIS_DASHBOARD_SURREAL_ENDPOINT (required for surreal-ws)
  • STASIS_DASHBOARD_SURREAL_KV_PATH (required for surreal-kv)

Demo seeding remains opt-in and only applies to in-memory mode:

  • STASIS_DASHBOARD_DEMO_SEED=true

Why Stasis?

Most AI frameworks optimize for prompt composition. Stasis optimizes for production runtime behavior:

  • Durable execution across backend choices.
  • Orchestration reliability with explicit queues, workers, and policies.
  • Runtime observability and operational diagnostics.
  • Cluster coordination and endpoint routing support.
  • Typed tool contracts with schema-aware invocation.
  • Built-In WASM Compatible Workflow Engine with no code builder. Powered by Grapheme
  • Memory-aware workflows with recall, find, graph, store, evict, aggregate, transform, and rollup paths. Powered by Locus

Architecture

  • domain: Runtime models, policies, events, and error contracts.
  • application: Use-cases, orchestration pipelines, and runtime handlers.
  • ports: Stable inbound/outbound interfaces.
  • infrastructure: Adapters for in-memory, SurrealDB, networking, and providers.
  • sdk: Consumer-facing facades (StasisSdk, RuntimeSdk, ControlPlaneSdk).

Layout

Client App
    |
    v
StasisSdk / RuntimeSdk / ControlPlaneSdk
    |
    v
Application Runtime + Orchestration
    |
    v
Ports
    |
    v
Infrastructure Adapters (LLM, memory, storage, transport, workflow engine)
    |
    v
Providers / Surreal Backends / Cluster Integrations

Process Flow

Request/Trigger
      ↓
Workflow Runtime
      ↓
Durable Job Queue
      ↓
Workers / Agents
      ↓
Memory + Tool + LLM Adapters

SDK Surface

  • StasisSdk: agent registration and prompt invocation flows.
  • RuntimeSdk: enqueue, process, publish, recurring materialization, runtime stats, plus cancel / fail / delete / recover_stale / replay_dead_letter.
  • ControlPlaneSdk: endpoint and cluster coordination commands.

When To Use Which SDK

Use this as a practical selection guide:

  • StasisSdk:
    • Best for chat-style assistants, lightweight copilots, and direct request/response flows.
    • Start here when you do not need background workers, scheduling, or queue durability.
  • RuntimeSdk:
    • Add when work must run asynchronously, survive retries/failures, or execute on schedules.
    • Use for workflow pipelines, outbox delivery, and operational runtime visibility.
  • ControlPlaneSdk:
    • Add when orchestration is distributed across nodes/endpoints and needs coordination commands.
    • Use for endpoint routing, cluster ownership, and control-plane driven operations.

Typical adoption path:

  1. Start with StasisSdk for simple chat and agent prompts.
  2. Add RuntimeSdk when workloads become asynchronous or policy-driven.
  3. Add ControlPlaneSdk when operating multi-node or cluster-aware deployments.

Runtime Capabilities

  • Durable backend options for queue/thread state (surreal-ws / surreal-kv), with in-memory for local runs.
  • Typed job consumers (StasisJob / JobConsumer / JobContext) with enqueue_job builders; raw NewJob still works.
  • Durable wait_for / signal correlation and cooperative cancel for in-flight or deferred jobs.
  • Lifecycle hooks (JobConsumer::on_lifecycle) so app statuses such as pending/finishing can roll back on defer, retry, cancel, or dead-letter:
async fn on_lifecycle(&self, job: &Job, event: &JobLifecycleEvent) -> Result<()> {
    match event {
        JobLifecycleEvent::Succeeded => mark_done(job),
        JobLifecycleEvent::Deferred { .. } | JobLifecycleEvent::RetryScheduled { .. } => revert_pending(job),
        JobLifecycleEvent::Canceled { .. } | JobLifecycleEvent::DeadLettered { .. } => fail_card(job),
    }
}
  • Stale Leased/Running recovery as retryable failure; heartbeat extends the job lease.
  • Fenced resource leases (acquire_lease, fencing tokens, force_acquire_lease).
  • Retry and failure-policy aware job execution with bounded attempts.
  • Recurring schedule materialization and worker-driven queue processing.
  • Outbox publication workflows for delivery and endpoint diagnostics.
  • Runtime stats snapshots for enqueued/running/succeeded/failed/dead-letter visibility.
  • Cluster/control-plane primitives for node and endpoint coordination.

Documentation

mdBook

Build locally:

mdbook build docs-book

Serve locally:

mdbook serve docs-book --open

Codebase Health Analysis Provided by ACC