fix: improve streaming response stability for Windows

- Add proper HTTP client timeout configuration for ClaudeCustomProvider
  - connect_timeout: 30 seconds
  - timeout: 10 minutes (for long streaming responses)
  - tcp_keepalive: 60 seconds (keep connection alive)

- Enhance SSE streaming response headers:
  - Cache-Control: no-cache, no-store, must-revalidate
  - X-Accel-Buffering: no (disable nginx proxy buffering)
  - Transfer-Encoding: chunked

These changes should help resolve streaming truncation issues on Windows
when using Kiro OAuth credentials through aiclient2api proxy.

Fixes #68

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
coso
2026-01-07 00:14:51 +08:00
co-authored by Claude Opus 4.5
parent 25acd04798
commit 3b7055ed52
2 changed files with 27 additions and 5 deletions
+18 -2
View File
@@ -4,6 +4,7 @@ use crate::models::openai::{ChatCompletionRequest, ContentPart, MessageContent};
use reqwest::Client;
use serde::{Deserialize, Serialize};
use std::error::Error;
use std::time::Duration;
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct ClaudeCustomConfig {
@@ -17,11 +18,26 @@ pub struct ClaudeCustomProvider {
pub client: Client,
}
/// 创建配置好的 HTTP 客户端
///
/// 配置说明:
/// - connect_timeout: 连接超时 30 秒
/// - timeout: 总超时 10 分钟(流式响应可能很长)
/// - 不设置 pool_idle_timeout 以保持连接活跃
fn create_http_client() -> Client {
Client::builder()
.connect_timeout(Duration::from_secs(30))
.timeout(Duration::from_secs(600)) // 10 分钟总超时,支持长时间流式响应
.tcp_keepalive(Duration::from_secs(60)) // TCP keepalive 保持连接活跃
.build()
.unwrap_or_else(|_| Client::new())
}
impl Default for ClaudeCustomProvider {
fn default() -> Self {
Self {
config: ClaudeCustomConfig::default(),
client: Client::new(),
client: create_http_client(),
}
}
}
@@ -39,7 +55,7 @@ impl ClaudeCustomProvider {
base_url,
enabled: true,
},
client: Client::new(),
client: create_http_client(),
}
}
@@ -611,8 +611,10 @@ pub async fn call_provider_anthropic(
return Response::builder()
.status(StatusCode::OK)
.header(header::CONTENT_TYPE, "text/event-stream")
.header(header::CACHE_CONTROL, "no-cache")
.header(header::CACHE_CONTROL, "no-cache, no-store, must-revalidate")
.header("Connection", "keep-alive")
.header("X-Accel-Buffering", "no") // 禁用 nginx 等代理的缓冲
.header("Transfer-Encoding", "chunked")
.body(Body::from_stream(stream))
.unwrap_or_else(|_| {
(
@@ -823,8 +825,10 @@ pub async fn call_provider_anthropic(
return Response::builder()
.status(StatusCode::OK)
.header(header::CONTENT_TYPE, "text/event-stream")
.header(header::CACHE_CONTROL, "no-cache")
.header(header::CACHE_CONTROL, "no-cache, no-store, must-revalidate")
.header("Connection", "keep-alive")
.header("X-Accel-Buffering", "no") // 禁用 nginx 等代理的缓冲
.header("Transfer-Encoding", "chunked")
.body(Body::from_stream(stream))
.unwrap_or_else(|_| {
(
@@ -1834,8 +1838,10 @@ pub async fn call_provider_openai(
return Response::builder()
.status(StatusCode::OK)
.header(header::CONTENT_TYPE, "text/event-stream")
.header(header::CACHE_CONTROL, "no-cache")
.header(header::CACHE_CONTROL, "no-cache, no-store, must-revalidate")
.header("Connection", "keep-alive")
.header("X-Accel-Buffering", "no") // 禁用 nginx 等代理的缓冲
.header("Transfer-Encoding", "chunked")
.body(Body::from_stream(stream))
.unwrap_or_else(|_| {
(