diff --git a/src-tauri/src/commands/flow_monitor_cmd.rs b/src-tauri/src/commands/flow_monitor_cmd.rs index cef182789..613c142dc 100644 --- a/src-tauri/src/commands/flow_monitor_cmd.rs +++ b/src-tauri/src/commands/flow_monitor_cmd.rs @@ -507,6 +507,7 @@ pub async fn cleanup_flows( // 清理所有数据 - 使用当前时间 + 1天,确保删除所有数据 let before = chrono::Utc::now() + chrono::Duration::days(1); + // 清理文件存储 if let Some(file_store) = monitor.0.file_store() { match file_store.cleanup(before) { Ok(result) => { @@ -520,6 +521,14 @@ pub async fn cleanup_flows( } } } + + // 清理内存存储 + { + let memory_store = monitor.0.memory_store(); + let mut store = memory_store.write().await; + store.clear(); + tracing::info!("已清理内存存储"); + } } CleanupType::ByTime => { @@ -546,6 +555,15 @@ pub async fn cleanup_flows( } } } + + // 清理内存存储 + { + let memory_store = monitor.0.memory_store(); + let mut store = memory_store.write().await; + let memory_cleaned = store.cleanup_before(before); + cleaned_count += memory_cleaned; + tracing::info!("已清理内存存储 {} 条记录", memory_cleaned); + } } CleanupType::ByCount => { @@ -563,6 +581,15 @@ pub async fn cleanup_flows( } } } + + // 清理内存存储 - 对于按数量清理,清空所有内存数据 + // 因为文件存储已按保留策略清理,内存应保持一致 + { + let memory_store = monitor.0.memory_store(); + let mut store = memory_store.write().await; + store.clear(); + tracing::info!("已清理内存存储"); + } } CleanupType::ByStatus => { diff --git a/src-tauri/src/flow_monitor/memory_store.rs b/src-tauri/src/flow_monitor/memory_store.rs index 46bd598c1..2ce901a7c 100644 --- a/src-tauri/src/flow_monitor/memory_store.rs +++ b/src-tauri/src/flow_monitor/memory_store.rs @@ -506,6 +506,37 @@ impl FlowMemoryStore { self.ordered_ids.clear(); } + /// 按时间清理 Flow + /// + /// 删除指定时间之前创建的所有 Flow。 + /// + /// # 参数 + /// - `before`: 截止时间,早于此时间的 Flow 将被删除 + /// + /// # 返回 + /// 删除的 Flow 数量 + pub fn cleanup_before(&mut self, before: chrono::DateTime) -> usize { + let mut to_remove = Vec::new(); + + for id in &self.ordered_ids { + if let Some(flow_lock) = self.flows.get(id) { + if let Ok(flow) = flow_lock.read() { + if flow.timestamps.created < before { + to_remove.push(id.clone()); + } + } + } + } + + let count = to_remove.len(); + for id in to_remove { + self.flows.remove(&id); + self.ordered_ids.retain(|i| i != &id); + } + + count + } + /// 驱逐最旧的 Flow fn evict_oldest(&mut self) { if let Some(oldest_id) = self.ordered_ids.pop_front() { diff --git a/src/components/flow-monitor/FlowList.tsx b/src/components/flow-monitor/FlowList.tsx index e44115825..c3ef16a31 100644 --- a/src/components/flow-monitor/FlowList.tsx +++ b/src/components/flow-monitor/FlowList.tsx @@ -327,6 +327,66 @@ export function FlowList({ } }; + // 从模型名推断实际的模型提供商 + const inferProviderFromModel = (model: string): string | null => { + const modelLower = model.toLowerCase(); + + // DeepSeek 模型 + if (modelLower.includes("deepseek")) { + return "DeepSeek"; + } + // Claude 模型 + if ( + modelLower.includes("claude") || + modelLower.includes("anthropic") || + modelLower.includes("sonnet") || + modelLower.includes("opus") || + modelLower.includes("haiku") + ) { + return "Claude"; + } + // GPT/OpenAI 模型 + if ( + modelLower.includes("gpt") || + modelLower.includes("o1") || + modelLower.includes("o3") || + modelLower.includes("chatgpt") + ) { + return "OpenAI"; + } + // Gemini 模型 + if (modelLower.includes("gemini")) { + return "Gemini"; + } + // Qwen 模型 + if (modelLower.includes("qwen") || modelLower.includes("qwq")) { + return "Qwen"; + } + // Llama 模型 + if (modelLower.includes("llama")) { + return "Llama"; + } + // Mistral 模型 + if (modelLower.includes("mistral") || modelLower.includes("mixtral")) { + return "Mistral"; + } + + return null; + }; + + // 获取显示的提供商名称(优先使用模型推断的提供商) + const getDisplayProvider = ( + metadataProvider: string, + model: string, + ): string => { + const inferredProvider = inferProviderFromModel(model); + // 如果能从模型名推断出提供商,且与凭证池不同,则显示推断的提供商 + if (inferredProvider && inferredProvider !== metadataProvider) { + return inferredProvider; + } + return metadataProvider; + }; + const getProviderColor = (provider: string) => { const colors: Record = { Kiro: "bg-purple-100 text-purple-700 dark:bg-purple-900/30 dark:text-purple-300", @@ -339,6 +399,12 @@ export function FlowList({ Qwen: "bg-cyan-100 text-cyan-700 dark:bg-cyan-900/30 dark:text-cyan-300", Antigravity: "bg-pink-100 text-pink-700 dark:bg-pink-900/30 dark:text-pink-300", + DeepSeek: + "bg-indigo-100 text-indigo-700 dark:bg-indigo-900/30 dark:text-indigo-300", + Llama: + "bg-yellow-100 text-yellow-700 dark:bg-yellow-900/30 dark:text-yellow-300", + Mistral: + "bg-teal-100 text-teal-700 dark:bg-teal-900/30 dark:text-teal-300", }; return ( colors[provider] || @@ -553,6 +619,7 @@ export function FlowList({ onCopyId={(e) => handleCopyId(e, flow.id)} getStateIcon={getStateIcon} getProviderColor={getProviderColor} + getDisplayProvider={getDisplayProvider} formatTime={formatTime} /> ))} @@ -605,6 +672,7 @@ interface FlowListItemProps { onCopyId: (e: React.MouseEvent) => void; getStateIcon: (state: FlowState) => React.ReactNode; getProviderColor: (provider: string) => string; + getDisplayProvider: (metadataProvider: string, model: string) => string; formatTime: (timestamp: string) => string; } @@ -619,6 +687,7 @@ function FlowListItem({ onCopyId, getStateIcon, getProviderColor, + getDisplayProvider, formatTime, }: FlowListItemProps) { const hasToolCalls = @@ -669,14 +738,27 @@ function FlowListItem({ {/* 提供商 */} - - {flow.metadata.provider} - + {(() => { + const displayProvider = getDisplayProvider( + flow.metadata.provider, + flow.request.model, + ); + return ( + + {displayProvider} + + ); + })()} {/* 模型 */}