1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
//! Structured output example: typed output (run_typed) and hand-written
//! schema forms.
//!
//! Run: `cargo run --example structured` (uses FakeProvider, no real API
//! needed). Real-endpoint integration is in the comment at the end
//! (OpenAI-compatible, including the transport-mode fallback switch).
use molo::{Agent, FakeProvider, FakeReply, ReActAgent};
use schemars::JsonSchema;
use serde::Deserialize;
use serde_json::json;
/// The target output type: `schemars` derives generate the JSON Schema
/// (regenerated automatically on every run_typed), and `serde` derives handle
/// deserialization — the same pipeline as tool argument schemas.
#[derive(Debug, Deserialize, JsonSchema)]
struct Weather {
city: String,
#[schemars(description = "Temperature in Celsius")]
temperature: f64,
#[schemars(description = "Weather condition")]
condition: String,
}
#[tokio::main]
async fn main() -> Result<(), molo::AgentError> {
// Form 1: typed output — the return type is declared in the let annotation, and this run
// generates the schema from Weather automatically; on validation failure the error is fed
// back to the model for retry (separate budget, 3 attempts by default).
let mut agent = ReActAgent::new(
FakeProvider::new([FakeReply::Text(
r#"{"city":"Beijing","temperature":31.5,"condition":"Sunny"}"#.into(),
)]),
molo::tool::ToolRegistry::new(),
"You are a weather assistant; output JSON only",
);
let weather: Weather = agent
.run_typed("How is the weather in Beijing today")
.await?;
println!(
"typed output: city = {}, temperature = {}°C, condition = {}",
weather.city, weather.temperature, weather.condition
);
// Form 2: hand-written schema — run returns JSON text, and structural
// constraints are still validated framework-side (replies not matching the
// schema are fed back to the model for retry).
let mut agent = ReActAgent::new(
FakeProvider::new([FakeReply::Text(r#"{"city":"Shanghai"}"#.into())]),
molo::tool::ToolRegistry::new(),
"You are a weather assistant; output JSON only",
)
.with_structured_output(json!({
"type": "object",
"properties": { "city": { "type": "string" } },
"required": ["city"],
}));
let answer = agent.run("How is the weather in Shanghai").await?;
println!("hand-written schema: {answer}");
// Form 3: real endpoints (OpenAI / DeepSeek / Zhipu / Moonshot / Ollama compatible).
// The transport mode is configurable: Native (default, strict json_schema mode) /
// JsonObject (fallback, only constrains "is JSON") / Off (no response_format sent) —
// structural consistency is always guaranteed by framework-side validation.
//
// let provider = molo::OpenAiProvider::new(
// "https://api.openai.com/v1",
// "sk-...",
// "gpt-4o-mini",
// )
// .with_structured_output_mode(molo::StructuredOutputMode::Native);
// let mut agent = ReActAgent::new(provider, molo::tool::ToolRegistry::new(),
// "You are a weather assistant; output JSON only");
// let weather: Weather = agent.run_typed("How is the weather in Beijing today").await?;
Ok(())
}