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 #[serde(skip_serializing_if = "Option::is_none")]
37 pub workspace_id: Option<String>,
38}
39
40#[derive(Debug, Serialize, Deserialize, ToSchema)]
42pub struct ChatResponse {
43 pub response: String,
45 pub agent: String,
47 pub context_id: String,
49 pub sources: Option<Vec<Source>>,
51}
52
53#[derive(Debug, Serialize, Deserialize, ToSchema, Clone)]
55pub struct Source {
56 pub title: String,
58 pub url: Option<String>,
60 pub relevance_score: f32,
62}
63
64#[derive(Debug, Serialize, Deserialize, ToSchema)]
66pub struct ResearchRequest {
67 pub query: String,
69 pub depth: Option<u8>,
71 pub max_iterations: Option<u8>,
73}
74
75#[derive(Debug, Serialize, Deserialize, ToSchema)]
77pub struct ResearchResponse {
78 pub findings: String,
80 pub sources: Vec<Source>,
82 pub duration_ms: u64,
84}
85
86#[derive(Debug, Serialize, Deserialize, ToSchema)]
90pub struct RagIngestRequest {
91 pub collection: String,
93 pub content: String,
95 pub title: Option<String>,
97 pub source: Option<String>,
99 #[serde(default)]
101 pub tags: Vec<String>,
102 #[serde(default)]
104 pub chunking_strategy: Option<String>,
105}
106
107#[derive(Debug, Serialize, Deserialize, ToSchema)]
109pub struct RagIngestResponse {
110 pub chunks_created: usize,
112 pub document_ids: Vec<String>,
114 pub collection: String,
116}
117
118#[derive(Debug, Serialize, Deserialize, ToSchema)]
120pub struct RagSearchRequest {
121 pub collection: String,
123 pub query: String,
125 #[serde(default = "default_search_limit")]
127 pub limit: usize,
128 #[serde(default)]
130 pub strategy: Option<String>,
131 #[serde(default = "default_search_threshold")]
133 pub threshold: f32,
134 #[serde(default)]
136 pub rerank: bool,
137 #[serde(default)]
139 pub reranker_model: Option<String>,
140}
141
142fn default_search_limit() -> usize {
143 10
144}
145
146fn default_search_threshold() -> f32 {
147 0.0
148}
149
150#[derive(Debug, Serialize, Deserialize, ToSchema)]
152pub struct RagSearchResult {
153 pub id: String,
155 pub content: String,
157 pub score: f32,
159 pub metadata: DocumentMetadata,
161}
162
163#[derive(Debug, Serialize, Deserialize, ToSchema)]
165pub struct RagSearchResponse {
166 pub results: Vec<RagSearchResult>,
168 pub total: usize,
170 pub strategy: String,
172 pub reranked: bool,
174 pub duration_ms: u64,
176}
177
178#[derive(Debug, Serialize, Deserialize, ToSchema)]
180pub struct RagDeleteCollectionRequest {
181 pub collection: String,
183}
184
185#[derive(Debug, Serialize, Deserialize, ToSchema)]
187pub struct RagDeleteCollectionResponse {
188 pub success: bool,
190 pub collection: String,
192 pub documents_deleted: usize,
194}
195
196#[derive(Debug, Serialize, Deserialize, ToSchema)]
200pub struct WorkflowRequest {
201 pub query: String,
203 #[serde(default)]
205 pub context: std::collections::HashMap<String, serde_json::Value>,
206}
207
208#[derive(Debug, Serialize, Deserialize, ToSchema, Clone, PartialEq, Eq)]
215#[serde(rename_all = "lowercase")]
216#[non_exhaustive]
217pub enum AgentType {
218 Router,
220 Orchestrator,
222 Product,
224 Invoice,
226 Sales,
228 Finance,
230 #[serde(rename = "hr")]
232 HR,
233 #[serde(untagged)]
236 Custom(String),
237}
238
239impl AgentType {
240 pub fn as_str(&self) -> &str {
242 match self {
243 AgentType::Router => "router",
244 AgentType::Orchestrator => "orchestrator",
245 AgentType::Product => "product",
246 AgentType::Invoice => "invoice",
247 AgentType::Sales => "sales",
248 AgentType::Finance => "finance",
249 AgentType::HR => "hr",
250 AgentType::Custom(name) => name,
251 }
252 }
253
254 pub fn from_string(s: &str) -> Self {
256 match s.to_lowercase().as_str() {
257 "router" => AgentType::Router,
258 "orchestrator" => AgentType::Orchestrator,
259 "product" => AgentType::Product,
260 "invoice" => AgentType::Invoice,
261 "sales" => AgentType::Sales,
262 "finance" => AgentType::Finance,
263 "hr" => AgentType::HR,
264 _ => AgentType::Custom(s.to_string()),
265 }
266 }
267
268 pub fn is_builtin(&self) -> bool {
270 !matches!(self, AgentType::Custom(_))
271 }
272}
273
274impl std::fmt::Display for AgentType {
275 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
276 write!(f, "{}", self.as_str())
277 }
278}
279
280#[derive(Debug, Clone)]
282pub struct AgentContext {
283 pub user_id: String,
285 pub session_id: String,
287 pub conversation_history: Vec<Message>,
289 pub user_memory: Option<UserMemory>,
291}
292
293#[derive(Debug, Clone, Serialize, Deserialize)]
295pub struct Message {
296 pub role: MessageRole,
298 pub content: String,
300 pub timestamp: DateTime<Utc>,
302}
303
304#[derive(Debug, Clone, Serialize, Deserialize)]
306#[serde(rename_all = "lowercase")]
307pub enum MessageRole {
308 System,
310 User,
312 Assistant,
314}
315
316#[derive(Debug, Clone, Serialize, Deserialize)]
320pub struct UserMemory {
321 pub user_id: String,
323 pub preferences: Vec<Preference>,
325 pub facts: Vec<MemoryFact>,
327}
328
329#[derive(Debug, Clone, Serialize, Deserialize)]
331pub struct Preference {
332 pub category: String,
334 pub key: String,
336 pub value: String,
338 pub confidence: f32,
340}
341
342#[derive(Debug, Clone, Serialize, Deserialize)]
344pub struct MemoryFact {
345 pub id: String,
347 pub user_id: String,
349 pub category: String,
351 pub fact_key: String,
353 pub fact_value: String,
355 pub confidence: f32,
357 pub created_at: DateTime<Utc>,
359 pub updated_at: DateTime<Utc>,
361}
362
363#[derive(Debug, Serialize, Deserialize, Clone)]
367pub struct ToolDefinition {
368 pub name: String,
370 pub description: String,
372 pub parameters: serde_json::Value,
374}
375
376#[derive(Debug, Serialize, Deserialize, Clone)]
378pub struct ToolCall {
379 pub id: String,
381 pub name: String,
383 pub arguments: serde_json::Value,
385}
386
387#[derive(Debug, Serialize, Deserialize)]
389pub struct ToolResult {
390 pub tool_call_id: String,
392 pub result: serde_json::Value,
394}
395
396#[derive(Debug, Clone, Serialize, Deserialize)]
400pub struct Document {
401 pub id: String,
403 pub content: String,
405 pub metadata: DocumentMetadata,
407 pub embedding: Option<Vec<f32>>,
409}
410
411#[derive(Debug, Clone, Default, Serialize, Deserialize, ToSchema)]
413pub struct DocumentMetadata {
414 #[serde(default)]
416 pub title: String,
417 #[serde(default)]
419 pub source: String,
420 #[serde(default = "default_datetime")]
422 pub created_at: DateTime<Utc>,
423 #[serde(default)]
425 pub tags: Vec<String>,
426}
427
428#[derive(Debug, Clone)]
430pub struct SearchQuery {
431 pub query: String,
433 pub limit: usize,
435 pub threshold: f32,
437 pub filters: Option<Vec<SearchFilter>>,
439}
440
441#[derive(Debug, Clone)]
443pub struct SearchFilter {
444 pub field: String,
446 pub value: String,
448}
449
450#[derive(Debug, Clone)]
452pub struct SearchResult {
453 pub document: Document,
455 pub score: f32,
457}
458
459#[derive(Debug, Serialize, Deserialize, ToSchema)]
463pub struct LoginRequest {
464 pub email: String,
466 pub password: String,
468}
469
470#[derive(Debug, Serialize, Deserialize, ToSchema)]
472pub struct RegisterRequest {
473 pub email: String,
475 pub password: String,
477 pub name: String,
479}
480
481#[derive(Debug, Serialize, Deserialize, ToSchema)]
483pub struct TokenResponse {
484 pub access_token: String,
486 pub refresh_token: String,
488 pub expires_in: i64,
490}
491
492#[derive(Debug, Serialize, Deserialize, Clone)]
494pub struct Claims {
495 pub sub: String,
497 pub email: String,
499 pub exp: usize,
501 pub iat: usize,
503}
504
505#[derive(Debug, Clone, Copy, Serialize)]
510#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
511pub enum ErrorCode {
512 DatabaseError,
514 LlmError,
516 AuthenticationFailed,
518 AuthorizationFailed,
520 NotFound,
522 InvalidInput,
524 ConfigurationError,
526 ExternalServiceError,
528 InternalError,
530}
531
532#[derive(Debug, thiserror::Error)]
534pub enum AppError {
535 #[error("Database error: {0}")]
537 Database(String),
538
539 #[error("LLM error: {0}")]
541 LLM(String),
542
543 #[error("Authentication error: {0}")]
545 Auth(String),
546
547 #[error("Not found: {0}")]
549 NotFound(String),
550
551 #[error("Invalid input: {0}")]
553 InvalidInput(String),
554
555 #[error("Configuration error: {0}")]
557 Configuration(String),
558
559 #[error("External service error: {0}")]
561 External(String),
562
563 #[error("Internal error: {0}")]
565 Internal(String),
566
567 #[error("Service unavailable: {0}")]
569 Unavailable(String),
570
571 #[error("Rate limited: {0}")]
573 RateLimited(String),
574}
575
576impl AppError {
577 pub fn code(&self) -> ErrorCode {
579 match self {
580 AppError::Database(_) => ErrorCode::DatabaseError,
581 AppError::LLM(_) => ErrorCode::LlmError,
582 AppError::Auth(_) => ErrorCode::AuthenticationFailed,
583 AppError::NotFound(_) => ErrorCode::NotFound,
584 AppError::InvalidInput(_) => ErrorCode::InvalidInput,
585 AppError::Configuration(_) => ErrorCode::ConfigurationError,
586 AppError::External(_) => ErrorCode::ExternalServiceError,
587 AppError::Internal(_) => ErrorCode::InternalError,
588 AppError::Unavailable(_) => ErrorCode::InternalError,
589 AppError::RateLimited(_) => ErrorCode::InternalError,
590 }
591 }
592
593 fn is_internal(&self) -> bool {
595 matches!(
596 self,
597 AppError::Database(_)
598 | AppError::LLM(_)
599 | AppError::Configuration(_)
600 | AppError::Internal(_)
601 )
602 }
603}
604
605impl From<std::io::Error> for AppError {
608 fn from(err: std::io::Error) -> Self {
609 AppError::Internal(format!("IO error: {}", err))
610 }
611}
612
613impl From<serde_json::Error> for AppError {
614 fn from(err: serde_json::Error) -> Self {
615 AppError::InvalidInput(format!("JSON error: {}", err))
616 }
617}
618
619impl axum::response::IntoResponse for AppError {
620 fn into_response(self) -> axum::response::Response {
621 if self.is_internal() {
623 tracing::error!(error = %self, code = ?self.code(), "Internal error occurred");
624 }
625
626 let (status, message) = match &self {
627 AppError::Database(msg) => (axum::http::StatusCode::INTERNAL_SERVER_ERROR, msg.clone()),
628 AppError::LLM(msg) => (axum::http::StatusCode::INTERNAL_SERVER_ERROR, msg.clone()),
629 AppError::Auth(msg) => (axum::http::StatusCode::UNAUTHORIZED, msg.clone()),
630 AppError::NotFound(msg) => (axum::http::StatusCode::NOT_FOUND, msg.clone()),
631 AppError::InvalidInput(msg) => (axum::http::StatusCode::BAD_REQUEST, msg.clone()),
632 AppError::Configuration(msg) => {
633 (axum::http::StatusCode::INTERNAL_SERVER_ERROR, msg.clone())
634 }
635 AppError::External(msg) => (axum::http::StatusCode::BAD_GATEWAY, msg.clone()),
636 AppError::Internal(msg) => (axum::http::StatusCode::INTERNAL_SERVER_ERROR, msg.clone()),
637 AppError::Unavailable(msg) => (axum::http::StatusCode::SERVICE_UNAVAILABLE, msg.clone()),
638 AppError::RateLimited(msg) => (axum::http::StatusCode::TOO_MANY_REQUESTS, msg.clone()),
639 };
640
641 let body = serde_json::json!({
642 "error": message,
643 "code": self.code()
644 });
645
646 (status, axum::Json(body)).into_response()
647 }
648}
649
650pub type Result<T> = std::result::Result<T, AppError>;