klieo-workflow 3.8.1

Declarative no-code workflow substrate for the klieo agent framework.
Documentation
//! In-process doubles and an `AgentContext` harness shared by the crate's
//! unit tests and its integration tests. Gated behind `cfg(test)` or the
//! `test-support` feature; never part of a production build.

use async_trait::async_trait;
use klieo_bus_memory::MemoryBus;
use klieo_core::agent::AgentContext;
use klieo_core::llm::LlmClient;
use klieo_core::test_utils::{
    FakeLlmClient, FakeToolInvoker, InMemoryEpisodic, InMemoryLongTerm, InMemoryShortTerm,
    ScriptedLlm,
};
use klieo_flows::{Flow, FlowError};
use serde_json::Value;
use std::sync::Arc;

/// An `LlmClient` handle that `compile()` can resolve but that errors if
/// actually invoked — safe for compile-only tests that merely need a
/// handle to resolve. Reuses `klieo-core`'s own test fake (an empty
/// response script) rather than hand-rolling every `LlmClient` method.
pub fn dummy_llm() -> Arc<dyn LlmClient> {
    Arc::new(FakeLlmClient::new("dummy"))
}

/// An `LlmClient` whose completion always returns `reply` verbatim, for
/// asserting that a node routed to its own registered model. Repeat-safe:
/// every call returns the same reply, so one handle can back several nodes.
pub fn stub_llm(reply: &str) -> Arc<dyn LlmClient> {
    Arc::new(ScriptedLlm::new(vec![reply.to_string()]))
}

/// A `Flow` that ignores its input and returns `{ "tag": <tag> }`, for
/// exercising subflow nodes without a live LLM.
pub fn const_subflow(tag: &'static str) -> Arc<dyn Flow> {
    struct Const(&'static str);
    #[async_trait]
    impl Flow for Const {
        fn name(&self) -> &str {
            self.0
        }
        async fn run(&self, _ctx: AgentContext, _input: Value) -> Result<Value, FlowError> {
            Ok(serde_json::json!({ "tag": self.0 }))
        }
    }
    Arc::new(Const(tag))
}

/// A real `AgentContext` on an in-process bus + in-memory fakes, for tests
/// that run a compiled flow. Agent nodes override `ctx.llm` per node, so the
/// placeholder client installed here is never invoked.
pub fn test_ctx() -> AgentContext {
    let bus = MemoryBus::new();
    AgentContext::builder()
        .llm(dummy_llm())
        .short_term(Arc::new(InMemoryShortTerm::default()))
        .long_term(Arc::new(InMemoryLongTerm::default()))
        .episodic(Arc::new(InMemoryEpisodic::default()))
        .pubsub(bus.pubsub)
        .kv(bus.kv)
        .request_reply(bus.request_reply)
        .jobs(bus.jobs)
        .tools(Arc::new(FakeToolInvoker::new()))
        .agent_name("workflow-unit-test")
        .build()
        .expect("all required AgentContext fields set")
}