use agent_sdk::llm::{
ChatOutcome, ChatRequest, ChatResponse, ContentBlock, LlmProvider, Message, ResponseFormat,
StopReason, Usage,
};
use agent_sdk::{StructuredConfig, run_structured};
use async_trait::async_trait;
use serde::Deserialize;
use serde_json::json;
#[derive(Debug, Deserialize)]
struct Person {
name: String,
age: u32,
}
struct StubProvider {
model: String,
}
impl StubProvider {
fn new() -> Self {
Self {
model: "stub-model".to_string(),
}
}
}
#[async_trait]
impl LlmProvider for StubProvider {
async fn chat(&self, request: ChatRequest) -> anyhow::Result<ChatOutcome> {
let tool_name = request
.tools
.as_ref()
.and_then(|t| t.first())
.map_or("respond", |t| t.name.as_str())
.to_string();
Ok(ChatOutcome::Success(ChatResponse {
id: "stub".to_string(),
content: vec![ContentBlock::ToolUse {
id: "call_1".to_string(),
name: tool_name,
input: json!({ "name": "Ada Lovelace", "age": 36 }),
thought_signature: None,
}],
model: self.model().to_string(),
stop_reason: Some(StopReason::ToolUse),
usage: Usage {
input_tokens: 0,
output_tokens: 0,
cached_input_tokens: 0,
cache_creation_input_tokens: 0,
},
}))
}
fn model(&self) -> &str {
&self.model
}
fn provider(&self) -> &'static str {
"stub"
}
}
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let schema = json!({
"type": "object",
"properties": {
"name": { "type": "string" },
"age": { "type": "integer", "minimum": 0 }
},
"required": ["name", "age"],
"additionalProperties": false
});
let request = ChatRequest::new(
"Extract the person described by the user.",
vec![Message::user("Ada Lovelace is 36.")],
)
.with_response_format(ResponseFormat::new("person", schema));
let out = run_structured(&StubProvider::new(), request, StructuredConfig::default()).await?;
let person: Person = serde_json::from_value(out.value.clone())?;
println!("validated value : {}", out.value);
println!("re-prompts : {}", out.retries);
println!("typed : {} (age {})", person.name, person.age);
Ok(())
}