1use std::sync::mpsc;
2use std::sync::Arc;
3
4use crate::config::{resolve_provider_key, resolve_provider_name, Config};
5use serde::{Deserialize, Serialize};
6
7mod anthropic;
8mod openai;
9mod openrouter;
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
12pub enum Role {
13 System,
14 User,
15 Assistant,
16}
17
18#[derive(Debug, Clone, Serialize, Deserialize)]
19pub struct Message {
20 pub role: Role,
21 pub content: String,
22}
23
24#[derive(Debug, Clone)]
25pub enum ApiResponse {
26 TokenChunk { token: String },
27 TurnComplete,
28 Error { message: String },
29 Exit,
30}
31
32pub trait Provider: Send + Sync + 'static {
33 fn stream(&self, messages: &[Message], tx: mpsc::Sender<ApiResponse>);
34 fn handle_command(&self, _cmd: &str, _tx: mpsc::Sender<ApiResponse>) {}
37
38 fn help_lines(&self) -> &[&str] {
41 &[]
42 }
43}
44
45pub enum ProviderKind {
46 Anthropic,
47 OpenAi,
48 OpenRouter,
49}
50
51pub struct ProviderResolved {
52 pub kind: ProviderKind,
53 pub api_key: String,
54 pub model: String,
55}
56
57impl ProviderResolved {
58 pub fn kind_name(&self) -> &'static str {
59 match self.kind {
60 ProviderKind::Anthropic => "anthropic",
61 ProviderKind::OpenAi => "openai",
62 ProviderKind::OpenRouter => "openrouter",
63 }
64 }
65}
66
67pub fn resolve_provider(cfg: &Config) -> Option<ProviderResolved> {
68 let name = resolve_provider_name(cfg)?;
69 let api_key = resolve_provider_key(cfg)?;
70 let model = cfg
71 .provider
72 .as_ref()
73 .and_then(|p| p.model.clone())
74 .unwrap_or_else(|| {
75 match name.as_str() {
76 "anthropic" => "claude-sonnet-4-6",
77 "openai" => "gpt-4o-mini",
78 "openrouter" => "openai/gpt-4o-mini",
79 _ => "default",
80 }
81 .to_string()
82 });
83
84 let kind = match name.as_str() {
85 "anthropic" => ProviderKind::Anthropic,
86 "openai" => ProviderKind::OpenAi,
87 "openrouter" => ProviderKind::OpenRouter,
88 _ => return None,
89 };
90
91 Some(ProviderResolved { kind, api_key, model })
92}
93
94pub fn build_provider(cfg: &Config) -> Option<Arc<dyn Provider>> {
95 let resolved = resolve_provider(cfg)?;
96 let provider: Arc<dyn Provider> = match resolved.kind {
97 ProviderKind::Anthropic => Arc::new(anthropic::AnthropicProvider {
98 api_key: resolved.api_key,
99 model: resolved.model,
100 }),
101 ProviderKind::OpenAi => Arc::new(openai::OpenAiProvider {
102 api_key: resolved.api_key,
103 model: resolved.model,
104 }),
105 ProviderKind::OpenRouter => Arc::new(openrouter::OpenRouterProvider {
106 api_key: resolved.api_key,
107 model: resolved.model,
108 }),
109 };
110 Some(provider)
111}