Skip to content

Commit 93f7ea2

Browse files
refactor(libsy): tighten public api boundaries
Signed-off-by: nachiketb <nachiketb@nvidia.com>
1 parent eea4e01 commit 93f7ea2

30 files changed

Lines changed: 376 additions & 167 deletions

crates/libsy-llm-client/README.md

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -76,11 +76,14 @@ fn build_client() -> switchyard_llm_client::Result<TranslatingLlmClient> {
7676

7777
```rust
7878
use switchyard_llm_client::{LlmClientError, TranslatingLlmClient};
79-
use switchyard_protocol::{completion_text, text_request, Context, LlmResponse, Request};
79+
use switchyard_protocol::{ContentBlock, Context, LlmRequest, LlmResponse, Message, Request, Role};
8080

8181
async fn ask(client: &TranslatingLlmClient) -> switchyard_llm_client::Result<String> {
8282
let request = Request {
83-
llm_request: text_request(None, "Say hello in five words."),
83+
llm_request: LlmRequest {
84+
messages: vec![Message::text(Role::User, "Say hello in five words.")],
85+
..LlmRequest::default()
86+
},
8487
raw_request: None,
8588
metadata: None,
8689
};
@@ -91,7 +94,15 @@ async fn ask(client: &TranslatingLlmClient) -> switchyard_llm_client::Result<Str
9194
.await?;
9295

9396
match response.llm_response {
94-
LlmResponse::Agg(agg) => Ok(completion_text(&agg)),
97+
LlmResponse::Agg(agg) => Ok(agg
98+
.outputs
99+
.first()
100+
.and_then(|output| output.content.first())
101+
.and_then(|block| match block {
102+
ContentBlock::Text { text } => Some(text.clone()),
103+
_ => None,
104+
})
105+
.unwrap_or_default()),
95106
LlmResponse::Stream(_) => Err(LlmClientError::InvalidResponse {
96107
source: "expected a buffered response".into(),
97108
}),
@@ -106,13 +117,16 @@ Set `stream` on the IR request and drive the returned chunk stream:
106117
```rust
107118
use futures_util::StreamExt;
108119
use switchyard_llm_client::TranslatingLlmClient;
109-
use switchyard_protocol::{text_request, Context, LlmResponse, LlmResponseChunk, Request};
120+
use switchyard_protocol::{Context, LlmRequest, LlmResponse, LlmResponseChunk, Message, Request, Role};
110121

111122
async fn stream(
112123
client: &TranslatingLlmClient,
113124
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
114-
let mut llm_request = text_request(None, "Count to five.");
115-
llm_request.stream = true;
125+
let llm_request = LlmRequest {
126+
messages: vec![Message::text(Role::User, "Count to five.")],
127+
stream: true,
128+
..LlmRequest::default()
129+
};
116130
let request = Request { llm_request, raw_request: None, metadata: None };
117131

118132
let response = client

crates/libsy-llm-client/src/client.rs

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -772,13 +772,38 @@ mod tests {
772772
use std::thread::JoinHandle;
773773

774774
use serde_json::json;
775-
use switchyard_protocol::{LlmRequest, completion_text, text_request};
775+
use switchyard_protocol::{AggLlmResponse, ContentBlock, LlmRequest, Message, Role};
776776
use wiremock::matchers::{method, path};
777777
use wiremock::{Mock, MockServer, ResponseTemplate};
778778

779779
use super::*;
780780
use crate::backend::HttpBackendConfig;
781781

782+
fn text_request(model: Option<String>, prompt: impl Into<String>) -> LlmRequest {
783+
LlmRequest {
784+
model,
785+
messages: vec![Message::text(Role::User, prompt)],
786+
..LlmRequest::default()
787+
}
788+
}
789+
790+
fn completion_text(response: &AggLlmResponse) -> String {
791+
response
792+
.outputs
793+
.first()
794+
.map(|output| {
795+
output
796+
.content
797+
.iter()
798+
.filter_map(|block| match block {
799+
ContentBlock::Text { text } => Some(text.as_str()),
800+
_ => None,
801+
})
802+
.collect::<String>()
803+
})
804+
.unwrap_or_default()
805+
}
806+
782807
fn config(base_url: &str) -> HttpBackendConfig {
783808
HttpBackendConfig {
784809
base_url: base_url.to_string(),

crates/libsy/examples/ensemble.rs

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,11 @@ use parking_lot::Mutex;
2727

2828
use tokio::sync::Notify;
2929

30+
mod support;
31+
use support::{completion_text, prompt_text, text_request};
3032
use switchyard_libsy::{Algorithm, Driver, LibsyError, LlmTarget, LlmTargetSet, Result};
3133
use switchyard_llm_client::{Backend, HttpBackendConfig, ModelConfig, TranslatingLlmClient};
32-
use switchyard_protocol::{
33-
Context, Decision, LlmResponse, Request, Response, RoutedLlmClient, completion_text,
34-
prompt_text, text_request,
35-
};
34+
use switchyard_protocol::{Context, Decision, LlmResponse, Request, Response, RoutedLlmClient};
3635

3736
const CANDIDATE_MODELS: [&str; 3] = [
3837
"nvidia/qwen/qwen3.6-27b",
@@ -553,12 +552,12 @@ mod tests {
553552
use super::*;
554553
use std::sync::atomic::{AtomicBool, Ordering};
555554

555+
use crate::support::{completion_text, prompt_text, text_request, text_response};
556556
use futures::StreamExt;
557557
use switchyard_libsy::LlmTarget;
558558
use switchyard_protocol::{
559559
LlmRequest, LlmResponse, LlmResponseChunk, LlmResponseStreamEvent, Message, Response, Role,
560-
RoutedLlmClient, SamplingParams, Signals, ToolChoice, ToolDefinition, completion_text,
561-
prompt_text, text_request, text_response,
560+
RoutedLlmClient, SamplingParams, Signals, ToolChoice, ToolDefinition,
562561
};
563562
use tokio::sync::Semaphore;
564563

crates/libsy/examples/research_agent.rs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,13 @@
1414
use std::sync::Arc;
1515

1616
use async_trait::async_trait;
17+
mod support;
18+
use support::{completion_text, text_request, text_response};
1719
use switchyard_libsy::{
1820
Algorithm, LibsyError, LlmClassifierConfig, LlmTarget, LlmTargetSet, LlmTaskClassifier, Result,
1921
TaskClassifierConfig,
2022
};
21-
use switchyard_protocol::{
22-
Context, Decision, LlmResponse, Request, Response, RoutedLlmClient, completion_text,
23-
text_request, text_response,
24-
};
23+
use switchyard_protocol::{Context, Decision, LlmResponse, Request, Response, RoutedLlmClient};
2524

2625
const CLASSIFIER: &str = "classifier/model";
2726
const STRONG: &str = "strong/model";

crates/libsy/examples/research_agent_core.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,13 @@
1111
1212
use std::sync::Arc;
1313

14+
mod support;
15+
use support::{completion_text, text_request, text_response};
1416
use switchyard_libsy::{
1517
Algorithm, LibsyError, LlmClassifierConfig, LlmTarget, LlmTargetSet, LlmTaskClassifier, Result,
1618
Step, TaskClassifierConfig,
1719
};
18-
use switchyard_protocol::{
19-
Context, Decision, LlmResponse, Request, Response, completion_text, text_request, text_response,
20-
};
20+
use switchyard_protocol::{Context, Decision, LlmResponse, Request, Response};
2121
use tokio_stream::StreamExt;
2222

2323
const CLASSIFIER: &str = "classifier/model";

crates/libsy/examples/streaming_agent.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,11 @@ use std::io::Write;
1717
use std::sync::Arc;
1818

1919
use futures::StreamExt;
20+
mod support;
21+
use support::{completion_text, text_request};
2022
use switchyard_libsy::{Algorithm, LibsyError, LlmTarget, LlmTargetSet, Random, Result, Step};
2123
use switchyard_protocol::{
22-
Context, LlmResponse, LlmResponseChunk, LlmResponseStream, Request, Response, completion_text,
23-
text_request,
24+
Context, LlmResponse, LlmResponseChunk, LlmResponseStream, Request, Response,
2425
};
2526

2627
/// The "real" model call the agent makes to fulfill an offloaded promise: a response
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
#![allow(dead_code)]
5+
6+
use switchyard_protocol::{
7+
AggLlmResponse, ContentBlock, LlmRequest, Message, ResponseOutput, Role,
8+
};
9+
10+
pub fn text_request(model: Option<String>, prompt: impl Into<String>) -> LlmRequest {
11+
LlmRequest {
12+
model,
13+
messages: vec![Message::text(Role::User, prompt)],
14+
..LlmRequest::default()
15+
}
16+
}
17+
18+
pub fn prompt_text(request: &LlmRequest) -> String {
19+
request
20+
.messages
21+
.iter()
22+
.filter(|message| message.role == Role::User)
23+
.filter_map(|message| message.text_content("\n"))
24+
.collect::<Vec<_>>()
25+
.join("\n")
26+
}
27+
28+
pub fn text_response(model: Option<String>, completion: impl Into<String>) -> AggLlmResponse {
29+
AggLlmResponse {
30+
model,
31+
outputs: vec![ResponseOutput {
32+
role: Role::Assistant,
33+
content: vec![ContentBlock::Text {
34+
text: completion.into(),
35+
}],
36+
stop_reason: None,
37+
}],
38+
..AggLlmResponse::default()
39+
}
40+
}
41+
42+
pub fn completion_text(response: &AggLlmResponse) -> String {
43+
response
44+
.outputs
45+
.first()
46+
.map(|output| {
47+
output
48+
.content
49+
.iter()
50+
.filter_map(|block| match block {
51+
ContentBlock::Text { text } => Some(text.as_str()),
52+
_ => None,
53+
})
54+
.collect::<String>()
55+
})
56+
.unwrap_or_default()
57+
}

crates/libsy/src/algorithms/fall_through.rs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -353,12 +353,10 @@ mod tests {
353353
use super::*;
354354
use crate::algorithms::util::prompts;
355355
use crate::core::classifier::Classification;
356+
use crate::text::{completion_text, text_request, text_response};
356357
use crate::{SystemPromptProcessor, TargetPrompts};
357358

358-
use switchyard_protocol::{
359-
LlmClientError, LlmRequest, LlmResponse, Message, Metadata, Role, completion_text,
360-
text_request, text_response,
361-
};
359+
use switchyard_protocol::{LlmClientError, LlmRequest, LlmResponse, Message, Metadata, Role};
362360

363361
#[derive(Debug, thiserror::Error)]
364362
#[error("{0}")]

crates/libsy/src/algorithms/llm_class.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -915,9 +915,8 @@ mod tests {
915915
use serde_json::Value;
916916

917917
use super::*;
918-
use switchyard_protocol::{
919-
LlmClientError, LlmRequest, Metadata, completion_text, text_request, text_response,
920-
};
918+
use crate::text::{completion_text, text_request, text_response};
919+
use switchyard_protocol::{LlmClientError, LlmRequest, Metadata};
921920

922921
use crate::algorithms::util::llm_judge::Judge;
923922
use crate::core::algorithm::Algorithm;

crates/libsy/src/algorithms/noop.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ use switchyard_protocol::{Context, Decision};
1717
pub struct Noop {}
1818

1919
/// Test decision carrying the inbound model or a fixed placeholder.
20-
pub struct NoopDecision {
20+
pub(crate) struct NoopDecision {
2121
model: String,
2222
}
2323

0 commit comments

Comments
 (0)