Mock LLM Responder — Swift 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 Swift implementation — the same logic the interactive tool runs, in a shareable, citable form.
// mock-llm-responder — Swift port: deterministic mock LLM responses (seeded PRNG + token math).
import Foundation
let poemWords = [
"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.
func hashString(_ s: String) -> UInt32 {
var h: UInt32 = 0x811c9dc5
for u in s.utf8 { h = (h ^ UInt32(u)) &* 0x01000193 }
return h
}
/// mulberry32 — tiny seeded PRNG; same seed, same sequence, forever.
func mulberry32(seed: UInt32) -> () -> Double {
var a = seed
return {
a = a &+ 0x6d2b79f5
var t = (a ^ (a >> 15)) &* (a | 1)
t = (t &+ ((t ^ (t >> 7)) &* (t | 61))) ^ t
return Double(t ^ (t >> 14)) / 4294967296.0
}
}
/// ~4 chars per token, floor of 1 — deterministic, no tokenizer needed.
func tokenCount(_ text: String) -> Int {
if text.isEmpty { return 0 }
return max(1, Int((Double(text.utf16.count) / 4).rounded(.up)))
}
/// Cut text so it fits in maxTokens tokens (4 chars each).
func truncateToTokens(_ text: String, maxTokens: Int) -> String {
if tokenCount(text) <= maxTokens { return text }
var cut = String(text.prefix(maxTokens * 4))
while let last = cut.last, last.isWhitespace { cut.removeLast() }
return cut
}
/// One poem line of 5-7 vocabulary words.
func makeLine(_ rng: () -> Double) -> String {
let n = 5 + Int(rng() * 3)
return (0..<n).map { _ in poemWords[Int(rng() * Double(poemWords.count))] }
.joined(separator: " ")
}
/// Poem-ish lorem, grown line by line until the token budget is full.
func buildPoem(seed: UInt32, maxTokens: Int) -> String {
let rng = mulberry32(seed: seed)
var text = ""
while true {
let line = makeLine(rng)
let candidate = text.isEmpty ? line : text + "\n" + line
if !text.isEmpty && tokenCount(candidate) > maxTokens { break }
text = candidate
}
return truncateToTokens(text, maxTokens: maxTokens)
}
/// Split content into stream chunks: word tokens keep their trailing
/// whitespace, so any grouping reassembles to the original content.
func chunkContent(_ content: String, perChunk: Int) -> [String] {
var words: [String] = []
var i = content.startIndex
while i < content.endIndex {
var j = i
while j < content.endIndex, !content[j].isWhitespace { j = content.index(after: j) }
while j < content.endIndex, content[j].isWhitespace { j = content.index(after: j) }
words.append(String(content[i..<j]))
i = j
}
return stride(from: 0, to: words.count, by: perChunk).map {
words[$0..<min($0 + perChunk, words.count)].joined()
}
}
let spec = "streamed-lorem|mock-gpt-4o-mini|24"
let poem = buildPoem(seed: hashString(spec + "|poem"), maxTokens: 24)
print(String(format: "id=chatcmpl-mock-%08x", hashString(spec + "|id")))
print(poem)
print("tokens=\(tokenCount(poem)) chunks=\(chunkContent(poem, perChunk: 4).count)")
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 →