Files
cursor-byok/apps/desktop/src/components/CallDetails.tsx
T

82 lines
4.0 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import type { CallDetail } from "../api";
import { JsonEditor } from "./ui/JsonEditor";
import { Tabs, type TabItem } from "./ui/Tabs";
import styles from "./CallDetails.module.scss";
const show = (value: string | number | null) => value ?? "-";
const timing = (value: number | null) => value == null ? "-" : `${value} ms`;
export function CallDetails({ detail }: { detail: CallDetail }) {
const { call, request, response_chunks: chunks, cursor_trace: cursorTrace } = detail;
const responseBody = chunks.map((chunk) => chunk.data).join("");
const responseBytes = chunks.reduce((total, chunk) => total + chunk.byte_count, 0);
const fields: Array<[string, string | number]> = [
["Call ID", call.call_id],
[t("调用类型"), call.call_kind === "cursor_official" ? t("Cursor 官方") : "LLM"],
[t("路由"), call.route === "cursor_official" ? t("Cursor 官方") : "BYOK"],
["Run ID", call.run_id],
["Conversation ID", call.conversation_id],
[t("上游调用序号"), call.provider_call_index],
["Model Hash", show(call.model_hash)],
[t("上游类型"), call.provider_type],
[t("上游地址"), call.provider_url],
[t("最终请求类型"), call.request_type],
[t("最终请求地址"), call.request_url],
["Model ID", call.model_id],
[t("显示名称"), call.display_name],
[t("思考强度"), show(call.reasoning_effort)],
["Fast", call.fast == null ? "-" : call.fast ? t("是") : t("否")],
[t("状态"), call.status],
["Finish Reason", show(call.finish_reason)],
["HTTP Status", show(call.http_status)],
["Created At", `${call.created_at_ms} · ${new Date(call.created_at_ms).toLocaleString()}`],
[t("耗时"), timing(call.duration_ms)],
["TTFB", timing(call.ttfb_ms)],
["TTFT", timing(call.ttft_ms)],
["Input Token", show(call.input_tokens)],
["Output Token", show(call.output_tokens)],
["Total Token", show(call.total_tokens)],
["Cache Read Token", show(call.cache_read_tokens)],
["Cache Write Token", show(call.cache_write_tokens)],
["Reasoning Token", show(call.reasoning_tokens)],
[t("消息数"), call.message_count],
[t("工具数"), call.tool_count],
[t("详细记录"), call.detailed ? t("是") : t("否")],
["Error Kind", show(call.error_kind)],
["Error Message", show(call.error_message)],
];
const tabs: TabItem[] = [
{ value: "call", label: t("调用信息"), content: <section>
<dl className={styles.details}>{fields.map(([label, value]) => <div key={label}><dt>{label}</dt><dd><code>{value}</code></dd></div>)}</dl>
</section> },
{ value: "request", label: t("请求"), content: <section>
{request ? <>
<div className={styles.meta}>{t("字节数")}:{request.byte_count}</div>
<h4>{t("请求头")}</h4><JsonEditor ariaLabel={t("请求头")} value={JSON.stringify(request.headers)} readOnly />
<h4>{t("请求体")}</h4><JsonEditor ariaLabel={t("请求体")} value={JSON.stringify(request.body)} readOnly detail />
</> : <div className={styles.empty}>{t("未记录请求内容,请开启详细记录后重试。")}</div>}
</section> },
{ value: "response", label: t("响应流"), content: <section>
{chunks.length > 0 ? <>
<div className={styles.meta}>{t("分块数")}:{chunks.length} · {t("字节数")}:{responseBytes}</div>
<JsonEditor ariaLabel={t("响应流")} value={responseBody} readOnly detail />
</> : <div className={styles.empty}>{t("未记录响应内容,请开启详细记录后重试。")}</div>}
</section> },
];
if (cursorTrace) tabs.push({ value: "cursor-trace", label: t("Cursor 追踪"), content: <section>
<div className={styles.meta}>
Request ID:{cursorTrace.trace.request_id} · {t("工件数")}:{cursorTrace.artifacts.length}
</div>
<JsonEditor
ariaLabel={t("Cursor 追踪")}
value={JSON.stringify({ trace: cursorTrace.trace, artifacts: cursorTrace.artifacts })}
readOnly
detail
/>
</section> });
return <div className={styles.root}><Tabs items={tabs} /></div>;
}