Skip to main content

serve/
lib.rs

1//! # serve (experimental)
2//!
3//! An agent framework in the style of [Topcoat], with hosting modelled on
4//! [eve], built on the `everruns` runtime. It is part of the
5//! [Everruns](https://everruns.com) ecosystem.
6//!
7//! > **Experimental.** serve is a proof of concept. Every API here may change
8//! > or disappear; it is published as `everruns-serve` but not covered by
9//! > the everruns stability policy.
10//!
11//! ```no_run
12//! use serve::prelude::*;
13//!
14//! /// Rolls dice for board-game nights.
15//! #[agent]
16//! fn assistant() -> Agent {
17//!     Agent::builder()
18//!         .model("anthropic/claude-sonnet-5")
19//!         .instructions("Roll dice when asked.")
20//!         .build()
21//! }
22//!
23//! /// Roll one die with the given number of sides.
24//! #[tool]
25//! async fn roll_dice(cx: &Cx, sides: u32) -> Result<u32> {
26//!     cx.progress(format!("rolling a d{sides}")).await;
27//!     Ok(sides)
28//! }
29//!
30//! #[tokio::main]
31//! async fn main() -> serve::Result {
32//!     serve::start(App::builder().discover().build()).await
33//! }
34//! ```
35//!
36//! A real app also calls `serve::assets!()` once and embeds `agent/**` with
37//! `serve_build::embed()` in `build.rs`, so prompts and skills can live in
38//! Markdown files (see `md!`).
39//!
40//! The pieces:
41//!
42//! - **Attribute macros** ([`agent`], [`tool`], [`channel`], [`schedule`],
43//!   [`connection`], [`eval`]) register items at link time.
44//! - **[`App::builder().discover()`](AppBuilder::discover)** collects them,
45//!   validates the file-layout conventions, and resolves `agent/**` assets
46//!   embedded by `serve-build`.
47//! - **[`Manifest`]** is what the build declares and the host provides:
48//!   schedules, channel routes, secrets, the sandbox, model strings.
49//! - **[`Cx`]** is the one context type: the tool call (session, turn, call
50//!   id, progress), connections, secrets, starting sessions.
51//! - **[`start`]** runs the binary as `dev`, `start`, `manifest`, `eval` or
52//!   `deploy`, serving everywhere the same `/v1` wire API, a subset of the
53//!   everruns server's session API (so the everruns SDK can drive it).
54//!
55//! [Topcoat]: https://github.com/tokio-rs/topcoat
56//! [eve]: https://vercel.com/docs/eve
57
58#![cfg_attr(test, allow(clippy::unwrap_used, clippy::expect_used))]
59
60// Lets the macros' `::serve::…` paths resolve in this crate's own tests.
61extern crate self as serve;
62
63#[cfg(feature = "a2a")]
64mod a2a;
65#[cfg(feature = "ag-ui")]
66mod ag_ui;
67mod agent;
68mod app;
69mod channel;
70mod cli;
71mod config;
72mod connection;
73mod cx;
74mod eval;
75mod gateway;
76mod host;
77mod hosting;
78mod manifest;
79mod registry;
80mod scheduler;
81mod server;
82mod store;
83#[cfg(test)]
84mod wire_tests;
85
86pub use agent::{Agent, AgentBuilder, Instructions, Markdown};
87pub use app::{App, AppBuilder, Mode};
88pub use channel::{Channel, ChannelEvent, Inbound, Slack, Webhook};
89pub use cli::start;
90pub use config::{AppConfig, SandboxKind};
91pub use connection::{McpServer, Secret};
92pub use cx::{Cx, DeliveryTarget, StartSession};
93pub use eval::{EvalCx, EvalReport, EvalResult, OnApproval, TurnCheck, TurnRecord};
94pub use hosting::{MicroVm, Server, ServerBuilder, data_dir};
95pub use manifest::Manifest;
96
97pub use serve_macros::{agent, channel, connection, eval, schedule, tool};
98
99/// Simulated models for running agents offline, re-exported from `everruns`.
100pub mod sim {
101    pub use everruns::{LlmSimConfig, OnExhausted, SimToolCall, SimTurn};
102
103    /// A scripted simulator that replays `turns` for every user message: tool
104    /// calls first, then the reply. Loops, so each message sees the same
105    /// script. Used when no model gateway is configured.
106    pub fn script(turns: impl IntoIterator<Item = SimTurn>) -> LlmSimConfig {
107        LlmSimConfig::scripted(turns.into_iter().collect()).with_on_exhausted(OnExhausted::Loop)
108    }
109
110    /// A scripted tool call for [`script`].
111    pub fn call(name: impl Into<String>, arguments: serde_json::Value) -> SimTurn {
112        SimTurn::ToolCalls(vec![SimToolCall {
113            name: name.into(),
114            arguments,
115            id: None,
116        }])
117    }
118
119    /// A scripted assistant reply for [`script`].
120    pub fn reply(text: impl Into<String>) -> SimTurn {
121        SimTurn::Assistant(text.into())
122    }
123}
124
125/// The error type used across serve. Anything `std::error::Error` converts
126/// into it with `?`.
127pub type Error = anyhow::Error;
128
129/// `Result` with serve's [`Error`]. `-> Result` alone means `Result<()>`.
130pub type Result<T = (), E = Error> = std::result::Result<T, E>;
131
132/// Everything an application file usually imports.
133pub mod prelude {
134    pub use crate::{
135        Agent, App, Channel, Cx, DeliveryTarget, EvalCx, McpServer, Result, Secret, Slack, Webhook,
136        agent, channel, connection, eval, md, schedule, tool,
137    };
138    pub use anyhow::{anyhow, bail, ensure};
139    pub use serde::{Deserialize, Serialize};
140    pub use serde_json::json;
141}
142
143/// Include the assets `serve_build::embed()` generated for this crate.
144///
145/// Call once, at the crate root. Without it `agent/**` files are not in the
146/// binary and `discover()` reports them missing.
147#[macro_export]
148macro_rules! assets {
149    () => {
150        include!(concat!(env!("OUT_DIR"), "/serve_assets.rs"));
151        $crate::__private::inventory::submit! {
152            $crate::__private::AppInfoRegistration {
153                name: env!("CARGO_PKG_NAME"),
154                version: env!("CARGO_PKG_VERSION"),
155            }
156        }
157    };
158}
159
160/// Embed a Markdown file from this crate's `agent/` directory.
161///
162/// The text is compiled in (so a missing file is a build error), and in `dev`
163/// mode it is re-read from disk on every new session, so prompt edits
164/// hot-reload without a rebuild.
165#[macro_export]
166macro_rules! md {
167    ($path:literal) => {
168        $crate::Markdown::__embedded(
169            $path,
170            include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/agent/", $path)),
171            concat!(env!("CARGO_MANIFEST_DIR"), "/agent/", $path),
172        )
173    };
174}
175
176/// Runtime support for macro expansions. Not an API.
177#[doc(hidden)]
178pub mod __private {
179    pub use crate::registry::*;
180    pub use futures::future::BoxFuture;
181    pub use inventory;
182    pub use schemars;
183    pub use serde;
184    pub use serde_json::Value;
185}