Skip to main content

switchyard_libsy/algorithms/
noop.rs

1// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2// SPDX-License-Identifier: Apache-2.0
3
4//! Test-only algorithm that returns a hard-coded response without calling a backend.
5
6use std::sync::Arc;
7
8use switchyard_protocol::{
9    AggLlmResponse, ContentBlock, LlmResponse, Request, Response, ResponseOutput, Role, StopReason,
10};
11
12use crate::Result;
13use crate::core::algorithm::{Algorithm, Driver};
14use switchyard_protocol::{Context, Decision};
15
16/// Test helper that returns a hard-coded response without routing or model I/O.
17pub struct Noop {}
18
19/// Test decision carrying the inbound model or a fixed placeholder.
20pub struct NoopDecision {
21    model: String,
22}
23
24impl Decision for NoopDecision {
25    fn selected_model(&self) -> &str {
26        &self.model
27    }
28    fn reasoning(&self) -> Option<&str> {
29        None
30    }
31    fn as_any(&self) -> &dyn std::any::Any {
32        self
33    }
34}
35
36#[async_trait::async_trait]
37impl Algorithm for Noop {
38    fn name(&self) -> &str {
39        "noop"
40    }
41
42    async fn create_run_task(
43        self: Arc<Self>,
44        ctx: Context,
45        driver: Driver,
46        request: Request,
47    ) -> Result<Response> {
48        let model = request
49            .requested_model()
50            .unwrap_or("switchyard/noop")
51            .to_string();
52        let decision: Arc<dyn Decision> = Arc::new(NoopDecision {
53            model: model.clone(),
54        });
55        driver.info(ctx, decision.clone()).await?;
56
57        let llm_response = LlmResponse::Agg(AggLlmResponse {
58            id: Some("switchyard-noop".to_string()),
59            model: Some(model),
60            outputs: vec![ResponseOutput {
61                role: Role::Assistant,
62                content: vec![ContentBlock::Text {
63                    text: "OK".to_string(),
64                }],
65                stop_reason: Some(StopReason::EndTurn),
66            }],
67            ..Default::default()
68        });
69        let response = Response {
70            llm_response,
71            metadata: request.metadata.clone(),
72        };
73        Ok(response)
74    }
75}
76
77#[cfg(test)]
78mod tests {
79    use switchyard_protocol::{LlmRequest, Message, Role};
80
81    use super::*;
82
83    #[tokio::test]
84    async fn test_noop_algo() -> Result<()> {
85        const TEST_MODEL: &str = "test_noop_algo";
86        let request = Request {
87            llm_request: LlmRequest {
88                model: Some(TEST_MODEL.to_string()),
89                messages: vec![Message::text(Role::User, "hi")],
90                ..LlmRequest::default()
91            },
92            raw_request: None,
93            metadata: None,
94        };
95
96        let a: Arc<dyn Algorithm> = Arc::new(Noop {});
97        let (decisions, response) = a.run(Context::default(), request).await?;
98        let Some(decision) = decisions.first() else {
99            panic!("Expected exactly one Decision");
100        };
101        assert_eq!(decision.selected_model(), TEST_MODEL);
102        assert_eq!(response.selected_model(), Some(TEST_MODEL));
103        Ok(())
104    }
105}