mirror of
https://github.com/farion1231/cc-switch.git
synced 2026-06-14 10:46:51 +08:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6af2b8671c |
@@ -36,6 +36,13 @@ pub struct StreamCheckConfig {
|
|||||||
pub codex_model: String,
|
pub codex_model: String,
|
||||||
/// Gemini 测试模型
|
/// Gemini 测试模型
|
||||||
pub gemini_model: String,
|
pub gemini_model: String,
|
||||||
|
/// 检查提示词
|
||||||
|
#[serde(default = "default_test_prompt")]
|
||||||
|
pub test_prompt: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn default_test_prompt() -> String {
|
||||||
|
"Who are you?".to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for StreamCheckConfig {
|
impl Default for StreamCheckConfig {
|
||||||
@@ -47,6 +54,7 @@ impl Default for StreamCheckConfig {
|
|||||||
claude_model: "claude-haiku-4-5-20251001".to_string(),
|
claude_model: "claude-haiku-4-5-20251001".to_string(),
|
||||||
codex_model: "gpt-5.1-codex@low".to_string(),
|
codex_model: "gpt-5.1-codex@low".to_string(),
|
||||||
gemini_model: "gemini-3-pro-preview".to_string(),
|
gemini_model: "gemini-3-pro-preview".to_string(),
|
||||||
|
test_prompt: default_test_prompt(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -110,7 +118,7 @@ impl StreamCheckService {
|
|||||||
Ok(last_result.unwrap_or_else(|| StreamCheckResult {
|
Ok(last_result.unwrap_or_else(|| StreamCheckResult {
|
||||||
status: HealthStatus::Failed,
|
status: HealthStatus::Failed,
|
||||||
success: false,
|
success: false,
|
||||||
message: "检查失败".to_string(),
|
message: "Check failed".to_string(),
|
||||||
response_time_ms: None,
|
response_time_ms: None,
|
||||||
http_status: None,
|
http_status: None,
|
||||||
model_used: String::new(),
|
model_used: String::new(),
|
||||||
@@ -130,17 +138,18 @@ impl StreamCheckService {
|
|||||||
|
|
||||||
let base_url = adapter
|
let base_url = adapter
|
||||||
.extract_base_url(provider)
|
.extract_base_url(provider)
|
||||||
.map_err(|e| AppError::Message(format!("提取 base_url 失败: {e}")))?;
|
.map_err(|e| AppError::Message(format!("Failed to extract base_url: {e}")))?;
|
||||||
|
|
||||||
let auth = adapter
|
let auth = adapter
|
||||||
.extract_auth(provider)
|
.extract_auth(provider)
|
||||||
.ok_or_else(|| AppError::Message("未找到 API Key".to_string()))?;
|
.ok_or_else(|| AppError::Message("API Key not found".to_string()))?;
|
||||||
|
|
||||||
// 使用全局 HTTP 客户端(已包含代理配置)
|
// 使用全局 HTTP 客户端(已包含代理配置)
|
||||||
let client = crate::proxy::http_client::get();
|
let client = crate::proxy::http_client::get();
|
||||||
let request_timeout = std::time::Duration::from_secs(config.timeout_secs);
|
let request_timeout = std::time::Duration::from_secs(config.timeout_secs);
|
||||||
|
|
||||||
let model_to_test = Self::resolve_test_model(app_type, provider, config);
|
let model_to_test = Self::resolve_test_model(app_type, provider, config);
|
||||||
|
let test_prompt = &config.test_prompt;
|
||||||
|
|
||||||
let result = match app_type {
|
let result = match app_type {
|
||||||
AppType::Claude => {
|
AppType::Claude => {
|
||||||
@@ -149,13 +158,21 @@ impl StreamCheckService {
|
|||||||
&base_url,
|
&base_url,
|
||||||
&auth,
|
&auth,
|
||||||
&model_to_test,
|
&model_to_test,
|
||||||
|
test_prompt,
|
||||||
request_timeout,
|
request_timeout,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
AppType::Codex => {
|
AppType::Codex => {
|
||||||
Self::check_codex_stream(&client, &base_url, &auth, &model_to_test, request_timeout)
|
Self::check_codex_stream(
|
||||||
.await
|
&client,
|
||||||
|
&base_url,
|
||||||
|
&auth,
|
||||||
|
&model_to_test,
|
||||||
|
test_prompt,
|
||||||
|
request_timeout,
|
||||||
|
)
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
AppType::Gemini => {
|
AppType::Gemini => {
|
||||||
Self::check_gemini_stream(
|
Self::check_gemini_stream(
|
||||||
@@ -163,6 +180,7 @@ impl StreamCheckService {
|
|||||||
&base_url,
|
&base_url,
|
||||||
&auth,
|
&auth,
|
||||||
&model_to_test,
|
&model_to_test,
|
||||||
|
test_prompt,
|
||||||
request_timeout,
|
request_timeout,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
@@ -179,7 +197,7 @@ impl StreamCheckService {
|
|||||||
Ok(StreamCheckResult {
|
Ok(StreamCheckResult {
|
||||||
status: health_status,
|
status: health_status,
|
||||||
success: true,
|
success: true,
|
||||||
message: "检查成功".to_string(),
|
message: "Check succeeded".to_string(),
|
||||||
response_time_ms: Some(response_time),
|
response_time_ms: Some(response_time),
|
||||||
http_status: Some(status_code),
|
http_status: Some(status_code),
|
||||||
model_used: model,
|
model_used: model,
|
||||||
@@ -201,32 +219,68 @@ impl StreamCheckService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Claude 流式检查
|
/// Claude 流式检查
|
||||||
|
///
|
||||||
|
/// 严格按照 Claude CLI 真实请求格式构建请求
|
||||||
async fn check_claude_stream(
|
async fn check_claude_stream(
|
||||||
client: &Client,
|
client: &Client,
|
||||||
base_url: &str,
|
base_url: &str,
|
||||||
auth: &AuthInfo,
|
auth: &AuthInfo,
|
||||||
model: &str,
|
model: &str,
|
||||||
|
test_prompt: &str,
|
||||||
timeout: std::time::Duration,
|
timeout: std::time::Duration,
|
||||||
) -> Result<(u16, String), AppError> {
|
) -> Result<(u16, String), AppError> {
|
||||||
let base = base_url.trim_end_matches('/');
|
let base = base_url.trim_end_matches('/');
|
||||||
|
// URL 必须包含 ?beta=true 参数(某些中转服务依赖此参数验证请求来源)
|
||||||
let url = if base.ends_with("/v1") {
|
let url = if base.ends_with("/v1") {
|
||||||
format!("{base}/messages")
|
format!("{base}/messages?beta=true")
|
||||||
} else {
|
} else {
|
||||||
format!("{base}/v1/messages")
|
format!("{base}/v1/messages?beta=true")
|
||||||
};
|
};
|
||||||
|
|
||||||
let body = json!({
|
let body = json!({
|
||||||
"model": model,
|
"model": model,
|
||||||
"max_tokens": 1,
|
"max_tokens": 1,
|
||||||
"messages": [{ "role": "user", "content": "hi" }],
|
"messages": [{ "role": "user", "content": test_prompt }],
|
||||||
"stream": true
|
"stream": true
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 获取本地系统信息
|
||||||
|
let os_name = Self::get_os_name();
|
||||||
|
let arch_name = Self::get_arch_name();
|
||||||
|
|
||||||
|
// 严格按照 Claude CLI 请求格式设置 headers
|
||||||
let response = client
|
let response = client
|
||||||
.post(&url)
|
.post(&url)
|
||||||
|
// 认证 headers(双重认证)
|
||||||
|
.header("authorization", format!("Bearer {}", auth.api_key))
|
||||||
.header("x-api-key", &auth.api_key)
|
.header("x-api-key", &auth.api_key)
|
||||||
|
// Anthropic 必需 headers
|
||||||
.header("anthropic-version", "2023-06-01")
|
.header("anthropic-version", "2023-06-01")
|
||||||
.header("Content-Type", "application/json")
|
.header(
|
||||||
|
"anthropic-beta",
|
||||||
|
"claude-code-20250219,interleaved-thinking-2025-05-14",
|
||||||
|
)
|
||||||
|
.header("anthropic-dangerous-direct-browser-access", "true")
|
||||||
|
// 内容类型 headers
|
||||||
|
.header("content-type", "application/json")
|
||||||
|
.header("accept", "application/json")
|
||||||
|
.header("accept-encoding", "identity")
|
||||||
|
.header("accept-language", "*")
|
||||||
|
// 客户端标识 headers
|
||||||
|
.header("user-agent", "claude-cli/2.1.2 (external, cli)")
|
||||||
|
.header("x-app", "cli")
|
||||||
|
// x-stainless SDK headers(动态获取本地系统信息)
|
||||||
|
.header("x-stainless-lang", "js")
|
||||||
|
.header("x-stainless-package-version", "0.70.0")
|
||||||
|
.header("x-stainless-os", os_name)
|
||||||
|
.header("x-stainless-arch", arch_name)
|
||||||
|
.header("x-stainless-runtime", "node")
|
||||||
|
.header("x-stainless-runtime-version", "v22.20.0")
|
||||||
|
.header("x-stainless-retry-count", "0")
|
||||||
|
.header("x-stainless-timeout", "600")
|
||||||
|
// 其他 headers
|
||||||
|
.header("sec-fetch-mode", "cors")
|
||||||
|
.header("connection", "keep-alive")
|
||||||
.timeout(timeout)
|
.timeout(timeout)
|
||||||
.json(&body)
|
.json(&body)
|
||||||
.send()
|
.send()
|
||||||
@@ -245,52 +299,63 @@ impl StreamCheckService {
|
|||||||
if let Some(chunk) = stream.next().await {
|
if let Some(chunk) = stream.next().await {
|
||||||
match chunk {
|
match chunk {
|
||||||
Ok(_) => Ok((status, model.to_string())),
|
Ok(_) => Ok((status, model.to_string())),
|
||||||
Err(e) => Err(AppError::Message(format!("读取流失败: {e}"))),
|
Err(e) => Err(AppError::Message(format!("Stream read failed: {e}"))),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Err(AppError::Message("未收到响应数据".to_string()))
|
Err(AppError::Message("No response data received".to_string()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Codex 流式检查
|
/// Codex 流式检查
|
||||||
|
///
|
||||||
|
/// 严格按照 Codex CLI 真实请求格式构建请求 (Responses API)
|
||||||
async fn check_codex_stream(
|
async fn check_codex_stream(
|
||||||
client: &Client,
|
client: &Client,
|
||||||
base_url: &str,
|
base_url: &str,
|
||||||
auth: &AuthInfo,
|
auth: &AuthInfo,
|
||||||
model: &str,
|
model: &str,
|
||||||
|
test_prompt: &str,
|
||||||
timeout: std::time::Duration,
|
timeout: std::time::Duration,
|
||||||
) -> Result<(u16, String), AppError> {
|
) -> Result<(u16, String), AppError> {
|
||||||
let base = base_url.trim_end_matches('/');
|
let base = base_url.trim_end_matches('/');
|
||||||
|
// Codex CLI 使用 /v1/responses 端点 (OpenAI Responses API)
|
||||||
let url = if base.ends_with("/v1") {
|
let url = if base.ends_with("/v1") {
|
||||||
format!("{base}/chat/completions")
|
format!("{base}/responses")
|
||||||
} else {
|
} else {
|
||||||
format!("{base}/v1/chat/completions")
|
format!("{base}/v1/responses")
|
||||||
};
|
};
|
||||||
|
|
||||||
// 解析模型名和推理等级 (支持 model@level 或 model#level 格式)
|
// 解析模型名和推理等级 (支持 model@level 或 model#level 格式)
|
||||||
let (actual_model, reasoning_effort) = Self::parse_model_with_effort(model);
|
let (actual_model, reasoning_effort) = Self::parse_model_with_effort(model);
|
||||||
|
|
||||||
|
// 获取本地系统信息
|
||||||
|
let os_name = Self::get_os_name();
|
||||||
|
let arch_name = Self::get_arch_name();
|
||||||
|
|
||||||
|
// Responses API 请求体格式 (input 必须是数组)
|
||||||
let mut body = json!({
|
let mut body = json!({
|
||||||
"model": actual_model,
|
"model": actual_model,
|
||||||
"messages": [
|
"input": [{ "role": "user", "content": test_prompt }],
|
||||||
{ "role": "system", "content": "" },
|
|
||||||
{ "role": "assistant", "content": "" },
|
|
||||||
{ "role": "user", "content": "hi" }
|
|
||||||
],
|
|
||||||
"max_tokens": 1,
|
|
||||||
"temperature": 0,
|
|
||||||
"stream": true
|
"stream": true
|
||||||
});
|
});
|
||||||
|
|
||||||
// 如果是推理模型,添加 reasoning_effort
|
// 如果是推理模型,添加 reasoning_effort
|
||||||
if let Some(effort) = reasoning_effort {
|
if let Some(effort) = reasoning_effort {
|
||||||
body["reasoning_effort"] = json!(effort);
|
body["reasoning"] = json!({ "effort": effort });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 严格按照 Codex CLI 请求格式设置 headers
|
||||||
let response = client
|
let response = client
|
||||||
.post(&url)
|
.post(&url)
|
||||||
.header("Authorization", format!("Bearer {}", auth.api_key))
|
.header("authorization", format!("Bearer {}", auth.api_key))
|
||||||
.header("Content-Type", "application/json")
|
.header("content-type", "application/json")
|
||||||
|
.header("accept", "text/event-stream")
|
||||||
|
.header("accept-encoding", "identity")
|
||||||
|
.header(
|
||||||
|
"user-agent",
|
||||||
|
format!("codex_cli_rs/0.80.0 ({os_name} 15.7.2; {arch_name}) Terminal"),
|
||||||
|
)
|
||||||
|
.header("originator", "codex_cli_rs")
|
||||||
.timeout(timeout)
|
.timeout(timeout)
|
||||||
.json(&body)
|
.json(&body)
|
||||||
.send()
|
.send()
|
||||||
@@ -308,10 +373,10 @@ impl StreamCheckService {
|
|||||||
if let Some(chunk) = stream.next().await {
|
if let Some(chunk) = stream.next().await {
|
||||||
match chunk {
|
match chunk {
|
||||||
Ok(_) => Ok((status, model.to_string())),
|
Ok(_) => Ok((status, model.to_string())),
|
||||||
Err(e) => Err(AppError::Message(format!("读取流失败: {e}"))),
|
Err(e) => Err(AppError::Message(format!("Stream read failed: {e}"))),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Err(AppError::Message("未收到响应数据".to_string()))
|
Err(AppError::Message("No response data received".to_string()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -321,6 +386,7 @@ impl StreamCheckService {
|
|||||||
base_url: &str,
|
base_url: &str,
|
||||||
auth: &AuthInfo,
|
auth: &AuthInfo,
|
||||||
model: &str,
|
model: &str,
|
||||||
|
test_prompt: &str,
|
||||||
timeout: std::time::Duration,
|
timeout: std::time::Duration,
|
||||||
) -> Result<(u16, String), AppError> {
|
) -> Result<(u16, String), AppError> {
|
||||||
let base = base_url.trim_end_matches('/');
|
let base = base_url.trim_end_matches('/');
|
||||||
@@ -328,7 +394,7 @@ impl StreamCheckService {
|
|||||||
|
|
||||||
let body = json!({
|
let body = json!({
|
||||||
"model": model,
|
"model": model,
|
||||||
"messages": [{ "role": "user", "content": "hi" }],
|
"messages": [{ "role": "user", "content": test_prompt }],
|
||||||
"max_tokens": 1,
|
"max_tokens": 1,
|
||||||
"temperature": 0,
|
"temperature": 0,
|
||||||
"stream": true
|
"stream": true
|
||||||
@@ -355,10 +421,10 @@ impl StreamCheckService {
|
|||||||
if let Some(chunk) = stream.next().await {
|
if let Some(chunk) = stream.next().await {
|
||||||
match chunk {
|
match chunk {
|
||||||
Ok(_) => Ok((status, model.to_string())),
|
Ok(_) => Ok((status, model.to_string())),
|
||||||
Err(e) => Err(AppError::Message(format!("读取流失败: {e}"))),
|
Err(e) => Err(AppError::Message(format!("Stream read failed: {e}"))),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Err(AppError::Message("未收到响应数据".to_string()))
|
Err(AppError::Message("No response data received".to_string()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -373,7 +439,6 @@ impl StreamCheckService {
|
|||||||
/// 解析模型名和推理等级 (支持 model@level 或 model#level 格式)
|
/// 解析模型名和推理等级 (支持 model@level 或 model#level 格式)
|
||||||
/// 返回 (实际模型名, Option<推理等级>)
|
/// 返回 (实际模型名, Option<推理等级>)
|
||||||
fn parse_model_with_effort(model: &str) -> (String, Option<String>) {
|
fn parse_model_with_effort(model: &str) -> (String, Option<String>) {
|
||||||
// 查找 @ 或 # 分隔符
|
|
||||||
if let Some(pos) = model.find('@').or_else(|| model.find('#')) {
|
if let Some(pos) = model.find('@').or_else(|| model.find('#')) {
|
||||||
let actual_model = model[..pos].to_string();
|
let actual_model = model[..pos].to_string();
|
||||||
let effort = model[pos + 1..].to_string();
|
let effort = model[pos + 1..].to_string();
|
||||||
@@ -386,17 +451,14 @@ impl StreamCheckService {
|
|||||||
|
|
||||||
fn should_retry(msg: &str) -> bool {
|
fn should_retry(msg: &str) -> bool {
|
||||||
let lower = msg.to_lowercase();
|
let lower = msg.to_lowercase();
|
||||||
lower.contains("timeout")
|
lower.contains("timeout") || lower.contains("abort") || lower.contains("timed out")
|
||||||
|| lower.contains("abort")
|
|
||||||
|| lower.contains("中断")
|
|
||||||
|| lower.contains("超时")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn map_request_error(e: reqwest::Error) -> AppError {
|
fn map_request_error(e: reqwest::Error) -> AppError {
|
||||||
if e.is_timeout() {
|
if e.is_timeout() {
|
||||||
AppError::Message("请求超时".to_string())
|
AppError::Message("Request timeout".to_string())
|
||||||
} else if e.is_connect() {
|
} else if e.is_connect() {
|
||||||
AppError::Message(format!("连接失败: {e}"))
|
AppError::Message(format!("Connection failed: {e}"))
|
||||||
} else {
|
} else {
|
||||||
AppError::Message(e.to_string())
|
AppError::Message(e.to_string())
|
||||||
}
|
}
|
||||||
@@ -443,6 +505,26 @@ impl StreamCheckService {
|
|||||||
.map(|m| m.as_str().trim().to_string())
|
.map(|m| m.as_str().trim().to_string())
|
||||||
.filter(|value| !value.is_empty())
|
.filter(|value| !value.is_empty())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 获取操作系统名称(映射为 Claude CLI 使用的格式)
|
||||||
|
fn get_os_name() -> &'static str {
|
||||||
|
match std::env::consts::OS {
|
||||||
|
"macos" => "MacOS",
|
||||||
|
"linux" => "Linux",
|
||||||
|
"windows" => "Windows",
|
||||||
|
other => other,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 获取 CPU 架构名称(映射为 Claude CLI 使用的格式)
|
||||||
|
fn get_arch_name() -> &'static str {
|
||||||
|
match std::env::consts::ARCH {
|
||||||
|
"aarch64" => "arm64",
|
||||||
|
"x86_64" => "x86_64",
|
||||||
|
"x86" => "x86",
|
||||||
|
other => other,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@@ -467,9 +549,10 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_should_retry() {
|
fn test_should_retry() {
|
||||||
assert!(StreamCheckService::should_retry("请求超时"));
|
assert!(StreamCheckService::should_retry("Request timeout"));
|
||||||
assert!(StreamCheckService::should_retry("request timeout"));
|
assert!(StreamCheckService::should_retry("request timed out"));
|
||||||
assert!(!StreamCheckService::should_retry("API Key 无效"));
|
assert!(StreamCheckService::should_retry("connection abort"));
|
||||||
|
assert!(!StreamCheckService::should_retry("API Key invalid"));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -497,4 +580,33 @@ mod tests {
|
|||||||
assert_eq!(model, "gpt-4o-mini");
|
assert_eq!(model, "gpt-4o-mini");
|
||||||
assert_eq!(effort, None);
|
assert_eq!(effort, None);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_get_os_name() {
|
||||||
|
let os_name = StreamCheckService::get_os_name();
|
||||||
|
// 确保返回非空字符串
|
||||||
|
assert!(!os_name.is_empty());
|
||||||
|
// 在 macOS 上应该返回 "MacOS"
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
assert_eq!(os_name, "MacOS");
|
||||||
|
// 在 Linux 上应该返回 "Linux"
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
assert_eq!(os_name, "Linux");
|
||||||
|
// 在 Windows 上应该返回 "Windows"
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
assert_eq!(os_name, "Windows");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_get_arch_name() {
|
||||||
|
let arch_name = StreamCheckService::get_arch_name();
|
||||||
|
// 确保返回非空字符串
|
||||||
|
assert!(!arch_name.is_empty());
|
||||||
|
// 在 ARM64 上应该返回 "arm64"
|
||||||
|
#[cfg(target_arch = "aarch64")]
|
||||||
|
assert_eq!(arch_name, "arm64");
|
||||||
|
// 在 x86_64 上应该返回 "x86_64"
|
||||||
|
#[cfg(target_arch = "x86_64")]
|
||||||
|
assert_eq!(arch_name, "x86_64");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { useState, useEffect } from "react";
|
|||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
|
import { Textarea } from "@/components/ui/textarea";
|
||||||
import { Label } from "@/components/ui/label";
|
import { Label } from "@/components/ui/label";
|
||||||
import { Alert, AlertDescription } from "@/components/ui/alert";
|
import { Alert, AlertDescription } from "@/components/ui/alert";
|
||||||
import { Save, Loader2 } from "lucide-react";
|
import { Save, Loader2 } from "lucide-react";
|
||||||
@@ -25,6 +26,7 @@ export function ModelTestConfigPanel() {
|
|||||||
claudeModel: "claude-haiku-4-5-20251001",
|
claudeModel: "claude-haiku-4-5-20251001",
|
||||||
codexModel: "gpt-5.1-codex@low",
|
codexModel: "gpt-5.1-codex@low",
|
||||||
geminiModel: "gemini-3-pro-preview",
|
geminiModel: "gemini-3-pro-preview",
|
||||||
|
testPrompt: "Who are you?",
|
||||||
});
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -43,6 +45,7 @@ export function ModelTestConfigPanel() {
|
|||||||
claudeModel: data.claudeModel,
|
claudeModel: data.claudeModel,
|
||||||
codexModel: data.codexModel,
|
codexModel: data.codexModel,
|
||||||
geminiModel: data.geminiModel,
|
geminiModel: data.geminiModel,
|
||||||
|
testPrompt: data.testPrompt || "Who are you?",
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
setError(String(e));
|
setError(String(e));
|
||||||
@@ -66,6 +69,7 @@ export function ModelTestConfigPanel() {
|
|||||||
claudeModel: config.claudeModel,
|
claudeModel: config.claudeModel,
|
||||||
codexModel: config.codexModel,
|
codexModel: config.codexModel,
|
||||||
geminiModel: config.geminiModel,
|
geminiModel: config.geminiModel,
|
||||||
|
testPrompt: config.testPrompt || "Who are you?",
|
||||||
};
|
};
|
||||||
await saveStreamCheckConfig(parsed);
|
await saveStreamCheckConfig(parsed);
|
||||||
toast.success(t("streamCheck.configSaved"), {
|
toast.success(t("streamCheck.configSaved"), {
|
||||||
@@ -189,6 +193,21 @@ export function ModelTestConfigPanel() {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* 检查提示词配置 */}
|
||||||
|
<div className="space-y-2">
|
||||||
|
<Label htmlFor="testPrompt">{t("streamCheck.testPrompt")}</Label>
|
||||||
|
<Textarea
|
||||||
|
id="testPrompt"
|
||||||
|
value={config.testPrompt}
|
||||||
|
onChange={(e) =>
|
||||||
|
setConfig({ ...config, testPrompt: e.target.value })
|
||||||
|
}
|
||||||
|
placeholder="Who are you?"
|
||||||
|
rows={2}
|
||||||
|
className="min-h-[60px]"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex justify-end">
|
<div className="flex justify-end">
|
||||||
|
|||||||
@@ -1192,7 +1192,8 @@
|
|||||||
"checkParams": "Check Parameters",
|
"checkParams": "Check Parameters",
|
||||||
"timeout": "Timeout (seconds)",
|
"timeout": "Timeout (seconds)",
|
||||||
"maxRetries": "Max Retries",
|
"maxRetries": "Max Retries",
|
||||||
"degradedThreshold": "Degraded Threshold (ms)"
|
"degradedThreshold": "Degraded Threshold (ms)",
|
||||||
|
"testPrompt": "Test Prompt"
|
||||||
},
|
},
|
||||||
"proxyConfig": {
|
"proxyConfig": {
|
||||||
"proxyEnabled": "Proxy Enabled",
|
"proxyEnabled": "Proxy Enabled",
|
||||||
|
|||||||
@@ -1186,7 +1186,8 @@
|
|||||||
"checkParams": "チェックパラメーター",
|
"checkParams": "チェックパラメーター",
|
||||||
"timeout": "タイムアウト(秒)",
|
"timeout": "タイムアウト(秒)",
|
||||||
"maxRetries": "最大リトライ回数",
|
"maxRetries": "最大リトライ回数",
|
||||||
"degradedThreshold": "劣化しきい値(ミリ秒)"
|
"degradedThreshold": "劣化しきい値(ミリ秒)",
|
||||||
|
"testPrompt": "テストプロンプト"
|
||||||
},
|
},
|
||||||
"proxyConfig": {
|
"proxyConfig": {
|
||||||
"proxyEnabled": "プロキシ有効",
|
"proxyEnabled": "プロキシ有効",
|
||||||
|
|||||||
@@ -1192,7 +1192,8 @@
|
|||||||
"checkParams": "检查参数",
|
"checkParams": "检查参数",
|
||||||
"timeout": "超时时间(秒)",
|
"timeout": "超时时间(秒)",
|
||||||
"maxRetries": "最大重试次数",
|
"maxRetries": "最大重试次数",
|
||||||
"degradedThreshold": "降级阈值(毫秒)"
|
"degradedThreshold": "降级阈值(毫秒)",
|
||||||
|
"testPrompt": "检查提示词"
|
||||||
},
|
},
|
||||||
"proxyConfig": {
|
"proxyConfig": {
|
||||||
"proxyEnabled": "代理总开关",
|
"proxyEnabled": "代理总开关",
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ export interface StreamCheckConfig {
|
|||||||
claudeModel: string;
|
claudeModel: string;
|
||||||
codexModel: string;
|
codexModel: string;
|
||||||
geminiModel: string;
|
geminiModel: string;
|
||||||
|
testPrompt: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface StreamCheckResult {
|
export interface StreamCheckResult {
|
||||||
|
|||||||
Reference in New Issue
Block a user