mirror of
https://github.com/farion1231/cc-switch.git
synced 2026-07-29 09:37:37 +08:00
refactor(proxy): replace panic-prone unwrap/expect with safe patterns
Harden the proxy module against panics by removing optimistic
unwrap()/expect() calls in favor of pattern matching and graceful
fallbacks:
- copilot_optimizer/cache_injector: bind Value::Array/String directly
instead of is_array()+as_array().unwrap(); use is_none_or and in-place
string mutation
- hyper_client: gate the raw-write path with if-let + filter instead of
has_cases + unwrap()
- gemini_shadow: recover poisoned RwLock via into_inner() rather than
panicking, with warn logging
- streaming_codex_chat: replace expect("tool state exists") with
let-else (return/continue)
- merge_tool_results: early-return when messages is absent
- sse: fall back to from_utf8_lossy on the UTF-8 boundary slice
No behavior change on the happy path; all proxy tests pass.
This commit is contained in:
@@ -6,7 +6,7 @@
|
||||
|
||||
use serde_json::Value;
|
||||
use std::collections::{HashMap, VecDeque};
|
||||
use std::sync::RwLock;
|
||||
use std::sync::{RwLock, RwLockReadGuard, RwLockWriteGuard};
|
||||
|
||||
/// Composite key for a Gemini shadow session.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
@@ -145,7 +145,7 @@ impl GeminiShadowStore {
|
||||
let key = GeminiShadowKey::new(provider_id, session_id);
|
||||
let turn = GeminiAssistantTurn::new(assistant_content, tool_calls);
|
||||
|
||||
let mut inner = self.inner.write().expect("gemini shadow lock poisoned");
|
||||
let mut inner = self.write_inner();
|
||||
Self::touch_session_order(&mut inner.session_order, &key);
|
||||
|
||||
let snapshot = {
|
||||
@@ -193,7 +193,7 @@ impl GeminiShadowStore {
|
||||
session_id: &str,
|
||||
) -> Option<GeminiShadowSessionSnapshot> {
|
||||
let key = GeminiShadowKey::new(provider_id, session_id);
|
||||
let mut inner = self.inner.write().expect("gemini shadow lock poisoned");
|
||||
let mut inner = self.write_inner();
|
||||
let snapshot = inner
|
||||
.sessions
|
||||
.get(&key)
|
||||
@@ -208,7 +208,7 @@ impl GeminiShadowStore {
|
||||
#[allow(dead_code)]
|
||||
pub fn clear_session(&self, provider_id: &str, session_id: &str) -> bool {
|
||||
let key = GeminiShadowKey::new(provider_id, session_id);
|
||||
let mut inner = self.inner.write().expect("gemini shadow lock poisoned");
|
||||
let mut inner = self.write_inner();
|
||||
let removed = inner.sessions.remove(&key).is_some();
|
||||
if removed {
|
||||
Self::remove_key_from_order(&mut inner.session_order, &key);
|
||||
@@ -219,7 +219,7 @@ impl GeminiShadowStore {
|
||||
/// Remove all sessions for a provider.
|
||||
#[allow(dead_code)]
|
||||
pub fn clear_provider(&self, provider_id: &str) -> usize {
|
||||
let mut inner = self.inner.write().expect("gemini shadow lock poisoned");
|
||||
let mut inner = self.write_inner();
|
||||
let keys: Vec<_> = inner
|
||||
.sessions
|
||||
.keys()
|
||||
@@ -236,11 +236,21 @@ impl GeminiShadowStore {
|
||||
/// Number of tracked sessions.
|
||||
#[allow(dead_code)]
|
||||
pub fn session_count(&self) -> usize {
|
||||
self.inner
|
||||
.read()
|
||||
.expect("gemini shadow lock poisoned")
|
||||
.sessions
|
||||
.len()
|
||||
self.read_inner().sessions.len()
|
||||
}
|
||||
|
||||
fn read_inner(&self) -> RwLockReadGuard<'_, GeminiShadowInner> {
|
||||
self.inner.read().unwrap_or_else(|poisoned| {
|
||||
log::warn!("[GeminiShadow] recovering poisoned read lock");
|
||||
poisoned.into_inner()
|
||||
})
|
||||
}
|
||||
|
||||
fn write_inner(&self) -> RwLockWriteGuard<'_, GeminiShadowInner> {
|
||||
self.inner.write().unwrap_or_else(|poisoned| {
|
||||
log::warn!("[GeminiShadow] recovering poisoned write lock");
|
||||
poisoned.into_inner()
|
||||
})
|
||||
}
|
||||
|
||||
fn snapshot_session(
|
||||
|
||||
@@ -442,7 +442,9 @@ impl ChatToResponsesState {
|
||||
|
||||
if should_add {
|
||||
let assigned = self.next_output_index();
|
||||
let state = self.tools.get_mut(&chat_index).expect("tool state exists");
|
||||
let Some(state) = self.tools.get_mut(&chat_index) else {
|
||||
return events;
|
||||
};
|
||||
state.added = true;
|
||||
if state.call_id.is_empty() {
|
||||
state.call_id = format!("call_{chat_index}");
|
||||
@@ -655,7 +657,9 @@ impl ChatToResponsesState {
|
||||
.unwrap_or(false)
|
||||
{
|
||||
let assigned = self.next_output_index();
|
||||
let state = self.tools.get_mut(&key).expect("tool state exists");
|
||||
let Some(state) = self.tools.get_mut(&key) else {
|
||||
continue;
|
||||
};
|
||||
state.added = true;
|
||||
if state.call_id.is_empty() {
|
||||
state.call_id = format!("call_{key}");
|
||||
@@ -687,7 +691,9 @@ impl ChatToResponsesState {
|
||||
events.push(event);
|
||||
}
|
||||
|
||||
let state = self.tools.get_mut(&key).expect("tool state exists");
|
||||
let Some(state) = self.tools.get_mut(&key) else {
|
||||
continue;
|
||||
};
|
||||
let output_index = state.output_index.unwrap_or(0);
|
||||
let arguments = canonicalize_tool_arguments_str(&state.arguments);
|
||||
let item = response_function_call_item(
|
||||
|
||||
Reference in New Issue
Block a user