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, ModelId, Request, Response, ResponseOutput, Role,
10    StopReason,
11};
12
13use crate::core::algorithm::{Algorithm, Driver};
14use crate::{Result, RoutingOutcome};
15
16/// Test helper that returns a hard-coded response without routing or model I/O.
17pub struct Noop {}
18
19#[async_trait::async_trait]
20impl Algorithm for Noop {
21    fn name(&self) -> &str {
22        "noop"
23    }
24
25    async fn route(self: Arc<Self>, _driver: Driver, request: Request) -> Result<RoutingOutcome> {
26        let streaming = request.llm_request.stream;
27        let model_id = request
28            .model_id()
29            .unwrap_or_else(|| ModelId::from("switchyard/noop"));
30        tracing::info!(target = %model_id, "noop returned its synthetic response");
31        let aggregate = AggLlmResponse {
32            id: Some("switchyard-noop".to_string()),
33            model: Some(model_id.to_string()),
34            outputs: vec![ResponseOutput {
35                url_citations: Vec::new(),
36                role: Role::Assistant,
37                content: vec![ContentBlock::Text {
38                    text: "OK".to_string(),
39                }],
40                stop_reason: Some(StopReason::EndTurn),
41            }],
42            ..Default::default()
43        };
44        let llm_response = if streaming {
45            LlmResponse::Stream(aggregate.into_stream())
46        } else {
47            LlmResponse::Agg(aggregate)
48        };
49        let response = Response {
50            llm_response,
51            metadata: request.metadata.clone(),
52            upstream_headers: http::HeaderMap::new(),
53        };
54        Ok(RoutingOutcome::answered(model_id, request, response))
55    }
56}
57
58#[cfg(test)]
59mod tests {
60    use switchyard_protocol::{LlmRequest, Message, Role};
61
62    use super::*;
63    use crate::core::testing::{echo, test_drive};
64
65    #[tokio::test]
66    async fn test_noop_algo() -> Result<()> {
67        const TEST_MODEL: &str = "test_noop_algo";
68        let request = Request {
69            llm_request: LlmRequest {
70                model: Some(TEST_MODEL.to_string()),
71                messages: vec![Message::text(Role::User, "hi")],
72                ..LlmRequest::default()
73            },
74            raw_request: None,
75            metadata: None,
76        };
77
78        // `Noop` synthesizes its own response and never offloads a call, so `echo` is
79        // never reached.
80        let a: Arc<dyn Algorithm> = Arc::new(Noop {});
81        let (selected_model, response) = test_drive(a, request, echo()).await?;
82        assert_eq!(selected_model, TEST_MODEL);
83        assert_eq!(response.selected_model(), Some(TEST_MODEL));
84        Ok(())
85    }
86
87    #[tokio::test]
88    async fn test_noop_algo_streams_when_requested() -> Result<()> {
89        let request = Request {
90            llm_request: LlmRequest {
91                model: Some("streaming-noop".to_string()),
92                messages: vec![Message::text(Role::User, "hi")],
93                stream: true,
94                ..LlmRequest::default()
95            },
96            raw_request: None,
97            metadata: None,
98        };
99
100        let algorithm: Arc<dyn Algorithm> = Arc::new(Noop {});
101        let (_, response) = test_drive(algorithm, request, echo()).await?;
102
103        assert!(matches!(response.llm_response, LlmResponse::Stream(_)));
104        Ok(())
105    }
106}