fetch_ml/cli/src/commands/annotate.zig
Jeremie Fraeys 68062831b0
refactor(cli): remove redundant doc comments from command files
Removed duplicate help text from doc comments:
- log.zig: Removed usage examples (in printUsage)
- annotate.zig: Removed usage examples (in printUsage)
- experiment.zig: Removed usage examples (in printUsage)

Rationale: printUsage() already contains detailed help text.
Doc comments should not duplicate this information.

All tests pass.
2026-03-05 11:06:28 -05:00

136 lines
4.8 KiB
Zig

const std = @import("std");
const config = @import("../config.zig");
const db = @import("../db.zig");
const core = @import("../core.zig");
const manifest_lib = @import("../manifest.zig");
pub fn execute(allocator: std.mem.Allocator, args: []const []const u8) !void {
var flags = core.flags.CommonFlags{};
var command_args = try core.flags.parseCommon(allocator, args, &flags);
defer command_args.deinit(allocator);
core.output.setMode(if (flags.json) .json else .text);
if (flags.help) {
return printUsage();
}
if (command_args.items.len < 1) {
std.log.err("Usage: ml annotate <run_id> [options]", .{});
return error.MissingArgument;
}
const run_id = command_args.items[0];
// Parse metadata options
const text = core.flags.parseKVFlag(command_args.items, "text");
const hypothesis = core.flags.parseKVFlag(command_args.items, "hypothesis");
const outcome = core.flags.parseKVFlag(command_args.items, "outcome");
const confidence = core.flags.parseKVFlag(command_args.items, "confidence");
const privacy = core.flags.parseKVFlag(command_args.items, "privacy");
const author = core.flags.parseKVFlag(command_args.items, "author");
// Check that at least one option is provided
if (text == null and hypothesis == null and outcome == null and privacy == null) {
std.log.err("No metadata provided. Use --text, --hypothesis, --outcome, or --privacy", .{});
return error.MissingMetadata;
}
const cfg = try config.Config.load(allocator);
defer {
var mut_cfg = cfg;
mut_cfg.deinit(allocator);
}
// Get DB path
const db_path = try cfg.getDBPath(allocator);
defer allocator.free(db_path);
var database = try db.DB.init(allocator, db_path);
defer database.close();
// Verify run exists
const check_sql = "SELECT 1 FROM ml_runs WHERE run_id = ?;";
const check_stmt = try database.prepare(check_sql);
defer db.DB.finalize(check_stmt);
try db.DB.bindText(check_stmt, 1, run_id);
const has_row = try db.DB.step(check_stmt);
if (!has_row) {
std.log.err("Run not found: {s}", .{run_id});
return error.RunNotFound;
}
// Add text note as a tag
if (text) |t| {
try addTag(allocator, &database, run_id, "note", t, author);
}
// Add hypothesis
if (hypothesis) |h| {
try addTag(allocator, &database, run_id, "hypothesis", h, author);
}
// Add outcome
if (outcome) |o| {
try addTag(allocator, &database, run_id, "outcome", o, author);
if (confidence) |c| {
try addTag(allocator, &database, run_id, "confidence", c, author);
}
}
// Add privacy level
if (privacy) |p| {
try addTag(allocator, &database, run_id, "privacy", p, author);
}
// Checkpoint WAL
database.checkpointOnExit();
if (flags.json) {
std.debug.print("{{\"success\":true,\"run_id\":\"{s}\",\"action\":\"note_added\"}}\n", .{run_id});
} else {
std.debug.print("Added note to run {s}\n", .{run_id[0..8]});
}
}
fn addTag(
allocator: std.mem.Allocator,
database: *db.DB,
run_id: []const u8,
key: []const u8,
value: []const u8,
author: ?[]const u8,
) !void {
const full_value = if (author) |a|
try std.fmt.allocPrint(allocator, "{s} (by {s})", .{ value, a })
else
try allocator.dupe(u8, value);
defer allocator.free(full_value);
const sql = "INSERT INTO ml_tags (run_id, key, value) VALUES (?, ?, ?);";
const stmt = try database.prepare(sql);
defer db.DB.finalize(stmt);
try db.DB.bindText(stmt, 1, run_id);
try db.DB.bindText(stmt, 2, key);
try db.DB.bindText(stmt, 3, full_value);
_ = try db.DB.step(stmt);
}
fn printUsage() !void {
std.debug.print("Usage: ml annotate <run_id> [options]\n\n", .{});
std.debug.print("Add metadata annotations to a run.\n\n", .{});
std.debug.print("Options:\n", .{});
std.debug.print("\t--text <string>\t\tFree-form annotation\n", .{});
std.debug.print("\t--hypothesis <string>\tResearch hypothesis\n", .{});
std.debug.print("\t--outcome <status>\tOutcome: validates/refutes/inconclusive\n", .{});
std.debug.print("\t--confidence <0-1>\tConfidence in outcome\n", .{});
std.debug.print("\t--privacy <level>\tPrivacy: private/team/public\n", .{});
std.debug.print("\t--author <name>\t\tAuthor of the annotation\n", .{});
std.debug.print("\t--help, -h\t\tShow this help\n", .{});
std.debug.print("\t--json\t\t\tOutput structured JSON\n\n", .{});
std.debug.print("Examples:\n", .{});
std.debug.print("\tml annotate abc123 --text \"Try lr=3e-4 next\"\n", .{});
std.debug.print("\tml annotate abc123 --hypothesis \"LR scaling helps\"\n", .{});
std.debug.print("\tml annotate abc123 --outcome validates --confidence 0.9\n", .{});
}