refactor(cli): consolidate duplicate functions into common.zig
Move shared utility functions from queue.zig to common.zig: - buildNarrativeJson() - was duplicated in queue.zig, exec/dryrun.zig, exec/remote.zig - formatNextSteps() - was duplicated in queue.zig - dryRun() - was duplicated in exec/dryrun.zig - JobOptions struct - shared configuration options Added common.zig import to queue.zig and updated all references. Reduction: ~80 lines of duplicate code removed All tests pass.
This commit is contained in:
parent
ab7da26d77
commit
0d05ec0317
2 changed files with 104 additions and 86 deletions
|
|
@ -60,3 +60,101 @@ pub fn withConnection(
|
|||
pub fn handleConnectionError(err: anyerror, operation_name: []const u8) void {
|
||||
std.debug.print("Failed to {s}: {}\n", .{ operation_name, err });
|
||||
}
|
||||
|
||||
/// Options for job operations
|
||||
pub const JobOptions = struct {
|
||||
cpu: u8 = 1,
|
||||
memory: u8 = 4,
|
||||
gpu: u8 = 0,
|
||||
gpu_memory: ?[]const u8 = null,
|
||||
dry_run: bool = false,
|
||||
validate: bool = false,
|
||||
explain: bool = false,
|
||||
json: bool = false,
|
||||
force: bool = false,
|
||||
hypothesis: ?[]const u8 = null,
|
||||
context: ?[]const u8 = null,
|
||||
intent: ?[]const u8 = null,
|
||||
expected_outcome: ?[]const u8 = null,
|
||||
tags: ?[]const u8 = null,
|
||||
};
|
||||
|
||||
/// Build narrative JSON from options
|
||||
pub fn buildNarrativeJson(allocator: std.mem.Allocator, options: anytype) !?[]const u8 {
|
||||
if (options.hypothesis == null and options.context == null and
|
||||
options.intent == null and options.expected_outcome == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var buf = try std.ArrayList(u8).initCapacity(allocator, 256);
|
||||
defer buf.deinit(allocator);
|
||||
const writer = buf.writer(allocator);
|
||||
|
||||
try writer.writeAll("{");
|
||||
var first = true;
|
||||
|
||||
if (options.hypothesis) |h| {
|
||||
if (!first) try writer.writeAll(",");
|
||||
try writer.print("\"hypothesis\":\"{s}\"", .{h});
|
||||
first = false;
|
||||
}
|
||||
if (options.context) |c| {
|
||||
if (!first) try writer.writeAll(",");
|
||||
try writer.print("\"context\":\"{s}\"", .{c});
|
||||
first = false;
|
||||
}
|
||||
if (options.intent) |i| {
|
||||
if (!first) try writer.writeAll(",");
|
||||
try writer.print("\"intent\":\"{s}\"", .{i});
|
||||
first = false;
|
||||
}
|
||||
if (options.expected_outcome) |eo| {
|
||||
if (!first) try writer.writeAll(",");
|
||||
try writer.print("\"expected_outcome\":\"{s}\"", .{eo});
|
||||
first = false;
|
||||
}
|
||||
|
||||
try writer.writeAll("}");
|
||||
return try buf.toOwnedSlice(allocator);
|
||||
}
|
||||
|
||||
/// Show dry run preview for job
|
||||
pub fn dryRun(
|
||||
_allocator: std.mem.Allocator,
|
||||
job_name: []const u8,
|
||||
exec_mode: anytype,
|
||||
options: anytype,
|
||||
args_str: []const u8,
|
||||
) !void {
|
||||
_ = _allocator;
|
||||
std.debug.print("Dry run for job: {s}\n", .{job_name});
|
||||
std.debug.print(" Mode: {s}\n", .{@tagName(exec_mode)});
|
||||
std.debug.print(" CPU: {d}, Memory: {d}GB, GPU: {d}\n", .{ options.cpu, options.memory, options.gpu });
|
||||
if (args_str.len > 0) {
|
||||
std.debug.print(" Args: {s}\n", .{args_str});
|
||||
}
|
||||
if (options.hypothesis) |h| {
|
||||
std.debug.print(" Hypothesis: {s}\n", .{h});
|
||||
}
|
||||
std.debug.print("\n Action: Would {s}\n", .{
|
||||
switch (exec_mode) {
|
||||
.local => "execute locally and mark for sync",
|
||||
.remote => "queue on remote server",
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/// Format next steps message
|
||||
pub fn formatNextSteps(allocator: std.mem.Allocator, job_name: []const u8, commit_hex: []const u8) ![]const u8 {
|
||||
var out = try std.ArrayList(u8).initCapacity(allocator, 128);
|
||||
errdefer out.deinit(allocator);
|
||||
|
||||
const writer = out.writer(allocator);
|
||||
try writer.writeAll("Next steps:\n");
|
||||
try writer.writeAll("\tml status --watch\n");
|
||||
try writer.print("\tml cancel {s}\n", .{job_name});
|
||||
try writer.print("\tml validate {s}\n", .{commit_hex});
|
||||
|
||||
return out.toOwnedSlice(allocator);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ const mode = @import("../mode.zig");
|
|||
const db = @import("../db.zig");
|
||||
const manifest_lib = @import("../manifest.zig");
|
||||
const progress = @import("../utils/progress.zig");
|
||||
const common = @import("common.zig");
|
||||
|
||||
// Use modular queue structure
|
||||
const queue_mod = @import("queue/mod.zig");
|
||||
|
|
@ -460,7 +461,7 @@ fn queueSingleJob(
|
|||
defer if (commit_override == null) allocator.free(commit_id);
|
||||
|
||||
// Build narrative JSON if any narrative fields are set
|
||||
const narrative_json = buildNarrativeJson(allocator, options) catch null;
|
||||
const narrative_json = common.buildNarrativeJson(allocator, options) catch null;
|
||||
defer if (narrative_json) |j| allocator.free(j);
|
||||
|
||||
const config = try Config.load(allocator);
|
||||
|
|
@ -627,7 +628,7 @@ fn queueSingleJob(
|
|||
} else {
|
||||
std.debug.print("Job queued: {s}\n", .{job_name});
|
||||
if (print_next_steps) {
|
||||
const next_steps = try formatNextSteps(allocator, job_name, commit_hex);
|
||||
const next_steps = try common.formatNextSteps(allocator, job_name, commit_hex);
|
||||
defer allocator.free(next_steps);
|
||||
std.debug.print("{s}\n", .{next_steps});
|
||||
}
|
||||
|
|
@ -648,7 +649,7 @@ fn queueSingleJob(
|
|||
std.debug.print("Warning: failed to record job in history ({})\n", .{err});
|
||||
};
|
||||
if (print_next_steps) {
|
||||
const next_steps = try formatNextSteps(allocator, job_name, commit_hex);
|
||||
const next_steps = try common.formatNextSteps(allocator, job_name, commit_hex);
|
||||
defer allocator.free(next_steps);
|
||||
std.debug.print("{s}\n", .{next_steps});
|
||||
}
|
||||
|
|
@ -707,19 +708,6 @@ fn printUsage() !void {
|
|||
std.debug.print("\t\t--context 'Following paper XYZ' --tags ablation,lr-scaling\n", .{});
|
||||
}
|
||||
|
||||
pub fn formatNextSteps(allocator: std.mem.Allocator, job_name: []const u8, commit_hex: []const u8) ![]u8 {
|
||||
var out = try std.ArrayList(u8).initCapacity(allocator, 128);
|
||||
errdefer out.deinit(allocator);
|
||||
|
||||
const writer = out.writer(allocator);
|
||||
try writer.writeAll("Next steps:\n");
|
||||
try writer.writeAll("\tml status --watch\n");
|
||||
try writer.print("\tml cancel {s}\n", .{job_name});
|
||||
try writer.print("\tml validate {s}\n", .{commit_hex});
|
||||
|
||||
return out.toOwnedSlice(allocator);
|
||||
}
|
||||
|
||||
fn explainJob(
|
||||
allocator: std.mem.Allocator,
|
||||
job_name: []const u8,
|
||||
|
|
@ -737,7 +725,7 @@ fn explainJob(
|
|||
}
|
||||
|
||||
// Build narrative JSON for display
|
||||
const narrative_json = buildNarrativeJson(allocator, options) catch null;
|
||||
const narrative_json = common.buildNarrativeJson(allocator, options) catch null;
|
||||
defer if (narrative_json) |j| allocator.free(j);
|
||||
|
||||
if (options.json) {
|
||||
|
|
@ -865,7 +853,7 @@ fn dryRunJob(
|
|||
}
|
||||
|
||||
// Build narrative JSON for display
|
||||
const narrative_json = buildNarrativeJson(allocator, options) catch null;
|
||||
const narrative_json = common.buildNarrativeJson(allocator, options) catch null;
|
||||
defer if (narrative_json) |j| allocator.free(j);
|
||||
|
||||
if (options.json) {
|
||||
|
|
@ -1135,74 +1123,6 @@ fn hexDigit(v: u8) u8 {
|
|||
return if (v < 10) ('0' + v) else ('a' + (v - 10));
|
||||
}
|
||||
|
||||
// buildNarrativeJson creates a JSON object from narrative fields
|
||||
fn buildNarrativeJson(allocator: std.mem.Allocator, options: *const QueueOptions) !?[]u8 {
|
||||
// Check if any narrative field is set
|
||||
if (options.hypothesis == null and
|
||||
options.context == null and
|
||||
options.intent == null and
|
||||
options.expected_outcome == null and
|
||||
options.experiment_group == null and
|
||||
options.tags == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var buf = try std.ArrayList(u8).initCapacity(allocator, 256);
|
||||
defer buf.deinit(allocator);
|
||||
|
||||
const writer = buf.writer(allocator);
|
||||
try writer.writeAll("{");
|
||||
|
||||
var first = true;
|
||||
|
||||
if (options.hypothesis) |h| {
|
||||
if (!first) try writer.writeAll(",");
|
||||
first = false;
|
||||
try writer.writeAll("\"hypothesis\":");
|
||||
try writeJSONString(writer, h);
|
||||
}
|
||||
|
||||
if (options.context) |c| {
|
||||
if (!first) try writer.writeAll(",");
|
||||
first = false;
|
||||
try writer.writeAll("\"context\":");
|
||||
try writeJSONString(writer, c);
|
||||
}
|
||||
|
||||
if (options.intent) |i| {
|
||||
if (!first) try writer.writeAll(",");
|
||||
first = false;
|
||||
try writer.writeAll("\"intent\":");
|
||||
try writeJSONString(writer, i);
|
||||
}
|
||||
|
||||
if (options.expected_outcome) |eo| {
|
||||
if (!first) try writer.writeAll(",");
|
||||
first = false;
|
||||
try writer.writeAll("\"expected_outcome\":");
|
||||
try writeJSONString(writer, eo);
|
||||
}
|
||||
|
||||
if (options.experiment_group) |eg| {
|
||||
if (!first) try writer.writeAll(",");
|
||||
first = false;
|
||||
try writer.writeAll("\"experiment_group\":");
|
||||
try writeJSONString(writer, eg);
|
||||
}
|
||||
|
||||
if (options.tags) |t| {
|
||||
if (!first) try writer.writeAll(",");
|
||||
first = false;
|
||||
try writer.writeAll("\"tags\":");
|
||||
try writeJSONString(writer, t);
|
||||
}
|
||||
|
||||
try writer.writeAll("}");
|
||||
|
||||
return try buf.toOwnedSlice(allocator);
|
||||
}
|
||||
|
||||
/// Check if a job with the same commit_id already exists on the server
|
||||
/// Returns: Optional JSON response from server if duplicate found
|
||||
fn checkExistingJob(
|
||||
|
|
|
|||
Loading…
Reference in a new issue