stasis-rs 0.1.0

Durable AI orchestration framework with runtime jobs, lineage, and memory integration
Documentation

Stasis

Stasis is a Rust framework for AI orchestration with durable runtime jobs, cluster-aware control plane primitives, and memory integration hooks.

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

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).

SDK Surface

  • StasisSdk: agent registration and prompt invocation flows.
  • RuntimeSdk: enqueue, process, publish, recurring materialization, runtime stats.
  • ControlPlaneSdk: endpoint and cluster coordination commands.

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(())
}

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

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.

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:

CI-friendly smoke harness:

Embedded Dashboard

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

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_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

Documentation

mdBook

Build locally:

mdbook build docs-book

Serve locally:

mdbook serve docs-book --open