UNPKG

@botpress/adk-cli

Version:

Command-line interface for the Botpress Agent Development Kit (ADK)

245 lines (243 loc) 8.58 kB
// @bun import { parseFilterTokens } from "./chunk-wk6ynbab.js"; import { SqliteTraceStore } from "./chunk-2re833ev.js"; import { ensureJsonOnlyFormat } from "./chunk-7sfagm12.js"; import { findAgentRootOrFail } from "./chunk-kk3h6qaj.js"; import { createCliLogger, source_default } from "./chunk-gzwt1qdr.js"; import"./chunk-nxy2ya5r.js"; import"./chunk-wzj4dc7n.js"; import { AgentProject } from "./chunk-p0hjqn4r.js"; import"./chunk-np5wcwfv.js"; import"./chunk-dq2xpa24.js"; import"./chunk-6w0knnta.js"; import"./chunk-40x04ckt.js"; import"./chunk-t76d8fxx.js"; import"./chunk-nh2akp42.js"; import"./chunk-0fdvzjbh.js"; import"./chunk-2a5b6azq.js"; import"./chunk-vay209b5.js"; import"./chunk-3xrpxgq4.js"; import"./chunk-rfm3jr1m.js"; import"./chunk-w346ejn9.js"; import"./chunk-knvm2anf.js"; import"./chunk-65h5trb5.js"; import"./chunk-s2akeqpw.js"; import"./chunk-6771vrjp.js"; import"./chunk-g8mm42v1.js"; import"./chunk-50hzjdck.js"; import"./chunk-nn2jb0x0.js"; import"./chunk-v8xvth6j.js"; import"./chunk-kkk13rcb.js"; import"./chunk-ytpp1kam.js"; import"./chunk-na956zz3.js"; import"./chunk-f4bw8q7c.js"; import"./chunk-0v8vgrns.js"; import"./chunk-54qt5g7m.js"; import"./chunk-dhs2bg35.js"; // src/commands/adk-conversations.ts function formatDuration(ms) { if (ms == null) return "---"; if (ms < 1) return "<1ms"; if (ms < 1000) return `${Math.round(ms)}ms`; if (ms < 60000) return `${(ms / 1000).toFixed(1)}s`; return `${(ms / 60000).toFixed(1)}m`; } function formatTimestamp(ts) { return new Date(ts).toLocaleString("en-US", { hour12: false }); } function statusIcon(hasErrors) { return hasErrors ? source_default.red("\u2717") : source_default.green("\u2713"); } function buildConversationTurns(allSpans, options) { const byTrace = new Map; for (const span of allSpans) { const group = byTrace.get(span.id.trace) ?? []; group.push(span); byTrace.set(span.id.trace, group); } const turns = []; for (const [traceId, spans] of byTrace) { const roots = spans.filter((s) => s.id.parent === null); const sorted = (roots.length > 0 ? roots : spans).sort((a, b) => a.timing.startedAt - b.timing.startedAt); const root = sorted[0]; const hasError = spans.some((s) => s.status === "error"); const hasRunning = spans.some((s) => s.status === "running"); const status = hasError ? "error" : hasRunning ? "running" : "ok"; let trigger = null; for (const span of spans) { if (span.name === "request.incoming" || span.name === "handler.conversation" || span.name === "handler.trigger") { trigger = span.label && span.label !== span.name ? `${span.name} (${span.label})` : span.name; break; } } const toolCalls = []; for (const span of spans) { if (span.name === "autonomous.tool" || span.name === "handler.action") { toolCalls.push({ name: span.label && span.label !== span.name ? span.label : span.name, status: span.status, duration: span.timing.duration ?? null, error: span.error }); } } const errors = []; for (const span of spans) { if (span.status === "error" && span.error) { errors.push(`[${span.name}] ${span.error}`); } } let llmContent; if (options.includeLlm) { llmContent = []; for (const span of spans) { if (span.name === "cognitive.request" || span.name === "autonomous.iteration") { llmContent.push({ name: span.name, data: span.data }); } } } turns.push({ traceId, startedAt: root.timing.startedAt, duration: root.timing.duration ?? null, status, trigger, toolCalls, errors, llmContent }); } return turns.sort((a, b) => a.startedAt - b.startedAt); } function formatConversationDetail(conversationId, turns) { const lines = []; lines.push(`Conversation ${source_default.bold(conversationId)} (${turns.length} turn${turns.length === 1 ? "" : "s"})`); lines.push(""); for (let i = 0;i < turns.length; i++) { const turn = turns[i]; const time = formatTimestamp(turn.startedAt); const dur = formatDuration(turn.duration); const icon = turn.status === "error" ? source_default.red("\u2717") : turn.status === "running" ? source_default.yellow("\u25CC") : source_default.green("\u2713"); const traceShort = source_default.dim(turn.traceId.slice(0, 8)); lines.push(` ${i + 1}. [${time}] ${icon} ${dur} ${traceShort} ${turn.trigger ?? "unknown"}`); for (const tool of turn.toolCalls) { const toolIcon = tool.status === "error" ? source_default.red("\u2717") : source_default.green("\u2713"); lines.push(` ${toolIcon} ${tool.name} ${source_default.dim(formatDuration(tool.duration))}`); if (tool.error) { lines.push(` ${source_default.red(tool.error)}`); } } for (const err of turn.errors) { if (!turn.toolCalls.some((t) => t.error && err.endsWith(`] ${t.error}`))) { lines.push(` ${source_default.red(err)}`); } } if (turn.llmContent?.length) { lines.push(` ${source_default.dim("\u2500\u2500\u2500 LLM \u2500\u2500\u2500")}`); for (const entry of turn.llmContent) { const dataStr = typeof entry.data === "string" ? entry.data : JSON.stringify(entry.data, null, 2); lines.push(` ${source_default.magenta(entry.name)}`); for (const line of dataStr.split(` `)) { lines.push(` ${source_default.dim(line)}`); } } } } return lines.join(` `); } async function adkConversationsList(tokens, options) { const logger = createCliLogger({ format: options?.format }); ensureJsonOnlyFormat(options.format); const { options: filters, errors } = parseFilterTokens(tokens); if (errors.length > 0) { for (const err of errors) { logger.error(`${err.message}${err.suggestion ? ` \u2014 ${err.suggestion}` : ""}`); } process.exit(1); } const agentRoot = await findAgentRootOrFail(process.cwd()); const project = await AgentProject.load(agentRoot); const botId = project.agentInfo?.devId ?? project.agentInfo?.botId; const store = new SqliteTraceStore(agentRoot); try { const conversations = store.listConversations({ limit: filters.limit ?? 20, since: filters.since, botId }); if (conversations.length === 0) { logger.info("No conversations found.").result({ conversations: [] }); return; } for (const conv of conversations) { const lastTime = formatTimestamp(conv.lastSeen); const icon = statusIcon(conv.hasErrors); const idDisplay = conv.conversationId; const channel = conv.integration ? `${conv.integration}${conv.channel ? `/${conv.channel}` : ""}` : ""; logger.info(`${icon} ${source_default.bold(idDisplay)} ${source_default.dim(lastTime)} ${conv.traceCount} turn${conv.traceCount === 1 ? "" : "s"}${channel ? ` ${source_default.cyan(channel)}` : ""}`).result({ conversationId: conv.conversationId, firstSeen: new Date(conv.firstSeen).toISOString(), lastSeen: new Date(conv.lastSeen).toISOString(), traceCount: conv.traceCount, integration: conv.integration, channel: conv.channel, hasErrors: conv.hasErrors }); } } finally { store.close(); } } async function adkConversationsShow(conversationId, options) { const logger = createCliLogger({ format: options?.format }); ensureJsonOnlyFormat(options.format); const agentRoot = await findAgentRootOrFail(process.cwd()); const store = new SqliteTraceStore(agentRoot); try { const spans = store.getTraceByAttribute("conversationId", conversationId); if (spans.length === 0) { logger.info(`No traces found for conversation "${conversationId}".`).result({ conversationId, turns: [] }); return; } const turns = buildConversationTurns(spans, { includeLlm: options.includeLlm }); logger.info(formatConversationDetail(conversationId, turns)).result({ conversationId, turnCount: turns.length, turns: turns.map((t) => ({ traceId: t.traceId, startedAt: new Date(t.startedAt).toISOString(), duration: t.duration, status: t.status, trigger: t.trigger, toolCalls: t.toolCalls, errors: t.errors, ...t.llmContent ? { llmContent: t.llmContent } : {} })) }); } finally { store.close(); } } export { adkConversationsShow, adkConversationsList };