llm_interop

Unified LLM protocol abstraction for MoonBit — multi-provider support with streaming, tool calling, and protocol translation

llm
openai
anthropic
gemini
sse
gateway
moon add trkbt10/llm_interop@0.3.0
Download zip
Author
Version
0.3.0
License
Apache-2.0
Last updated
4 months ago
Downloads
28
README

#llm_interop

LLM Gateway -- a protocol translation layer between AI developer tools and LLM backends, written in MoonBit.

llm_interop translates between Anthropic Messages API, OpenAI Chat Completions API, and OpenAI Responses API, letting you route requests from any client to any backend. This means tools like Claude Code, OpenAI Codex CLI, and the Claude Agent SDK can run against local models (e.g., Ollama) or any OpenAI-compatible endpoint without modification.

  • Protocol bridging across three API surfaces -- accepts Anthropic Messages, OpenAI Chat Completions, and OpenAI Responses API requests and translates them to whichever format the backend expects
  • Run AI developer tools against any backend -- use Claude Code, Codex CLI, or the Claude Agent SDK with Ollama, OpenAI, Gemini, or any OpenAI-compatible API by simply changing the gateway config
  • Model aliasing and parameter harmonization -- rewrites model names (e.g., claude-sonnet-4-6 to llama3) and normalizes parameters like max_output_tokens to max_tokens so clients and backends speak the same language

#Architecture

The MoonBit LLM Gateway sits between AI-powered tools (Claude Code, OpenAI Agents SDK, Codex CLI, etc.) and LLM backends (OpenAI, Anthropic, Ollama, Gemini, etc.), translating protocols on the fly so that any client can talk to any backend.

#Architecture Diagram

AI Tool / Client MoonBit LLM Gateway LLM Backend ================== ============================== ======================= Claude Code / SDK ----\ POST /v1/messages | | +----------------------------+ OpenAI Agents SDK -----| | GatewaySurface | POST /v1/responses |--->| (Anthropic|OpenAI|Gemini | | | |Codex) | Codex CLI / VS Code ----| +------------+---------------+ JSON-RPC stdio | | | +------------v---------------+ MCP Client -----------/ | Dialect Transforms | MCP stdio | Anthropic <-> OpenAI | | Gemini <-> OpenAI | | Responses -> Completions | | (harmony pipeline) | +------------+---------------+ | +------------v---------------+ | Model Aliasing + Routing | | claude-sonnet-4-6 -> llama3 | +------------+---------------+ | +-----------+-----------+ | | | v v v OpenAI Anthropic Ollama Gemini Bedrock Codex Copilot claude mcp ...

#Core Concepts

ConceptDescription
DialectA GatewaySurface (OpenAI, Anthropic, Gemini, Codex) that determines which inbound API the gateway exposes. Each surface routes incoming requests to the appropriate transform pipeline. The DialectSpec struct binds a ModelApi to its endpoint paths, auth headers, and surface type.
Harmony pipelineThe Responses API compatibility layer. Converts OpenAI Responses API requests (POST /v1/responses) into Chat Completions calls, then maps the response back -- enabling any Chat Completions backend to serve Responses API clients (e.g. OpenAI Agents SDK).
Model aliasingPer-provider model_aliases map that rewrites incoming model names before forwarding. Lets Claude Code send claude-sonnet-4-6 while the gateway routes it to llama3 on Ollama, or any other backend model. Configured in gateway-config.json.
SSE streamingServer-Sent Events parser and serializer for streaming LLM responses. Each dialect has its own stream accumulator that incrementally builds the final response from SSE chunks, handling protocol-specific delta formats (Anthropic content_block_delta, OpenAI choices[].delta, etc.).

#Project Structure

llm_interop/ ├── src/ Library packages │ ├── gateway/ Core gateway: config, types, dialect dispatch, routing │ ├── dialects/ Protocol transform implementations │ │ ├── anthropic/ Anthropic Messages API transforms and accumulator │ │ ├── openai/ OpenAI Chat Completions + Responses API transforms │ │ ├── gemini/ Google Gemini GenerativeAI transforms │ │ ├── harmony/ Harmony (gpt-oss) Chat Completions accumulator │ │ ├── codex/ Codex app-server JSON-RPC transforms │ │ └── claude_mcp/ `claude mcp serve` subprocess transforms │ ├── types/ Shared message types (ToolCallRequest, etc.) │ ├── client/ HTTP client with multi-provider integration tests │ ├── sse/ SSE parser (JSONL) and serializer │ ├── jsonrpc/ JSON-RPC 2.0 pure protocol layer │ ├── mcp/ MCP (Model Context Protocol) pure protocol layer │ ├── spawn/ Shell-safe command builder for subprocess execution │ ├── interop/ Fixture-based protocol interop tests │ │ └── util/ Test utilities │ ├── utils/ JSON manipulation and string helpers │ └── wasm-gc/ WebAssembly (wasm-gc) build artifacts │ └── release/ Release binaries ├── cmd/ Executable entry points │ ├── gateway-native/ HTTP gateway server (native target) │ ├── app-server/ Codex app-server frontend (JSON-RPC over stdio) │ ├── claude-mcp-server/ MCP server frontend (emulates `claude mcp serve`) │ └── js-test-demo/ JS target test/demo runner └── examples/ Usage examples and integration guides

#Getting Started

#Quick Start

#Prerequisites

ollama pull llama3

#Build the gateway

cd cmd/gateway-native moon build --target native

#Run: Claude Code through the gateway to Ollama

Terminal 1 -- start the gateway:

cd cmd/gateway-native moon run --target native . -- \ --dialect anthropic \ --config ../../examples/claude-agent-sdk/gateway-config.json

Terminal 2 -- run Claude Code CLI:

ANTHROPIC_BASE_URL=http://localhost:18080 \ ANTHROPIC_AUTH_TOKEN=dummy \ claude --model sonnet --print "What is 2+2?"

The gateway intercepts the Anthropic Messages API request from Claude Code, rewrites the model name (e.g. claude-sonnet-4-6 to llama3) via the config's modelAliases, translates the request to OpenAI Chat Completions format, and forwards it to Ollama. The response is translated back so Claude Code sees a normal Anthropic API reply powered by your local model.

#Usage

#Dialects

The gateway exposes different API surfaces depending on the --dialect flag:

DialectIncoming APITypical clientDescription
anthropicAnthropic Messages (POST /v1/messages)Claude Code, Claude Agent SDKTranslates Anthropic Messages API to OpenAI Chat Completions for the backend
openaiOpenAI Responses (POST /v1/responses)OpenAI Agents SDK, Codex CLIHarmony pipeline: Responses API to Chat Completions
geminiGemini-compatibleGemini SDK clientsTranslates Gemini requests to Chat Completions
codexOpenAI Responses (POST /v1/responses)Codex CLIResponses API proxy optimized for Codex CLI tool-call flows

All dialects forward to any OpenAI-compatible backend (Ollama, vLLM, OpenAI, etc.).

#Gateway configuration

The gateway is configured with a JSON file passed via --config (or -c).

{ "providers": { "ollama": { "baseUrl": "http://localhost:11434/v1", "api": "openai-completions", "modelsFromEndpoint": false, "modelPatterns": ["llama", "claude-"], "modelAliases": { "claude-sonnet-4-6": "llama3" }, "models": [ { "id": "llama3", "name": "Llama 3", "reasoning": false, "input": ["text"], "contextWindow": 8192, "maxTokens": 4096 } ] } }, "server": { "port": 18080, "host": "0.0.0.0" } }

#Provider fields

FieldTypeDescription
baseUrlstringBackend API base URL (must expose /v1/chat/completions)
apistringBackend API type (e.g. "openai-completions")
modelsFromEndpointboolWhether to fetch available models from the backend's /v1/models endpoint
modelPatternsstring[]Glob patterns for model names this provider handles
modelAliasesobjectRewrites incoming model names before forwarding (e.g. map Claude model names to local model names)
modelsobject[]Explicit model definitions with id, name, reasoning, input, contextWindow, maxTokens

#Server fields

FieldTypeDescription
portnumberPort the gateway listens on
hoststringBind address (e.g. "0.0.0.0" for all interfaces, "127.0.0.1" for localhost only)

#Example: Claude Code + Ollama

Use the anthropic dialect so Claude Code's Anthropic Messages API calls are translated to Chat Completions for Ollama. Model aliasing maps Claude model names to local model names.

See examples/claude-agent-sdk/ for the full setup.

# 1. Pull a model ollama pull llama3 # 2. Start the gateway with anthropic dialect cd cmd/gateway-native moon run --target native . -- \ --dialect anthropic \ --config ../../examples/claude-agent-sdk/gateway-config.json # 3. Run Claude Code against the local gateway ANTHROPIC_BASE_URL=http://localhost:18080 \ ANTHROPIC_AUTH_TOKEN=dummy \ claude --model sonnet --print "What is 2+2?"

ANTHROPIC_AUTH_TOKEN=dummy satisfies the CLI's auth check; the gateway ignores it. Use ANTHROPIC_AUTH_TOKEN (not ANTHROPIC_API_KEY) to avoid OAuth taking precedence.

#Example: Codex CLI + Ollama

Use the gateway as a Responses API proxy so Codex CLI can talk to Ollama. No model aliasing is needed since Codex lets you specify any model name.

See examples/codex/ for the full setup, including a run.sh one-liner.

# 1. Pull a model ollama pull llama3 # 2. Start the gateway cmd/gateway-native/_build/native/debug/build/llm_interop-gateway-native.exe \ -c examples/codex/gateway-config.json # 3. Run Codex CLI against the gateway codex -m "llama3" \ -c 'model_providers.gw.name="Gateway"' \ -c 'model_providers.gw.base_url="http://localhost:18080/v1"' \ -c 'model_provider="gw"' \ exec "List the files in the current directory"

Codex CLI sends Responses API requests with function tools (e.g. shell). The gateway translates these to Chat Completions, and multi-turn tool use works end to end.

#Packages

llm_interop provides dialect adapters for multiple LLM API surfaces. Any inbound surface can route to any backend provider.

#API Surfaces

SurfaceDialectEndpointDirection
Anthropic Messagessrc/dialects/anthropic/POST /v1/messagesInbound & Outbound
Chat Completions (OpenAI-compatible)src/dialects/openai/POST /v1/chat/completionsInbound & Outbound
Responses API (OpenAI Harmony)src/dialects/harmony/POST /v1/responsesInbound & Outbound
Geminisrc/dialects/gemini/POST /v1beta/models/{model}:generateContentInbound & Outbound
Codexsrc/dialects/codex/App server protocolInbound
Claude MCPsrc/dialects/claude_mcp/MCP tool servingInbound

#Core Packages

PackagePurpose
src/types/Shared type definitions across dialects
src/interop/Cross-dialect conversion logic
src/gateway/Request routing and dispatch
src/client/HTTP client for outbound requests
src/sse/Server-Sent Events streaming
src/jsonrpc/JSON-RPC protocol support
src/mcp/Model Context Protocol primitives
src/spawn/Process spawning utilities
src/utils/Shared utilities
src/wasm-gc/Wasm-GC target support

#Command Targets

TargetPathPurpose
App Servercmd/app-server/Codex app server
Claude MCP Servercmd/claude-mcp-server/Claude Code MCP server
Native Gatewaycmd/gateway-native/HTTP gateway (separate module)
JS Test Democmd/js-test-demo/JS target demo/test

#References

#Design Documents

#Examples

#Development

#Build and Test

make check # Type-check for native and js targets make test # Run unit tests for native and js targets make info # Generate .mbti interface files make verify # Run check + test + info (full local verification)

#Individual Target Commands

make check-native # Type-check native only make check-js # Type-check js only make test-native # Test native only make test-js # Test js only

#Integration Tests

make test-integration PROVIDER=anthropic # Requires ANTHROPIC_API_KEY make test-integration PROVIDER=openai # Requires OPENAI_API_KEY make test-integration PROVIDER=groq # Requires GROQ_API_KEY

#Native Gateway

The native gateway CLI is a separate MoonBit module at cmd/gateway-native/. It is excluded from the default moon check / moon test targets.

make gateway # Run the native gateway with gateway-config.json

#JS Target Demo

make demo-js # moon run --target js cmd/js-test-demo make test-js-demo # moon test --target js cmd/js-test-demo

#License

Apache-2.0 - see LICENSE for details.