mlua_swarm/worker/baseline.rs
1//! Baseline agent — the canonical `RustFn` worker for bootstrap and
2//! smoke tests.
3//!
4//! The library-layer source of truth that structurally removes the
5//! pattern of each binary (server / MCP adapter / one-shot runner) inlining
6//! its own "let's just wire up a quick echo" duplicate. Same shape as
7//! `enhance/blueprint.rs`:
8//!
9//! - Export the agent name as a **role-literal const** ([`AG_IDENTITY`]).
10//! The naming reflects the function itself — an identity /
11//! passthrough of `ctx` — and deliberately avoids demo / echo
12//! framing.
13//! - Bake **the real implementation** ([`extend_with_baseline`]). Not a
14//! false-positive fake — a straightforward passthrough that returns
15//! the input prompt in the value field, so callers can build smoke
16//! tests around "the baseline agent exists".
17//! - **A builder** ([`extend_with_baseline`]) that adds the RustFn to
18//! an existing `RustFnInProcessSpawnerFactory`. Same shape as
19//! `enhance::blueprint::extend_factory`.
20//!
21//! ## Usage
22//!
23//! ```ignore
24//! use mlua_swarm_engine_core::baseline::{extend_with_baseline, AG_IDENTITY};
25//! use mlua_swarm_engine_core::RustFnInProcessSpawnerFactory;
26//!
27//! let factory = extend_with_baseline(RustFnInProcessSpawnerFactory::new());
28//! // The factory now has a RustFn registered under fn_id = AG_IDENTITY ("identity").
29//! ```
30//!
31//! Blueprint flows reference it as `spec: {"fn_id": baseline::AG_IDENTITY}`
32//! (or the literal `"identity"`).
33
34use crate::blueprint::compiler::RustFnInProcessSpawnerFactory;
35use crate::worker::adapter::WorkerResult;
36use serde_json::json;
37
38/// The baseline `RustFn` agent name — `"identity"`.
39///
40/// Role: a passthrough worker that echoes the input prompt into
41/// `value`. The logical id referenced from both the flow's `Step.ref`
42/// and the agent spec's `fn_id`.
43pub const AG_IDENTITY: &str = "identity";
44
45/// Builder that adds the baseline `RustFn` to a base factory.
46///
47/// Only one worker exists today ([`AG_IDENTITY`]); if additional
48/// baseline axes appear, they will be collected here.
49pub fn extend_with_baseline(base: RustFnInProcessSpawnerFactory) -> RustFnInProcessSpawnerFactory {
50 base.register_fn(AG_IDENTITY, |inv| async move {
51 Ok(WorkerResult {
52 value: json!({
53 "by": "baseline-identity",
54 "agent": inv.agent,
55 "echoed": inv.prompt,
56 }),
57 ok: true,
58 })
59 })
60}
61
62#[cfg(test)]
63mod tests {
64 use super::*;
65 use crate::blueprint::compiler::{SpawnerFactory, SpawnerFactoryKind};
66 use crate::blueprint::{AgentDef, AgentKind};
67 use serde_json::json;
68
69 #[test]
70 fn ag_identity_is_stable_literal() {
71 assert_eq!(AG_IDENTITY, "identity");
72 }
73
74 #[test]
75 fn extend_with_baseline_builds_identity_adapter() {
76 let factory = extend_with_baseline(RustFnInProcessSpawnerFactory::new());
77 assert_eq!(
78 <RustFnInProcessSpawnerFactory as SpawnerFactoryKind>::KIND,
79 AgentKind::RustFn
80 );
81 let agent_def = AgentDef {
82 name: "id".into(),
83 kind: AgentKind::RustFn,
84 spec: json!({ "fn_id": AG_IDENTITY }),
85 profile: None,
86 meta: None,
87 };
88 factory
89 .build(&agent_def, None)
90 .expect("AG_IDENTITY fn must be registered for AgentDef build");
91 }
92}