Mock LLM Responder — Java 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 Java implementation — the same logic the interactive tool runs, in a shareable, citable form.
// mock-llm-responder — Java port: deterministic mock LLM responses (seeded PRNG + token math).
import java.util.ArrayList;
import java.util.List;
import java.util.function.DoubleSupplier;
public class MockLlmResponder {
static final String[] POEM_WORDS = {
"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. */
static int hashString(String s) {
int h = 0x811c9dc5; // FNV offset basis
for (int i = 0; i < s.length(); i++) { h ^= s.charAt(i); h *= 0x01000193; }
return h;
}
/** mulberry32 — tiny seeded PRNG; same seed, same sequence, forever. */
static DoubleSupplier mulberry32(int seed) {
int[] a = { seed };
return () -> {
a[0] += 0x6d2b79f5;
int x = a[0];
int t = (x ^ (x >>> 15)) * (x | 1);
t = (t + ((t ^ (t >>> 7)) * (t | 61))) ^ t;
return Integer.toUnsignedLong(t ^ (t >>> 14)) / 4294967296.0;
};
}
/** ~4 chars per token, floor of 1 — deterministic, no tokenizer needed. */
static int tokenCount(String text) {
return text.isEmpty() ? 0 : Math.max(1, (text.length() + 3) / 4);
}
/** Cut text so it fits in maxTokens tokens (4 chars each). */
static String truncateToTokens(String text, int maxTokens) {
if (tokenCount(text) <= maxTokens) return text;
return text.substring(0, maxTokens * 4).stripTrailing();
}
/** One poem line of 5-7 vocabulary words. */
static String makeLine(DoubleSupplier rng) {
int n = 5 + (int) (rng.getAsDouble() * 3);
StringBuilder b = new StringBuilder();
for (int i = 0; i < n; i++)
b.append(i > 0 ? " " : "").append(POEM_WORDS[(int) (rng.getAsDouble() * POEM_WORDS.length)]);
return b.toString();
}
/** Poem-ish lorem, grown line by line until the token budget is full. */
static String buildPoem(int seed, int maxTokens) {
DoubleSupplier rng = mulberry32(seed);
String text = "";
for (;;) {
String candidate = text.isEmpty() ? makeLine(rng) : text + "\n" + makeLine(rng);
if (!text.isEmpty() && tokenCount(candidate) > maxTokens) break;
text = candidate;
}
return truncateToTokens(text, maxTokens);
}
/** Split content into stream chunks. Chunks reassemble to the exact content. */
static List<String> chunkContent(String content, int perChunk) {
List<String> words = new ArrayList<>(), chunks = new ArrayList<>();
for (int i = 0; i < content.length();) {
int j = i; // scan one word token: non-space run + trailing whitespace
while (j < content.length() && !Character.isWhitespace(content.charAt(j))) j++;
while (j < content.length() && Character.isWhitespace(content.charAt(j))) j++;
words.add(content.substring(i, j));
i = j;
}
for (int k = 0; k < words.size(); k += perChunk) {
StringBuilder b = new StringBuilder();
for (int m = k; m < Math.min(k + perChunk, words.size()); m++) b.append(words.get(m));
chunks.add(b.toString()); // chunks rejoin to the exact original content
}
return chunks;
}
public static void main(String[] args) {
String spec = "streamed-lorem|mock-gpt-4o-mini|24", poem = buildPoem(hashString(spec + "|poem"), 24);
System.out.printf("id=chatcmpl-mock-%08x%n", hashString(spec + "|id"));
System.out.println(poem);
System.out.println("tokens=" + tokenCount(poem) + " chunks=" + chunkContent(poem, 4).size());
}
}
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 →