From 53fa308f001304f11751715f1276b99e623a44f7 Mon Sep 17 00:00:00 2001 From: "Nikolay.Ivanov" Date: Tue, 5 May 2026 20:57:55 +0300 Subject: [PATCH 1/4] add nullwatch-py Python SDK to community SDKs --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index e7cdff4..1ce2da9 100644 --- a/README.md +++ b/README.md @@ -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-py](https://github.com/Viroslav/nullwatch-py)** — 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. \ No newline at end of file From 94b963233e9eb7f324675571db9dc80bd033bc8a Mon Sep 17 00:00:00 2001 From: Viroslav <98733029+Viroslav@users.noreply.github.com> Date: Fri, 8 May 2026 17:41:22 +0500 Subject: [PATCH 2/4] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 1ce2da9..bd9aaf6 100644 --- a/README.md +++ b/README.md @@ -369,4 +369,4 @@ This keeps the service headless while letting `nullhub` own install/setup UI. - Add alert rules and anomaly summaries that `nullhub` can render. ## Community SDKs -- **[nullwatch-py](https://github.com/Viroslav/nullwatch-py)** — 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. \ No newline at end of file +- **[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. From 5916c5863e65ce055bcb339bb604d9b03e161c8e Mon Sep 17 00:00:00 2001 From: Kolesnikov Dmitry Date: Thu, 7 May 2026 18:55:25 +0300 Subject: [PATCH 3/4] fix of path error --- src/compat/fs.zig | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/compat/fs.zig b/src/compat/fs.zig index 2be980d..206e922 100644 --- a/src/compat/fs.zig +++ b/src/compat/fs.zig @@ -278,7 +278,23 @@ pub fn accessAbsolute(absolute_path: []const u8, options: Dir.AccessOptions) Io. } pub fn makeDirAbsolute(absolute_path: []const u8) Io.Dir.CreateDirError!void { - try Io.Dir.createDirAbsolute(shared.io(), absolute_path, .default_dir); + const parent_dir = path.dirname(absolute_path); + if (parent_dir) |parent| { + var dir = openDirAbsolute(parent, .{}) catch |err| switch (err) { + error.FileNotFound => { + try makeDirAbsolute(parent); + return makeDirAbsolute(absolute_path); + }, + else => { + return error.PathAlreadyExists; + }, + }; + defer dir.close(); + const base_name = path.basename(absolute_path); + try dir.makeDir(base_name); + } else { + try Io.Dir.createDirAbsolute(shared.io(), absolute_path, .default_dir); + } } pub fn deleteFileAbsolute(absolute_path: []const u8) Io.Dir.DeleteFileError!void { From 86433d750739703978c3b64234159f1f6d13924e Mon Sep 17 00:00:00 2001 From: Igor Somov Date: Thu, 14 May 2026 09:25:36 -0300 Subject: [PATCH 4/4] fix: use Zig 0.16 dir path creation --- src/compat/fs.zig | 44 +++++++++++++++++++++++--------------------- src/from_json.zig | 5 +---- src/store.zig | 5 +---- 3 files changed, 25 insertions(+), 29 deletions(-) diff --git a/src/compat/fs.zig b/src/compat/fs.zig index 206e922..f2025b1 100644 --- a/src/compat/fs.zig +++ b/src/compat/fs.zig @@ -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; } @@ -278,23 +275,11 @@ pub fn accessAbsolute(absolute_path: []const u8, options: Dir.AccessOptions) Io. } pub fn makeDirAbsolute(absolute_path: []const u8) Io.Dir.CreateDirError!void { - const parent_dir = path.dirname(absolute_path); - if (parent_dir) |parent| { - var dir = openDirAbsolute(parent, .{}) catch |err| switch (err) { - error.FileNotFound => { - try makeDirAbsolute(parent); - return makeDirAbsolute(absolute_path); - }, - else => { - return error.PathAlreadyExists; - }, - }; - defer dir.close(); - const base_name = path.basename(absolute_path); - try dir.makeDir(base_name); - } else { - try Io.Dir.createDirAbsolute(shared.io(), absolute_path, .default_dir); - } + 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 { @@ -321,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); +} diff --git a/src/from_json.zig b/src/from_json.zig index f37b6d8..a7e12c9 100644 --- a/src/from_json.zig +++ b/src/from_json.zig @@ -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; } diff --git a/src/store.zig b/src/store.zig index a9ada2d..eddffc0 100644 --- a/src/store.zig +++ b/src/store.zig @@ -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; }