Mock LLM Responder — Kotlin 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 Kotlin implementation — the same logic the interactive tool runs, in a shareable, citable form.
// mock-llm-responder — Kotlin port: deterministic mock LLM responses (seeded PRNG + token math).
private val POEM_WORDS = listOf(
"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. */
fun hashString(s: String): UInt {
var h = 0x811c9dc5u
for (c in s) h = (h xor c.code.toUInt()) * 0x01000193u
return h
}
/** mulberry32 — tiny seeded PRNG; same seed, same sequence, forever. */
fun mulberry32(seed: UInt): () -> Double {
var a = seed
return {
a += 0x6d2b79f5u
var t = (a xor (a shr 15)) * (a or 1u)
t = (t + ((t xor (t shr 7)) * (t or 61u))) xor t
(t xor (t shr 14)).toDouble() / 4294967296.0
}
}
/** ~4 chars per token, floor of 1 — deterministic, no tokenizer needed. */
fun tokenCount(text: String): Int =
if (text.isEmpty()) 0 else maxOf(1, (text.length + 3) / 4)
/** Cut text so it fits in maxTokens tokens (4 chars each). */
fun truncateToTokens(text: String, maxTokens: Int): String {
if (tokenCount(text) <= maxTokens) return text
return text.take(maxTokens * 4).trimEnd()
}
/** One poem line of 5-7 vocabulary words. */
private fun makeLine(rng: () -> Double): String {
val n = 5 + (rng() * 3).toInt()
return List(n) { POEM_WORDS[(rng() * POEM_WORDS.size).toInt()] }.joinToString(" ")
}
/** Poem-ish lorem, grown line by line until the token budget is full. */
fun buildPoem(seed: UInt, maxTokens: Int): String {
val rng = mulberry32(seed)
var text = ""
while (true) {
val line = makeLine(rng)
val candidate = if (text.isEmpty()) line else "$text\n$line"
if (text.isNotEmpty() && tokenCount(candidate) > maxTokens) break
text = candidate
}
return truncateToTokens(text, maxTokens)
}
/** Split content into stream chunks. Chunks reassemble to the exact content. */
fun chunkContent(content: String, perChunk: Int): List<String> =
Regex("\\S+\\s*").findAll(content).map { it.value }.toList()
.chunked(perChunk).map { it.joinToString("") }
fun main() {
val spec = "streamed-lorem|mock-gpt-4o-mini|24"
val poem = buildPoem(hashString("$spec|poem"), 24)
println("id=chatcmpl-mock-${hashString("$spec|id").toString(16).padStart(8, '0')}")
println(poem)
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 →