Mock LLM Responder — Zig source
Generate deterministic mock LLM API responses - chat completion JSON, SSE event streams with chunk timing, and a replay curl - for testing clients without an API key.
This is the Zig implementation — the same logic the interactive tool runs, in a shareable, citable form.
// mock-llm-responder — Zig port: deterministic mock LLM responses (seeded PRNG + token math).
const std = @import("std");
/// Vocabulary for the poem-ish lorem scenarios.
const poem_words = [_][]const u8{
"cosmos", "nebula", "quantum", "signal", "photon", "drift", "orbit", "vector",
"cipher", "lumen", "aurora", "echo", "helix", "nova", "pulse", "tide",
"vertex", "zenith", "quasar", "ion", "halo", "flux", "prism", "comet",
};
/// FNV-1a 32-bit hash — turns the spec into a deterministic seed / id.
fn hashString(s: []const u8) u32 {
var h: u32 = 0x811c9dc5;
for (s) |c| { h ^= c; h = h *% 0x01000193; }
return h;
}
/// mulberry32 — tiny seeded PRNG; same seed, same sequence, forever.
const Rng = struct {
a: u32,
fn next(self: *Rng) f64 {
self.a +%= 0x6d2b79f5;
var t: u32 = (self.a ^ (self.a >> 15)) *% (self.a | 1);
t = (t +% ((t ^ (t >> 7)) *% (t | 61))) ^ t;
return @as(f64, @floatFromInt(t ^ (t >> 14))) / 4294967296.0;
}
};
/// ~4 chars per token, floor of 1 — deterministic, no tokenizer needed.
fn tokenCount(text: []const u8) u32 {
if (text.len == 0) return 0;
return @max(1, @as(u32, @intCast((text.len + 3) / 4)));
}
/// One poem line of 5-7 vocabulary words, assembled into `buf`.
fn makeLine(rng: *Rng, buf: []u8) []const u8 {
const n = 5 + @as(usize, @intFromFloat(rng.next() * 3));
var len: usize = 0;
var i: usize = 0;
while (i < n) : (i += 1) {
const w = poem_words[@as(usize, @intFromFloat(rng.next() * poem_words.len))];
if (i > 0) { buf[len] = ' '; len += 1; }
@memcpy(buf[len..][0..w.len], w);
len += w.len;
}
return buf[0..len];
}
/// Poem-ish lorem, grown line by line until the token budget is full.
fn buildPoem(seed: u32, max_tokens: u32, out: []u8) []const u8 {
var rng = Rng{ .a = seed };
var line_buf: [256]u8 = undefined;
var text: [2048]u8 = undefined;
var text_len: usize = 0;
while (true) {
const line = makeLine(&rng, &line_buf);
var cand: [2048]u8 = undefined;
var n: usize = 0;
if (text_len > 0) {
@memcpy(cand[0..text_len], text[0..text_len]);
cand[text_len] = '\n';
n = text_len + 1;
}
@memcpy(cand[n..][0..line.len], line);
n += line.len;
if (text_len > 0 and tokenCount(cand[0..n]) > max_tokens) break;
@memcpy(text[0..n], cand[0..n]);
text_len = n;
}
// Fit the budget: cut at max_tokens * 4 bytes, then trimEnd.
var end: usize = @min(text_len, max_tokens * 4);
while (end > 0 and (text[end - 1] == ' ' or text[end - 1] == '\n')) end -= 1;
@memcpy(out[0..end], text[0..end]);
return out[0..end];
}
pub fn main() !void {
const spec = "streamed-lorem|mock-gpt-4o-mini|24";
var poem_buf: [2048]u8 = undefined;
const poem = buildPoem(hashString(spec ++ "|poem"), 24, &poem_buf);
const stdout = std.io.getStdOut().writer();
try stdout.print("id=chatcmpl-mock-{x:0>8}\n", .{hashString(spec ++ "|id")});
try stdout.print("{s}\n", .{poem});
try stdout.print("tokens={d}\n", .{tokenCount(poem)});
}
Also available in 13 other languages
Every CosmoDev tool ships its pure logic in TypeScript (web) and Go (CLI), with authored implementations in a dozen-plus languages — the same contract, ported. Compare all languages side by side →