One request, four API specs
A system prompt, one user message, temperature 0.2, a 256-token cap. Sent to OpenAI, Anthropic, Gemini and Ollama, that conversation is four different documents. moonmuse gives MoonBit one contract above all four, and reads and writes each of them with the same code.
- 4wire formats
- 1request type
- 17packages
- 0vendor names in code
POST /v1/chat/completions
{
"model": "gpt-4o-mini",
"messages": [
{"role": "system", "content": "Answer in one sentence."},
{"role": "user", "content": "Why is the sky blue?"}
],
"temperature": 0.2,
"max_tokens": 256
}
POST /v1/messages
{
"model": "gpt-4o-mini",
"max_tokens": 256,
"system": "Answer in one sentence.",
"messages": [
{"role": "user", "content": [
{"type": "text", "text": "Why is the sky blue?"}
]}
],
"temperature": 0.2
}
POST /v1beta/models/gpt-4o-mini:generateContent
{
"systemInstruction": {
"parts": [{"text": "Answer in one sentence."}]
},
"contents": [
{"role": "user", "parts": [{"text": "Why is the sky blue?"}]}
],
"generationConfig": {
"temperature": 0.2,
"maxOutputTokens": 256
}
}
POST /api/chat
{
"model": "gpt-4o-mini",
"messages": [
{"role": "system", "content": "Answer in one sentence."},
{"role": "user", "content": "Why is the sky blue?"}
],
"stream": false,
"options": {
"temperature": 0.2,
"num_predict": 256
}
}
The bodies in these tabs are written by this library, compiled to JavaScript and running on this page.
01 · The idea
Name the vendor once
A provider is anything that can answer a request. Which vendor, whether it runs in a data centre or on the laptop, and which JSON it speaks are details behind that one value.
let provider : &@moonmuse.Provider =
@openai.Client::new(api_key="sk-…")
let request = @moonmuse.Request::new(
"gpt-4o-mini",
[@moonmuse.Message::user("Why is the sky blue?")],
temperature=0.2,
max_tokens=256,
)
let reply = provider.chat(request)
println(reply.text())
Ask for an answer, a stream, vectors, or the model list. Six implementations ship here, and a layer is a provider too — so retry, fallback, routing and caching wrap in any order.
One vendor's wire format, read and written. The client and the gateway use the same value, which is why they cannot disagree about a field.
02 · What it does
Everything around one call
The request is the small part. These are the parts a second integration usually has to write again.
One callback
Text, a tool call starting, its arguments arriving in pieces, then done — the same four deltas from a JSON chunk, seven named events, a candidate array or a line of NDJSON.
A schema and a function
Toolbox::call is the only place a call becomes a result, and a tool that fails produces an error result rather than an exception.
Retry, fall back, cache
Only failures another attempt could avoid are retried. Fallback switches on the same classification; the cache key covers every field that decides an answer.
An answer that fits
Ask for JSON against a schema, check the reply, and send it back with the reason when it does not fit — up to a budget.
Four APIs out of one
Point serve at any provider and it answers as OpenAI, Anthropic, Gemini or Ollama. The official client libraries cannot tell it from the vendor.
Traces under the conventions
One client span per call and token metrics, under the OpenTelemetry GenAI names. Message bodies stay out unless asked for.
03 · The module
One package per concern
A package that is not imported is not compiled or linked, so the local-model and serving halves cost nothing to a program that only asks for a completion.
moonmuseMessages, requests, replies, the two contracts, the event-stream readeropenaiThe chat format and every endpoint that speaks itanthropicThe Messages API, its event stream, its token countergeminigenerateContent, streamGenerateContent, embeddingsollamaThe native /api endpoints and the newline-delimited streambedrockSigV4 signing and the Converse API, event-stream framing includedlayersRetry, fallback, rate limiting, caching, routingtoolboxTool execution and the tool loopstructuredA JSON answer that fits a schema, with a repair looppromptPrompt templates, rendered against a value mapmodelsContext windows, output limits, prices, capabilitiescountToken counting over a vocabularyserveAny provider exported as four APIstransportThe one place a call becomes bytes on a socketlocalA local engine behind the same contractotelTraces and metrics by the GenAI conventionsfakeA scripted provider, for tests above the wire04 · Where to go