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                    responses: None,
39                    text: "OK".to_string(),
40                }],
41                stop_reason: Some(StopReason::EndTurn),
42            }],
43            ..Default::default()
44        };
45        let llm_response = if streaming {
46            LlmResponse::Stream(aggregate.into_stream())
47        } else {
48            LlmResponse::Agg(aggregate)
49        };
50        let response = Response {
51            llm_response,
52            metadata: request.metadata.clone(),
53            upstream_headers: http::HeaderMap::new(),
54        };
55        Ok(RoutingOutcome::answered(model_id, request, response))
56    }
57}
58
59#[cfg(test)]
60mod tests {
61    use switchyard_protocol::{LlmRequest, Message, Role};
62
63    use super::*;
64    use crate::core::testing::{echo, test_drive};
65
66    #[tokio::test]
67    async fn test_noop_algo() -> Result<()> {
68        const TEST_MODEL: &str = "test_noop_algo";
69        let request = Request {
70            llm_request: LlmRequest {
71                model: Some(TEST_MODEL.to_string()),
72                messages: vec![Message::text(Role::User, "hi")],
73                ..LlmRequest::default()
74            },
75            raw_request: None,
76            metadata: None,
77        };
78
79        // `Noop` synthesizes its own response and never offloads a call, so `echo` is
80        // never reached.
81        let a: Arc<dyn Algorithm> = Arc::new(Noop {});
82        let (selected_model, response) = test_drive(a, request, echo()).await?;
83        assert_eq!(selected_model, TEST_MODEL);
84        assert_eq!(response.selected_model(), Some(TEST_MODEL));
85        Ok(())
86    }
87
88    #[tokio::test]
89    async fn test_noop_algo_streams_when_requested() -> Result<()> {
90        let request = Request {
91            llm_request: LlmRequest {
92                model: Some("streaming-noop".to_string()),
93                messages: vec![Message::text(Role::User, "hi")],
94                stream: true,
95                ..LlmRequest::default()
96            },
97            raw_request: None,
98            metadata: None,
99        };
100
101        let algorithm: Arc<dyn Algorithm> = Arc::new(Noop {});
102        let (_, response) = test_drive(algorithm, request, echo()).await?;
103
104        assert!(matches!(response.llm_response, LlmResponse::Stream(_)));
105        Ok(())
106    }
107}