use std::collections::BTreeMap;
use std::time::Duration;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct Transcript {
pub prompt: String,
#[serde(default)]
pub final_output: String,
#[serde(default)]
pub tool_calls: Vec<ToolCall>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub usage: Option<Usage>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub elapsed: Option<Duration>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub turns: Option<usize>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub skill_invoked: Option<String>,
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
pub metadata: BTreeMap<String, String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ToolCall {
pub name: String,
#[serde(default)]
pub arguments: serde_json::Value,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub ok: Option<bool>,
}
impl ToolCall {
pub fn new(name: impl Into<String>) -> Self {
Self {
name: name.into(),
arguments: serde_json::Value::Null,
ok: None,
}
}
}
#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize)]
pub struct Usage {
#[serde(default)]
pub input_tokens: u64,
#[serde(default)]
pub output_tokens: u64,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub cost_usd: Option<f64>,
}
impl Usage {
#[must_use]
pub fn total_tokens(&self) -> u64 {
self.input_tokens.saturating_add(self.output_tokens)
}
}