va_ai_api_bridge/providers/
mod.rs1mod dashscope;
2mod deepseek;
3mod kimi;
4mod mimo;
5mod minimax;
6mod reasoning_blob;
7mod xai;
8mod zai;
9
10use serde::{Deserialize, Serialize};
11use serde_json::Value;
12
13use crate::{UniversalEvent, WireProtocol};
14
15pub use dashscope::DashScopeBridgeAdapter;
16pub use deepseek::DeepSeekBridgeAdapter;
17pub use kimi::KimiBridgeAdapter;
18pub use mimo::MimoBridgeAdapter;
19pub use minimax::MiniMaxBridgeAdapter;
20pub use xai::XaiBridgeAdapter;
21pub use zai::ZaiBridgeAdapter;
22
23#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
24#[serde(rename_all = "kebab-case")]
25pub enum ProviderRequestSource {
26 OpenAiResponses,
27 OpenAiChat,
28 AnthropicMessages,
29 GeminiGenerateContent,
30}
31
32impl ProviderRequestSource {
33 pub fn from_protocol(protocol: WireProtocol) -> Self {
34 match protocol {
35 WireProtocol::OpenAiResponses => Self::OpenAiResponses,
36 WireProtocol::OpenAiChat => Self::OpenAiChat,
37 WireProtocol::AnthropicMessages => Self::AnthropicMessages,
38 WireProtocol::GeminiGenerateContent => Self::GeminiGenerateContent,
39 }
40 }
41
42 pub(crate) fn supports_deepseek_reasoning_replay(self) -> bool {
43 matches!(
44 self,
45 Self::OpenAiResponses | Self::AnthropicMessages | Self::GeminiGenerateContent
46 )
47 }
48}
49
50#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
51#[serde(rename_all = "camelCase")]
52pub struct DeepSeekBridgeSettings {
53 #[serde(default, skip_serializing_if = "is_false")]
54 pub thinking: bool,
55 #[serde(default, skip_serializing_if = "is_false")]
56 pub replay_reasoning_content: bool,
57}
58
59impl DeepSeekBridgeSettings {
60 pub fn is_empty(&self) -> bool {
61 !self.thinking && !self.replay_reasoning_content
62 }
63}
64
65#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
66#[serde(rename_all = "camelCase")]
67pub struct ProviderBridgeAdapterConfig {
68 #[serde(default, skip_serializing_if = "DeepSeekBridgeSettings::is_empty")]
69 pub deepseek: DeepSeekBridgeSettings,
70 #[serde(default, skip_serializing_if = "Option::is_none")]
71 pub thinking_enabled: Option<bool>,
72}
73
74#[derive(Debug, Clone)]
75pub enum ProviderBridgeAdapter {
76 None,
77 DeepSeek(DeepSeekBridgeAdapter),
78 Kimi(KimiBridgeAdapter),
79 Mimo(MimoBridgeAdapter),
80 MiniMax(MiniMaxBridgeAdapter),
81 DashScope(DashScopeBridgeAdapter),
82 Xai(XaiBridgeAdapter),
83 Zai(ZaiBridgeAdapter),
84}
85
86impl ProviderBridgeAdapter {
87 pub fn for_provider(
88 provider_id: &str,
89 _target_protocol: WireProtocol,
90 config: ProviderBridgeAdapterConfig,
91 ) -> Self {
92 match provider_id {
93 "deepseek" => Self::DeepSeek(DeepSeekBridgeAdapter::new(config.deepseek)),
94 "kimi" => Self::Kimi(KimiBridgeAdapter::default()),
95 "mimo" => Self::Mimo(MimoBridgeAdapter),
96 "minimax" => Self::MiniMax(MiniMaxBridgeAdapter::default()),
97 "dashscope" | "qwen" => {
98 Self::DashScope(DashScopeBridgeAdapter::new(config.thinking_enabled))
99 }
100 "xai" => Self::Xai(XaiBridgeAdapter),
101 "zai" => Self::Zai(ZaiBridgeAdapter::new(config.thinking_enabled)),
102 _ => Self::None,
103 }
104 }
105
106 pub fn prepare_responses_request(&mut self, request: &mut Value) {
107 match self {
108 Self::None => {}
109 Self::DeepSeek(_) => {}
110 Self::Kimi(_) => {}
111 Self::Mimo(_) => {}
112 Self::MiniMax(_) => {}
113 Self::DashScope(_) => {}
114 Self::Xai(adapter) => adapter.prepare_responses_request(request),
115 Self::Zai(_) => {}
116 }
117 }
118
119 pub fn prepare_chat_request(
120 &mut self,
121 source: ProviderRequestSource,
122 original_request: &Value,
123 chat_request: &mut Value,
124 ) {
125 match self {
126 Self::None => {}
127 Self::DeepSeek(adapter) => {
128 adapter.prepare_chat_request(source, original_request, chat_request)
129 }
130 Self::Kimi(_) => {}
131 Self::Mimo(adapter) => {
132 adapter.prepare_chat_request(source, original_request, chat_request)
133 }
134 Self::MiniMax(adapter) => adapter.prepare_chat_request(chat_request),
135 Self::DashScope(adapter) => {
136 adapter.prepare_chat_request(original_request, chat_request)
137 }
138 Self::Xai(_) => {}
139 Self::Zai(adapter) => adapter.prepare_chat_request(original_request, chat_request),
140 }
141 }
142
143 pub fn prepare_anthropic_request(
144 &mut self,
145 source: ProviderRequestSource,
146 request: &mut Value,
147 ) {
148 match self {
149 Self::None => {}
150 Self::DeepSeek(adapter) => adapter.prepare_anthropic_request(source, request),
151 Self::Kimi(adapter) => adapter.prepare_anthropic_request(request),
152 Self::Mimo(_) => {}
153 Self::MiniMax(_) => {}
154 Self::DashScope(_) => {}
155 Self::Xai(_) => {}
156 Self::Zai(_) => {}
157 }
158 }
159
160 pub fn normalize_chat_response(&mut self, response: &mut Value) {
161 match self {
162 Self::None => {}
163 Self::DeepSeek(_) => {}
164 Self::Kimi(_) => {}
165 Self::Mimo(adapter) => adapter.normalize_chat_response(response),
166 Self::MiniMax(_) => {}
167 Self::DashScope(_) => {}
168 Self::Xai(_) => {}
169 Self::Zai(_) => {}
170 }
171 }
172
173 pub fn transform_upstream_events(&mut self, events: &mut Vec<UniversalEvent>) {
174 match self {
175 Self::None => {}
176 Self::DeepSeek(_) => {}
177 Self::Kimi(adapter) => adapter.transform_upstream_events(events),
178 Self::Mimo(_) => {}
179 Self::MiniMax(adapter) => adapter.transform_upstream_events(events),
180 Self::DashScope(_) => {}
181 Self::Xai(_) => {}
182 Self::Zai(_) => {}
183 }
184 }
185}
186
187fn is_false(value: &bool) -> bool {
188 !*value
189}