vifu-runtime 0.1.2

Small, stateful, embeddable Agent Runtime primitives for Vifu.
Documentation
use std::sync::Arc;
use std::time::{Duration, Instant};

use vifu_runtime::prelude::*;

#[test]
fn public_api_exposes_the_headless_runtime() {
    let mut runtime = HeadlessRuntime::new();
    let advance = runtime.dispatch(RuntimeCommand::new(
        "command-1",
        "application.input",
        json!({ "text": "Hello" }),
    ));

    assert_eq!(advance.snapshot.revision, 1);
}

#[test]
fn public_api_invokes_a_registered_provider() {
    struct EchoProvider;

    impl AgentProvider for EchoProvider {
        fn supports(&self, capability: &str) -> bool {
            capability == "chat"
        }

        fn invoke<'a>(
            &'a self,
            request: ProviderRequest,
            _cancellation: CancellationToken,
        ) -> ProviderFuture<'a> {
            Box::pin(async move {
                Ok(ProviderResponse {
                    data: request.data,
                    metadata: json!({}),
                    state: None,
                })
            })
        }
    }

    let runtime = VifuRuntime::new("embedded-app").unwrap();
    runtime
        .register_provider("echo", Arc::new(EchoProvider))
        .unwrap();
    runtime
        .register_agent(AgentDefinition {
            id: "guide".to_string(),
            name: "Guide".to_string(),
            provider: "echo".to_string(),
            capabilities: vec!["chat".to_string()],
            metadata: json!({}),
        })
        .unwrap();
    runtime
        .register_endpoint(EndpointDefinition {
            name: "guide".to_string(),
            agent: "guide".to_string(),
            capability: "chat".to_string(),
            timeout_ms: 500,
        })
        .unwrap();
    let handle = runtime
        .start_invoke(InvocationInput::json("guide", json!({ "text": "Hello" })))
        .unwrap();
    let deadline = Instant::now() + Duration::from_secs(1);
    let output = loop {
        let poll = runtime.poll_invocation(&handle).unwrap();
        if let Some(output) = poll.output {
            break output;
        }
        assert!(Instant::now() < deadline, "embedded invocation timed out");
        std::thread::sleep(Duration::from_millis(5));
    };

    assert_eq!(
        output.data,
        InvocationData::Json(json!({ "text": "Hello" }))
    );
}