mirror of
https://github.com/aiclientproxy/proxycast.git
synced 2026-09-24 23:10:56 +08:00
fix: 修复 Flow Monitor 响应内容显示和提供商显示问题 (#75)
- 修复凭证池模式下非流式响应的 content 和 body 字段未保存问题 - 从响应 JSON 中提取 content、tool_calls 和 usage 信息 - 添加 provider_id 字段用于显示实际的提供商名称 - 优化清理完成弹窗样式,使用自定义 Modal 组件 - 美化排序下拉框,使用 Radix UI Select 组件 - 添加详细的调试日志用于追踪 Flow 显示问题 - 修复所有测试代码中缺少 provider_id 字段的问题
This commit is contained in:
@@ -35,6 +35,12 @@ export function CleanupDialog({
|
||||
const [cleanupType, setCleanupType] = useState<CleanupType>("ByTime");
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [showSuccess, setShowSuccess] = useState(false);
|
||||
const [cleanupResult, setCleanupResult] = useState<{
|
||||
cleaned_count: number;
|
||||
cleaned_files: number;
|
||||
freed_bytes: number;
|
||||
} | null>(null);
|
||||
|
||||
// 时间清理选项
|
||||
const [retentionDays, setRetentionDays] = useState(7);
|
||||
@@ -105,14 +111,9 @@ export function CleanupDialog({
|
||||
|
||||
const result = await flowMonitorApi.cleanupFlows(request);
|
||||
|
||||
// 显示清理结果
|
||||
const sizeText = formatBytes(result.freed_bytes);
|
||||
alert(
|
||||
`清理完成!\n删除了 ${result.cleaned_count} 条记录\n清理了 ${result.cleaned_files} 个文件\n释放了 ${sizeText} 空间`,
|
||||
);
|
||||
|
||||
onSuccess();
|
||||
onClose();
|
||||
// 保存清理结果并显示成功提示
|
||||
setCleanupResult(result);
|
||||
setShowSuccess(true);
|
||||
} catch (e) {
|
||||
setError(e instanceof Error ? e.message : String(e));
|
||||
} finally {
|
||||
@@ -120,6 +121,13 @@ export function CleanupDialog({
|
||||
}
|
||||
};
|
||||
|
||||
const handleSuccessClose = () => {
|
||||
setShowSuccess(false);
|
||||
setCleanupResult(null);
|
||||
onSuccess();
|
||||
onClose();
|
||||
};
|
||||
|
||||
const formatBytes = (bytes: number): string => {
|
||||
if (bytes === 0) return "0 B";
|
||||
const k = 1024;
|
||||
@@ -149,6 +157,105 @@ export function CleanupDialog({
|
||||
"IFlow",
|
||||
];
|
||||
|
||||
// 如果显示成功提示,渲染成功对话框
|
||||
if (showSuccess && cleanupResult) {
|
||||
return (
|
||||
<Modal isOpen={isOpen} onClose={handleSuccessClose} maxWidth="max-w-md">
|
||||
<div className="px-6 py-8">
|
||||
{/* 成功图标 */}
|
||||
<div className="flex justify-center mb-6">
|
||||
<div className="relative">
|
||||
<div className="w-20 h-20 bg-green-100 dark:bg-green-900/30 rounded-full flex items-center justify-center">
|
||||
<svg
|
||||
className="w-10 h-10 text-green-600 dark:text-green-400"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={2}
|
||||
d="M5 13l4 4L19 7"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
{/* 动画圆环 */}
|
||||
<div className="absolute inset-0 w-20 h-20 border-4 border-green-200 dark:border-green-800 rounded-full animate-ping opacity-75"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 标题 */}
|
||||
<h3 className="text-2xl font-bold text-center mb-2">清理完成!</h3>
|
||||
<p className="text-center text-muted-foreground mb-6">
|
||||
已成功清理日志数据
|
||||
</p>
|
||||
|
||||
{/* 清理统计 */}
|
||||
<div className="space-y-3 mb-6">
|
||||
<div className="flex items-center justify-between p-4 bg-muted/50 rounded-lg">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="w-10 h-10 bg-blue-100 dark:bg-blue-900/30 rounded-lg flex items-center justify-center">
|
||||
<Hash className="w-5 h-5 text-blue-600 dark:text-blue-400" />
|
||||
</div>
|
||||
<span className="text-sm text-muted-foreground">删除记录</span>
|
||||
</div>
|
||||
<span className="text-lg font-semibold">
|
||||
{cleanupResult.cleaned_count} 条
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-between p-4 bg-muted/50 rounded-lg">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="w-10 h-10 bg-purple-100 dark:bg-purple-900/30 rounded-lg flex items-center justify-center">
|
||||
<svg
|
||||
className="w-5 h-5 text-purple-600 dark:text-purple-400"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={2}
|
||||
d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<span className="text-sm text-muted-foreground">清理文件</span>
|
||||
</div>
|
||||
<span className="text-lg font-semibold">
|
||||
{cleanupResult.cleaned_files} 个
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-between p-4 bg-gradient-to-r from-green-50 to-emerald-50 dark:from-green-900/20 dark:to-emerald-900/20 rounded-lg border border-green-200 dark:border-green-800">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="w-10 h-10 bg-green-100 dark:bg-green-900/50 rounded-lg flex items-center justify-center">
|
||||
<HardDrive className="w-5 h-5 text-green-600 dark:text-green-400" />
|
||||
</div>
|
||||
<span className="text-sm font-medium text-green-900 dark:text-green-100">
|
||||
释放空间
|
||||
</span>
|
||||
</div>
|
||||
<span className="text-lg font-bold text-green-600 dark:text-green-400">
|
||||
{formatBytes(cleanupResult.freed_bytes)}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 确认按钮 */}
|
||||
<button
|
||||
onClick={handleSuccessClose}
|
||||
className="w-full py-3 bg-primary hover:bg-primary/90 text-white font-medium rounded-lg transition-colors"
|
||||
>
|
||||
完成
|
||||
</button>
|
||||
</div>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal isOpen={isOpen} onClose={onClose} maxWidth="max-w-2xl">
|
||||
{/* 标题 */}
|
||||
|
||||
@@ -383,7 +383,10 @@ function FlowDetailHeader({
|
||||
<div className="rounded-lg border bg-card p-4">
|
||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
|
||||
<InfoItem label="模型" value={flow.request.model} />
|
||||
<InfoItem label="提供商" value={flow.metadata.provider} />
|
||||
<InfoItem
|
||||
label="提供商"
|
||||
value={flow.metadata.provider_id || flow.metadata.provider}
|
||||
/>
|
||||
<InfoItem label="类型" value={formatFlowType(flow.flow_type)} />
|
||||
<InfoItem
|
||||
label="创建时间"
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import React, { useState, useEffect, useCallback } from "react";
|
||||
import * as Select from "@radix-ui/react-select";
|
||||
import {
|
||||
CheckCircle2,
|
||||
XCircle,
|
||||
@@ -21,6 +22,7 @@ import {
|
||||
Activity,
|
||||
Bell,
|
||||
BellOff,
|
||||
Check,
|
||||
} from "lucide-react";
|
||||
import {
|
||||
flowMonitorApi,
|
||||
@@ -570,16 +572,59 @@ export function FlowList({
|
||||
)}
|
||||
</button>
|
||||
)}
|
||||
<select
|
||||
className="rounded border bg-background px-2 py-1 text-sm"
|
||||
<Select.Root
|
||||
value={sortBy}
|
||||
onChange={(e) => setSortBy(e.target.value as FlowSortBy)}
|
||||
onValueChange={(value) => setSortBy(value as FlowSortBy)}
|
||||
>
|
||||
<option value="created_at">按时间</option>
|
||||
<option value="duration">按耗时</option>
|
||||
<option value="total_tokens">按 Token</option>
|
||||
<option value="model">按模型</option>
|
||||
</select>
|
||||
<Select.Trigger className="inline-flex min-w-[120px] items-center justify-between gap-2 rounded-md border border-input bg-background px-3 py-1.5 text-sm shadow-sm transition-colors hover:bg-accent hover:text-accent-foreground focus:outline-none focus:ring-2 focus:ring-ring">
|
||||
<Select.Value />
|
||||
<Select.Icon>
|
||||
<ChevronDown className="h-4 w-4 opacity-50" />
|
||||
</Select.Icon>
|
||||
</Select.Trigger>
|
||||
<Select.Portal>
|
||||
<Select.Content className="relative z-50 min-w-[120px] overflow-hidden rounded-md border border-border bg-white dark:bg-gray-900 text-foreground shadow-lg animate-in fade-in-80 data-[side=bottom]:slide-in-from-top-2 data-[side=top]:slide-in-from-bottom-2">
|
||||
<Select.Viewport className="p-1 bg-white dark:bg-gray-900">
|
||||
<Select.Item
|
||||
value="created_at"
|
||||
className="relative flex cursor-pointer select-none items-center rounded-sm px-8 py-2 text-sm outline-none transition-colors hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50"
|
||||
>
|
||||
<Select.ItemIndicator className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
|
||||
<Check className="h-4 w-4" />
|
||||
</Select.ItemIndicator>
|
||||
<Select.ItemText>按时间</Select.ItemText>
|
||||
</Select.Item>
|
||||
<Select.Item
|
||||
value="duration"
|
||||
className="relative flex cursor-pointer select-none items-center rounded-sm px-8 py-2 text-sm outline-none transition-colors hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50"
|
||||
>
|
||||
<Select.ItemIndicator className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
|
||||
<Check className="h-4 w-4" />
|
||||
</Select.ItemIndicator>
|
||||
<Select.ItemText>按耗时</Select.ItemText>
|
||||
</Select.Item>
|
||||
<Select.Item
|
||||
value="total_tokens"
|
||||
className="relative flex cursor-pointer select-none items-center rounded-sm px-8 py-2 text-sm outline-none transition-colors hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50"
|
||||
>
|
||||
<Select.ItemIndicator className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
|
||||
<Check className="h-4 w-4" />
|
||||
</Select.ItemIndicator>
|
||||
<Select.ItemText>按 Token</Select.ItemText>
|
||||
</Select.Item>
|
||||
<Select.Item
|
||||
value="model"
|
||||
className="relative flex cursor-pointer select-none items-center rounded-sm px-8 py-2 text-sm outline-none transition-colors hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50"
|
||||
>
|
||||
<Select.ItemIndicator className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
|
||||
<Check className="h-4 w-4" />
|
||||
</Select.ItemIndicator>
|
||||
<Select.ItemText>按模型</Select.ItemText>
|
||||
</Select.Item>
|
||||
</Select.Viewport>
|
||||
</Select.Content>
|
||||
</Select.Portal>
|
||||
</Select.Root>
|
||||
<button
|
||||
onClick={() => setSortDesc(!sortDesc)}
|
||||
className="rounded border px-2 py-1 text-sm hover:bg-muted"
|
||||
|
||||
@@ -333,6 +333,7 @@ export interface RoutingInfo {
|
||||
*/
|
||||
export interface FlowMetadata {
|
||||
provider: ProviderType;
|
||||
provider_id?: string; // 实际的 provider ID(如 "deepseek", "moonshot" 等)
|
||||
credential_id?: string;
|
||||
credential_name?: string;
|
||||
retry_count: number;
|
||||
|
||||
Reference in New Issue
Block a user