(props: {
logs: ModelCallLogRecord[];
onRefresh: () => void;
onClear: () => void;
})
| 2211 | } |
| 2212 | |
| 2213 | function RawLogsPanel(props: { |
| 2214 | logs: ModelCallLogRecord[]; |
| 2215 | onRefresh: () => void; |
| 2216 | onClear: () => void; |
| 2217 | }) { |
| 2218 | const { t } = useI18n(); |
| 2219 | const latestLogs = [...props.logs].sort((a, b) => b.sequence - a.sequence); |
| 2220 | const llmLogCount = props.logs.filter((log) => log.kind === "llm").length; |
| 2221 | const embeddingLogCount = props.logs.filter((log) => log.kind === "embedding").length; |
| 2222 | return ( |
| 2223 | <div className="space-y-3"> |
| 2224 | <div className="flex items-center justify-between gap-2"> |
| 2225 | <div className="min-w-0 text-xs text-muted-foreground"> |
| 2226 | <div>{t(`浏览器缓存 ${props.logs.length} 条`, `Browser cache: ${props.logs.length} item(s)`)}</div> |
| 2227 | <div className="mt-1 flex flex-wrap gap-1.5"> |
| 2228 | <Badge className="border-border bg-muted text-muted-foreground">LLM {llmLogCount}</Badge> |
| 2229 | <Badge className="border-border bg-muted text-muted-foreground">Embedding {embeddingLogCount}</Badge> |
| 2230 | </div> |
| 2231 | </div> |
| 2232 | <div className="flex gap-2"> |
| 2233 | <Button variant="outline" size="sm" onClick={props.onRefresh}> |
| 2234 | {t("同步日志", "Sync logs")} |
| 2235 | </Button> |
| 2236 | <Button variant="outline" size="sm" onClick={props.onClear} disabled={props.logs.length === 0}> |
| 2237 | <Trash2 className="h-4 w-4" /> |
| 2238 | {t("删除日志", "Delete logs")} |
| 2239 | </Button> |
| 2240 | </div> |
| 2241 | </div> |
| 2242 | {latestLogs.length === 0 ? ( |
| 2243 | <EmptyState title={t("暂无原始日志", "No raw logs yet")} description={t("上传、检索或对话触发 LLM / Embedding 后会显示原始请求和返回。", "Raw requests and responses appear after upload, search, or chat triggers LLM / Embedding calls.")} /> |
| 2244 | ) : latestLogs.map((log) => ( |
| 2245 | <Card key={log.id} className={cn(log.status === "FAILED" && "border-red-200 bg-red-50/60")}> |
| 2246 | <CardContent className="space-y-3"> |
| 2247 | <div className="flex items-start justify-between gap-3"> |
| 2248 | <div className="min-w-0"> |
| 2249 | <div className="flex items-center gap-2"> |
| 2250 | <Badge>{log.kind === "llm" ? "LLM" : "Embedding"}</Badge> |
| 2251 | <div className="truncate text-sm font-semibold">{log.operation}</div> |
| 2252 | </div> |
| 2253 | <div className="mt-1 text-xs text-muted-foreground"> |
| 2254 | #{log.sequence} · {formatDate(log.createdAt)} · {log.durationMs} {t("毫秒", "ms")} |
| 2255 | </div> |
| 2256 | </div> |
| 2257 | <Badge className={log.status === "FAILED" ? "border-red-200 bg-red-50 text-red-700" : ""}> |
| 2258 | {log.status === "FAILED" ? t("失败", "Failed") : t("成功", "Succeeded")} |
| 2259 | </Badge> |
| 2260 | </div> |
| 2261 | <JsonBlock title={t("请求", "Request")} value={log.request} compact preserveRaw /> |
| 2262 | {log.response !== undefined ? <JsonBlock title={t("返回", "Response")} value={log.response} compact preserveRaw /> : null} |
| 2263 | {log.error ? ( |
| 2264 | <div className="rounded-md bg-red-50 p-2 text-xs leading-5 text-red-700">{log.error}</div> |
| 2265 | ) : null} |
| 2266 | </CardContent> |
| 2267 | </Card> |
| 2268 | ))} |
| 2269 | </div> |
| 2270 | ); |
nothing calls this directly
no test coverage detected