修复一些问题,作为v0.1.3-rc.4的补充

This commit is contained in:
wisdgod
2025-01-27 17:39:31 +08:00
parent c58f2697f0
commit 00a6980da9
8 changed files with 162 additions and 141 deletions

View File

@@ -34,6 +34,7 @@ reqwest = { version = "0.12.12", default-features = false, features = ["gzip", "
rkyv = { version = "0.7.45", default-features = false, features = ["alloc", "std", "bytecheck", "size_64", "validation", "std"] } rkyv = { version = "0.7.45", default-features = false, features = ["alloc", "std", "bytecheck", "size_64", "validation", "std"] }
serde = { version = "1.0.217", default-features = false, features = ["std", "derive"] } serde = { version = "1.0.217", default-features = false, features = ["std", "derive"] }
serde_json = { package = "sonic-rs", version = "0.3.17" } serde_json = { package = "sonic-rs", version = "0.3.17" }
# serde_json = "1.0.137"
sha2 = { version = "0.10.8", default-features = false } sha2 = { version = "0.10.8", default-features = false }
sysinfo = { version = "0.33.1", default-features = false, features = ["system"] } sysinfo = { version = "0.33.1", default-features = false, features = ["system"] }
tokio = { version = "1.43.0", features = ["rt-multi-thread", "macros", "net", "sync", "time", "fs", "signal"] } tokio = { version = "1.43.0", features = ["rt-multi-thread", "macros", "net", "sync", "time", "fs", "signal"] }

View File

@@ -6,7 +6,7 @@ RUN apt-get update && \
build-essential protobuf-compiler pkg-config libssl-dev nodejs npm \ build-essential protobuf-compiler pkg-config libssl-dev nodejs npm \
&& rm -rf /var/lib/apt/lists/* && rm -rf /var/lib/apt/lists/*
COPY . . COPY . .
ENV RUSTFLAGS="-C link-arg=-s -C target-cpu=native" ENV RUSTFLAGS="-C link-arg=-s -C target-cpu=x86-64-v3"
RUN cargo build --release && \ RUN cargo build --release && \
cp target/release/cursor-api /app/cursor-api cp target/release/cursor-api /app/cursor-api
@@ -18,7 +18,7 @@ RUN apt-get update && \
build-essential protobuf-compiler pkg-config libssl-dev nodejs npm \ build-essential protobuf-compiler pkg-config libssl-dev nodejs npm \
&& rm -rf /var/lib/apt/lists/* && rm -rf /var/lib/apt/lists/*
COPY . . COPY . .
ENV RUSTFLAGS="-C link-arg=-s -C target-cpu=native" ENV RUSTFLAGS="-C link-arg=-s -C target-cpu=apple-m1"
RUN cargo build --release && \ RUN cargo build --release && \
cp target/release/cursor-api /app/cursor-api cp target/release/cursor-api /app/cursor-api

View File

@@ -1,9 +1,7 @@
# Dockerfile.cross # Dockerfile.cross
# 使用与你 GitHub Actions 中相同的基础镜像 FROM --platform=linux/amd64 rust:1.84.0-slim-bookworm
FROM rust:1.84.0-slim-bookworm
# 设置工作目录
WORKDIR /app WORKDIR /app
# 安装必要的软件包 # 安装必要的软件包

View File

@@ -48,32 +48,32 @@ def_pub_const!(GEMINI_2_0_FLASH_EXP, "gemini-2.0-flash-exp");
def_pub_const!(DEEPSEEK_V3, "deepseek-v3"); def_pub_const!(DEEPSEEK_V3, "deepseek-v3");
def_pub_const!(DEEPSEEK_R1, "deepseek-r1"); def_pub_const!(DEEPSEEK_R1, "deepseek-r1");
#[derive(Clone, PartialEq, rkyv::Archive, rkyv::Deserialize, rkyv::Serialize)] // #[derive(Clone, PartialEq, rkyv::Archive, rkyv::Deserialize, rkyv::Serialize)]
pub enum ModelType { // pub enum ModelType {
Claude35Sonnet, // Claude35Sonnet,
Gpt4, // Gpt4,
Gpt4o, // Gpt4o,
Claude3Opus, // Claude3Opus,
CursorFast, // CursorFast,
CursorSmall, // CursorSmall,
Gpt35Turbo, // Gpt35Turbo,
Gpt4Turbo202404, // Gpt4Turbo202404,
Gpt4o128k, // Gpt4o128k,
Gemini15Flash500k, // Gemini15Flash500k,
Claude3Haiku200k, // Claude3Haiku200k,
Claude35Sonnet200k, // Claude35Sonnet200k,
Claude35Sonnet20241022, // Claude35Sonnet20241022,
Gpt4oMini, // Gpt4oMini,
O1Mini, // O1Mini,
O1Preview, // O1Preview,
O1, // O1,
Claude35Haiku, // Claude35Haiku,
GeminiExp1206, // GeminiExp1206,
Gemini20FlashThinkingExp, // Gemini20FlashThinkingExp,
Gemini20FlashExp, // Gemini20FlashExp,
DeepseekV3, // DeepseekV3,
DeepseekR1, // DeepseekR1,
} // }
macro_rules! create_model { macro_rules! create_model {
($($id:expr, $owner:expr),* $(,)?) => { ($($id:expr, $owner:expr),* $(,)?) => {
@@ -95,64 +95,64 @@ macro_rules! count {
(($id:expr, $owner:expr) $( ($id2:expr, $owner2:expr) )*) => (1 + count!($( ($id2, $owner2) )*)); (($id:expr, $owner:expr) $( ($id2:expr, $owner2:expr) )*) => (1 + count!($( ($id2, $owner2) )*));
} }
impl ModelType { // impl ModelType {
pub fn as_str_name(&self) -> &'static str { // pub fn as_str_name(&self) -> &'static str {
match self { // match self {
ModelType::Claude35Sonnet => CLAUDE_3_5_SONNET, // ModelType::Claude35Sonnet => CLAUDE_3_5_SONNET,
ModelType::Gpt4 => GPT_4, // ModelType::Gpt4 => GPT_4,
ModelType::Gpt4o => GPT_4O, // ModelType::Gpt4o => GPT_4O,
ModelType::Claude3Opus => CLAUDE_3_OPUS, // ModelType::Claude3Opus => CLAUDE_3_OPUS,
ModelType::CursorFast => CURSOR_FAST, // ModelType::CursorFast => CURSOR_FAST,
ModelType::CursorSmall => CURSOR_SMALL, // ModelType::CursorSmall => CURSOR_SMALL,
ModelType::Gpt35Turbo => GPT_3_5_TURBO, // ModelType::Gpt35Turbo => GPT_3_5_TURBO,
ModelType::Gpt4Turbo202404 => GPT_4_TURBO_2024_04_09, // ModelType::Gpt4Turbo202404 => GPT_4_TURBO_2024_04_09,
ModelType::Gpt4o128k => GPT_4O_128K, // ModelType::Gpt4o128k => GPT_4O_128K,
ModelType::Gemini15Flash500k => GEMINI_1_5_FLASH_500K, // ModelType::Gemini15Flash500k => GEMINI_1_5_FLASH_500K,
ModelType::Claude3Haiku200k => CLAUDE_3_HAIKU_200K, // ModelType::Claude3Haiku200k => CLAUDE_3_HAIKU_200K,
ModelType::Claude35Sonnet200k => CLAUDE_3_5_SONNET_200K, // ModelType::Claude35Sonnet200k => CLAUDE_3_5_SONNET_200K,
ModelType::Claude35Sonnet20241022 => CLAUDE_3_5_SONNET_20241022, // ModelType::Claude35Sonnet20241022 => CLAUDE_3_5_SONNET_20241022,
ModelType::Gpt4oMini => GPT_4O_MINI, // ModelType::Gpt4oMini => GPT_4O_MINI,
ModelType::O1Mini => O1_MINI, // ModelType::O1Mini => O1_MINI,
ModelType::O1Preview => O1_PREVIEW, // ModelType::O1Preview => O1_PREVIEW,
ModelType::O1 => O1, // ModelType::O1 => O1,
ModelType::Claude35Haiku => CLAUDE_3_5_HAIKU, // ModelType::Claude35Haiku => CLAUDE_3_5_HAIKU,
ModelType::GeminiExp1206 => GEMINI_EXP_1206, // ModelType::GeminiExp1206 => GEMINI_EXP_1206,
ModelType::Gemini20FlashThinkingExp => GEMINI_2_0_FLASH_THINKING_EXP, // ModelType::Gemini20FlashThinkingExp => GEMINI_2_0_FLASH_THINKING_EXP,
ModelType::Gemini20FlashExp => GEMINI_2_0_FLASH_EXP, // ModelType::Gemini20FlashExp => GEMINI_2_0_FLASH_EXP,
ModelType::DeepseekV3 => DEEPSEEK_V3, // ModelType::DeepseekV3 => DEEPSEEK_V3,
ModelType::DeepseekR1 => DEEPSEEK_R1, // ModelType::DeepseekR1 => DEEPSEEK_R1,
} // }
} // }
pub fn from_str_name(id :&str) -> Option<ModelType> { // pub fn from_str_name(id :&str) -> Option<ModelType> {
match id { // match id {
CLAUDE_3_5_SONNET => Some(ModelType::Claude35Sonnet), // CLAUDE_3_5_SONNET => Some(ModelType::Claude35Sonnet),
GPT_4 => Some(ModelType::Gpt4), // GPT_4 => Some(ModelType::Gpt4),
GPT_4O => Some(ModelType::Gpt4o), // GPT_4O => Some(ModelType::Gpt4o),
CLAUDE_3_OPUS => Some(ModelType::Claude3Opus), // CLAUDE_3_OPUS => Some(ModelType::Claude3Opus),
CURSOR_FAST => Some(ModelType::CursorFast), // CURSOR_FAST => Some(ModelType::CursorFast),
CURSOR_SMALL => Some(ModelType::CursorSmall), // CURSOR_SMALL => Some(ModelType::CursorSmall),
GPT_3_5_TURBO => Some(ModelType::Gpt35Turbo), // GPT_3_5_TURBO => Some(ModelType::Gpt35Turbo),
GPT_4_TURBO_2024_04_09 => Some(ModelType::Gpt4Turbo202404), // GPT_4_TURBO_2024_04_09 => Some(ModelType::Gpt4Turbo202404),
GPT_4O_128K => Some(ModelType::Gpt4o128k), // GPT_4O_128K => Some(ModelType::Gpt4o128k),
GEMINI_1_5_FLASH_500K => Some(ModelType::Gemini15Flash500k), // GEMINI_1_5_FLASH_500K => Some(ModelType::Gemini15Flash500k),
CLAUDE_3_HAIKU_200K => Some(ModelType::Claude3Haiku200k), // CLAUDE_3_HAIKU_200K => Some(ModelType::Claude3Haiku200k),
CLAUDE_3_5_SONNET_200K => Some(ModelType::Claude35Sonnet200k), // CLAUDE_3_5_SONNET_200K => Some(ModelType::Claude35Sonnet200k),
CLAUDE_3_5_SONNET_20241022 => Some(ModelType::Claude35Sonnet20241022), // CLAUDE_3_5_SONNET_20241022 => Some(ModelType::Claude35Sonnet20241022),
GPT_4O_MINI => Some(ModelType::Gpt4oMini), // GPT_4O_MINI => Some(ModelType::Gpt4oMini),
O1_MINI => Some(ModelType::O1Mini), // O1_MINI => Some(ModelType::O1Mini),
O1_PREVIEW => Some(ModelType::O1Preview), // O1_PREVIEW => Some(ModelType::O1Preview),
O1 => Some(ModelType::O1), // O1 => Some(ModelType::O1),
CLAUDE_3_5_HAIKU => Some(ModelType::Claude35Haiku), // CLAUDE_3_5_HAIKU => Some(ModelType::Claude35Haiku),
GEMINI_EXP_1206 => Some(ModelType::GeminiExp1206), // GEMINI_EXP_1206 => Some(ModelType::GeminiExp1206),
GEMINI_2_0_FLASH_THINKING_EXP => Some(ModelType::Gemini20FlashThinkingExp), // GEMINI_2_0_FLASH_THINKING_EXP => Some(ModelType::Gemini20FlashThinkingExp),
GEMINI_2_0_FLASH_EXP => Some(ModelType::Gemini20FlashExp), // GEMINI_2_0_FLASH_EXP => Some(ModelType::Gemini20FlashExp),
DEEPSEEK_V3 => Some(ModelType::DeepseekV3), // DEEPSEEK_V3 => Some(ModelType::DeepseekV3),
DEEPSEEK_R1 => Some(ModelType::DeepseekR1), // DEEPSEEK_R1 => Some(ModelType::DeepseekR1),
_ => None, // _ => None,
} // }
} // }
} // }
create_model!( create_model!(
CLAUDE_3_5_SONNET, ANTHROPIC, CLAUDE_3_5_SONNET, ANTHROPIC,

View File

@@ -491,8 +491,13 @@ pub async fn handle_chat(
} }
StreamMessage::Debug(debug_prompt) => { StreamMessage::Debug(debug_prompt) => {
if let Ok(mut state) = ctx.state.try_lock() { if let Ok(mut state) = ctx.state.try_lock() {
if let Some(last_log) = state.request_logs.last_mut() { if let Some(log) = state
last_log.prompt = Some(debug_prompt); .request_logs
.iter_mut()
.rev()
.find(|log| log.id == ctx.current_id)
{
log.prompt = Some(debug_prompt);
} }
} }
} }
@@ -507,7 +512,7 @@ pub async fn handle_chat(
let mut stream = response.bytes_stream(); let mut stream = response.bytes_stream();
// 处理第一个chunk并获取first_result // 处理第一个chunk并获取first_result
while decoder.lock().await.has_no_first_result() { while !decoder.lock().await.is_first_result_ready() {
match stream.next().await { match stream.next().await {
Some(first_chunk) => { Some(first_chunk) => {
let chunk = first_chunk.map_err(|e| { let chunk = first_chunk.map_err(|e| {
@@ -642,9 +647,8 @@ pub async fn handle_chat(
let mut decoder = StreamDecoder::new(); let mut decoder = StreamDecoder::new();
let mut full_text = String::with_capacity(1024); let mut full_text = String::with_capacity(1024);
let mut stream = response.bytes_stream(); let mut stream = response.bytes_stream();
let mut all_chunks = Vec::new();
// 收集所有的chunks // 逐个处理chunks
while let Some(chunk) = stream.next().await { while let Some(chunk) = stream.next().await {
let chunk = chunk.map_err(|e| { let chunk = chunk.map_err(|e| {
let error_message = format!("Failed to read response chunk: {}", e); let error_message = format!("Failed to read response chunk: {}", e);
@@ -653,12 +657,34 @@ pub async fn handle_chat(
Json(ChatError::RequestFailed(error_message).to_json()), Json(ChatError::RequestFailed(error_message).to_json()),
) )
})?; })?;
all_chunks.extend(chunk);
}
// 一次性解码所有数据 // 立即处理当前chunk
let messages = match decoder.decode(&all_chunks, convert_web_ref) { match decoder.decode(&chunk, convert_web_ref) {
Ok(msgs) => msgs, Ok(messages) => {
for message in messages {
match message {
StreamMessage::Content(text) => {
if first_chunk_time.is_none() {
first_chunk_time = Some(start_time.elapsed().as_secs_f64());
}
full_text.push_str(&text);
}
StreamMessage::Debug(debug_prompt) => {
if let Ok(mut state) = state.try_lock() {
if let Some(log) = state
.request_logs
.iter_mut()
.rev()
.find(|log| log.id == current_id)
{
log.prompt = Some(debug_prompt);
}
}
}
_ => {}
}
}
}
Err(StreamError::ChatError(error)) => { Err(StreamError::ChatError(error)) => {
let error_response = error.to_error_response(); let error_response = error.to_error_response();
return Err(( return Err((
@@ -675,25 +701,6 @@ pub async fn handle_chat(
}; };
return Err((StatusCode::INTERNAL_SERVER_ERROR, Json(error_response))); return Err((StatusCode::INTERNAL_SERVER_ERROR, Json(error_response)));
} }
};
// 处理所有消息
for message in messages {
match message {
StreamMessage::Content(text) => {
if first_chunk_time.is_none() {
first_chunk_time = Some(start_time.elapsed().as_secs_f64());
}
full_text.push_str(&text);
}
StreamMessage::Debug(debug_prompt) => {
if let Ok(mut state) = state.try_lock() {
if let Some(last_log) = state.request_logs.last_mut() {
last_log.prompt = Some(debug_prompt);
}
}
}
_ => {}
} }
} }

View File

@@ -79,7 +79,7 @@ impl StreamDecoder {
// 获取第一个结果的引用 // 获取第一个结果的引用
pub fn take_first_result(&mut self) -> Option<Vec<StreamMessage>> { pub fn take_first_result(&mut self) -> Option<Vec<StreamMessage>> {
if self.is_incomplete() { if !self.buffer.is_empty() {
return None; return None;
} }
if self.first_result.is_some() { if self.first_result.is_some() {
@@ -88,12 +88,13 @@ impl StreamDecoder {
self.first_result.take() self.first_result.take()
} }
#[cfg(test)]
fn is_incomplete(&self) -> bool { fn is_incomplete(&self) -> bool {
!self.buffer.is_empty() !self.buffer.is_empty()
} }
pub fn has_no_first_result(&self) -> bool { pub fn is_first_result_ready(&self) -> bool {
self.first_result.is_none() self.first_result.is_some() && self.buffer.is_empty() && !self.first_result_taken
} }
pub fn decode(&mut self, data: &[u8], convert_web_ref: bool) -> Result<Vec<StreamMessage>, StreamError> { pub fn decode(&mut self, data: &[u8], convert_web_ref: bool) -> Result<Vec<StreamMessage>, StreamError> {

View File

@@ -39,14 +39,14 @@ use tower_http::{cors::CorsLayer, limit::RequestBodyLimitLayer};
#[tokio::main] #[tokio::main]
async fn main() { async fn main() {
// 设置自定义 panic hook // 设置自定义 panic hook
std::panic::set_hook(Box::new(|info| { // std::panic::set_hook(Box::new(|info| {
// std::env::set_var("RUST_BACKTRACE", "1"); // // std::env::set_var("RUST_BACKTRACE", "1");
if let Some(msg) = info.payload().downcast_ref::<String>() { // if let Some(msg) = info.payload().downcast_ref::<String>() {
eprintln!("{}", msg); // eprintln!("{}", msg);
} else if let Some(msg) = info.payload().downcast_ref::<&str>() { // } else if let Some(msg) = info.payload().downcast_ref::<&str>() {
eprintln!("{}", msg); // eprintln!("{}", msg);
} // }
})); // }));
// 加载环境变量 // 加载环境变量
dotenvy::dotenv().ok(); dotenvy::dotenv().ok();

View File

@@ -106,6 +106,15 @@
style="display: none;"> style="display: none;">
</div> </div>
<div class="form-group">
<label>包含网络引用:</label>
<select id="include_web_references">
<option value="">保持不变</option>
<option value="true">启用</option>
<option value="false">禁用</option>
</select>
</div>
<div class="form-group"> <div class="form-group">
<label>共享令牌(空表示禁用):</label> <label>共享令牌(空表示禁用):</label>
<input type="text" id="shareToken"> <input type="text" id="shareToken">
@@ -173,6 +182,8 @@
document.getElementById('usage_check_models_list').value = data.data.usage_check_models?.type === 'list' ? data.data.usage_check_models?.content || '' : document.getElementById('usage_check_models_list').value; document.getElementById('usage_check_models_list').value = data.data.usage_check_models?.type === 'list' ? data.data.usage_check_models?.content || '' : document.getElementById('usage_check_models_list').value;
document.getElementById('enable_dynamic_key').value = document.getElementById('enable_dynamic_key').value =
parseStringFromBoolean(data.data.enable_dynamic_key, ''); parseStringFromBoolean(data.data.enable_dynamic_key, '');
document.getElementById('include_web_references').value =
parseStringFromBoolean(data.data.include_web_references, '');
// 处理代理设置 // 处理代理设置
const proxies = data.data.proxies || ''; const proxies = data.data.proxies || '';
@@ -260,6 +271,9 @@
} }
})() })()
}), }),
...(document.getElementById('include_web_references').value && {
include_web_references: parseBooleanFromString(document.getElementById('include_web_references').value)
}),
share_token: document.getElementById('shareToken').value.trim(), share_token: document.getElementById('shareToken').value.trim(),
}; };