agtrace_providers/
traits.rs1use agtrace_types::{AgentEvent, ToolCallPayload, ToolKind, ToolOrigin};
2use anyhow::Result;
3use serde_json::Value;
4use std::path::{Path, PathBuf};
5
6pub trait LogDiscovery: Send + Sync {
13 fn id(&self) -> &'static str;
15
16 fn probe(&self, path: &Path) -> ProbeResult;
18
19 fn resolve_log_root(&self, project_root: &Path) -> Option<PathBuf>;
22
23 fn scan_sessions(&self, log_root: &Path) -> Result<Vec<SessionIndex>>;
25
26 fn extract_session_id(&self, path: &Path) -> Result<String>;
28
29 fn find_session_files(&self, log_root: &Path, session_id: &str) -> Result<Vec<PathBuf>>;
31}
32
33pub trait SessionParser: Send + Sync {
40 fn parse_file(&self, path: &Path) -> Result<Vec<AgentEvent>>;
42
43 fn parse_record(&self, content: &str) -> Result<Option<AgentEvent>>;
46}
47
48pub trait ToolMapper: Send + Sync {
55 fn classify(&self, tool_name: &str) -> (ToolOrigin, ToolKind);
57
58 fn normalize_call(&self, name: &str, args: Value, call_id: Option<String>) -> ToolCallPayload;
60
61 fn summarize(&self, kind: ToolKind, args: &Value) -> String;
63}
64
65#[derive(Debug, Clone, Copy, PartialEq)]
69pub enum ProbeResult {
70 Confidence(f32),
72 NoMatch,
74}
75
76impl ProbeResult {
77 pub fn match_high() -> Self {
79 ProbeResult::Confidence(1.0)
80 }
81
82 pub fn match_medium() -> Self {
84 ProbeResult::Confidence(0.5)
85 }
86
87 pub fn match_low() -> Self {
89 ProbeResult::Confidence(0.3)
90 }
91
92 pub fn is_match(&self) -> bool {
94 matches!(self, ProbeResult::Confidence(c) if *c > 0.0)
95 }
96
97 pub fn confidence(&self) -> f32 {
99 match self {
100 ProbeResult::Confidence(c) => *c,
101 ProbeResult::NoMatch => 0.0,
102 }
103 }
104}
105
106#[derive(Debug, Clone)]
115pub struct SessionIndex {
116 pub session_id: String,
117 pub timestamp: Option<String>,
118 pub latest_mod_time: Option<String>,
119 pub main_file: PathBuf,
120 pub sidechain_files: Vec<PathBuf>,
121 pub project_root: Option<String>,
122 pub snippet: Option<String>,
123}
124
125pub struct ProviderAdapter {
132 pub discovery: Box<dyn LogDiscovery>,
133 pub parser: Box<dyn SessionParser>,
134 pub mapper: Box<dyn ToolMapper>,
135}
136
137impl ProviderAdapter {
138 pub fn new(
139 discovery: Box<dyn LogDiscovery>,
140 parser: Box<dyn SessionParser>,
141 mapper: Box<dyn ToolMapper>,
142 ) -> Self {
143 Self {
144 discovery,
145 parser,
146 mapper,
147 }
148 }
149
150 pub fn from_name(provider_name: &str) -> Result<Self> {
152 match provider_name {
153 "claude_code" | "claude" => Ok(Self::claude()),
154 "codex" => Ok(Self::codex()),
155 "gemini" => Ok(Self::gemini()),
156 _ => anyhow::bail!("Unknown provider: {}", provider_name),
157 }
158 }
159
160 pub fn claude() -> Self {
162 Self::new(
163 Box::new(crate::claude::ClaudeDiscovery),
164 Box::new(crate::claude::ClaudeParser),
165 Box::new(crate::claude::ClaudeToolMapper),
166 )
167 }
168
169 pub fn codex() -> Self {
171 Self::new(
172 Box::new(crate::codex::CodexDiscovery),
173 Box::new(crate::codex::CodexParser),
174 Box::new(crate::codex::CodexToolMapper),
175 )
176 }
177
178 pub fn gemini() -> Self {
180 Self::new(
181 Box::new(crate::gemini::GeminiDiscovery),
182 Box::new(crate::gemini::GeminiParser),
183 Box::new(crate::gemini::GeminiToolMapper),
184 )
185 }
186
187 pub fn id(&self) -> &'static str {
189 self.discovery.id()
190 }
191
192 pub fn process_file(&self, path: &Path) -> Result<Vec<AgentEvent>> {
194 if !self.discovery.probe(path).is_match() {
195 anyhow::bail!(
196 "Provider {} cannot handle file: {}",
197 self.id(),
198 path.display()
199 );
200 }
201 self.parser.parse_file(path)
202 }
203}
204
205pub fn get_latest_mod_time_rfc3339(files: &[&std::path::Path]) -> Option<String> {
212 use chrono::{DateTime, Utc};
213 use std::time::SystemTime;
214
215 let mut latest: Option<SystemTime> = None;
216
217 for path in files {
218 if let Ok(metadata) = std::fs::metadata(path)
219 && let Ok(modified) = metadata.modified()
220 && (latest.is_none() || Some(modified) > latest) {
221 latest = Some(modified);
222 }
223 }
224
225 latest.map(|t| {
226 let dt: DateTime<Utc> = t.into();
227 dt.to_rfc3339()
228 })
229}