1use serde::{Deserialize, Serialize};
4use thiserror::Error;
5
6pub type AIResult<T> = Result<T, AIError>;
8
9#[derive(Debug, Error)]
11pub enum AIError {
12 #[error("API error: {0}")]
13 ApiError(String),
14
15 #[error("Configuration error: {0}")]
16 ConfigError(String),
17
18 #[error("Network error: {0}")]
19 NetworkError(#[from] reqwest::Error),
20
21 #[error("Rate limit exceeded")]
22 RateLimitExceeded,
23
24 #[error("Invalid API key")]
25 InvalidApiKey,
26
27 #[error("Model not available: {0}")]
28 ModelNotAvailable(String),
29
30 #[error("Unknown error: {0}")]
31 Unknown(String),
32}
33
34#[derive(Debug, Clone, Serialize, Deserialize)]
36pub enum AIConfig {
37 Anthropic {
39 api_key: String,
40 model: String,
41 },
42
43 OpenAI {
45 api_key: String,
46 model: String,
47 },
48
49 Local {
51 endpoint: String,
52 model: Option<String>,
53 },
54
55 Managed {
57 auth_token: String,
58 },
59
60 ClaudeCLI {
62 model: Option<String>,
63 },
64
65 Disabled,
67}
68
69impl Default for AIConfig {
70 fn default() -> Self {
71 Self::Disabled
72 }
73}
74
75#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
77#[serde(rename_all = "lowercase")]
78pub enum Role {
79 System,
80 User,
81 Assistant,
82}
83
84#[derive(Debug, Clone, Serialize, Deserialize)]
86pub struct Message {
87 pub role: Role,
88 pub content: String,
89}
90
91impl Message {
92 pub fn system(content: impl Into<String>) -> Self {
93 Self {
94 role: Role::System,
95 content: content.into(),
96 }
97 }
98
99 pub fn user(content: impl Into<String>) -> Self {
100 Self {
101 role: Role::User,
102 content: content.into(),
103 }
104 }
105
106 pub fn assistant(content: impl Into<String>) -> Self {
107 Self {
108 role: Role::Assistant,
109 content: content.into(),
110 }
111 }
112}
113
114#[derive(Debug, Clone, Serialize, Deserialize)]
116pub struct CompletionOptions {
117 pub temperature: f32,
118 pub max_tokens: usize,
119 pub stream: bool,
120}
121
122impl Default for CompletionOptions {
123 fn default() -> Self {
124 Self {
125 temperature: 0.7,
126 max_tokens: 1000,
127 stream: false,
128 }
129 }
130}