mirror of
https://github.com/lightpanda-io/browser.git
synced 2026-06-12 01:56:19 -04:00
agent: add --verbosity flag to control stderr output
This commit is contained in:
@@ -121,6 +121,20 @@ fn dumpValidator(_: Allocator, args: *std.process.ArgIterator) !?DumpFormat {
|
||||
|
||||
pub const AiProvider = std.meta.Tag(zenai.provider.Client);
|
||||
|
||||
/// Controls how chatty `agent` mode is on stderr. Defined here (rather
|
||||
/// than alongside Terminal) so it stays alongside the CLI flag and stays
|
||||
/// reachable from Config without an agent-side import cycle.
|
||||
pub const AgentVerbosity = enum {
|
||||
/// Only the final answer (stdout) and errors.
|
||||
quiet,
|
||||
/// + REPL banners, status/retry messages, direct-command results,
|
||||
/// and the `[tool: ...]` line for each LLM tool call. Default.
|
||||
normal,
|
||||
/// + the matching `[result: ...]` body for each tool call.
|
||||
/// Required by the harness in benchmarks/, which parses both lines.
|
||||
verbose,
|
||||
};
|
||||
|
||||
fn waitScriptFileValidator(allocator: Allocator, args: *std.process.ArgIterator) !?[:0]const u8 {
|
||||
const path = args.next() orelse {
|
||||
log.fatal(.app, "missing argument value", .{ .arg = "--wait-script-file" });
|
||||
@@ -190,6 +204,7 @@ const Commands = cli.Builder(.{
|
||||
.{ .name = "task", .type = ?[]const u8 },
|
||||
.{ .name = "task_attachments", .type = []const u8, .multiple = true },
|
||||
.{ .name = "mcp", .type = bool },
|
||||
.{ .name = "verbosity", .type = AgentVerbosity, .default = AgentVerbosity.normal },
|
||||
},
|
||||
.shared_options = CommonOptions,
|
||||
},
|
||||
@@ -299,7 +314,16 @@ pub fn wsMaxConcurrent(self: *const Config) u8 {
|
||||
|
||||
pub fn logLevel(self: *const Config) ?log.Level {
|
||||
return switch (self.mode) {
|
||||
inline .serve, .fetch, .mcp, .agent => |opts| opts.log_level,
|
||||
// In agent mode, the page itself spams `console.error` (mapped to
|
||||
// log.warn(.js, ...) in webapi/Console.zig). The build default is
|
||||
// `.warn`, which is far too chatty for non-verbose runs — most
|
||||
// sites trip third-party scripts that the agent can ignore. So
|
||||
// when the user hasn't set --log-level, let --verbosity pick.
|
||||
.agent => |opts| opts.log_level orelse switch (opts.verbosity) {
|
||||
.quiet, .normal => .err,
|
||||
.verbose => null,
|
||||
},
|
||||
inline .serve, .fetch, .mcp => |opts| opts.log_level,
|
||||
else => unreachable,
|
||||
};
|
||||
}
|
||||
@@ -770,6 +794,17 @@ pub fn printUsageAndExit(self: *const Config, success: bool) void {
|
||||
\\ MCP client. Requires --provider; cannot be combined
|
||||
\\ with --task, -i, or a script file.
|
||||
\\
|
||||
\\--verbosity Stderr chatter level: quiet, normal, or verbose.
|
||||
\\ Default: normal. quiet keeps only the final answer
|
||||
\\ (stdout) and errors; normal adds REPL banners,
|
||||
\\ direct-command results, and `[tool: ...]` lines;
|
||||
\\ verbose also prints each `[result: ...]` body
|
||||
\\ (required by the benchmarks harness). When --log-level
|
||||
\\ isn't set, quiet/normal raise it to err to suppress
|
||||
\\ page-side console.error spam; verbose keeps the
|
||||
\\ build default (warn). Pass --log-level explicitly
|
||||
\\ to override.
|
||||
\\
|
||||
\\The API key is read from the environment:
|
||||
\\ANTHROPIC_API_KEY, OPENAI_API_KEY, or GOOGLE_API_KEY.
|
||||
\\Ollama does not require an API key.
|
||||
|
||||
@@ -218,7 +218,7 @@ pub fn init(allocator: std.mem.Allocator, app: *App, opts: Config.Agent) !*Self
|
||||
.allocator = allocator,
|
||||
.ai_client = ai_client,
|
||||
.tool_executor = tool_executor,
|
||||
.terminal = Terminal.init(history_path),
|
||||
.terminal = Terminal.init(history_path, opts.verbosity, will_repl),
|
||||
.cmd_executor = undefined,
|
||||
.verifier = .{ .tool_executor = tool_executor },
|
||||
.recorder = .init(allocator, recorder_path),
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
const std = @import("std");
|
||||
const lp = @import("lightpanda");
|
||||
const browser_tools = lp.tools;
|
||||
const Config = lp.Config;
|
||||
const SlashCommand = @import("SlashCommand.zig");
|
||||
const c = @cImport({
|
||||
@cInclude("linenoise.h");
|
||||
@@ -15,7 +16,22 @@ const ansi_cyan = "\x1b[36m";
|
||||
const ansi_green = "\x1b[32m";
|
||||
const ansi_red = "\x1b[31m";
|
||||
|
||||
const Verbosity = Config.AgentVerbosity;
|
||||
|
||||
fn atLeast(level: Verbosity, min: Verbosity) bool {
|
||||
return @intFromEnum(level) >= @intFromEnum(min);
|
||||
}
|
||||
|
||||
history_path: ?[:0]const u8,
|
||||
verbosity: Verbosity,
|
||||
/// True when the user can type at us. Tool results are always shown
|
||||
/// here, regardless of verbosity, because in the REPL every tool call
|
||||
/// is something the user just asked for (a slash command, or natural
|
||||
/// language they sent to the LLM) — suppressing the body would leave
|
||||
/// them blind. The `--verbosity` dial only matters in non-interactive
|
||||
/// runs (one-shot `--task`, scripts, `--mcp`), where LLM tool traces
|
||||
/// are noise.
|
||||
is_repl: bool,
|
||||
|
||||
const CommandInfo = struct { name: [:0]const u8, hint: [:0]const u8 };
|
||||
|
||||
@@ -66,14 +82,14 @@ pub fn setSlashSchemas(schemas: []const SlashCommand.SchemaInfo) void {
|
||||
slash_schemas = schemas;
|
||||
}
|
||||
|
||||
pub fn init(history_path: ?[:0]const u8) Self {
|
||||
pub fn init(history_path: ?[:0]const u8, verbosity: Verbosity, is_repl: bool) Self {
|
||||
c.linenoiseSetMultiLine(1);
|
||||
c.linenoiseSetCompletionCallback(&completionCallback);
|
||||
c.linenoiseSetHintsCallback(&hintsCallback);
|
||||
if (history_path) |path| {
|
||||
_ = c.linenoiseHistoryLoad(path.ptr);
|
||||
}
|
||||
return .{ .history_path = history_path };
|
||||
return .{ .history_path = history_path, .verbosity = verbosity, .is_repl = is_repl };
|
||||
}
|
||||
|
||||
const completion_buf_len = 256;
|
||||
@@ -383,11 +399,13 @@ pub fn printAssistant(_: *Self, text: []const u8) void {
|
||||
|
||||
/// Print the result of an action command (GOTO, CLICK, ...) to stderr so
|
||||
/// stdout stays reserved for data-producing commands.
|
||||
pub fn printActionResult(_: *Self, text: []const u8) void {
|
||||
pub fn printActionResult(self: *Self, text: []const u8) void {
|
||||
if (!atLeast(self.verbosity, .normal)) return;
|
||||
std.debug.print("{s}\n", .{text});
|
||||
}
|
||||
|
||||
pub fn printToolCall(_: *Self, name: []const u8, args: []const u8) void {
|
||||
pub fn printToolCall(self: *Self, name: []const u8, args: []const u8) void {
|
||||
if (!self.is_repl and !atLeast(self.verbosity, .normal)) return;
|
||||
std.debug.print("\n{s}{s}[tool: {s}]{s} {s}\n", .{ ansi_dim, ansi_cyan, name, ansi_reset, args });
|
||||
}
|
||||
|
||||
@@ -399,7 +417,8 @@ pub fn printToolCall(_: *Self, name: []const u8, args: []const u8) void {
|
||||
// (1 MiB) via Agent.zig:capToolOutput.
|
||||
const max_result_display_len = 2000;
|
||||
|
||||
pub fn printToolResult(_: *Self, name: []const u8, result: []const u8) void {
|
||||
pub fn printToolResult(self: *Self, name: []const u8, result: []const u8) void {
|
||||
if (!self.is_repl and !atLeast(self.verbosity, .verbose)) return;
|
||||
const truncated = result[0..@min(result.len, max_result_display_len)];
|
||||
const ellipsis: []const u8 = if (result.len > max_result_display_len) "..." else "";
|
||||
std.debug.print("{s}{s}[result: {s}]{s} {s}{s}\n", .{ ansi_dim, ansi_green, name, ansi_reset, truncated, ellipsis });
|
||||
@@ -413,10 +432,12 @@ pub fn printErrorFmt(_: *Self, comptime fmt: []const u8, args: anytype) void {
|
||||
std.debug.print("{s}{s}Error: " ++ fmt ++ "{s}\n", .{ ansi_bold, ansi_red } ++ args ++ .{ansi_reset});
|
||||
}
|
||||
|
||||
pub fn printInfo(_: *Self, msg: []const u8) void {
|
||||
pub fn printInfo(self: *Self, msg: []const u8) void {
|
||||
if (!atLeast(self.verbosity, .normal)) return;
|
||||
std.debug.print("{s}{s}{s}\n", .{ ansi_dim, msg, ansi_reset });
|
||||
}
|
||||
|
||||
pub fn printInfoFmt(_: *Self, comptime fmt: []const u8, args: anytype) void {
|
||||
pub fn printInfoFmt(self: *Self, comptime fmt: []const u8, args: anytype) void {
|
||||
if (!atLeast(self.verbosity, .normal)) return;
|
||||
std.debug.print("{s}" ++ fmt ++ "{s}\n", .{ansi_dim} ++ args ++ .{ansi_reset});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user