1use chrono::{DateTime, Utc};
13use serde::{Deserialize, Serialize};
14use utoipa::ToSchema;
15
16fn default_datetime() -> DateTime<Utc> {
18 Utc::now()
19}
20
21#[derive(Debug, Serialize, Deserialize, ToSchema)]
25pub struct ChatRequest {
26 pub message: String,
28 #[serde(skip_serializing_if = "Option::is_none")]
30 pub agent_type: Option<AgentType>,
31 #[serde(skip_serializing_if = "Option::is_none")]
33 pub context_id: Option<String>,
34}
35
36#[derive(Debug, Serialize, Deserialize, ToSchema)]
38pub struct ChatResponse {
39 pub response: String,
41 pub agent: String,
43 pub context_id: String,
45 pub sources: Option<Vec<Source>>,
47}
48
49#[derive(Debug, Serialize, Deserialize, ToSchema, Clone)]
51pub struct Source {
52 pub title: String,
54 pub url: Option<String>,
56 pub relevance_score: f32,
58}
59
60#[derive(Debug, Serialize, Deserialize, ToSchema)]
62pub struct ResearchRequest {
63 pub query: String,
65 pub depth: Option<u8>,
67 pub max_iterations: Option<u8>,
69}
70
71#[derive(Debug, Serialize, Deserialize, ToSchema)]
73pub struct ResearchResponse {
74 pub findings: String,
76 pub sources: Vec<Source>,
78 pub duration_ms: u64,
80}
81
82#[derive(Debug, Serialize, Deserialize, ToSchema)]
86pub struct RagIngestRequest {
87 pub collection: String,
89 pub content: String,
91 pub title: Option<String>,
93 pub source: Option<String>,
95 #[serde(default)]
97 pub tags: Vec<String>,
98 #[serde(default)]
100 pub chunking_strategy: Option<String>,
101}
102
103#[derive(Debug, Serialize, Deserialize, ToSchema)]
105pub struct RagIngestResponse {
106 pub chunks_created: usize,
108 pub document_ids: Vec<String>,
110 pub collection: String,
112}
113
114#[derive(Debug, Serialize, Deserialize, ToSchema)]
116pub struct RagSearchRequest {
117 pub collection: String,
119 pub query: String,
121 #[serde(default = "default_search_limit")]
123 pub limit: usize,
124 #[serde(default)]
126 pub strategy: Option<String>,
127 #[serde(default = "default_search_threshold")]
129 pub threshold: f32,
130 #[serde(default)]
132 pub rerank: bool,
133 #[serde(default)]
135 pub reranker_model: Option<String>,
136}
137
138fn default_search_limit() -> usize {
139 10
140}
141
142fn default_search_threshold() -> f32 {
143 0.0
144}
145
146#[derive(Debug, Serialize, Deserialize, ToSchema)]
148pub struct RagSearchResult {
149 pub id: String,
151 pub content: String,
153 pub score: f32,
155 pub metadata: DocumentMetadata,
157}
158
159#[derive(Debug, Serialize, Deserialize, ToSchema)]
161pub struct RagSearchResponse {
162 pub results: Vec<RagSearchResult>,
164 pub total: usize,
166 pub strategy: String,
168 pub reranked: bool,
170 pub duration_ms: u64,
172}
173
174#[derive(Debug, Serialize, Deserialize, ToSchema)]
176pub struct RagDeleteCollectionRequest {
177 pub collection: String,
179}
180
181#[derive(Debug, Serialize, Deserialize, ToSchema)]
183pub struct RagDeleteCollectionResponse {
184 pub success: bool,
186 pub collection: String,
188 pub documents_deleted: usize,
190}
191
192#[derive(Debug, Serialize, Deserialize, ToSchema)]
196pub struct WorkflowRequest {
197 pub query: String,
199 #[serde(default)]
201 pub context: std::collections::HashMap<String, serde_json::Value>,
202}
203
204#[derive(Debug, Serialize, Deserialize, ToSchema, Clone, PartialEq, Eq)]
211#[serde(rename_all = "lowercase")]
212#[non_exhaustive]
213pub enum AgentType {
214 Router,
216 Orchestrator,
218 Product,
220 Invoice,
222 Sales,
224 Finance,
226 #[serde(rename = "hr")]
228 HR,
229 #[serde(untagged)]
232 Custom(String),
233}
234
235impl AgentType {
236 pub fn as_str(&self) -> &str {
238 match self {
239 AgentType::Router => "router",
240 AgentType::Orchestrator => "orchestrator",
241 AgentType::Product => "product",
242 AgentType::Invoice => "invoice",
243 AgentType::Sales => "sales",
244 AgentType::Finance => "finance",
245 AgentType::HR => "hr",
246 AgentType::Custom(name) => name,
247 }
248 }
249
250 pub fn from_string(s: &str) -> Self {
252 match s.to_lowercase().as_str() {
253 "router" => AgentType::Router,
254 "orchestrator" => AgentType::Orchestrator,
255 "product" => AgentType::Product,
256 "invoice" => AgentType::Invoice,
257 "sales" => AgentType::Sales,
258 "finance" => AgentType::Finance,
259 "hr" => AgentType::HR,
260 _ => AgentType::Custom(s.to_string()),
261 }
262 }
263
264 pub fn is_builtin(&self) -> bool {
266 !matches!(self, AgentType::Custom(_))
267 }
268}
269
270impl std::fmt::Display for AgentType {
271 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
272 write!(f, "{}", self.as_str())
273 }
274}
275
276#[derive(Debug, Clone)]
278pub struct AgentContext {
279 pub user_id: String,
281 pub session_id: String,
283 pub conversation_history: Vec<Message>,
285 pub user_memory: Option<UserMemory>,
287}
288
289#[derive(Debug, Clone, Serialize, Deserialize)]
291pub struct Message {
292 pub role: MessageRole,
294 pub content: String,
296 pub timestamp: DateTime<Utc>,
298}
299
300#[derive(Debug, Clone, Serialize, Deserialize)]
302#[serde(rename_all = "lowercase")]
303pub enum MessageRole {
304 System,
306 User,
308 Assistant,
310}
311
312#[derive(Debug, Clone, Serialize, Deserialize)]
316pub struct UserMemory {
317 pub user_id: String,
319 pub preferences: Vec<Preference>,
321 pub facts: Vec<MemoryFact>,
323}
324
325#[derive(Debug, Clone, Serialize, Deserialize)]
327pub struct Preference {
328 pub category: String,
330 pub key: String,
332 pub value: String,
334 pub confidence: f32,
336}
337
338#[derive(Debug, Clone, Serialize, Deserialize)]
340pub struct MemoryFact {
341 pub id: String,
343 pub user_id: String,
345 pub category: String,
347 pub fact_key: String,
349 pub fact_value: String,
351 pub confidence: f32,
353 pub created_at: DateTime<Utc>,
355 pub updated_at: DateTime<Utc>,
357}
358
359#[derive(Debug, Serialize, Deserialize, Clone)]
363pub struct ToolDefinition {
364 pub name: String,
366 pub description: String,
368 pub parameters: serde_json::Value,
370}
371
372#[derive(Debug, Serialize, Deserialize, Clone)]
374pub struct ToolCall {
375 pub id: String,
377 pub name: String,
379 pub arguments: serde_json::Value,
381}
382
383#[derive(Debug, Serialize, Deserialize)]
385pub struct ToolResult {
386 pub tool_call_id: String,
388 pub result: serde_json::Value,
390}
391
392#[derive(Debug, Clone, Serialize, Deserialize)]
396pub struct Document {
397 pub id: String,
399 pub content: String,
401 pub metadata: DocumentMetadata,
403 pub embedding: Option<Vec<f32>>,
405}
406
407#[derive(Debug, Clone, Default, Serialize, Deserialize, ToSchema)]
409pub struct DocumentMetadata {
410 #[serde(default)]
412 pub title: String,
413 #[serde(default)]
415 pub source: String,
416 #[serde(default = "default_datetime")]
418 pub created_at: DateTime<Utc>,
419 #[serde(default)]
421 pub tags: Vec<String>,
422}
423
424#[derive(Debug, Clone)]
426pub struct SearchQuery {
427 pub query: String,
429 pub limit: usize,
431 pub threshold: f32,
433 pub filters: Option<Vec<SearchFilter>>,
435}
436
437#[derive(Debug, Clone)]
439pub struct SearchFilter {
440 pub field: String,
442 pub value: String,
444}
445
446#[derive(Debug, Clone)]
448pub struct SearchResult {
449 pub document: Document,
451 pub score: f32,
453}
454
455#[derive(Debug, Serialize, Deserialize, ToSchema)]
459pub struct LoginRequest {
460 pub email: String,
462 pub password: String,
464}
465
466#[derive(Debug, Serialize, Deserialize, ToSchema)]
468pub struct RegisterRequest {
469 pub email: String,
471 pub password: String,
473 pub name: String,
475}
476
477#[derive(Debug, Serialize, Deserialize, ToSchema)]
479pub struct TokenResponse {
480 pub access_token: String,
482 pub refresh_token: String,
484 pub expires_in: i64,
486}
487
488#[derive(Debug, Serialize, Deserialize, Clone)]
490pub struct Claims {
491 pub sub: String,
493 pub email: String,
495 pub exp: usize,
497 pub iat: usize,
499}
500
501#[derive(Debug, Clone, Copy, Serialize)]
506#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
507pub enum ErrorCode {
508 DatabaseError,
510 LlmError,
512 AuthenticationFailed,
514 AuthorizationFailed,
516 NotFound,
518 InvalidInput,
520 ConfigurationError,
522 ExternalServiceError,
524 InternalError,
526}
527
528#[derive(Debug, thiserror::Error)]
530pub enum AppError {
531 #[error("Database error: {0}")]
533 Database(String),
534
535 #[error("LLM error: {0}")]
537 LLM(String),
538
539 #[error("Authentication error: {0}")]
541 Auth(String),
542
543 #[error("Not found: {0}")]
545 NotFound(String),
546
547 #[error("Invalid input: {0}")]
549 InvalidInput(String),
550
551 #[error("Configuration error: {0}")]
553 Configuration(String),
554
555 #[error("External service error: {0}")]
557 External(String),
558
559 #[error("Internal error: {0}")]
561 Internal(String),
562}
563
564impl AppError {
565 pub fn code(&self) -> ErrorCode {
567 match self {
568 AppError::Database(_) => ErrorCode::DatabaseError,
569 AppError::LLM(_) => ErrorCode::LlmError,
570 AppError::Auth(_) => ErrorCode::AuthenticationFailed,
571 AppError::NotFound(_) => ErrorCode::NotFound,
572 AppError::InvalidInput(_) => ErrorCode::InvalidInput,
573 AppError::Configuration(_) => ErrorCode::ConfigurationError,
574 AppError::External(_) => ErrorCode::ExternalServiceError,
575 AppError::Internal(_) => ErrorCode::InternalError,
576 }
577 }
578
579 fn is_internal(&self) -> bool {
581 matches!(
582 self,
583 AppError::Database(_)
584 | AppError::LLM(_)
585 | AppError::Configuration(_)
586 | AppError::Internal(_)
587 )
588 }
589}
590
591impl From<std::io::Error> for AppError {
594 fn from(err: std::io::Error) -> Self {
595 AppError::Internal(format!("IO error: {}", err))
596 }
597}
598
599impl From<serde_json::Error> for AppError {
600 fn from(err: serde_json::Error) -> Self {
601 AppError::InvalidInput(format!("JSON error: {}", err))
602 }
603}
604
605impl axum::response::IntoResponse for AppError {
606 fn into_response(self) -> axum::response::Response {
607 if self.is_internal() {
609 tracing::error!(error = %self, code = ?self.code(), "Internal error occurred");
610 }
611
612 let (status, message) = match &self {
613 AppError::Database(msg) => (axum::http::StatusCode::INTERNAL_SERVER_ERROR, msg.clone()),
614 AppError::LLM(msg) => (axum::http::StatusCode::INTERNAL_SERVER_ERROR, msg.clone()),
615 AppError::Auth(msg) => (axum::http::StatusCode::UNAUTHORIZED, msg.clone()),
616 AppError::NotFound(msg) => (axum::http::StatusCode::NOT_FOUND, msg.clone()),
617 AppError::InvalidInput(msg) => (axum::http::StatusCode::BAD_REQUEST, msg.clone()),
618 AppError::Configuration(msg) => {
619 (axum::http::StatusCode::INTERNAL_SERVER_ERROR, msg.clone())
620 }
621 AppError::External(msg) => (axum::http::StatusCode::BAD_GATEWAY, msg.clone()),
622 AppError::Internal(msg) => (axum::http::StatusCode::INTERNAL_SERVER_ERROR, msg.clone()),
623 };
624
625 let body = serde_json::json!({
626 "error": message,
627 "code": self.code()
628 });
629
630 (status, axum::Json(body)).into_response()
631 }
632}
633
634pub type Result<T> = std::result::Result<T, AppError>;