mirror of
https://github.com/farion1231/cc-switch.git
synced 2026-07-30 02:14:43 +08:00
a35d112cd4
This major refactoring introduces a modular provider adapter architecture to support format transformation between different AI API formats. New features: - Add ProviderAdapter trait for unified provider abstraction - Implement Claude, Codex, and Gemini adapters with specific logic - Add Anthropic ↔ OpenAI format transformation for OpenRouter compatibility - Support model mapping from provider configuration (ANTHROPIC_MODEL, etc.) - Add OpenRouter preset to Claude provider presets Refactoring: - Extract authentication logic into auth.rs with AuthInfo and AuthStrategy - Move URL building and request transformation to individual adapters - Simplify ProviderRouter to only use proxy target providers - Refactor RequestForwarder to use adapter-based request/response handling - Use whitelist mode for header forwarding (only pass necessary headers) Architecture: - providers/adapter.rs: ProviderAdapter trait definition - providers/auth.rs: AuthInfo, AuthStrategy types - providers/claude.rs: Claude adapter with OpenRouter detection - providers/codex.rs: Codex (OpenAI) adapter - providers/gemini.rs: Gemini (Google) adapter - providers/models/: Anthropic and OpenAI API data models - providers/transform.rs: Bidirectional format transformation
114 lines
3.0 KiB
Rust
114 lines
3.0 KiB
Rust
//! OpenAI API 数据模型
|
|
//!
|
|
//! 用于 OpenAI Chat Completions API 的请求/响应格式转换
|
|
|
|
use serde::{Deserialize, Serialize};
|
|
use serde_json::Value;
|
|
|
|
/// OpenAI 请求
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct OpenAIRequest {
|
|
pub model: String,
|
|
pub messages: Vec<OpenAIMessage>,
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub max_tokens: Option<u32>,
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub temperature: Option<f32>,
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub stream: Option<bool>,
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub tools: Option<Vec<OpenAITool>>,
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub tool_choice: Option<Value>,
|
|
}
|
|
|
|
/// OpenAI 消息
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct OpenAIMessage {
|
|
pub role: String,
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub content: Option<Value>, // String 或 Vec<ContentPart>
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub tool_calls: Option<Vec<OpenAIToolCall>>,
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub tool_call_id: Option<String>,
|
|
}
|
|
|
|
/// OpenAI 内容部分
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
#[serde(tag = "type")]
|
|
pub enum OpenAIContentPart {
|
|
#[serde(rename = "text")]
|
|
Text { text: String },
|
|
#[serde(rename = "image_url")]
|
|
ImageUrl { image_url: ImageUrl },
|
|
}
|
|
|
|
/// 图片 URL
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct ImageUrl {
|
|
pub url: String,
|
|
}
|
|
|
|
/// OpenAI 工具调用
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct OpenAIToolCall {
|
|
pub id: String,
|
|
#[serde(rename = "type")]
|
|
pub call_type: String,
|
|
pub function: OpenAIFunction,
|
|
}
|
|
|
|
/// OpenAI 函数
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct OpenAIFunction {
|
|
pub name: String,
|
|
pub arguments: String, // JSON 字符串
|
|
}
|
|
|
|
/// OpenAI 工具定义
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct OpenAITool {
|
|
#[serde(rename = "type")]
|
|
pub tool_type: String,
|
|
pub function: OpenAIFunctionDef,
|
|
}
|
|
|
|
/// OpenAI 函数定义
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct OpenAIFunctionDef {
|
|
pub name: String,
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub description: Option<String>,
|
|
pub parameters: Value,
|
|
}
|
|
|
|
/// OpenAI 响应
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct OpenAIResponse {
|
|
pub id: String,
|
|
pub object: String,
|
|
pub created: u64,
|
|
pub model: String,
|
|
pub choices: Vec<OpenAIChoice>,
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub usage: Option<OpenAIUsage>,
|
|
}
|
|
|
|
/// OpenAI 选择
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct OpenAIChoice {
|
|
pub index: u32,
|
|
pub message: OpenAIMessage,
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub finish_reason: Option<String>,
|
|
}
|
|
|
|
/// OpenAI 使用量
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct OpenAIUsage {
|
|
pub prompt_tokens: u32,
|
|
pub completion_tokens: u32,
|
|
pub total_tokens: u32,
|
|
}
|