mirror of
https://github.com/farion1231/cc-switch.git
synced 2026-08-04 11:43:57 +08:00
fix(proxy): reduce unnecessary Copilot premium interaction consumption
- Fix request classification: treat messages containing tool_result as agent continuation instead of user-initiated, preventing false premium charges on every tool call - Add subagent detection via __SUBAGENT_MARKER__ and metadata._agent_ fallback, setting x-interaction-type=conversation-subagent - Add deterministic x-interaction-id derived from session ID to group requests into a single billing interaction - Add orphan tool_result sanitization to prevent upstream API errors that could cause retries and duplicate billing - Reorder pipeline: classify (on original body) → sanitize → merge → warmup, ensuring classification sees raw tool_result semantics - Enable warmup downgrade by default with gpt-5-mini model - Enhance session ID extraction priority chain for Copilot cache keys - Detect infinite whitespace bug in streaming tool call arguments
This commit is contained in:
@@ -81,13 +81,50 @@ pub fn transform_claude_request_for_api_format(
|
||||
provider: &Provider,
|
||||
api_format: &str,
|
||||
) -> Result<serde_json::Value, ProxyError> {
|
||||
match api_format {
|
||||
"openai_responses" => {
|
||||
let cache_key = provider
|
||||
// Copilot 场景:优先从 metadata.user_id 提取 session ID 作为 cache key
|
||||
// 格式: "uuid_sessionId" → 提取 "_" 后面的部分作为 session 标识
|
||||
// 同一会话的请求共享 cache key,提升 Copilot 缓存命中率
|
||||
let is_copilot = provider
|
||||
.meta
|
||||
.as_ref()
|
||||
.and_then(|m| m.provider_type.as_deref())
|
||||
== Some("github_copilot")
|
||||
|| provider
|
||||
.settings_config
|
||||
.get("baseUrl")
|
||||
.and_then(|v| v.as_str())
|
||||
.is_some_and(|u| u.contains("githubcopilot.com"));
|
||||
let session_cache_key: Option<String> = if is_copilot {
|
||||
let metadata = body.get("metadata");
|
||||
// Session 提取优先级(与 forwarder 和 session.rs 统一):
|
||||
// 1. metadata.user_id 中的 _session_ 后缀
|
||||
// 2. metadata.session_id(直接字段)
|
||||
metadata
|
||||
.and_then(|m| m.get("user_id"))
|
||||
.and_then(|v| v.as_str())
|
||||
.and_then(super::super::session::parse_session_from_user_id)
|
||||
.or_else(|| {
|
||||
metadata
|
||||
.and_then(|m| m.get("session_id"))
|
||||
.and_then(|v| v.as_str())
|
||||
.filter(|s| !s.is_empty())
|
||||
.map(|s| s.to_string())
|
||||
})
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let cache_key = session_cache_key
|
||||
.as_deref()
|
||||
.or_else(|| {
|
||||
provider
|
||||
.meta
|
||||
.as_ref()
|
||||
.and_then(|m| m.prompt_cache_key.as_deref())
|
||||
.unwrap_or(&provider.id);
|
||||
})
|
||||
.unwrap_or(&provider.id);
|
||||
match api_format {
|
||||
"openai_responses" => {
|
||||
// Codex OAuth (ChatGPT Plus/Pro 反代) 需要在请求体里强制 store: false
|
||||
// + include: ["reasoning.encrypted_content"],由 transform 层统一处理。
|
||||
let is_codex_oauth = provider
|
||||
@@ -455,6 +492,7 @@ impl ProviderAdapter for ClaudeAdapter {
|
||||
HeaderName::from_static("x-interaction-type"),
|
||||
HeaderValue::from_static("conversation-agent"),
|
||||
),
|
||||
// x-interaction-id 由 forwarder 按需注入(仅在有 session 时)
|
||||
(
|
||||
HeaderName::from_static("x-vscode-user-agent-library-version"),
|
||||
HeaderValue::from_static("electron-fetch"),
|
||||
|
||||
Reference in New Issue
Block a user