A dialect-agnostic LLM IR, one wire mapping per provider protocol, a data-driven provider registry, and a transport a browser can implement
Dependencies
moon add marianoguerra/llmlet registry = @catalog.default_registry()
guard registry.dialect_of("anthropic:claude-sonnet-5") is Some(dialect)
let body = dialect.lower_context(context) // -> provider request JSON
let reply = dialect.lift_message(json) // -> ContextMessage| Package | What it is |
|---|---|
| llm | LlmContext and the messages, blocks, usage and per-turn params it is made of; the Dialect trait; StreamEvent; ModelInfo; the Registry; LlmTransport and RelayTransport |
| llm/anthropic | anthropic-messages |
| llm/openai | openai-responses and openai-completions, plus a probed capability table |
| llm/gemini | google-generative-ai — the one whose endpoint carries the model |
| llm/openrouter | openrouter, which borrows the completions mapping |
| llm/catalog | The four providers as data. Credential-free |
| llm/wire | Reading a key out of an environment, resolving an endpoint with it, and an HTTP transport. The only native package here |
let registry = @catalog.default_registry().with_provider({
id: "lmstudio",
name: "LM Studio",
api: "openai-completions",
endpoint: "http://localhost:1234/v1/chat/completions",
auth: NoAuth,
key_env: "",
model_env: "",
default_model: "qwen3-coder",
models: [@llm.ModelInfo::permissive("qwen3-coder")],
headers: { "content-type": "application/json" },
telemetry_name: "lmstudio",
server_address: "localhost",
})pub fn api_spec() -> @llm.ApiSpec {
{ id: "my-api", make: info => MyDialect::make(model=info.id), caps: id => @llm.ModelInfo::permissive(id) }
}pub(open) trait Dialect {
fn id(Self) -> String
fn model(Self) -> String = _
fn lower_context(Self, LlmContext) -> Json
fn lift_message(Self, Json) -> ContextMessage raise DialectError
fn stream_init(Self) -> StreamState = _
fn stream_feed(Self, StreamState, chunk~ : String) -> (StreamState, Array[StreamEvent]) raise DialectError = _
fn stream_finish(Self, StreamState) -> ContextMessage raise DialectError = _
}impl Show for DialectErrorpub(all) enum ContextMessage {
UserMsg(content~ : Array[UserBlock])
AssistantMsg(content~ : Array[AssistantBlock], stop~ : StopReason, model~ : String, response_id~ : String?, usage~ : Usage?, error~ : String?)
ToolResultMsg(tool_call_id~ : String, tool_name~ : String, content~ : Array[UserBlock], is_error~ : Bool)
} derive(ToJson, Debug)pub(all) struct LlmParams {
effort : ThinkingLevel?
pro_mode : Bool
reasoning_context : ReasoningContext?
reasoning_summary : Bool
max_output_tokens : Int?
cache_key : String?
cache_retention : CacheRetention
} derive(ToJson, Debug)pub(all) struct ModelInfo {
id : String
api : String?
reasoning : Bool
thinking_levels : Array[(ThinkingLevel, String)]
supports_pro_mode : Bool
reasoning_contexts : Array[ReasoningContext]
input : Array[Modality]
context_window : Int
max_output_tokens : Int?
cost : Cost?
} derive(ToJson, Debug)pub(all) struct RelayTransport {
post : (String, String, String, (String) -> Unit, (String) -> Unit) -> Unit
stop : (String) -> Unit
}impl LlmTransport for RelayTransportfn send(self : RelayTransport, handle~ : String, target~ : String, body~ : Json, headers~ : Map[String, String], on_chunk~ : (String) -> Unit, on_done~ : () -> Unit, on_error~ : (String) -> Unit) -> Unitfn RelayTransport::make(post~ : (String, String, String, (String) -> Unit, (String) -> Unit) -> Unit, stop? : (String) -> Unit) -> RelayTransportpub(all) enum StreamEvent {
Start(model~ : String)
TextStart(content_index~ : Int)
TextDelta(content_index~ : Int, delta~ : String)
TextEnd(content_index~ : Int)
ThinkingStart(content_index~ : Int)
ThinkingDelta(content_index~ : Int, delta~ : String)
ThinkingEnd(content_index~ : Int)
ToolCallStart(content_index~ : Int, id~ : String, name~ : String)
ToolCallDelta(content_index~ : Int, delta~ : String)
ToolCallEnd(content_index~ : Int)
Done(stop~ : StopReason, usage~ : Usage?)
Error(message~ : String)
} derive(Eq, ToJson, Debug)A dialect-agnostic LLM IR, one wire mapping per provider protocol, a data-driven provider registry, and a transport a browser can implement
Dependencies