Skip to content

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 →