roboticus-api 0.11.4

HTTP routes, WebSocket, auth, rate limiting, and dashboard for the Roboticus agent runtime
Documentation
//! Re-export shim — core inference module lives in `roboticus_pipeline::core`.

// Re-export pipeline-owned surface so existing `core::*` imports keep working.
#[allow(unused_imports)]
pub(crate) use roboticus_pipeline::core::{
    InferenceInput, InferenceOutput, PipelineResult, PreparedContextSnapshot, PreparedInference,
    always_include_operational_tools, build_retry_request, check_cache, execute_inference_pipeline,
    post_turn_ingest, prepare_inference, record_cost, refine_session_nickname,
    run_inference_and_react, sanitize_model_output, store_in_cache,
};

#[cfg(test)]
mod tests {
    use super::{always_include_operational_tools, build_retry_request};
    use roboticus_llm::format::{ToolDefinition, UnifiedMessage, UnifiedRequest};
    use roboticus_pipeline::decomposition_types::DelegationProvenance;
    use roboticus_pipeline::guard_fallback::{deterministic_guard_fallback, is_task_like_turn};
    use roboticus_pipeline::guard_registry::{GuardContext, GuardId};
    use roboticus_pipeline::intent_registry::Intent;

    #[test]
    fn operational_tools_remain_visible_to_task_workflow() {
        let tools = always_include_operational_tools();
        for required in [
            "get_memory_stats",
            "get_runtime_context",
            "list-subagent-roster",
            "list-available-skills",
            "compose-subagent",
            "compose-skill",
            "orchestrate-subagents",
        ] {
            assert!(
                tools.iter().any(|tool| tool == required),
                "missing required always-include tool: {required}"
            );
        }
    }

    fn test_prepared_inference() -> super::PreparedInference {
        super::PreparedInference {
            model: "test-model".to_string(),
            request: UnifiedRequest {
                model: "test-model".to_string(),
                messages: vec![UnifiedMessage {
                    role: "user".to_string(),
                    content: "Do the task".to_string(),
                    parts: None,
                }],
                max_tokens: Some(512),
                temperature: None,
                system: None,
                quality_target: None,
                tools: vec![ToolDefinition {
                    name: "compose-subagent".to_string(),
                    description: "compose".to_string(),
                    parameters: serde_json::json!({"type":"object"}),
                }],
            },
            cache_hash: "cache".to_string(),
            system_prompt_hash: "system".to_string(),
            query_embedding: None,
            intents: vec![Intent::Execution],
            previous_assistant: None,
            context_snapshot: super::PreparedContextSnapshot {
                complexity_level: "medium".to_string(),
                token_budget: 0,
                system_prompt_tokens: 0,
                memory_tokens: 0,
                history_tokens: 0,
                history_depth: 0,
                memory_tiers_json: None,
                retrieved_memories_json: None,
            },
            retrieval_metrics: roboticus_agent::retrieval::RetrievalMetrics::default(),
            tool_search_stats: None,
            delegated_execution_result: None,
        }
    }

    fn test_guard_context<'a>(tool_results: &'a [(String, String)]) -> GuardContext<'a> {
        let provenance = Box::leak(Box::new(DelegationProvenance::default()));
        GuardContext {
            user_prompt: "Do the task",
            intents: &[Intent::Execution],
            tool_results,
            agent_name: "Duncan",
            resolved_model: "test-model",
            delegation_provenance: provenance,
            previous_assistant: None,
            prior_assistant_messages: &[],
            semantic_guard_scores: std::collections::HashMap::new(),
            subagent_names: vec![],
        }
    }

    #[test]
    fn task_deferral_retry_preserves_tools_and_state() {
        let prepared = test_prepared_inference();
        let tool_results = vec![(
            "list-subagent-roster".to_string(),
            "{\"subagent_count\":0,\"subagents\":[]}".to_string(),
        )];
        let ctx = test_guard_context(&tool_results);

        let retry = build_retry_request(GuardId::TaskDeferral, &prepared, &ctx);

        assert_eq!(retry.tools.len(), prepared.request.tools.len());
        let directive = retry.messages.last().expect("retry directive");
        assert!(directive.content.contains("Prior tool state:"));
        assert!(directive.content.contains("subagent_count"));
        assert!(directive.content.contains("compose what is missing"));
    }

    #[test]
    fn output_contract_retry_uses_prior_execution_state() {
        let prepared = test_prepared_inference();
        let tool_results = vec![(
            "list_directory".to_string(),
            "FIRMWARE.toml Governance.md Ledger.md".to_string(),
        )];
        let ctx = test_guard_context(&tool_results);

        let retry = build_retry_request(GuardId::OutputContract, &prepared, &ctx);

        assert!(retry.tools.is_empty());
        let directive = retry.messages.last().expect("retry directive");
        assert!(
            directive
                .content
                .contains("exact requested output contract")
        );
        assert!(directive.content.contains("Prior execution state:"));
        assert!(directive.content.contains("no headings"));
    }

    #[test]
    fn low_value_retry_does_not_preserve_tools() {
        let prepared = test_prepared_inference();
        let empty_results: Vec<(String, String)> = Vec::new();
        let ctx = test_guard_context(&empty_results);

        let retry = build_retry_request(GuardId::LowValueParroting, &prepared, &ctx);

        assert!(retry.tools.is_empty());
    }

    #[test]
    fn internal_protocol_retry_preserves_tools() {
        let prepared = test_prepared_inference();
        let empty_results: Vec<(String, String)> = Vec::new();
        let ctx = test_guard_context(&empty_results);

        let retry = build_retry_request(GuardId::InternalProtocol, &prepared, &ctx);

        assert_eq!(retry.tools.len(), prepared.request.tools.len());
        let directive = retry.messages.last().expect("retry directive");
        assert!(directive.content.contains("executable tool call"));
        assert!(
            directive
                .content
                .contains("Do not narrate internal protocol")
        );
    }

    #[test]
    fn task_like_turn_detects_operational_work() {
        assert!(is_task_like_turn(&[Intent::Execution]));
        assert!(is_task_like_turn(&[Intent::Delegation]));
        assert!(!is_task_like_turn(&[Intent::CapabilitySummary]));
    }

    #[test]
    fn output_contract_guard_fallback_salvages_exact_bullets() {
        let rescued = deterministic_guard_fallback(
            GuardId::OutputContract,
            "## Summary\n- one\n- two\n- three",
            &[],
            "Return exactly three bullet points.",
            "Duncan",
        );
        assert_eq!(rescued, "- one\n- two\n- three");
    }

    #[test]
    fn internal_protocol_guard_fallback_salvages_delegate_outputs_into_exact_bullets() {
        let tool_results = vec![
            (
                "delegate-subagent".to_string(),
                "delegated_subagent=revenue_strategist\nsubtask 1 -> revenue_strategist\n1. Retainer Micro-Agency\n2. Finance Flash Newsletter".to_string(),
            ),
            (
                "delegate-subagent".to_string(),
                "delegated_subagent=saas_ideator\nsubtask 1 -> saas_ideator\n**Product:** ScopeGuard".to_string(),
            ),
        ];
        let rescued = deterministic_guard_fallback(
            GuardId::InternalProtocol,
            "",
            &tool_results,
            "Return exactly three bullet points: two revenue ideas and one SaaS idea.",
            "Duncan",
        );
        assert_eq!(
            rescued,
            "- Retainer Micro-Agency\n- Finance Flash Newsletter\n- Product: ScopeGuard"
        );
    }

    #[test]
    fn task_deferral_guard_fallback_surfaces_runtime_boundary_blocker() {
        let tool_results = vec![(
            "get_runtime_context".to_string(),
            "{\"workspace\":\"/Users/jmachen/.roboticus/workspace\"}".to_string(),
        )];
        let rescued = deterministic_guard_fallback(
            GuardId::TaskDeferral,
            "I'll inspect the runtime context first, then read the forbidden file.",
            &tool_results,
            "This is a task. Inspect what you need, then read /root/forbidden.txt and return exactly one bullet point with the result.",
            "Duncan",
        );
        assert_eq!(
            rescued,
            "- Blocked: /root/forbidden.txt is outside my allowed runtime boundaries in this environment, so I cannot read it directly."
        );
    }

    #[test]
    fn generic_degraded_fallback_is_detected() {
        assert!(
            roboticus_pipeline::guard_fallback::is_generic_degraded_fallback(
                "Duncan here. The prior generation degraded. I am returning a concrete fallback: state the exact outcome format you want."
            )
        );
        assert!(
            roboticus_pipeline::guard_fallback::is_generic_degraded_fallback(
                "I wasn't able to produce a good response to that. Could you rephrase or try again?"
            )
        );
        assert!(
            !roboticus_pipeline::guard_fallback::is_generic_degraded_fallback("- concrete result")
        );
    }
}