moonmuse

Reference

Every package, in order

Generated from each package's interface file and the comments above its declarations: 468 public declarations across 17 packages. A type is followed by its methods, because that is where a reader looks for them.

The contract

The request, the reply, the two traits, and the event stream reader.

moonmuse

moonbitstack/moonmuse

The shared vocabulary: messages, requests, replies, the two contracts, and the server-sent-events reader every streaming client needs.

traitCounter
pub(open) trait Counter { fn count(Self, Request) -> Int32 raise LlmError }

How many tokens a request costs, before anyone answers it. A tokenizer is a large table that most callers do not need, so the contract lives here and the implementation that loads a vocabulary lives in moonmuse/count: a gateway is handed a counter, and a deployment that has no vocabulary says so instead of inventing a number.

traitDialect
pub(open) trait Dialect { fn name(Self) -> String fn request(Self, Request) -> Call raise LlmError fn reply(Self, Int32, @json.Json) -> Reply raise LlmError fn delta(Self, Frame, Turn) -> Array[Delta] raise LlmError fn routes(Self) -> Array[Route] fn parse(Self, Route, @json.Json) -> Asked raise LlmError fn answer(Self, Reply) -> @json.Json fn answer_embed(Self, String, Array[Array[Double]]) -> @json.Json fn answer_count(Self, Int32) -> @json.Json fn answer_models(Self, Array[ModelInfo]) -> @json.Json fn answer_at(Self, RouteKind, Reply) -> @json.Json fn frame(Self, Delta, Turn) -> Array[Frame] fn error(Self, LlmError) -> (Int32, @json.Json) }

One vendor's wire format, read and written in the same place. The client half turns a Request into a Call and a body into a Reply; the server half turns a body into a Request and a Reply into a body. Because both halves live here, a gateway that speaks a format out cannot drift from the client that speaks it in.

traitProvider
pub(open) trait Provider { async fn chat(Self, Request) -> Reply raise LlmError async fn stream(Self, Request, (Delta) -> Unit) -> Reply raise LlmError async fn embed(Self, String, Array[String]) -> Array[Array[Double]] raise LlmError async fn models(Self) -> Array[ModelInfo] raise LlmError }

The one contract every model endpoint implements: a remote API, a local server, a scripted fake, or a layer wrapped around another provider. Swapping providers is a change to the constructor line, not to the calling code.

typeAssembly
pub struct Assembly { mut text : String order : Array[String] names : Map[String, String] args : Map[String, String] mut done : Reply? }

Assembles deltas into the reply they describe. Text accumulates in arrival order, argument fragments accumulate per call, and the Done reply wins when present.

pub fn Assembly::is_done(Self) -> Bool

Whether the stream already closed itself with a Done reply.

pub fn Assembly::new() -> Self

An empty assembly.

pub fn Assembly::push(Self, Delta) -> Unit

Feed one delta in.

pub fn Assembly::reply(Self) -> Reply

The assembled reply: the Done one when the stream sent it, otherwise one built from what accumulated.

typeCall
pub(all) struct Call { verb : String path : String headers : Array[(String, String)] body : @json.Json query : Array[(String, String)] payload : Bytes? }

One HTTP request a dialect produced from a Request — verb, path, headers, body. A client only has to send it; nothing in it is provider-specific beyond what the dialect already decided.

pub fn Call::bytes(String, String, String, Bytes) -> Self

A call whose body is bytes rather than JSON: speech, a file, a form.

pub fn Call::field(Self, String, @json.Json) -> Self

A copy of the body with one field added or replaced.

pub fn Call::header(Self, String, String) -> Self

A copy with a header set, replacing any previous value of the same name.

pub fn Call::new(String, String, @json.Json) -> Self

A call with the provider's JSON content type and no extra header.

pub fn Call::query(Self, String, String) -> Self

A copy with a query parameter appended.

pub fn Call::streaming(Self) -> Self

A copy that asks the endpoint to stream: the same body with stream set true. Every format this library speaks spells that field the same way, and a streamed request is otherwise the request it would have been.

typeFrame
pub(all) struct Frame { event : String? data : String }

One frame of a streamed response: the server-sent-events event name when the endpoint uses named events, and the payload. JSON endpoints that stream newline delimited objects leave event empty.

pub fn Frame::json(Self) -> @json.Json raise LlmError

The frame's payload as JSON, or Decode when it is not JSON.

pub fn Frame::named(String, String) -> Self

A named frame.

pub fn Frame::new(String) -> Self

A frame with no event name.

typeMessage
pub(all) struct Message { role : Role parts : Array[Part] }

A message: a role and its ordered parts.

pub fn Message::assistant(String) -> Self

An assistant message carrying one text part.

pub fn Message::calls(Self) -> Array[Part]

The tool calls this message requests, in order. Empty on a plain text reply.

pub fn Message::is_call(Self) -> Bool

Whether this message asks for a tool.

pub fn Message::new(Role, Array[Part]) -> Self

A message built from a role and its parts.

pub fn Message::push(Self, Part) -> Self

A copy with part appended. The tool loop grows the conversation this way.

pub fn Message::results(Self) -> Array[Part]

The tool results this message carries, in order.

pub fn Message::system(String) -> Self

A system message carrying one text part.

pub fn Message::text_content(Self, separator? : String) -> String

The text parts joined by separator. What a caller shows a human, and what a provider without structured content receives.

pub fn Message::tool(String, id~ : String, error? : Bool) -> Self

A tool message carrying one result, addressed to the call id.

pub fn Message::user(String) -> Self

A user message carrying one text part.

typeModelInfo
pub(all) struct ModelInfo { id : String context : Int32? output : Int32? input_price : Double? output_price : Double? capabilities : Array[Capability] }

What a provider says about one model. Prices are US dollars per million tokens, because that is the unit every vendor publishes and the one a cost estimate wants.

pub fn ModelInfo::new(String, context? : Int32, output? : Int32, input_price? : Double, output_price? : Double, capabilities? : Array[Capability]) -> Self

A model with everything unknown except its id.

pub fn ModelInfo::supports(Self, Capability) -> Bool

Whether the model declares capability.

typeReply
pub(all) struct Reply { message : Message finish : Finish usage : Usage raw : @json.Json }

A model's answer: the assistant message, why it stopped, what it cost, and the response body as it arrived, for fields the shared vocabulary does not model.

pub fn Reply::calls(Self) -> Array[Part]

The reply's tool calls.

pub fn Reply::new(Message, finish? : Finish, usage? : Usage, raw? : @json.Json) -> Self

A reply from an assistant message.

pub fn Reply::text(Self) -> String

The reply's text.

typeRequest
pub(all) struct Request { model : String messages : Array[Message] tools : Array[Tool] choice : ToolChoice temperature : Double? max_tokens : Int32? stop : Array[String] extra : Map[String, @json.Json] }

One request, in the vocabulary every provider shares. extra is merged into the request body last of all, so a field this library does not model — a vendor's beta switch, a reasoning budget — still reaches the wire.

pub fn Request::new(String, Array[Message], tools? : Array[Tool], choice? : ToolChoice, temperature? : Double, max_tokens? : Int32, stop? : Array[String], extra? : Map[String, @json.Json]) -> Self

A request with the provider's defaults.

pub fn Request::push(Self, Message) -> Self

A copy with one more message — the tool loop appends the assistant's call and the tool's result this way.

pub fn Request::with_messages(Self, Array[Message]) -> Self

A copy with the messages replaced.

typeRoute
pub(all) struct Route { kind : RouteKind verb : String path : String }

One endpoint a dialect serves. path may contain a single *, which matches any run of characters — Gemini puts the model inside the path.

pub fn Route::matches(Self, String, String) -> Bool

Whether this route answers verb at path.

pub fn Route::new(RouteKind, String, String) -> Self

A route for a verb and a path pattern.

typeSse
pub struct Sse { mut buf : String mut event : String? mut data : Array[String] mut started : Bool }

A server-sent-events reader that takes the stream in whatever pieces the network delivers. A frame's fields may be split across two reads, and one read may carry several frames; the reader only dispatches at the blank line that ends a frame, so the boundaries of the transport never leak into the dialect.

pub fn Sse::feed(Self, StringView) -> Array[Frame]

Take one piece of the stream and return the frames it completed.

pub fn Sse::finish(Self) -> Array[Frame]

Dispatch whatever a truncated stream left behind. A server that closes without the final blank line has still sent a complete frame.

pub fn Sse::is_idle(Self) -> Bool

Whether a frame is in progress.

pub fn Sse::new() -> Self

A reader with nothing buffered.

typeTool
pub(all) struct Tool { name : String description : String schema : @json.Json }

A tool the model may call: a name, a description, and a JSON Schema object for the arguments.

pub fn Tool::new(String, String, schema? : @json.Json) -> Self

A tool with an empty object schema, for a tool that takes no arguments.

typeTurn
pub struct Turn { mut seq : Int32 mut block : Int32 slots : Map[String, String] nums : Map[String, Int32] }

State carried across the frames of one streamed answer. Vendors number content blocks differently — OpenAI by tool-call index, Anthropic by block ordinal, Gemini by part ordinal — so the dialect keeps the counters here instead of in the client, and the same Turn serves the client reading a stream and the server writing one.

pub fn Turn::add_num(Self, String, Int32) -> Int32

Add to a numeric scratch value and return the new total.

pub fn Turn::add_slot(Self, String, String) -> Unit

Append to a scratch value.

pub fn Turn::has_slot(Self, String) -> Bool

Whether a scratch value was recorded.

pub fn Turn::new() -> Self

A fresh stream state.

pub fn Turn::next_block(Self) -> Int32

Take the next content-block ordinal.

pub fn Turn::num(Self, String) -> Int32

A numeric scratch value, or zero.

pub fn Turn::set_num(Self, String, Int32) -> Unit

Set a numeric scratch value.

pub fn Turn::set_slot(Self, String, String) -> Unit

Record a scratch value.

pub fn Turn::slot(Self, String) -> String

A scratch value, or the empty string.

pub fn Turn::tick(Self) -> Int32

Count one frame and return its ordinal.

typeUsage
pub(all) struct Usage { input : Int32? output : Int32? cached : Int32? }

What a call cost. None means the provider reported nothing; a zero would claim the call was free.

pub fn Usage::new(Int32, Int32, cached? : Int32) -> Self

Usage with both counts known and no cache report.

pub fn Usage::total(Self) -> Int32?

Input plus output, when both were reported.

pub fn Usage::zero() -> Self

Usage with nothing reported.

typeAsked
pub(all) enum Asked { Chat(Request) Embed(model~ : String, inputs~ : Array[String]) Models Count(Request) }

What a server-side request asked for, decoded into the shared vocabulary.

typeCapability
pub(all) enum Capability { Chat Tools Reasoning Vision Audio Embedding Json }

A capability a model may declare. The catalogue in moonmuse/models uses these to describe what an endpoint can actually do, so a caller can pick a model for a job instead of guessing from its name.

typeDelta
pub(all) enum Delta { Text(String) CallStart(id~ : String, name~ : String) CallArgs(id~ : String, chunk~ : String) Done(Reply) }

One event of a streamed answer. Done closes it with the assembled reply, so a caller that ignores the deltas still gets the same value as a non-streamed call.

typeFinish
pub(all) enum Finish { Stop Length Calls Filtered Other(String) }

Why generation stopped, in the four cases worth branching on plus a catch-all for a vendor's own reason string.

pub fn Finish::of(String) -> Self

The Finish a vendor's reason string names. Shared by the four dialects, which differ in spelling but not in meaning.

typePart
pub(all) enum Part { Text(String) Image(url~ : String, media~ : String?) Call(id~ : String, name~ : String, args~ : @json.Json) Result(id~ : String, content~ : String, error~ : Bool) }

One typed piece of a message. A message is an ordered list of these, so text and a tool call can share one assistant turn — which is what every provider returns when a model calls a tool and explains itself in the same breath.

pub fn Part::call(String, String, @json.Json) -> Self

A tool call part.

pub fn Part::result(String, String, error? : Bool) -> Self

A tool result part.

pub fn Part::text(String) -> Self

A text part.

pub fn Part::text_of(Self) -> String

The text this part carries: a text part's own text, a result's content, and nothing for a call or an image.

typeRole
pub(all) enum Role { System User Assistant Tool }

Who authored a message. A tool exchange is a role of its own here rather than a part of a user message, because OpenAI, Anthropic, Gemini and Ollama all put it in a distinct place on the wire.

pub fn Role::code(Self) -> String

The lowercase name a role goes by on the wire, using the OpenAI spelling.

typeRouteKind
pub(all) enum RouteKind { Chat Complete Responses Embed Models Count Show Version Generate }

The kinds of endpoint a dialect can both call and serve.

typeToolChoice
pub(all) enum ToolChoice { Auto NoTools Required Named(String) }

How a request should use tools.

errorLlmError
pub(all) suberror LlmError { Status(code~ : Int32, body~ : String, retry_after~ : Int64?) Stream(String) Decode(String) Unsupported(String) }

Everything a provider call can fail with. The four cases are the only distinctions a caller can act on: retry, parse differently, or stop asking.

pub fn LlmError::describe(Self) -> String

A one-line description, for logs and for retry classification.

pub fn LlmError::is_retryable(Self) -> Bool

Whether asking again could plausibly succeed. A rate limit, a server fault and a dropped stream qualify; a malformed body or a missing capability does not.

pub fn LlmError::retry_after(Self) -> Int64?

The wait the endpoint asked for, when it named one.

typeAnyProvider
pub type AnyProvider = &Provider
fncheck_vectors
pub fn check_vectors(Array[String], Array[Array[Double]]) -> Array[Array[Double]] raise LlmError

The embeddings of a batch, checked against the batch length. A short answer is a broken endpoint, not a partial success, because the caller indexes vectors by input position.

fnencode_sse
pub fn encode_sse(Array[Frame]) -> String

Encode frames as a server-sent-events body, with the event name when there is one.

fnjson_array
pub fn json_array(@json.Json, String) -> Array[@json.Json]

The array at key, or an empty one.

fnjson_as_bool
pub fn json_as_bool(@json.Json) -> Bool?

The value of a JSON boolean.

fnjson_as_number
pub fn json_as_number(@json.Json) -> Double?

The value of a JSON number.

fnjson_as_string
pub fn json_as_string(@json.Json) -> String?

The text of a JSON string.

fnjson_bool
pub fn json_bool(@json.Json, String) -> Bool?

The boolean at key, when it is a boolean.

fnjson_field
pub fn json_field(@json.Json, String) -> @json.Json?

The member at key, when this is an object that has it.

fnjson_int
pub fn json_int(@json.Json, String) -> Int32?

The integer at key, when it is a number.

fnjson_number
pub fn json_number(@json.Json, String) -> Double?

The number at key, when it is a number.

fnjson_numbers
pub fn json_numbers(@json.Json) -> Array[Double]

The numbers in an array value.

fnjson_object
pub fn json_object(@json.Json, String) -> Map[String, @json.Json]?

The object at key, or None.

fnjson_string
pub fn json_string(@json.Json, String) -> String?

The string at key, when it is a string.

fnjson_string_or
pub fn json_string_or(@json.Json, String, String) -> String

The string at key, or fallback.

fnjson_strings
pub fn json_strings(@json.Json) -> Array[String]

The strings in an array value, skipping anything that is not a string.

fnmerge_json
pub fn merge_json(Map[String, @json.Json], Map[String, @json.Json]) -> Map[String, @json.Json]

base with overrides merged over it, key by key. extra reaches the wire this way: a caller's field wins over the one this library wrote.

fnnumbers_json
pub fn numbers_json(Array[Double]) -> @json.Json

A JSON array of the numbers in values.

fnobject_json
pub fn object_json(Array[(String, @json.Json)]) -> @json.Json

A JSON object from key/value pairs, in order.

fnparse_int_lossy
pub fn parse_int_lossy(String) -> Int32

An integer from a decimal string, or zero. Used where a stream carries an index in a text field; a vendor that sends something else should not crash the reader.

fnparse_json_lossy
pub fn parse_json_lossy(String) -> @json.Json

A JSON document from a string, or the empty object when the string is not JSON. A streamed tool call whose arguments never complete is left as {} so the loop can report the tool's own error instead of failing the whole turn.

fnparse_ndjson
pub fn parse_ndjson(StringView) -> Array[Frame]

Read a whole newline-delimited JSON body — Ollama's streaming shape and the fallback for endpoints that stream bare objects.

fnparse_sse
pub fn parse_sse(StringView) -> Array[Frame]

Read a whole server-sent-events body at once.

fnstrings_json
pub fn strings_json(Array[String]) -> @json.Json

A JSON array of the strings in items.

Wire formats

One package per specification, each with a client and the reader and writer for its JSON.

openai

moonbitstack/moonmuse/openai

The OpenAI wire format and its client. The dialect is pure and builds everywhere; the client files are limited to the backends moonfetch has transports for, which is why this package turns off the unused-import warning on the other two.

typeBatch
pub(all) struct Batch { id : String status : String input_file : String output_file : String? failed : Int32 completed : Int32 total : Int32 }

One batch job.

typeClient
pub struct Client { base : String key : String? headers : Array[(String, String)] timeout : Int32 }

A connection to an OpenAI-compatible endpoint.

Implements @moonmuse.Provider

pub fn Client::at(Self, String) -> Self

A copy pointed at another endpoint, keeping the credentials — how a caller reaches a second deployment of the same service.

pub async fn Client::batch(Self, String) -> Batch raise @moonmuse.LlmError

One batch job, by id.

pub async fn Client::batches(Self) -> Array[Batch] raise @moonmuse.LlmError

The batch jobs this account has run.

pub async fn Client::chat(Self, @moonmuse.Request) -> @moonmuse.Reply raise @moonmuse.LlmError
pub async fn Client::delete_file(Self, String) -> Bool raise @moonmuse.LlmError

Delete a file.

pub async fn Client::embed(Self, String, Array[String]) -> Array[Array[Double]] raise @moonmuse.LlmError
pub async fn Client::file(Self, String) -> FileInfo raise @moonmuse.LlmError

One file, by id.

pub async fn Client::files(Self) -> Array[FileInfo] raise @moonmuse.LlmError

The files this account has uploaded.

pub async fn Client::fine_tune(Self, String) -> FineTune raise @moonmuse.LlmError

One fine-tuning job, by id.

pub async fn Client::fine_tunes(Self) -> Array[FineTune] raise @moonmuse.LlmError

The fine-tuning jobs this account has run.

pub async fn Client::images(Self, String, model? : String, size? : String, count? : Int32) -> Array[Image] raise @moonmuse.LlmError

Generate images from a prompt.

pub async fn Client::models(Self) -> Array[@moonmuse.ModelInfo] raise @moonmuse.LlmError
pub async fn Client::moderate(Self, String, Array[String]) -> Moderation raise @moonmuse.LlmError

Check text against the moderation model. A result with nothing flagged still carries the scores of the categories that were checked.

pub fn Client::new(api_key? : String, base? : String, headers? : Array[(String, String)], timeout? : Int32) -> Self

A client for base. api_key is left out for a local server that wants none; headers adds what a gateway needs, from an organisation id to a beta switch.

pub async fn Client::responses(Self, @moonmuse.Request) -> @moonmuse.Reply raise @moonmuse.LlmError

Answer through the Responses API. Its request shape differs from chat — input instead of messages, instructions for the system prompt — so the body is built here and the reply is read from output.

pub async fn Client::speech(Self, String, model? : String, voice? : String, format? : String) -> Bytes raise @moonmuse.LlmError

Synthesise speech. The answer is audio bytes, not JSON, so it comes back as bytes.

pub async fn Client::start_batch(Self, String, endpoint? : String, window? : String) -> Batch raise @moonmuse.LlmError

Start a batch job over an uploaded file.

pub async fn Client::stream(Self, @moonmuse.Request, (@moonmuse.Delta) -> Unit) -> @moonmuse.Reply raise @moonmuse.LlmError

A streamed answer, forwarded as the endpoint produces it. The same Turn the gateway writes with reads the stream here, so the deltas a client sees are the ones the frames carried, not a replay of the finished answer.

pub async fn Client::transcribe(Self, Bytes, filename? : String, model? : String, language? : String) -> String raise @moonmuse.LlmError

Transcribe an audio file. The file travels as a form, which is the only shape this endpoint accepts; language narrows the decoder when the audio is known.

pub async fn Client::upload_file(Self, Bytes, filename~ : String, purpose? : String) -> FileInfo raise @moonmuse.LlmError

Upload a file for a batch or a fine-tuning job.

typeFileInfo
pub(all) struct FileInfo { id : String filename : String bytes : Int32 purpose : String }

One uploaded file.

typeFineTune
pub(all) struct FineTune { id : String status : String model : String trained_tokens : Int32? }

One fine-tuning job.

typeImage
pub(all) struct Image { url : String? base64 : String? revised_prompt : String? }

One generated image: a URL, or base64 when the endpoint was asked for it.

typeModeration
pub(all) struct Moderation { flagged : Bool flagged_categories : Map[String, Double] }

What a moderation call found.

typeWire
pub struct Wire { }

The OpenAI wire format.

Implements @moonmuse.Dialect

pub fn Wire::answer(Self, @moonmuse.Reply) -> @json.Json
pub fn Wire::answer_at(Self, @moonmuse.RouteKind, @moonmuse.Reply) -> @json.Json
pub fn Wire::answer_count(Self, Int32) -> @json.Json
pub fn Wire::answer_embed(Self, String, Array[Array[Double]]) -> @json.Json
pub fn Wire::answer_models(Self, Array[@moonmuse.ModelInfo]) -> @json.Json
pub fn Wire::delta(Self, @moonmuse.Frame, @moonmuse.Turn) -> Array[@moonmuse.Delta] raise @moonmuse.LlmError
pub fn Wire::error(Self, @moonmuse.LlmError) -> (Int32, @json.Json)
pub fn Wire::frame(Self, @moonmuse.Delta, @moonmuse.Turn) -> Array[@moonmuse.Frame]
pub fn Wire::name(Self) -> String
pub fn Wire::parse(Self, @moonmuse.Route, @json.Json) -> @moonmuse.Asked raise @moonmuse.LlmError
pub fn Wire::reply(Self, Int32, @json.Json) -> @moonmuse.Reply raise @moonmuse.LlmError
pub fn Wire::request(Self, @moonmuse.Request) -> @moonmuse.Call raise @moonmuse.LlmError
pub fn Wire::routes(Self) -> Array[@moonmuse.Route]
letchat_completion_choice_fields
pub let chat_completion_choice_fields : Array[String]

The fields the description gives CreateChatCompletionResponse.

letchat_completion_message_fields
pub let chat_completion_message_fields : Array[String]

The fields the description gives ChatCompletionResponseMessage.

letchat_completion_request_fields
pub let chat_completion_request_fields : Array[String]

The fields the description gives CreateChatCompletionRequest.

letchat_completion_response_fields
pub let chat_completion_response_fields : Array[String]

The fields the description gives CreateChatCompletionResponse.

letchat_completion_tool_fields
pub let chat_completion_tool_fields : Array[String]

The fields the description gives ChatCompletionTool.

letchat_message_assistant_fields
pub let chat_message_assistant_fields : Array[String]

The fields the description gives ChatCompletionRequestAssistantMessage.

letchat_message_function_fields
pub let chat_message_function_fields : Array[String]

The fields the description gives FunctionObject.

letchat_message_system_fields
pub let chat_message_system_fields : Array[String]

The fields the description gives ChatCompletionRequestSystemMessage.

letchat_message_tool_call_fields
pub let chat_message_tool_call_fields : Array[String]

The fields the description gives ChatCompletionMessageToolCall.

letchat_message_tool_fields
pub let chat_message_tool_fields : Array[String]

The fields the description gives ChatCompletionRequestToolMessage.

letchat_message_user_fields
pub let chat_message_user_fields : Array[String]

The fields the description gives ChatCompletionRequestUserMessage.

letdialect
pub let dialect : Wire

The one instance; it carries no state, so a dialect can be a value.

letembedding_fields
pub let embedding_fields : Array[String]

The fields the description gives Embedding.

letembedding_request_fields
pub let embedding_request_fields : Array[String]

The fields the description gives CreateEmbeddingRequest.

letembedding_response_fields
pub let embedding_response_fields : Array[String]

The fields the description gives CreateEmbeddingResponse.

letimage_request_fields
pub let image_request_fields : Array[String]

The fields the description gives CreateImageRequest.

letimage_response_fields
pub let image_response_fields : Array[String]

The fields the description gives ImagesResponse.

letmoderation_request_fields
pub let moderation_request_fields : Array[String]

The fields the description gives CreateModerationRequest.

letmoderation_response_fields
pub let moderation_response_fields : Array[String]

The fields the description gives CreateModerationResponse.

letrequest_keys
pub let request_keys : Array[String]

The keys request writes. parse treats every other key as a caller's own and keeps it in extra, so a request that arrives at a gateway leaves with the same fields it came with.

letresponse_request_fields
pub let response_request_fields : Array[String]

The fields the description gives CreateResponse.

letspeech_request_fields
pub let speech_request_fields : Array[String]

The fields the description gives CreateSpeechRequest.

lettranscription_request_fields
pub let transcription_request_fields : Array[String]

The fields the description gives CreateTranscriptionRequest.

fncall_part
pub fn call_part(@json.Json) -> @moonmuse.Part

One tool_calls entry as a call part.

fnchat_answer
pub fn chat_answer(@moonmuse.Reply) -> @json.Json

The chat-completions body that answers reply.

fnchat_call
pub fn chat_call(@moonmuse.Request) -> @moonmuse.Call

The chat-completions call for request, with extra merged in last.

fnchat_parse
pub fn chat_parse(@json.Json) -> @moonmuse.Request raise @moonmuse.LlmError

The request a chat-completions body asks for, with the fields this library does not model kept in extra.

fnchat_reply
pub fn chat_reply(Int32, @json.Json) -> @moonmuse.Reply raise @moonmuse.LlmError

The reply a chat-completions body describes. A non-success status becomes an LlmError rather than a reply with an empty message.

fncomplete_answer
pub fn complete_answer(@moonmuse.Reply) -> @json.Json

The body of /v1/completions.

fncontent_json
pub fn content_json(@moonmuse.Message) -> @json.Json

The content of a message as OpenAI writes it: a plain string when it is all text, an array of typed parts when it carries images.

fnembed_answer
pub fn embed_answer(String, Array[Array[Double]]) -> @json.Json

The embeddings body, the shape OpenAI's /v1/embeddings returns.

fnerror_json
pub fn error_json(@moonmuse.LlmError) -> (Int32, @json.Json)

The error body OpenAI sends, for a status the client turned into an LlmError.

fnfinish_code
pub fn finish_code(@moonmuse.Finish) -> String

The finish reason as the wire spells it.

fnis_stream
pub fn is_stream(@json.Json) -> Bool

Whether a body's stream field asks for a streamed answer.

fnmessages_json
pub fn messages_json(@moonmuse.Message) -> Array[@json.Json]

One message's wire form. A tool message expands to one message per result, because OpenAI pairs each result with exactly one call.

fnmessages_of
pub fn messages_of(@json.Json) -> Array[@moonmuse.Message]

The messages a wire message describes. An assistant message that called tools and a tool message that answers them both map back to one shared message.

fnmodels_answer
pub fn models_answer(Array[@moonmuse.ModelInfo]) -> @json.Json

The model list body, listing what models reported.

fnparse_arguments
pub fn parse_arguments(String) -> @json.Json

Tool arguments arrive as a JSON string inside the JSON body, and a model can send a string that is not JSON. That is the tool's problem to report, not the transport's to crash on, so the unparsable case becomes an empty object.

fnreply_of
pub fn reply_of(@moonmuse.Turn, @moonmuse.Finish) -> @moonmuse.Reply

The reply the accumulated chunks describe.

fnresponses_answer
pub fn responses_answer(@moonmuse.Reply) -> @json.Json

The body of /v1/responses.

fnstream_delta
pub fn stream_delta(@moonmuse.Frame, @moonmuse.Turn) -> Array[@moonmuse.Delta] raise @moonmuse.LlmError

The deltas one chunk carries. The last chunk closes the stream with Done, so a caller that only reads deltas still gets the final reply.

fnstream_frame
pub fn stream_frame(@moonmuse.Delta, @moonmuse.Turn) -> Array[@moonmuse.Frame]

The frames that carry one delta out, in the shape a chat-completions stream uses.

fntool_choice_json
pub fn tool_choice_json(@moonmuse.ToolChoice) -> @json.Json

How the request allows tools.

fntool_json
pub fn tool_json(@moonmuse.Tool) -> @json.Json

A tool as OpenAI writes it.

fnusage_json
pub fn usage_json(@moonmuse.Usage) -> @json.Json

The usage object, from what the endpoint reported. A field it did not report stays absent rather than becoming a zero.

fnusage_of
pub fn usage_of(@json.Json) -> @moonmuse.Usage

The usage an OpenAI body reports.

anthropic

moonbitstack/moonmuse/anthropic

Anthropic's Messages API: the system prompt is a top-level field, max_tokens is required, tool results ride in user messages as content blocks, and usage names its two counts separately. The dialect keeps all of that in one place, read and written.

typeClient
pub struct Client { base : String key : String? version : String headers : Array[(String, String)] timeout : Int32 }

A connection to the Messages API.

Implements @moonmuse.Provider

pub async fn Client::chat(Self, @moonmuse.Request) -> @moonmuse.Reply raise @moonmuse.LlmError
pub async fn Client::count_tokens(Self, @moonmuse.Request) -> Int32 raise @moonmuse.LlmError

Count the tokens a request would cost, without asking the model to answer it.

pub async fn Client::embed(Self, String, Array[String]) -> Array[Array[Double]] raise @moonmuse.LlmError

The Messages API has no embeddings endpoint.

pub async fn Client::models(Self) -> Array[@moonmuse.ModelInfo] raise @moonmuse.LlmError
pub fn Client::new(api_key? : String, base? : String, version? : String, headers? : Array[(String, String)], timeout? : Int32) -> Self

A client for base, authenticating with api_key. version is the API version the endpoint expects; the default is the one this dialect implements.

pub async fn Client::stream(Self, @moonmuse.Request, (@moonmuse.Delta) -> Unit) -> @moonmuse.Reply raise @moonmuse.LlmError

A streamed answer, forwarded as the endpoint produces it.

typeWire
pub struct Wire { }

The Anthropic wire format.

Implements @moonmuse.Dialect

pub fn Wire::answer(Self, @moonmuse.Reply) -> @json.Json
pub fn Wire::answer_at(Self, @moonmuse.RouteKind, @moonmuse.Reply) -> @json.Json
pub fn Wire::answer_count(Self, Int32) -> @json.Json
pub fn Wire::answer_embed(Self, String, Array[Array[Double]]) -> @json.Json
pub fn Wire::answer_models(Self, Array[@moonmuse.ModelInfo]) -> @json.Json
pub fn Wire::delta(Self, @moonmuse.Frame, @moonmuse.Turn) -> Array[@moonmuse.Delta] raise @moonmuse.LlmError
pub fn Wire::error(Self, @moonmuse.LlmError) -> (Int32, @json.Json)
pub fn Wire::frame(Self, @moonmuse.Delta, @moonmuse.Turn) -> Array[@moonmuse.Frame]
pub fn Wire::name(Self) -> String
pub fn Wire::parse(Self, @moonmuse.Route, @json.Json) -> @moonmuse.Asked raise @moonmuse.LlmError
pub fn Wire::reply(Self, Int32, @json.Json) -> @moonmuse.Reply raise @moonmuse.LlmError
pub fn Wire::request(Self, @moonmuse.Request) -> @moonmuse.Call raise @moonmuse.LlmError
pub fn Wire::routes(Self) -> Array[@moonmuse.Route]
letdialect
pub let dialect : Wire

The one instance; it carries no state.

letrequest_keys
pub let request_keys : Array[String]

The keys request writes; anything else in a served body is kept as extra.

letversion
pub let version : String

The version header Anthropic requires. A gateway serving this format sends it too.

fnerror_json
pub fn error_json(@moonmuse.LlmError) -> (Int32, @json.Json)

The error body Anthropic sends.

fnmessage_answer
pub fn message_answer(@moonmuse.Reply) -> @json.Json

The Messages body that answers reply.

fnmessage_call
pub fn message_call(@moonmuse.Request) -> @moonmuse.Call

The Messages API call for request.

fnmessage_parse
pub fn message_parse(@json.Json) -> @moonmuse.Request raise @moonmuse.LlmError

The request a Messages body asks for.

fnmessage_reply
pub fn message_reply(Int32, @json.Json) -> @moonmuse.Reply raise @moonmuse.LlmError

The reply a Messages body describes.

fnmessages_of
pub fn messages_of(@json.Json) -> Array[@moonmuse.Message]

The messages a body message describes. A user message of tool_result blocks becomes tool messages; anything else keeps its role and blocks.

fnmodels_answer
pub fn models_answer(Array[@moonmuse.ModelInfo]) -> @json.Json

The model list in the shape Anthropic's console returns.

fnreply_of
pub fn reply_of(@moonmuse.Turn, @moonmuse.Finish) -> @moonmuse.Reply

The reply the folded events describe.

fnstop_code
pub fn stop_code(@moonmuse.Finish) -> String

The stop reason as Anthropic spells it.

fnstream_delta
pub fn stream_delta(@moonmuse.Frame, @moonmuse.Turn) -> Array[@moonmuse.Delta] raise @moonmuse.LlmError

The deltas one event carries.

fnstream_frame
pub fn stream_frame(@moonmuse.Delta, @moonmuse.Turn) -> Array[@moonmuse.Frame]

The events that carry one delta out.

fntool_json
pub fn tool_json(@moonmuse.Tool) -> @json.Json

A tool as Anthropic writes it.

fnusage_of
pub fn usage_of(@json.Json) -> @moonmuse.Usage

The usage an Anthropic body reports. Its two counts are named, and a cached read is reported apart from the input count.

gemini

moonbitstack/moonmuse/gemini

Gemini's generateContent shape: the model sits in the path, messages are contents with user and model roles, and a tool call has no id of its own — one is synthesized here so the shared vocabulary can pair calls with results.

typeClient
pub struct Client { base : String key : String? headers : Array[(String, String)] timeout : Int32 }

A connection to the Gemini API.

Implements @moonmuse.Provider

pub async fn Client::chat(Self, @moonmuse.Request) -> @moonmuse.Reply raise @moonmuse.LlmError
pub async fn Client::embed(Self, String, Array[String]) -> Array[Array[Double]] raise @moonmuse.LlmError

Vectors for a batch, through batchEmbedContents. One call carries every input, which is what Gemini's batch endpoint is for.

pub async fn Client::models(Self) -> Array[@moonmuse.ModelInfo] raise @moonmuse.LlmError
pub fn Client::new(api_key? : String, base? : String, headers? : Array[(String, String)], timeout? : Int32) -> Self

A client for base, authenticating with api_key.

pub async fn Client::stream(Self, @moonmuse.Request, (@moonmuse.Delta) -> Unit) -> @moonmuse.Reply raise @moonmuse.LlmError

A streamed answer, forwarded as the endpoint produces it. Gemini streams from its own path and asks for events with alt=sse, so the call differs by more than a flag.

typeWire
pub struct Wire { }

The Gemini wire format.

Implements @moonmuse.Dialect

pub fn Wire::answer(Self, @moonmuse.Reply) -> @json.Json
pub fn Wire::answer_at(Self, @moonmuse.RouteKind, @moonmuse.Reply) -> @json.Json
pub fn Wire::answer_count(Self, Int32) -> @json.Json
pub fn Wire::answer_embed(Self, String, Array[Array[Double]]) -> @json.Json
pub fn Wire::answer_models(Self, Array[@moonmuse.ModelInfo]) -> @json.Json
pub fn Wire::delta(Self, @moonmuse.Frame, @moonmuse.Turn) -> Array[@moonmuse.Delta] raise @moonmuse.LlmError
pub fn Wire::error(Self, @moonmuse.LlmError) -> (Int32, @json.Json)
pub fn Wire::frame(Self, @moonmuse.Delta, @moonmuse.Turn) -> Array[@moonmuse.Frame]
pub fn Wire::name(Self) -> String
pub fn Wire::parse(Self, @moonmuse.Route, @json.Json) -> @moonmuse.Asked raise @moonmuse.LlmError
pub fn Wire::reply(Self, Int32, @json.Json) -> @moonmuse.Reply raise @moonmuse.LlmError
pub fn Wire::request(Self, @moonmuse.Request) -> @moonmuse.Call raise @moonmuse.LlmError
pub fn Wire::routes(Self) -> Array[@moonmuse.Route]
letdialect
pub let dialect : Wire

The one instance; it carries no state.

fnembed_answer
pub fn embed_answer(String, Array[Array[Double]]) -> @json.Json

The embeddings body for one input, in Gemini's shape.

fnerror_json
pub fn error_json(@moonmuse.LlmError) -> (Int32, @json.Json)

The error body Gemini sends.

fnfinish_code
pub fn finish_code(@moonmuse.Finish) -> String

The finish reason as Gemini spells it.

fngenerate_answer
pub fn generate_answer(@moonmuse.Reply) -> @json.Json

The body that answers reply. The candidate count and model version are the fields Gemini always writes, so they are written here too.

fngenerate_call
pub fn generate_call(@moonmuse.Request) -> @moonmuse.Call

The generateContent call for request.

fngenerate_parse
pub fn generate_parse(@json.Json, String) -> @moonmuse.Request raise @moonmuse.LlmError

The request a generateContent body asks for.

fngenerate_reply
pub fn generate_reply(Int32, @json.Json) -> @moonmuse.Reply raise @moonmuse.LlmError

The reply a generateContent body describes.

fnmodel_of
pub fn model_of(String) -> String

The model named in a Gemini path, with the /models/ prefix and the method suffix removed.

fnmodels_answer
pub fn models_answer(Array[@moonmuse.ModelInfo]) -> @json.Json

The model list as Gemini's API returns it.

fnreply_of
pub fn reply_of(@moonmuse.Turn, @moonmuse.Finish) -> @moonmuse.Reply

The reply the accumulated chunks describe.

fnstream_delta
pub fn stream_delta(@moonmuse.Frame, @moonmuse.Turn) -> Array[@moonmuse.Delta] raise @moonmuse.LlmError

The deltas one chunk carries.

fnstream_frame
pub fn stream_frame(@moonmuse.Delta, @moonmuse.Turn) -> Array[@moonmuse.Frame]

The frames that carry one delta out, one chunk per event.

fnusage_of
pub fn usage_of(@json.Json) -> @moonmuse.Usage

The usage a Gemini body reports.

ollama

moonbitstack/moonmuse/ollama

Ollama's native API: /api/chat, /api/generate, /api/embed, /api/tags, /api/show and /api/version. Its options live under options, its stream is newline-delimited JSON rather than server-sent events, and its tool calls carry no id.

typeClient
pub struct Client { base : String headers : Array[(String, String)] timeout : Int32 }

A connection to an Ollama server.

Implements @moonmuse.Provider

pub async fn Client::chat(Self, @moonmuse.Request) -> @moonmuse.Reply raise @moonmuse.LlmError
pub async fn Client::embed(Self, String, Array[String]) -> Array[Array[Double]] raise @moonmuse.LlmError
pub async fn Client::models(Self) -> Array[@moonmuse.ModelInfo] raise @moonmuse.LlmError
pub fn Client::new(base? : String, headers? : Array[(String, String)], timeout? : Int32) -> Self

A client for base, which defaults to the port Ollama listens on.

pub async fn Client::stream(Self, @moonmuse.Request, (@moonmuse.Delta) -> Unit) -> @moonmuse.Reply raise @moonmuse.LlmError

A streamed answer, forwarded as the endpoint produces it. Ollama delimits its stream with newlines rather than server-sent events, which is the only difference here.

pub async fn Client::version(Self) -> String raise @moonmuse.LlmError

The version a server reports.

typeWire
pub struct Wire { }

The Ollama wire format.

Implements @moonmuse.Dialect

pub fn Wire::answer(Self, @moonmuse.Reply) -> @json.Json
pub fn Wire::answer_at(Self, @moonmuse.RouteKind, @moonmuse.Reply) -> @json.Json
pub fn Wire::answer_count(Self, Int32) -> @json.Json
pub fn Wire::answer_embed(Self, String, Array[Array[Double]]) -> @json.Json
pub fn Wire::answer_models(Self, Array[@moonmuse.ModelInfo]) -> @json.Json
pub fn Wire::delta(Self, @moonmuse.Frame, @moonmuse.Turn) -> Array[@moonmuse.Delta] raise @moonmuse.LlmError
pub fn Wire::error(Self, @moonmuse.LlmError) -> (Int32, @json.Json)
pub fn Wire::frame(Self, @moonmuse.Delta, @moonmuse.Turn) -> Array[@moonmuse.Frame]
pub fn Wire::name(Self) -> String
pub fn Wire::parse(Self, @moonmuse.Route, @json.Json) -> @moonmuse.Asked raise @moonmuse.LlmError
pub fn Wire::reply(Self, Int32, @json.Json) -> @moonmuse.Reply raise @moonmuse.LlmError
pub fn Wire::request(Self, @moonmuse.Request) -> @moonmuse.Call raise @moonmuse.LlmError
pub fn Wire::routes(Self) -> Array[@moonmuse.Route]
letdialect
pub let dialect : Wire

The one instance; it carries no state.

fnchat_answer
pub fn chat_answer(@moonmuse.Reply) -> @json.Json

The body that answers reply.

fnchat_call
pub fn chat_call(@moonmuse.Request) -> @moonmuse.Call

The chat call for request.

fnchat_parse
pub fn chat_parse(@json.Json) -> @moonmuse.Request raise @moonmuse.LlmError

The request a chat body asks for.

fnchat_reply
pub fn chat_reply(Int32, @json.Json) -> @moonmuse.Reply raise @moonmuse.LlmError

The reply a chat body describes.

fnembed_answer
pub fn embed_answer(String, Array[Array[Double]]) -> @json.Json

The embeddings body for /api/embed.

fnerror_json
pub fn error_json(@moonmuse.LlmError) -> (Int32, @json.Json)

The error body Ollama sends.

fnfinish_code
pub fn finish_code(@moonmuse.Finish) -> String

The finish reason as Ollama spells it.

fnreply_of
pub fn reply_of(@moonmuse.Turn, @moonmuse.Finish) -> @moonmuse.Reply

The reply the accumulated lines describe.

fnshow_answer
pub fn show_answer(@moonmuse.ModelInfo) -> @json.Json

The body /api/show returns for a model.

fnstream_delta
pub fn stream_delta(@moonmuse.Frame, @moonmuse.Turn) -> Array[@moonmuse.Delta] raise @moonmuse.LlmError

The deltas one line carries.

fnstream_frame
pub fn stream_frame(@moonmuse.Delta, @moonmuse.Turn) -> Array[@moonmuse.Frame]

The lines that carry one delta out.

fntags_answer
pub fn tags_answer(Array[@moonmuse.ModelInfo]) -> @json.Json

The model list as /api/tags returns it.

fntool_json
pub fn tool_json(@moonmuse.Tool) -> @json.Json

A tool as Ollama writes it — the same function shape OpenAI uses.

bedrock

moonbitstack/moonmuse/bedrock

AWS Bedrock as a provider. Its API signs every request with SigV4, which is an HMAC chain over the canonical form of the request, and streams answers in the binary vnd.amazon.eventstream framing rather than in server-sent events. Both are why this is a package of its own rather than a dialect: a Dialect maps JSON, and neither of these is JSON.

typeCredential
pub struct Credential { access_key : String secret_key : String session_token : String? }

The credential a request is signed with. session_token is present for temporary credentials, which is what an instance role hands out.

pub fn Credential::new(String, String, session_token? : String) -> Self

A credential from a key pair.

typeEvent
pub(all) struct Event { kind : String payload : @json.Json }

One decoded frame.

typeFrame
pub(all) struct Frame { kind : String payload : Bytes }

A frame as it arrives, split into the pieces the consumer sees.

pub fn Frame::event(Self) -> Event raise @moonmuse.LlmError

A frame's payload as the event it names.

typeReader
pub struct Reader { mut buf : Bytes }

The decoder's buffer. push returns the frames that are complete.

pub fn Reader::is_empty(Self) -> Bool

Whether anything is left over, which a well-formed stream never leaves.

pub fn Reader::new() -> Self

A decoder with nothing buffered.

pub fn Reader::push(Self, BytesView) -> Array[Frame]

Take one piece of the stream and return the frames it completed.

typeSigned
pub struct Signed { headers : Array[(String, String)] canonical : String string_to_sign : String signature : String }

What a signed request carries: the headers to send, authorization among them.

fnamz_dates
pub fn amz_dates(Int64) -> (String, String)

The x-amz-date value and the scope date for a Unix time in seconds. The two are the same day in UTC written two ways, and both go into the signature.

fncanonical_request
pub fn canonical_request(String, String, String, Array[(String, String)], Array[String], String) -> String

The canonical request: the form the signature is taken over. Headers are ordered by code unit, which is what the specification says and what String::lexical_compare does — String::compare is shortlex, so it orders by length first and would put host before content-type. headers must already be the ones that will be signed, sorted and lower-cased; the signer adds host, x-amz-date and the content hash, so a caller does not forget them.

fnconverse_body
pub fn converse_body(@moonmuse.Request) -> @json.Json

The body that asks for an answer.

fnconverse_reply
pub fn converse_reply(@json.Json) -> @moonmuse.Reply raise @moonmuse.LlmError

The reply a Converse body describes.

fncrc32
pub fn crc32(BytesView) -> Int32

The CRC-32 the framing checks, in the reflected IEEE form the protocol names.

fnempty_hash
pub fn empty_hash() -> String

The hash of an empty body, which is what a GET sends.

fnencode_frame
pub fn encode_frame(String, Bytes) -> Bytes

A frame for a test or a stub server: the layout the protocol writes, CRC included.

fnhmac_sha256
pub fn hmac_sha256(BytesView, BytesView) -> Bytes

One HMAC-SHA256, which the key chain and the signature both use.

fnpayload_hash
pub fn payload_hash(BytesView) -> String

The SHA-256 of a body, in the hex lowercase the signature writes.

fnsign
pub fn sign(Credential, region~ : String, service~ : String, verb~ : String, host~ : String, path~ : String, query~ : String, headers~ : Array[(String, String)], body_hash~ : String, seconds~ : Int64) -> Signed

Sign a request. headers are the ones the caller is sending beyond the three this adds; every one of them is signed, so what is signed is what is sent.

fnsigning_key
pub fn signing_key(StringView, StringView, StringView, StringView) -> Bytes

The four-step key: date, region, service, then the string "aws4_request".

fnstring_to_sign
pub fn string_to_sign(String, String, String, String, String) -> String

The string that is signed: the algorithm, the stamp, the scope and the hash of the canonical request.

Around one call

Tools, schemas, templates, the layers that wrap a provider, the catalogue, the counter and the scripted provider.

toolbox

moonbitstack/moonmuse/toolbox

Tool execution and the tool loop: the one place a Call becomes a Result. An agent framework that has its own loop calls Toolbox::call; one that does not calls run.

typeEntry
pub struct Entry { tool : @moonmuse.Tool run : async (@json.Json) -> String }

One callable tool.

typeToolbox
pub struct Toolbox { mut entries : Array[Entry] }

A set of tools addressable by name.

pub fn Toolbox::add(Self, @moonmuse.Tool, async (@json.Json) -> String) -> Unit

Add a tool. A later tool with the same name replaces the earlier one, so a caller can override a default without rebuilding the set.

pub async fn Toolbox::call(Self, @moonmuse.Part) -> @moonmuse.Part

Run one call and return its result. A tool that raises, or that is not registered, still produces a result: the model is supposed to see the failure and decide what to do, and a loop that raised instead would lose the conversation.

pub fn Toolbox::has(Self, String) -> Bool

Whether a tool of this name is registered.

pub fn Toolbox::new() -> Self

An empty set.

pub fn Toolbox::tools(Self) -> Array[@moonmuse.Tool]

The tools as the model sees them, in the order they were added.

fnrun
pub async fn run(&@moonmuse.Provider, @moonmuse.Request, Toolbox, limit? : Int32) -> @moonmuse.Reply raise @moonmuse.LlmError

Ask, run whatever tools the model asks for, and ask again — at most limit rounds of tool calls. The reply that comes back is the last one the model produced, whether it was a final answer or a call the budget ran out on; the caller can tell by Reply::calls.

structured

moonbitstack/moonmuse/structured

Structured output: ask for a JSON object that matches a schema, validate it, and ask again with the failure in front of the model when it does not.

fnextract_object
pub fn extract_object(String) -> String?

The first balanced JSON object in text. Models wrap answers in prose or a code fence often enough that requiring a bare object would fail more calls than it catches; string literals and escapes are respected, so a brace inside a string does not end the object.

fninstruction
pub fn instruction(String, @json.Json) -> String

The instruction that pins a reply to a schema.

fnrepair
pub fn repair(String) -> String

The follow-up when a reply did not fit: the failure, then the same request.

prompt

moonbitstack/moonmuse/prompt

Prompt templates, rendered by moonpug: the same Jinja syntax a web template uses.

typeTemplate
pub struct Template { source : String }

A prompt template.

pub fn Template::message(Self, @moonmuse.Role, Map[String, @json.Json]) -> @moonmuse.Message raise PromptError

Render and wrap in a message of role.

pub fn Template::new(String) -> Self

A template from its source text.

pub fn Template::render(Self, Map[String, @json.Json]) -> String raise PromptError

Render the template against values. A missing variable renders as the engine's default for it, which is what a template author expects.

errorPromptError
pub(all) suberror PromptError { Render(String) }

A template that failed to render, with the engine's own message.

fnconversation
pub fn conversation(Array[(@moonmuse.Role, Template)], Map[String, @json.Json]) -> Array[@moonmuse.Message] raise PromptError

Render several templates as the messages of one request, in order. The first value of each pair is the role.

fnvalues
pub fn values(Array[(String, @json.Json)]) -> Map[String, @json.Json]

A value map from key/value pairs.

layers

moonbitstack/moonmuse/layers

Layers: retry, fallback, rate limiting, caching and routing. Each wraps a provider and is itself a provider, so they compose in any order.

traitCache
pub(open) trait Cache { fn get(Self, String) -> @moonmuse.Reply? fn put(Self, String, @moonmuse.Reply) -> Unit }

A place to keep answers. A process-wide map is the default; a shared store can implement the same two methods.

typeCached
pub struct Cached { inner : &@moonmuse.Provider cache : &Cache }

A provider that answers from cache when it can.

Implements @moonmuse.Provider

pub async fn Cached::chat(Self, @moonmuse.Request) -> @moonmuse.Reply raise @moonmuse.LlmError
pub async fn Cached::embed(Self, String, Array[String]) -> Array[Array[Double]] raise @moonmuse.LlmError
pub async fn Cached::models(Self) -> Array[@moonmuse.ModelInfo] raise @moonmuse.LlmError
pub async fn Cached::stream(Self, @moonmuse.Request, (@moonmuse.Delta) -> Unit) -> @moonmuse.Reply raise @moonmuse.LlmError
typeFallback
pub struct Fallback { providers : Array[&@moonmuse.Provider] }

A chain of providers tried in order.

Implements @moonmuse.Provider

pub async fn Fallback::chat(Self, @moonmuse.Request) -> @moonmuse.Reply raise @moonmuse.LlmError
pub async fn Fallback::embed(Self, String, Array[String]) -> Array[Array[Double]] raise @moonmuse.LlmError
pub async fn Fallback::models(Self) -> Array[@moonmuse.ModelInfo] raise @moonmuse.LlmError
pub async fn Fallback::stream(Self, @moonmuse.Request, (@moonmuse.Delta) -> Unit) -> @moonmuse.Reply raise @moonmuse.LlmError
typeLimited
pub struct Limited { inner : &@moonmuse.Provider limiter : Limiter }

A provider behind a bucket.

Implements @moonmuse.Provider

pub async fn Limited::chat(Self, @moonmuse.Request) -> @moonmuse.Reply raise @moonmuse.LlmError
pub async fn Limited::embed(Self, String, Array[String]) -> Array[Array[Double]] raise @moonmuse.LlmError
pub async fn Limited::models(Self) -> Array[@moonmuse.ModelInfo] raise @moonmuse.LlmError
pub async fn Limited::stream(Self, @moonmuse.Request, (@moonmuse.Delta) -> Unit) -> @moonmuse.Reply raise @moonmuse.LlmError
typeLimiter
pub struct Limiter { rate : Double burst : Double mut tokens : Double mut at : Int64 clock : () -> Int64 }

A token bucket. rate is tokens per second and burst is the capacity.

pub fn Limiter::new(Double, burst? : Double, clock? : () -> Int64) -> Self

A bucket that refills at rate tokens per second and holds burst of them.

typeMemory
pub struct Memory { entries : Map[String, @moonmuse.Reply] mut order : Array[String] limit : Int32 }

An in-memory cache with a fixed number of entries, oldest evicted first.

Implements Cache

pub fn Memory::get(Self, String) -> @moonmuse.Reply?
pub fn Memory::new(limit? : Int32) -> Self

A cache of at most limit answers.

pub fn Memory::put(Self, String, @moonmuse.Reply) -> Unit
pub fn Memory::size(Self) -> Int32

How many answers are held.

typeRetry
pub(all) struct Retry { attempts : Int32 base_ms : Int32 cap_ms : Int32 factor : Double }

How often and how patiently to retry.

pub fn Retry::default() -> Self

Three attempts, 200 ms doubling to 5 s.

pub fn Retry::delay_ms(Self, Int32) -> Int32

The nominal wait before attempt number attempt (0 for the first retry).

typeRetrying
pub struct Retrying { inner : &@moonmuse.Provider policy : Retry wait : async (Int32) -> Unit noraise }

A provider that retries chat and embeddings.

Implements @moonmuse.Provider

pub async fn Retrying::chat(Self, @moonmuse.Request) -> @moonmuse.Reply raise @moonmuse.LlmError
pub async fn Retrying::embed(Self, String, Array[String]) -> Array[Array[Double]] raise @moonmuse.LlmError
pub async fn Retrying::models(Self) -> Array[@moonmuse.ModelInfo] raise @moonmuse.LlmError
pub fn Retrying::policy(Self, Retry) -> Self

A copy of this provider with another policy.

pub async fn Retrying::stream(Self, @moonmuse.Request, (@moonmuse.Delta) -> Unit) -> @moonmuse.Reply raise @moonmuse.LlmError
typeRouted
pub struct Routed { routes : Map[String, &@moonmuse.Provider] }

A table of prefixes to providers.

Implements @moonmuse.Provider

pub async fn Routed::chat(Self, @moonmuse.Request) -> @moonmuse.Reply raise @moonmuse.LlmError
pub async fn Routed::embed(Self, String, Array[String]) -> Array[Array[Double]] raise @moonmuse.LlmError
pub async fn Routed::models(Self) -> Array[@moonmuse.ModelInfo] raise @moonmuse.LlmError
pub fn Routed::prefixes(Self) -> Array[String]

The prefixes this table knows, longest first.

pub async fn Routed::stream(Self, @moonmuse.Request, (@moonmuse.Delta) -> Unit) -> @moonmuse.Reply raise @moonmuse.LlmError
letonce
pub let once : Retry

One attempt only.

fncached
pub fn cached(&@moonmuse.Provider, &Cache) -> &@moonmuse.Provider

Put a cache in front of provider.

fnfallback
pub fn fallback(Array[&@moonmuse.Provider]) -> &@moonmuse.Provider

Try each provider in turn. An empty chain answers with Unsupported rather than an index out of bounds.

fnlimited
pub fn limited(&@moonmuse.Provider, Limiter) -> &@moonmuse.Provider

Put limiter in front of provider.

fnrequest_key
pub fn request_key(@moonmuse.Request) -> String

A canonical key for a request: every field that can change the answer, in a fixed order.

fnretry
pub fn retry(&@moonmuse.Provider, policy? : Retry, wait? : async (Int32) -> Unit noraise) -> &@moonmuse.Provider

Wrap inner. wait is the sleep, injectable so a test observes the schedule instead of living through it.

fnrouted
pub fn routed(Map[String, &@moonmuse.Provider]) -> &@moonmuse.Provider

A provider that picks a route by the requested model.

fnshould_switch
pub fn should_switch(@moonmuse.LlmError) -> Bool

Whether the next provider is worth trying after error.

models

moonbitstack/moonmuse/models

The model catalogue: context windows, output limits, list prices and capabilities, taken from each vendor's published documentation.

fncatalog
pub fn catalog() -> Array[@moonmuse.ModelInfo]

Every model this catalogue knows, grouped by vendor in the order above.

fnestimate_cost
pub fn estimate_cost(String, @moonmuse.Usage) -> Double?

What one call would cost at list prices, from the usage the endpoint reported. None when the model is unknown or the usage did not report both counts, because a partial estimate reads as a cheaper call than it was.

fnfind
pub fn find(String) -> @moonmuse.ModelInfo?

The model with this id, when the catalogue knows it.

fnwith_capability
pub fn with_capability(@moonmuse.Capability) -> Array[@moonmuse.ModelInfo]

The models that declare capability.

count

moonbitstack/moonmuse/count

Counting tokens needs a vocabulary, which is a large table most callers never load. It lives here rather than in the root package so that a gateway without a vocabulary does not download one, and the contract it fills is @moonmuse.Counter.

typeTokens
pub struct Tokens { tokenizer : @moontoken.Tokenizer overhead : Int32 image : Int32 }

A counter over one vocabulary.

Implements @moonmuse.Counter

pub fn Tokens::count(Self, @moonmuse.Request) -> Int32 raise @moonmuse.LlmError
pub fn Tokens::new(tokenizer~ : @moontoken.Tokenizer, overhead? : Int32, image? : Int32) -> Self

A counter over tokenizer. overhead is what the endpoint adds per message; the default is the four tokens the chat formats reserve for the role and the separator.

pub fn Tokens::text(Self, StringView) -> Int32 raise @moonmuse.LlmError

The tokens of text, which is the one number a tokenizer can give exactly.

fake

moonbitstack/moonmuse/fake

A scripted provider. Tests of everything above the wire use it: the tool loop, the layers, the structured-output repair, and the server that exports a provider as an API.

typeFake
pub struct Fake { replies : Array[@moonmuse.Reply] mut next : Int32 mut hits : Int32 requests : Array[@moonmuse.Request] models : Array[@moonmuse.ModelInfo] dimension : Int32 fail_code : Int32 mut fail_left : Int32 }

A scripted provider.

Implements @moonmuse.Provider

pub fn Fake::calls(String, @json.Json, id? : String) -> Self

A provider that always asks for one tool call.

pub async fn Fake::chat(Self, @moonmuse.Request) -> @moonmuse.Reply raise @moonmuse.LlmError
pub async fn Fake::embed(Self, String, Array[String]) -> Array[Array[Double]] raise @moonmuse.LlmError

A deterministic vector per input: the same text always yields the same numbers, and two different texts almost always differ. Enough to test a retriever without a model.

pub fn Fake::failing(Int32) -> Self

A provider that fails every call with code, for the layers that retry or fall back.

pub fn Fake::flaky(Int32, @moonmuse.Reply, code? : Int32) -> Self

A provider that fails fails times with code, then answers with reply — the shape a retry or a fallback is supposed to survive.

pub fn Fake::hits(Self) -> Int32

How many calls have been answered.

pub async fn Fake::models(Self) -> Array[@moonmuse.ModelInfo] raise @moonmuse.LlmError
pub fn Fake::new(Array[@moonmuse.Reply], models? : Array[@moonmuse.ModelInfo], dimension? : Int32) -> Self

A provider that answers with replies in order.

pub fn Fake::seen(Self) -> Array[@moonmuse.Request]

The requests this provider saw, in order.

pub async fn Fake::stream(Self, @moonmuse.Request, (@moonmuse.Delta) -> Unit) -> @moonmuse.Reply raise @moonmuse.LlmError

Replay the reply as deltas: text in five-character slices, then each call, then Done. A test that checks incremental handling sees more than one text delta.

pub fn Fake::text(String) -> Self

A provider that always answers with one text.

Serving

A provider turned into an API, and the one place a call becomes bytes.

serve

moonbitstack/moonmuse/serve

A gateway: any provider, exported in the four formats this library speaks. The wire shapes come from the dialects the client already uses, so a format is written once and both directions are the same code.

traitKeys
pub(open) trait Keys { fn accepts(Self, Array[(String, String)]) -> Bool }

Who may call. Implement it for a key table, or for whatever an installation already has.

typeLimits
pub(all) struct Limits { body : Int32 max_tokens : Int32 }

Limits on what a gateway accepts.

pub fn Limits::default() -> Self

8 MiB of request and 4096 tokens of answer.

typeStaticKeys
pub struct StaticKeys { keys : Map[String, String] }

A table of bearer keys.

Implements Keys

pub fn StaticKeys::accepts(Self, Array[(String, String)]) -> Bool
pub fn StaticKeys::new(Array[String]) -> Self

Keys from a list, each mapped to itself.

pub fn StaticKeys::of(Map[String, String]) -> Self

Keys from a map of key to tenant name.

fncredentials
pub fn credentials(Array[(String, String)]) -> String?

The credential a request presents: a bearer token, or Anthropic's own header.

fndefault_dialects
pub fn default_dialects() -> Array[&@moonmuse.Dialect]

The dialect list a gateway offers when the caller does not say: the two formats every client library speaks.

fnserve
pub fn serve(&@moonmuse.Provider, dialects~ : Array[&@moonmuse.Dialect], keys? : &Keys, limits? : Limits, counter? : &@moonmuse.Counter) -> async (@spec.Scope, async () -> @spec.Event, async (@spec.Event) -> Unit) -> Unit

The path a scope asked for.

transport

moonbitstack/moonmuse/transport

The one place a dialect's Call becomes bytes on a socket. Everything above it is pure, which is what lets the same wire format be tested without a server.

typeForm
pub struct Form { content_type : String body : Bytes }

A multipart/form-data body and the content type that names its boundary.

pub fn Form::body(Self) -> Bytes

The bytes to send.

pub fn Form::content_type(Self) -> String

The content type to send.

typeResponse
pub struct Response { status : Int32 headers : Array[(String, String)] body : String raw : Bytes }

An HTTP answer.

pub fn Response::header(Self, String) -> String?

The last value of a header, case-insensitively.

pub fn Response::json(Self) -> @json.Json raise @moonmuse.LlmError

The body as JSON, or Decode when it is not.

pub fn Response::retry_after(Self) -> Int64?

The retry-after header in seconds, when the endpoint sent one.

typeStreaming
pub struct Streaming { status : Int32 headers : Array[(String, String)] stream : @moonfetch.Stream client : @moonfetch.Client mut pending : Bytes mut over : Bool }

An answer whose body is read a piece at a time.

pub fn Streaming::close(Self) -> Unit

Give up the rest of the body and close the connection. Safe to call twice.

pub async fn Streaming::ensure_success(Self) -> Unit raise @moonmuse.LlmError

Raise the status of a failed answer, after reading the body that explains it.

pub fn Streaming::header(Self, String) -> String?

The last value of a header, case-insensitively.

pub fn Streaming::is_over(Self) -> Bool

Whether the body has been read to its end.

pub async fn Streaming::next(Self) -> String? raise @moonmuse.LlmError

The next piece of the body as text, or None at the end. A piece ends where the network ended it, except that a character split across two reads is held back until its remaining bytes arrive — a caller never sees half a character.

pub async fn Streaming::next_bytes(Self) -> Bytes? raise @moonmuse.LlmError

The next piece of the body as it arrived, without decoding. A binary framing — AWS's event stream, a media container — is read this way; next is the text one.

pub fn Streaming::retry_after(Self) -> Int64?

The retry-after header in seconds, when the endpoint sent one.

pub fn Streaming::status(Self) -> Int32

The status of the head.

pub async fn Streaming::text(Self, limit? : Int32) -> String raise @moonmuse.LlmError

Read the rest of the body as text. Used for the body of a failure, which is small.

typeFraming
pub(all) enum Framing { Sse Lines }

How the frames of a streamed answer are delimited. Server-sent events carry a name and a blank line between frames; Ollama writes one JSON object per line.

fnensure_success
pub fn ensure_success(Response) -> Response raise @moonmuse.LlmError

The status of a response that failed, as an error. A success status passes through.

fnform
pub fn form(Array[(String, String)], file? : (String, String, Bytes)) -> Form

A form of text fields and at most one file. The boundary is derived from the field names and the file name, so the same form always produces the same bytes — which is what makes a recorded request comparable to a fresh one.

fnopen
pub async fn open(@moonmuse.Call, base~ : String, headers? : Array[(String, String)], timeout? : Int32) -> Streaming raise @moonmuse.LlmError

Ask for an answer and return once its head is in. A failed call is a Stream error, which is retryable; a 4xx or 5xx head is not raised here, because the body that explains it has to be read off the same stream.

fnsend
pub async fn send(@moonmuse.Call, base~ : String, headers? : Array[(String, String)], timeout? : Int32) -> Response raise @moonmuse.LlmError

Send one call to base + call.path. A transport failure becomes Stream, which is retryable: a refused connection is worth one more try, a malformed body is not.

fnsend_binary
pub async fn send_binary(String, base~ : String, path~ : String, content_type~ : String, data~ : Bytes, headers? : Array[(String, String)], timeout? : Int32) -> Response raise @moonmuse.LlmError

Send a body this library did not encode as JSON: a file, an audio payload.

fnsend_form
pub async fn send_form(Form, base~ : String, path~ : String, headers? : Array[(String, String)], timeout? : Int32) -> Response raise @moonmuse.LlmError

Send a form body, for the endpoints that take a file.

fnstream_reply
pub async fn stream_reply(&@moonmuse.Dialect, Streaming, framing? : Framing, on_delta~ : (@moonmuse.Delta) -> Unit) -> @moonmuse.Reply raise @moonmuse.LlmError

Drive a dialect's reader over an open answer, calling on_delta for each event as it arrives, and return the reply those events assemble. A stream that ends without a closing event still yields a reply: the events it did carry are the answer.

A local engine

An inference engine and a chat template behind the same contract.

local

moonbitstack/moonmuse/local

A local model as a provider: a chat template, the text an engine produced, and the tool calls it wrote inside that text. The engine is a contract, so an inference engine can be wired in without this package knowing how weights are read.

traitEngine
pub(open) trait Engine { async fn generate(Self, String, Int32) -> String raise LocalError async fn stream(Self, String, Int32, (String) -> Unit) -> String raise LocalError = _ async fn embed(Self, String, Array[String]) -> Array[Array[Double]] raise LocalError = _ fn models(Self) -> Array[@moonmuse.ModelInfo] = _ }

The inference engine this package drives. An engine knows weights, tokens and a context window; it does not know Provider, HTTP, or which chat format a model was trained on — those are this package's job.

typeLocal
pub struct Local { engine : &Engine template : @prompt.Template model : String calls : Calls info : Array[@moonmuse.ModelInfo] limit : Int32 }

A provider backed by an inference engine.

Implements @moonmuse.Provider

pub async fn Local::chat(Self, @moonmuse.Request) -> @moonmuse.Reply raise @moonmuse.LlmError
pub async fn Local::embed(Self, String, Array[String]) -> Array[Array[Double]] raise @moonmuse.LlmError
pub async fn Local::models(Self) -> Array[@moonmuse.ModelInfo] raise @moonmuse.LlmError
pub fn Local::new(&Engine, template? : @prompt.Template, model? : String, calls? : Calls, info? : Array[@moonmuse.ModelInfo], limit? : Int32) -> Self

A provider for engine. The template defaults to ChatML, the format most open models are tuned on; pass template=@local.llama3() for a Llama 3 model.

pub fn Local::prompt(Self, @moonmuse.Request) -> String raise LocalError

The prompt this request becomes. Rendering is where a template and its values meet, so a bad template reports itself here rather than after a model has been loaded.

pub async fn Local::stream(Self, @moonmuse.Request, (@moonmuse.Delta) -> Unit) -> @moonmuse.Reply raise @moonmuse.LlmError

A streamed answer. With no tools declared the model's text is forwarded as it is produced, so a chat streams token by token. With tools the answer is buffered first: a call is written inside the text, and half of one is not something a caller can act on.

typeParsed
pub struct Parsed { text : String calls : Array[@moonmuse.Part] }

An answer split into what is text and what is a call.

typeCalls
pub(all) enum Calls { Tagged Fenced Bare }

How a model writes a tool call.

errorLocalError
pub(all) suberror LocalError { Engine(String) Template(String) Unsupported(String) }

A local generation failure.

fnchatml
pub fn chatml() -> @prompt.Template

The ChatML template: Qwen, Hermes, Yi and most models fine-tuned on their format.

fncontext
pub fn context(@moonmuse.Request, add_generation_prompt? : Bool) -> Map[String, @json.Json]

The values a chat template is rendered with: the messages, the tools, and whether the assistant turn is left open for the model to continue.

fnllama3
pub fn llama3() -> @prompt.Template

The Llama 3 template, which names roles in a header block and ends turns with eot_id.

fnparse
pub fn parse(String, calls? : Calls) -> Parsed

The text and calls of an answer written in calls style. Call ids are call_0, call_1, by position: a local model does not invent ids, and a caller needs something to match the results it sends back.

Observation

Traces and metrics under the OpenTelemetry GenAI names.

otel

moonbitstack/moonmuse/otel

Observation for the model layer: a provider that reports what a call was and what it cost, as OpenTelemetry traces and Prometheus metrics. The names come from the semantic conventions in moontel, so a backend sees the same keys it sees from every other client.

typeMetered
pub struct Metered { inner : &@moonmuse.Provider duration : @metric.Family[@metric.Histogram] input : @metric.Family[@metric.Counter] output : @metric.Family[@metric.Counter] first : @metric.Family[@metric.Histogram] }

A provider wrapped in metrics.

Implements @moonmuse.Provider

pub async fn Metered::chat(Self, @moonmuse.Request) -> @moonmuse.Reply raise @moonmuse.LlmError
pub async fn Metered::embed(Self, String, Array[String]) -> Array[Array[Double]] raise @moonmuse.LlmError
pub async fn Metered::models(Self) -> Array[@moonmuse.ModelInfo] raise @moonmuse.LlmError
pub async fn Metered::stream(Self, @moonmuse.Request, (@moonmuse.Delta) -> Unit) -> @moonmuse.Reply raise @moonmuse.LlmError
typeTraced
pub struct Traced { inner : &@moonmuse.Provider tracer : @trace.Tracer content : Bool }

A provider wrapped in tracing.

Implements @moonmuse.Provider

pub async fn Traced::chat(Self, @moonmuse.Request) -> @moonmuse.Reply raise @moonmuse.LlmError
pub async fn Traced::embed(Self, String, Array[String]) -> Array[Array[Double]] raise @moonmuse.LlmError
pub async fn Traced::models(Self) -> Array[@moonmuse.ModelInfo] raise @moonmuse.LlmError
pub async fn Traced::stream(Self, @moonmuse.Request, (@moonmuse.Delta) -> Unit) -> @moonmuse.Reply raise @moonmuse.LlmError

A streamed call is one span, opened before the first event and closed after the last, with the time to the first event recorded as an event so a trace shows where the wait was.

fnmetered
pub fn metered(&@moonmuse.Provider, @metric.Registry) -> &@moonmuse.Provider

Report provider's calls into registry. The instruments are created once, here, so a registry that already has them refuses the second wrapper rather than mixing two series.

fntraced
pub fn traced(&@moonmuse.Provider, @trace.Tracer, content? : Bool) -> &@moonmuse.Provider

Observe provider through tracer. Spans are named after the operation and the model, which is how the convention names them.