feat: add CLI Apps settings MVP
This commit is contained in:
@@ -1,3 +1,5 @@
|
||||
import type { ToolProgressEvent } from "@/lib/types";
|
||||
|
||||
/** Drop duplicate tool_call objects (same id or identical formatted trace). */
|
||||
export function dedupeToolCallsForUi(calls: unknown): unknown[] {
|
||||
if (!Array.isArray(calls) || calls.length === 0) return [];
|
||||
@@ -40,15 +42,60 @@ export function formatToolCallTrace(call: unknown): string | null {
|
||||
}
|
||||
|
||||
const VALID_PHASES = new Set(["start", "end", "error"]);
|
||||
const PHASE_RANK: Record<string, number> = { start: 1, end: 2, error: 3 };
|
||||
|
||||
export function toolTraceLinesFromEvents(events: unknown): string[] {
|
||||
export function normalizeToolProgressEvents(events: unknown): ToolProgressEvent[] {
|
||||
if (!Array.isArray(events)) return [];
|
||||
const seen = new Set<string>();
|
||||
const lines: string[] = [];
|
||||
const out: ToolProgressEvent[] = [];
|
||||
for (const event of events) {
|
||||
if (!event || typeof event !== "object") continue;
|
||||
const phase = (event as { phase?: unknown }).phase;
|
||||
const record = event as ToolProgressEvent;
|
||||
const phase = record.phase;
|
||||
if (!(phase && typeof phase === "string" && VALID_PHASES.has(phase))) continue;
|
||||
const name = typeof record.name === "string" ? record.name : "";
|
||||
const functionName =
|
||||
typeof (record as { function?: { name?: unknown } }).function?.name === "string"
|
||||
? String((record as { function?: { name?: unknown } }).function?.name)
|
||||
: "";
|
||||
if (!name && !functionName) continue;
|
||||
out.push(record);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
function toolEventKey(event: ToolProgressEvent): string {
|
||||
if (event.call_id) return `call:${event.call_id}`;
|
||||
return formatToolCallTrace(event) ?? JSON.stringify(event);
|
||||
}
|
||||
|
||||
export function mergeToolProgressEvents(
|
||||
previous: ToolProgressEvent[] | undefined,
|
||||
incoming: ToolProgressEvent[],
|
||||
): ToolProgressEvent[] {
|
||||
if (!previous?.length) return incoming;
|
||||
if (!incoming.length) return previous;
|
||||
const next = [...previous];
|
||||
const indexByKey = new Map(next.map((event, index) => [toolEventKey(event), index]));
|
||||
for (const event of incoming) {
|
||||
const key = toolEventKey(event);
|
||||
const existingIndex = indexByKey.get(key);
|
||||
if (existingIndex === undefined) {
|
||||
indexByKey.set(key, next.length);
|
||||
next.push(event);
|
||||
continue;
|
||||
}
|
||||
const existing = next[existingIndex];
|
||||
const incomingRank = PHASE_RANK[String(event.phase)] ?? 0;
|
||||
const existingRank = PHASE_RANK[String(existing.phase)] ?? 0;
|
||||
next[existingIndex] = incomingRank >= existingRank ? { ...existing, ...event } : existing;
|
||||
}
|
||||
return next;
|
||||
}
|
||||
|
||||
export function toolTraceLinesFromEvents(events: unknown): string[] {
|
||||
const seen = new Set<string>();
|
||||
const lines: string[] = [];
|
||||
for (const event of normalizeToolProgressEvents(events)) {
|
||||
const callId = (event as { call_id?: unknown }).call_id;
|
||||
if (callId && typeof callId === "string") {
|
||||
if (seen.has(callId)) continue;
|
||||
|
||||
Reference in New Issue
Block a user