output.ts

 1// SPDX-FileCopyrightText: Amolith <amolith@secluded.site>
 2//
 3// SPDX-License-Identifier: GPL-3.0-or-later
 4
 5import type { AgentEvent } from "@mariozechner/pi-agent-core";
 6
 7const MAX_OUTPUT_LINES = 20;
 8
 9export interface OutputOptions {
10  verbose: boolean;
11}
12
13export function formatToolOutput(output: string): string {
14  const lines = output.split(/\r?\n/);
15  if (lines.length <= MAX_OUTPUT_LINES) {
16    return output;
17  }
18
19  const truncated = lines.slice(0, MAX_OUTPUT_LINES).join("\n");
20  return `${truncated}\n(+${lines.length - MAX_OUTPUT_LINES} more lines)`;
21}
22
23export function createEventLogger(options: OutputOptions) {
24  return (event: AgentEvent) => {
25    if (!options.verbose) return;
26
27    if (event.type === "tool_execution_start") {
28      console.error(`\n[tool] ${event.toolName}`);
29      console.error(JSON.stringify(event.args, null, 2));
30    }
31
32    if (event.type === "tool_execution_end") {
33      if (event.isError) {
34        console.error("[tool error]");
35        console.error(String(event.result));
36        return;
37      }
38
39      const text = (event.result?.content ?? [])
40        .filter((content: { type: string }) => content.type === "text")
41        .map((content: { text?: string }) => content.text ?? "")
42        .join("");
43
44      if (text) {
45        console.error(formatToolOutput(text));
46      }
47    }
48  };
49}
50
51export function printUsageSummary(
52  usage:
53    | {
54        cost?: { total?: number };
55        totalTokens?: number;
56        output?: number;
57        input?: number;
58      }
59    | undefined,
60  requestCount?: number,
61) {
62  if (!usage) return;
63
64  const tokens = usage.totalTokens ?? (usage.output ?? 0) + (usage.input ?? 0);
65  const rawCost = usage.cost?.total;
66  const cost =
67    typeof rawCost === "number" && !Number.isNaN(rawCost) && rawCost > 0
68      ? rawCost
69      : undefined;
70
71  let line = `\nusage: ${tokens} tokens`;
72  if (requestCount !== undefined && requestCount > 0) {
73    line += ` across ${requestCount} ${requestCount === 1 ? "request" : "requests"}`;
74  }
75  if (cost !== undefined) {
76    line += `, cost $${cost.toFixed(4)}`;
77  }
78  console.error(line);
79}