Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -367,3 +367,6 @@ This keeps the service headless while letting `nullhub` own install/setup UI.
- Add dataset, prompt version, and experiment entities.
- Add regression diff endpoints for comparing prompt/model/strategy versions.
- Add alert rules and anomaly summaries that `nullhub` can render.

## Community SDKs
- **[nullwatch-python-sdk](https://github.com/nullclaw/nullwatch-python-sdk/)** — Python SDK with zero required dependencies. Ships built-in eval scorers for RAG hallucination detection ([LettuceDetect](https://github.com/KRLabsOrg/LettuceDetect)) and tool-call schema validation.
26 changes: 22 additions & 4 deletions src/compat/fs.zig
Original file line number Diff line number Diff line change
Expand Up @@ -222,10 +222,7 @@ pub const Dir = struct {
pub fn makePath(self: Dir, sub_path: []const u8) !void {
if (sub_path.len == 0) return;
if (path.isAbsolute(sub_path)) {
makeDirAbsolute(sub_path) catch |err| switch (err) {
error.PathAlreadyExists => return,
else => |e| return e,
};
try makePathAbsolute(sub_path);
return;
}

Expand Down Expand Up @@ -281,6 +278,10 @@ pub fn makeDirAbsolute(absolute_path: []const u8) Io.Dir.CreateDirError!void {
try Io.Dir.createDirAbsolute(shared.io(), absolute_path, .default_dir);
}

pub fn makePathAbsolute(absolute_path: []const u8) Io.Dir.CreateDirPathError!void {
try Io.Dir.createDirPath(Io.Dir.cwd(), shared.io(), absolute_path);
}

pub fn deleteFileAbsolute(absolute_path: []const u8) Io.Dir.DeleteFileError!void {
try Io.Dir.deleteFileAbsolute(shared.io(), absolute_path);
}
Expand All @@ -305,3 +306,20 @@ pub fn realpathAlloc(allocator: Allocator, file_path: []const u8) ![]u8 {
}
return try cwd().realpathAlloc(allocator, file_path);
}

test "makePathAbsolute creates nested absolute directories" {
var tmp = std.testing.tmpDir(.{});
defer tmp.cleanup();

const tmp_dir = Dir.wrap(tmp.dir);
const root = try tmp_dir.realpathAlloc(std.testing.allocator, ".");
defer std.testing.allocator.free(root);

const nested = try path.join(std.testing.allocator, &.{ root, "nested", "path" });
defer std.testing.allocator.free(nested);

try makePathAbsolute(nested);
try accessAbsolute(nested, .{});

try makePathAbsolute(nested);
}
5 changes: 1 addition & 4 deletions src/from_json.zig
Original file line number Diff line number Diff line change
Expand Up @@ -62,10 +62,7 @@ fn getU16(obj: std.json.ObjectMap, key: []const u8) ?u16 {

fn ensureHome(home: []const u8) !void {
if (std.fs.path.isAbsolute(home)) {
std_compat.fs.makeDirAbsolute(home) catch |err| switch (err) {
error.PathAlreadyExists => {},
else => return err,
};
try std_compat.fs.makePathAbsolute(home);
return;
}

Expand Down
5 changes: 1 addition & 4 deletions src/store.zig
Original file line number Diff line number Diff line change
Expand Up @@ -420,10 +420,7 @@ fn finalizeVerdict(summary: *domain.RunSummary) void {

fn ensureDirExists(path: []const u8) !void {
if (std.fs.path.isAbsolute(path)) {
std_compat.fs.makeDirAbsolute(path) catch |err| switch (err) {
error.PathAlreadyExists => {},
else => return err,
};
try std_compat.fs.makePathAbsolute(path);
return;
}

Expand Down
Loading