use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ToolSpec {
pub name: String,
pub description: String,
pub schema: serde_json::Value,
pub strict: bool,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ProviderToolCall {
pub call_id: String,
pub name: String,
pub arguments: serde_json::Value,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum TurnOutput {
ToolCalls(Vec<ProviderToolCall>),
Text(String),
}
impl TurnOutput {
pub fn tool_calls(&self) -> &[ProviderToolCall] {
match self {
TurnOutput::ToolCalls(calls) => calls,
TurnOutput::Text(_) => &[],
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum ToolChoicePolicy {
Auto,
None,
Required,
Specific(String),
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn a_text_turn_has_no_tool_calls() {
assert!(TurnOutput::Text("done".into()).tool_calls().is_empty());
}
#[test]
fn tool_calls_round_trip_through_serde() {
let turn = TurnOutput::ToolCalls(vec![ProviderToolCall {
call_id: "c1".into(),
name: "read_file".into(),
arguments: serde_json::json!({"path": "src/lib.rs"}),
}]);
let json = serde_json::to_string(&turn).unwrap();
let back: TurnOutput = serde_json::from_str(&json).unwrap();
assert_eq!(back, turn);
assert_eq!(back.tool_calls().len(), 1);
}
}