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.
136 lines
4.8 KiB
Zig
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", .{});
|
|
}
|