1pub mod anthropic;
7pub mod mistral;
8pub mod openai;
9
10use crate::data::Trace;
11use crate::errors::TramexError;
12
13#[derive(Debug, Clone)]
15pub struct AIRequest {
16 pub url: String,
18 pub headers: Vec<(String, String)>,
20 pub body: String,
22}
23
24#[derive(Debug, Clone, Default)]
26pub enum AIExplainStatus {
27 #[default]
29 Idle,
30 Loading,
32 Done(String),
34 Error(String),
36}
37
38pub trait AIConnector: Send + Sync {
43 fn name(&self) -> &'static str;
45
46 fn build_request(&self, trace: &Trace, api_key: &str) -> Result<AIRequest, TramexError>;
55
56 fn parse_response(&self, response_body: &str) -> Result<String, TramexError>;
64}
65
66#[derive(Default, Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
68pub enum AIProvider {
69 #[default]
71 Mistral,
72 OpenAI,
74 Anthropic,
76}
77
78impl std::fmt::Display for AIProvider {
79 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
80 match self {
81 Self::Mistral => write!(f, "Mistral"),
82 Self::OpenAI => write!(f, "OpenAI"),
83 Self::Anthropic => write!(f, "Anthropic"),
84 }
85 }
86}
87
88impl AIProvider {
89 pub fn all() -> &'static [AIProvider] {
91 &[AIProvider::Mistral, AIProvider::OpenAI, AIProvider::Anthropic]
92 }
93
94 pub fn available_models(&self) -> &'static [(&'static str, &'static str)] {
96 match self {
97 AIProvider::Mistral => &[
98 ("Small", "mistral-small-latest"),
99 ("Medium", "mistral-medium-latest"),
100 ("Large", "mistral-large-latest"),
101 ],
102 AIProvider::OpenAI => &[
103 ("GPT-4o mini", "gpt-4o-mini"),
104 ("GPT-4o", "gpt-4o"),
105 ("GPT-4.1", "gpt-4.1"),
106 ("GPT-5.3", "gpt-5.3"),
107 ("GPT-5.5", "gpt-5.5"),
108 ],
109 AIProvider::Anthropic => &[
110 ("Claude 3.5 Haiku", "claude-3-5-haiku-latest"),
111 ("Claude 3.5 Sonnet", "claude-3-5-sonnet-latest"),
112 ("Claude 3 Opus", "claude-3-opus-latest"),
113 ("Claude 4 Opus", "claude-4-opus-latest"),
114 ("Claude 5 Opus", "claude-5-opus-latest"),
115 ],
116 }
117 }
118
119 pub fn default_model(&self) -> &'static str {
121 match self {
122 AIProvider::Mistral => "mistral-medium-latest",
123 AIProvider::OpenAI => "gpt-4o-mini",
124 AIProvider::Anthropic => "claude-3-5-haiku-latest",
125 }
126 }
127}
128
129pub fn create_connector(provider: &AIProvider, model: &str) -> Box<dyn AIConnector> {
131 match provider {
132 AIProvider::Mistral => Box::new(mistral::MistralConnector::with_model(model)),
133 AIProvider::OpenAI => Box::new(openai::OpenAIConnector::with_model(model)),
134 AIProvider::Anthropic => Box::new(anthropic::AnthropicConnector::with_model(model)),
135 }
136}