diff --git a/Cargo.lock b/Cargo.lock index 52b6bbe..090501c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7,7 +7,6 @@ name = "OmegaCode" version = "0.1.0-alpha" dependencies = [ "anyhow", - "async-trait", "bytes", "chrono", "clap", @@ -37,7 +36,6 @@ dependencies = [ "thiserror 2.0.18", "tokio", "tokio-stream", - "tracing", "zip", ] diff --git a/Cargo.toml b/Cargo.toml index a25a366..b1c3450 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -41,6 +41,4 @@ thiserror = "2.0.18" hex = "0.4.3" dotenv = "0.15.0" bytes = "1.11.1" -rand = "0.10.1" -tracing = "0.1.44" -async-trait = "0.1.89" \ No newline at end of file +rand = "0.10.1" \ No newline at end of file diff --git a/src/core/action/mod.rs b/src/core/agent/mod.rs similarity index 100% rename from src/core/action/mod.rs rename to src/core/agent/mod.rs diff --git a/src/core/chat/common/call.rs b/src/core/chat/common/call.rs deleted file mode 100644 index 39f5b99..0000000 --- a/src/core/chat/common/call.rs +++ /dev/null @@ -1,355 +0,0 @@ -// ============================================================ -// ai/call/mod.rs -// ============================================================ - -use anyhow::Result; -use async_trait::async_trait; -use bytes::Bytes; -use futures_util::stream::BoxStream; -use serde::{Deserialize, Serialize}; -use serde_json::Value; - -// ============================================================ -// Role -// ============================================================ - -#[derive(Debug, Clone, Serialize, Deserialize)] -#[serde(rename_all = "lowercase")] -pub enum Role { - System, - User, - Assistant, - Tool, -} - -// ============================================================ -// Message -// ============================================================ - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct Message { - pub role: Role, - - pub content: Vec, -} - -// ============================================================ -// Content Part -// ============================================================ - -#[derive(Debug, Clone, Serialize, Deserialize)] -#[serde(tag = "type")] -pub enum ContentPart { - #[serde(rename = "text")] - Text { - text: String, - }, - - #[serde(rename = "image")] - Image { - url: String, - }, - - #[serde(rename = "thinking")] - Thinking { - text: String, - }, - - #[serde(rename = "tool_call")] - ToolCall { - id: String, - - name: String, - - arguments: Value, - }, - - #[serde(rename = "tool_result")] - ToolResult { - tool_call_id: String, - - content: String, - }, -} - -// ============================================================ -// Tool -// ============================================================ - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct ToolDefinition { - pub name: String, - - pub description: Option, - - pub parameters: Value, -} - -// ============================================================ -// Reasoning Config -// ============================================================ - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct ReasoningConfig { - pub enabled: bool, - - pub budget_tokens: Option, -} - -// ============================================================ -// Request -// ============================================================ - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct LlmRequest { - pub model: String, - - pub messages: Vec, - - #[serde(skip_serializing_if = "Option::is_none")] - pub temperature: Option, - - #[serde(skip_serializing_if = "Option::is_none")] - pub top_p: Option, - - #[serde(skip_serializing_if = "Option::is_none")] - pub max_tokens: Option, - - #[serde(default)] - pub stream: bool, - - #[serde(default)] - pub tools: Vec, - - #[serde(skip_serializing_if = "Option::is_none")] - pub reasoning: Option, - - #[serde(default)] - pub metadata: Value, -} - -// ============================================================ -// Usage -// ============================================================ - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct TokenUsage { - pub prompt_tokens: u32, - - pub completion_tokens: u32, - - pub total_tokens: u32, - - #[serde(skip_serializing_if = "Option::is_none")] - pub reasoning_tokens: Option, -} - -// ============================================================ -// Response -// ============================================================ - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct LlmResponse { - pub model: String, - - pub message: Message, - - #[serde(skip_serializing_if = "Option::is_none")] - pub usage: Option, - - #[serde(skip_serializing_if = "Option::is_none")] - pub finish_reason: Option, - - #[serde(default)] - pub metadata: Value, -} - -// ============================================================ -// Tool Call Event -// ============================================================ - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct ToolCallEvent { - pub id: String, - - pub name: String, - - pub arguments: Value, -} - -// ============================================================ -// Tool Result Event -// ============================================================ - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct ToolResultEvent { - pub tool_call_id: String, - - pub content: String, -} - -// ============================================================ -// Stream Event -// ============================================================ - -#[derive(Debug, Clone)] -pub enum LlmEvent { - Text(String), - - Reasoning(String), - - ToolCall(ToolCallEvent), - - ToolResult(ToolResultEvent), - - Usage(TokenUsage), - - Metadata(Value), - - Binary(Bytes), - - Done, -} - -// ============================================================ -// Stream Type -// ============================================================ - -pub type LlmStream = -BoxStream<'static, Result>; - -// ============================================================ -// Provider Trait -// ============================================================ - -#[async_trait] -pub trait LlmProvider: -Send + Sync + 'static -{ - /// provider name - fn name(&self) -> &'static str; - - /// supported models - async fn models(&self) -> Result>; - - /// supports reasoning - fn supports_reasoning(&self) -> bool { - false - } - - /// supports tools - fn supports_tools(&self) -> bool { - false - } - - /// supports stream - fn supports_stream(&self) -> bool { - true - } - - /// normal call - async fn call( - &self, - request: LlmRequest, - ) -> Result; - - /// stream call - async fn stream( - &self, - request: LlmRequest, - ) -> Result; -} - -// ============================================================ -// Provider Registry -// ============================================================ - -use std::collections::HashMap; -use std::sync::Arc; - -pub struct ProviderRegistry { - providers: - HashMap>, -} - -impl ProviderRegistry { - pub fn new() -> Self { - Self { - providers: HashMap::new(), - } - } - - pub fn register

( - mut self, - provider: P, - ) -> Self - where - P: LlmProvider, - { - self.providers.insert( - provider.name().to_string(), - Arc::new(provider), - ); - - self - } - - pub fn get( - &self, - name: &str, - ) -> Option> { - self.providers.get(name).cloned() - } -} - -// ============================================================ -// Call Manager -// ============================================================ - -pub struct CallManager { - registry: ProviderRegistry, -} - -impl CallManager { - pub fn new( - registry: ProviderRegistry, - ) -> Self { - Self { registry } - } - - pub async fn call( - &self, - provider: &str, - request: LlmRequest, - ) -> Result { - let provider = self - .registry - .get(provider) - .ok_or_else(|| { - anyhow::anyhow!( - "provider not found: {}", - provider - ) - })?; - - provider.call(request).await - } - - pub async fn stream( - &self, - provider: &str, - request: LlmRequest, - ) -> Result { - let provider = self - .registry - .get(provider) - .ok_or_else(|| { - anyhow::anyhow!( - "provider not found: {}", - provider - ) - })?; - - provider.stream(request).await - } -} \ No newline at end of file diff --git a/src/core/chat/common/mod.rs b/src/core/chat/common/mod.rs deleted file mode 100644 index 7d2e8c6..0000000 --- a/src/core/chat/common/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -mod call; -mod request; \ No newline at end of file diff --git a/src/core/chat/mod.rs b/src/core/chat/mod.rs deleted file mode 100644 index 18f4556..0000000 --- a/src/core/chat/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -mod client; -mod provider; -mod common; \ No newline at end of file diff --git a/src/core/chat/provider/deepseek/enums.rs b/src/core/chat/provider/deepseek/enums.rs deleted file mode 100644 index e241678..0000000 --- a/src/core/chat/provider/deepseek/enums.rs +++ /dev/null @@ -1,38 +0,0 @@ -use serde::{Deserialize, Serialize}; - -#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)] -#[serde(rename_all = "snake_case")] -pub enum Role { - System, - User, - Assistant, - Tool, -} - -#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)] -#[serde(rename_all = "snake_case")] -pub enum ToolType { - Function, -} - -#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)] -#[serde(rename_all = "snake_case")] -pub enum ResponseFormatType { - Text, - JsonObject, -} - -#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)] -#[serde(rename_all = "snake_case")] -pub enum FinishReason { - Stop, - Length, - ToolCalls, - ContentFilter, -} - -impl Default for ResponseFormatType { - fn default() -> Self { - Self::Text - } -} \ No newline at end of file diff --git a/src/core/chat/provider/deepseek/message.rs b/src/core/chat/provider/deepseek/message.rs deleted file mode 100644 index d50b93a..0000000 --- a/src/core/chat/provider/deepseek/message.rs +++ /dev/null @@ -1,47 +0,0 @@ -use serde::{Deserialize, Serialize}; -use super::enums::Role; - -#[derive(Debug, Serialize, Deserialize)] -#[serde(untagged)] -pub enum MessageContent { - Text(String), - Parts(Vec), -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct ContentPart { - pub r#type: String, - #[serde(skip_serializing_if = "Option::is_none")] - pub text: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub image_url: Option, -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct ImageUrl { - pub url: String, -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct ChatMessage { - pub role: Role, - pub content: MessageContent, - #[serde(skip_serializing_if = "Option::is_none")] - pub name: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub tool_call_id: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub tool_calls: Option>, -} - -impl Default for ChatMessage { - fn default() -> Self { - Self { - role: Role::User, - content: MessageContent::Text(String::new()), - name: None, - tool_call_id: None, - tool_calls: None, - } - } -} \ No newline at end of file diff --git a/src/core/chat/provider/deepseek/mod.rs b/src/core/chat/provider/deepseek/mod.rs deleted file mode 100644 index 46abd49..0000000 --- a/src/core/chat/provider/deepseek/mod.rs +++ /dev/null @@ -1,6 +0,0 @@ -mod chat; -mod model; -mod enums; -mod tool; -mod message; -mod test; \ No newline at end of file diff --git a/src/core/chat/provider/deepseek/test.rs b/src/core/chat/provider/deepseek/test.rs deleted file mode 100644 index 3456c68..0000000 --- a/src/core/chat/provider/deepseek/test.rs +++ /dev/null @@ -1,281 +0,0 @@ -// 修复:删除重复导入 -use crate::core::chat::provider::deepseek::{model::*, enums::*, message::*, tool::*, chat::*}; -use reqwest::Client; -use std::env; -use dotenv::dotenv; -use serde_json; - -const API_BASE: &str = "https://api.deepseek.com"; - -fn main() {} - -#[cfg(test)] -mod tests { - use super::*; - use futures_util::StreamExt; - - /// 获取 API Key(加载 .env 文件) - fn get_api_key() -> String { - dotenv().ok(); - env::var("API_KEY").expect("❌ 请在 .env 文件中设置 API_KEY 环境变量") - } - - // 1. 测试:获取模型列表 ✅ 修复:移除 /v1 前缀 - #[tokio::test] - async fn test_list_models() -> anyhow::Result<()> { - let client = Client::new(); - let auth = format!("Bearer {}", get_api_key()); - - let resp: ListModelsResponse = client - .get(format!("{API_BASE}/models")) // 修复点1 - .header("Authorization", &auth) - .send() - .await? - .json() - .await?; - - assert_eq!(resp.object, "list"); - assert!(!resp.data.is_empty()); - - println!("\n====================================="); - println!("📋 模型列表测试结果"); - println!("====================================="); - println!("{}", serde_json::to_string_pretty(&resp)?); - println!("✅ 模型列表测试通过,共 {} 个模型", resp.data.len()); - - Ok(()) - } - - // 2. 测试:普通对话 ✅ 修复:MessageContent 取值 - #[tokio::test] - async fn test_chat_normal() -> anyhow::Result<()> { - let client = Client::new(); - let auth = format!("Bearer {}", get_api_key()); - - let req = CreateChatCompletionRequest { - model: "deepseek-v4-flash".into(), - messages: vec![ChatMessage { - role: Role::User, - content: MessageContent::Text("你好".into()), - ..Default::default() - }], - max_tokens: Some(100), - ..Default::default() - }; - - let resp: ChatCompletionResponse = client - .post(format!("{API_BASE}/v1/chat/completions")) - .header("Authorization", &auth) - .json(&req) - .send() - .await? - .json() - .await?; - - assert!(!resp.choices.is_empty()); - assert_eq!(resp.model, "deepseek-v4-flash"); - - // 修复点2:直接获取 MessageContent 文本 - let reply = match &resp.choices[0].message.content { - MessageContent::Text(text) => text, - _ => "非文本消息", - }; - - println!("\n====================================="); - println!("💬 普通对话测试结果"); - println!("====================================="); - println!("AI 回复:{}", reply); - println!("完整响应:{}", serde_json::to_string_pretty(&resp)?); - println!("✅ 普通对话测试通过"); - - Ok(()) - } - - // 3. 测试:深度思考模型 ✅ 修复:删除不存在的 reasoning_content 字段 - #[tokio::test] - async fn test_chat_reasoner() -> anyhow::Result<()> { - let client = Client::new(); - let auth = format!("Bearer {}", get_api_key()); - - let req = CreateChatCompletionRequest { - model: "deepseek-reasoner".into(), - messages: vec![ChatMessage { - role: Role::User, - content: MessageContent::Text("1024*1024等于多少?".into()), - ..Default::default() - }], - ..Default::default() - }; - - let resp: ChatCompletionResponse = client - .post(format!("{API_BASE}/v1/chat/completions")) - .header("Authorization", &auth) - .json(&req) - .send() - .await? - .json() - .await?; - - assert!(!resp.choices.is_empty()); - - // 修复点3:移除无此字段的打印 - let reply = match &resp.choices[0].message.content { - MessageContent::Text(text) => text, - _ => "非文本消息", - }; - - println!("\n====================================="); - println!("🤯 深度思考模型测试结果"); - println!("====================================="); - println!("最终回复:{}", reply); - println!("完整响应:{}", serde_json::to_string_pretty(&resp)?); - println!("✅ 思考模型测试通过"); - - Ok(()) - } - - // 4. 测试:JSON 格式输出 ✅ 修复:MessageContent 取值 - #[tokio::test] - async fn test_chat_json_format() -> anyhow::Result<()> { - let client = Client::new(); - let auth = format!("Bearer {}", get_api_key()); - - let req = CreateChatCompletionRequest { - model: "deepseek-v4-flash".into(), - messages: vec![ChatMessage { - role: Role::User, - content: MessageContent::Text("返回一个包含name和age的JSON".into()), - ..Default::default() - }], - response_format: Some(ResponseFormat::default()), - ..Default::default() - }; - - let resp: ChatCompletionResponse = client - .post(format!("{API_BASE}/v1/chat/completions")) - .header("Authorization", &auth) - .json(&req) - .send() - .await? - .json() - .await?; - - assert!(!resp.choices.is_empty()); - - let json_result = match &resp.choices[0].message.content { - MessageContent::Text(text) => text, - _ => "非文本消息", - }; - - println!("\n====================================="); - println!("📄 JSON 格式输出测试结果"); - println!("====================================="); - println!("JSON 内容:{}", json_result); - println!("完整响应:{}", serde_json::to_string_pretty(&resp)?); - println!("✅ JSON输出测试通过"); - - Ok(()) - } - - // 5. 测试:工具调用 ✅ 无报错,保持原样 - #[tokio::test] - async fn test_chat_tool_call() -> anyhow::Result<()> { - let client = Client::new(); - let auth = format!("Bearer {}", get_api_key()); - - let req = CreateChatCompletionRequest { - model: "deepseek-v4-flash".into(), - messages: vec![ChatMessage { - role: Role::User, - content: MessageContent::Text("查询上海天气".into()), - ..Default::default() - }], - tools: Some(vec![Tool { - r#type: ToolType::Function, - function: FunctionObject { - name: "get_weather".into(), - description: Some("获取城市天气".into()), - parameters: Some(serde_json::to_value( - serde_json::json!({ - "type": "object", - "properties": { - "city": {"type": "string"} - }, - "required": ["city"] - }) - )?), - }, - }]), - tool_choice: Some(ToolChoice::Strategy("auto".into())), - ..Default::default() - }; - - let resp: ChatCompletionResponse = client - .post(format!("{API_BASE}/v1/chat/completions")) - .header("Authorization", &auth) - .json(&req) - .send() - .await? - .json() - .await?; - - assert!(resp.choices[0].message.tool_calls.is_some()); - - let tool_call = &resp.choices[0].message.tool_calls.as_ref().unwrap()[0]; - println!("\n====================================="); - println!("🔧 工具调用测试结果"); - println!("====================================="); - println!("调用函数:{}", tool_call.function.name); - println!("调用参数:{}", tool_call.function.arguments); - println!("完整响应:{}", serde_json::to_string_pretty(&resp)?); - println!("✅ 工具调用测试通过"); - - Ok(()) - } - - // 6. 测试:流式输出 ✅ 无报错,保持原样 - #[tokio::test] - async fn test_chat_stream() -> anyhow::Result<()> { - let client = Client::new(); - let auth = format!("Bearer {}", get_api_key()); - - let req = CreateChatCompletionRequest { - model: "deepseek-v4-flash".into(), - messages: vec![ChatMessage { - role: Role::User, - content: MessageContent::Text("介绍Rust".into()), - ..Default::default() - }], - stream: Some(true), - stream_options: Some(StreamOptions { - include_usage: Some(true), - }), - ..Default::default() - }; - - let mut stream = client - .post(format!("{API_BASE}/v1/chat/completions")) - .header("Authorization", &auth) - .json(&req) - .send() - .await? - .bytes_stream(); - - println!("\n====================================="); - println!("🌊 流式输出测试结果(实时打印)"); - println!("====================================="); - - let mut full_content = String::new(); - while let Some(chunk_result) = stream.next().await { - let chunk = chunk_result?; - let s = String::from_utf8_lossy(&chunk); - print!("{}", s); - full_content.push_str(&s); - } - - assert!(!full_content.is_empty()); - println!("\n✅ 流式输出测试通过"); - - Ok(()) - } -} \ No newline at end of file diff --git a/src/core/chat/provider/deepseek/tool.rs b/src/core/chat/provider/deepseek/tool.rs deleted file mode 100644 index 2bc3c1a..0000000 --- a/src/core/chat/provider/deepseek/tool.rs +++ /dev/null @@ -1,49 +0,0 @@ -use serde::{Deserialize, Serialize}; -use serde_json::Value; -use super::enums::ToolType; - -#[derive(Debug, Serialize, Deserialize)] -pub struct Tool { - pub r#type: ToolType, - pub function: FunctionObject, -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct FunctionObject { - pub name: String, - #[serde(skip_serializing_if = "Option::is_none")] - pub description: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub parameters: Option, -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct ToolCall { - pub id: String, - pub r#type: ToolType, - pub function: ToolCallFunction, -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct ToolCallFunction { - pub name: String, - pub arguments: String, -} - -#[derive(Debug, Serialize, Deserialize)] -#[serde(untagged)] -pub enum ToolChoice { - Strategy(String), - Tool(ToolChoiceObject), -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct ToolChoiceObject { - pub r#type: ToolType, - pub function: ToolChoiceFunction, -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct ToolChoiceFunction { - pub name: String, -} \ No newline at end of file diff --git a/src/core/chat/provider/anthropic/mod.rs b/src/core/config/mod.rs similarity index 100% rename from src/core/chat/provider/anthropic/mod.rs rename to src/core/config/mod.rs diff --git a/src/core/chat/provider/openai/mod.rs b/src/core/errors/mod.rs similarity index 100% rename from src/core/chat/provider/openai/mod.rs rename to src/core/errors/mod.rs diff --git a/src/core/mod.rs b/src/core/mod.rs index 6ec46a2..1807b9b 100644 --- a/src/core/mod.rs +++ b/src/core/mod.rs @@ -1,6 +1,8 @@ -pub mod action; -pub mod event; -pub mod update; -pub mod context; +pub mod transport; +pub mod tools; +pub mod config; +pub mod runtime; pub mod db; -pub mod chat; \ No newline at end of file +pub mod agent; +mod providers; +mod errors; \ No newline at end of file diff --git a/src/core/providers/anthropic/mod.rs b/src/core/providers/anthropic/mod.rs new file mode 100644 index 0000000..08c3d84 --- /dev/null +++ b/src/core/providers/anthropic/mod.rs @@ -0,0 +1,21 @@ +use crate::core::providers::basic::InterfaceFormat; + +mod model; + +pub struct Anthropic { + base_url: String, + anthropic_version: String, + interface_format: InterfaceFormat, + pub x_api_key: String, +} + +impl Default for Anthropic { + fn default() -> Self { + Anthropic { + base_url: "https://api.anthropic.com".to_string(), + anthropic_version: "2023-06-01".to_string(), + interface_format: InterfaceFormat::Messages, + x_api_key: "sk-ant-...".to_string() + } + } +} \ No newline at end of file diff --git a/src/core/chat/client.rs b/src/core/providers/anthropic/model.rs similarity index 100% rename from src/core/chat/client.rs rename to src/core/providers/anthropic/model.rs diff --git a/src/core/chat/provider/deepseek/chat.rs b/src/core/providers/basic/completions.rs similarity index 55% rename from src/core/chat/provider/deepseek/chat.rs rename to src/core/providers/basic/completions.rs index 861260b..46c292c 100644 --- a/src/core/chat/provider/deepseek/chat.rs +++ b/src/core/providers/basic/completions.rs @@ -1,6 +1,42 @@ use serde::{Deserialize, Serialize}; use serde_json::Value; -use super::{enums::*, message::ChatMessage, tool::*}; + +#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)] +#[serde(rename_all = "snake_case")] +pub enum Role { + System, + User, + Assistant, + Tool, +} + +#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)] +#[serde(rename_all = "snake_case")] +pub enum ToolType { + Function, +} + +#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)] +#[serde(rename_all = "snake_case")] +pub enum ResponseFormatType { + Text, + JsonObject, +} + +#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)] +#[serde(rename_all = "snake_case")] +pub enum FinishReason { + Stop, + Length, + ToolCalls, + ContentFilter, +} + +impl Default for ResponseFormatType { + fn default() -> Self { + Self::Text + } +} #[derive(Debug, Serialize, Deserialize, Default)] pub struct StreamOptions { @@ -114,4 +150,95 @@ pub struct PromptTokensDetails { #[derive(Debug, Serialize, Deserialize)] pub struct CompletionTokensDetails { pub reasoning_tokens: u32, +} + +#[derive(Debug, Serialize, Deserialize)] +#[serde(untagged)] +pub enum MessageContent { + Text(String), + Parts(Vec), +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct ContentPart { + pub r#type: String, + #[serde(skip_serializing_if = "Option::is_none")] + pub text: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub image_url: Option, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct ImageUrl { + pub url: String, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct ChatMessage { + pub role: Role, + pub content: MessageContent, + #[serde(skip_serializing_if = "Option::is_none")] + pub name: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub tool_call_id: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub tool_calls: Option>, +} + +impl Default for ChatMessage { + fn default() -> Self { + Self { + role: Role::User, + content: MessageContent::Text(String::new()), + name: None, + tool_call_id: None, + tool_calls: None, + } + } +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct Tool { + pub r#type: ToolType, + pub function: FunctionObject, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct FunctionObject { + pub name: String, + #[serde(skip_serializing_if = "Option::is_none")] + pub description: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub parameters: Option, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct ToolCall { + pub id: String, + pub r#type: ToolType, + pub function: ToolCallFunction, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct ToolCallFunction { + pub name: String, + pub arguments: String, +} + +#[derive(Debug, Serialize, Deserialize)] +#[serde(untagged)] +pub enum ToolChoice { + Strategy(String), + Tool(ToolChoiceObject), +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct ToolChoiceObject { + pub r#type: ToolType, + pub function: ToolChoiceFunction, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct ToolChoiceFunction { + pub name: String, } \ No newline at end of file diff --git a/src/core/context/mod.rs b/src/core/providers/basic/messages.rs similarity index 100% rename from src/core/context/mod.rs rename to src/core/providers/basic/messages.rs diff --git a/src/core/providers/basic/mod.rs b/src/core/providers/basic/mod.rs new file mode 100644 index 0000000..0328a11 --- /dev/null +++ b/src/core/providers/basic/mod.rs @@ -0,0 +1,9 @@ +mod completions; +mod responses; +mod messages; + +pub enum InterfaceFormat { + Completions, + Responses, + Messages +} \ No newline at end of file diff --git a/src/core/event/mod.rs b/src/core/providers/basic/responses.rs similarity index 100% rename from src/core/event/mod.rs rename to src/core/providers/basic/responses.rs diff --git a/src/core/providers/deepseek/mod.rs b/src/core/providers/deepseek/mod.rs new file mode 100644 index 0000000..15b74a0 --- /dev/null +++ b/src/core/providers/deepseek/mod.rs @@ -0,0 +1,19 @@ +use crate::core::providers::basic::InterfaceFormat; + +mod model; + +pub struct Deepseek { + base_url: String, + interface_format: InterfaceFormat, + pub authorization: String, +} + +impl Default for Deepseek { + fn default() -> Self { + Deepseek { + base_url: "https://api.deepseek.com".to_string(), + interface_format: InterfaceFormat::Completions, + authorization: "sk-...".to_string() + } + } +} \ No newline at end of file diff --git a/src/core/chat/provider/deepseek/model.rs b/src/core/providers/deepseek/model.rs similarity index 100% rename from src/core/chat/provider/deepseek/model.rs rename to src/core/providers/deepseek/model.rs diff --git a/src/core/chat/provider/mod.rs b/src/core/providers/mod.rs similarity index 52% rename from src/core/chat/provider/mod.rs rename to src/core/providers/mod.rs index 199127c..6312b2c 100644 --- a/src/core/chat/provider/mod.rs +++ b/src/core/providers/mod.rs @@ -1,3 +1,4 @@ mod anthropic; mod openai; -mod deepseek; \ No newline at end of file +mod deepseek; +mod basic; \ No newline at end of file diff --git a/src/core/providers/openai/mod.rs b/src/core/providers/openai/mod.rs new file mode 100644 index 0000000..e457d83 --- /dev/null +++ b/src/core/providers/openai/mod.rs @@ -0,0 +1,20 @@ +use crate::core::providers::basic::InterfaceFormat; + +mod request; +mod model; + +pub struct OpenAI { + base_url: String, + interface_format: InterfaceFormat, + pub authorization: String, +} + +impl Default for OpenAI { + fn default() -> Self { + OpenAI { + base_url: "https://api.openai.com".to_string(), + interface_format: InterfaceFormat::Responses, + authorization: "sk-...".to_string() + } + } +} \ No newline at end of file diff --git a/src/core/update/mod.rs b/src/core/providers/openai/model.rs similarity index 100% rename from src/core/update/mod.rs rename to src/core/providers/openai/model.rs diff --git a/src/core/providers/openai/request.rs b/src/core/providers/openai/request.rs new file mode 100644 index 0000000..02fd852 --- /dev/null +++ b/src/core/providers/openai/request.rs @@ -0,0 +1,6 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Serialize, Deserialize, Default)] +pub struct Request { + +} \ No newline at end of file diff --git a/src/core/runtime/mod.rs b/src/core/runtime/mod.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/core/tools/mod.rs b/src/core/tools/mod.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/core/chat/common/request.rs b/src/core/transport/client.rs similarity index 99% rename from src/core/chat/common/request.rs rename to src/core/transport/client.rs index 620c499..e0c4eea 100644 --- a/src/core/chat/common/request.rs +++ b/src/core/transport/client.rs @@ -17,8 +17,8 @@ use std::future::Future; use std::path::PathBuf; use std::pin::Pin; use std::time::Duration; +use log::{info, warn}; use tokio::fs; -use tracing::{info, warn}; // ============================================================ // Retry Config diff --git a/src/core/transport/mod.rs b/src/core/transport/mod.rs new file mode 100644 index 0000000..2322d1e --- /dev/null +++ b/src/core/transport/mod.rs @@ -0,0 +1 @@ +mod client; \ No newline at end of file