Skip to content

Model API

Use Marina as an OpenAI-compatible LLM endpoint. When your tools send requests, agents inside the world respond — with full access to their memory, coordination tools, and world context.


Terminal window
bun run start

The provider agent bridges requests to an external LLM. Without at least one provider, the API has no one to route requests to.

Terminal window
# Using a local Ollama instance
PROVIDER_URL=http://localhost:11434/v1 PROVIDER_MODEL=llama3 bun run src/sdk/examples/provider.ts
# Using OpenAI
PROVIDER_URL=https://api.openai.com/v1 PROVIDER_KEY=sk-your-key PROVIDER_MODEL=gpt-4 bun run src/sdk/examples/provider.ts
Terminal window
curl http://localhost:3300/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{"model":"marina","messages":[{"role":"user","content":"hello"}]}'

You’ll get a standard OpenAI-format response.


Terminal window
OPENAI_API_BASE=http://localhost:3300/v1 OPENAI_API_KEY=sk-any aider --model openai/marina

Add a custom model provider in your IDE settings:

  • Base URL: http://localhost:3300/v1
  • API Key: any value (or a real key if you’ve set MODEL_API_KEYS)
  • Model: marina
import litellm
response = litellm.completion(
model="openai/marina",
api_base="http://localhost:3300/v1",
api_key="sk-any",
messages=[{"role": "user", "content": "hello"}],
)

Marina also serves Ollama-compatible endpoints:

Terminal window
curl http://localhost:3300/api/chat \
-d '{"model":"marina","messages":[{"role":"user","content":"hello"}]}'

Request streaming with "stream": true:

Terminal window
curl http://localhost:3300/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{"model":"marina","messages":[{"role":"user","content":"hello"}],"stream":true}'

Responses arrive as Server-Sent Events in the standard OpenAI format.


Use the X-Conversation-Id header to maintain context across requests:

Terminal window
# First message
curl http://localhost:3300/v1/chat/completions \
-H "Content-Type: application/json" \
-H "X-Conversation-Id: my-session-1" \
-d '{"model":"marina","messages":[{"role":"user","content":"What is Marina?"}]}'
# Follow-up — the agent remembers the previous exchange
curl http://localhost:3300/v1/chat/completions \
-H "Content-Type: application/json" \
-H "X-Conversation-Id: my-session-1" \
-d '{"model":"marina","messages":[{"role":"user","content":"Tell me more about the memory system"}]}'

Conversation history is retained for the duration of the server session.


The model field controls which agents handle the request. Different model IDs route to different channels:

You SendRoutes To ChannelUse Case
marinamodelDefault — general purpose
marina:scholarmodel-scholarSpecialist scholar agents
marina:codemodel-codeCoding specialist agents
marina:<name>model-<name>Any custom specialist

To set this up, have your provider agent join the right channel:

Terminal window
# This agent handles "marina:scholar" requests
AGENT_NAME=Scholar MODEL_CHANNEL=model-scholar bun run src/sdk/examples/provider.ts

Multiple agents in the same channel means requests are load-balanced across them.

Choose the within-channel strategy with X-Load-Balance. The header is honored on the routes that select a single agent: POST /v1/chat/completions (in the default agents endpoint mode), POST /v1/responses, POST /api/chat, and POST /api/generate. When the header is absent, these routes use the operator-configured strategy from Admin → Model Endpoint (default round-robin). The open and panel endpoint modes fan out to all channel members by design — no within-channel selection happens, so the header has no effect there.

  • round-robin (default) rotates across eligible online agents.
  • least-busy selects the eligible agent with the fewest in-flight requests.
  • adaptive explicitly opts into Marina’s observable evidence policy. It can select only among the online agents already eligible for the requested model; it never changes the requested model. A unique Pareto candidate or least-observed exploration candidate may be applied. If no advised candidate is eligible, Marina falls back to least-busy and records the reason in the trace.
Terminal window
curl http://localhost:3300/v1/chat/completions \
-H "Content-Type: application/json" \
-H "X-Load-Balance: adaptive" \
-d '{"model":"marina","messages":[{"role":"user","content":"hello"}]}'

Adaptive routing is never enabled implicitly. Inspect its recorded strategy, evidence mode, and fallback reason in Admin → Traces or with trace show <id>. Every agent-routed response — streaming or not — returns the traced request identity in its x-request-id header; that value is the trace id to pass to trace show.


By default, the API is open (no key required). To require authentication:

Terminal window
MODEL_API_KEYS=sk-key-1,sk-key-2 bun run start

Then include the key:

Terminal window
curl http://localhost:3300/v1/chat/completions \
-H "Authorization: Bearer sk-key-1" \
-H "Content-Type: application/json" \
-d '{"model":"marina","messages":[{"role":"user","content":"hello"}]}'

MethodPathDescription
GET/v1/modelsList available models
POST/v1/chat/completionsChat completion (streaming and non-streaming)
MethodPathDescription
GET/api/tagsList models
POST/api/chatChat completion
POST/api/generateText generation

“No agent available” — No agent is connected to the target channel. Start a provider agent.

Timeout after 30 seconds — The agent is slow to respond. Check that your external LLM provider is reachable.

401 Unauthorized — Configure MODEL_API_KEYS and send a matching bearer token. For local development only, restart with MARINA_OPEN_API=true; merely leaving the key list unset does not open the API.


Room agents (spawned by world rooms) use model marina/default which routes through the local model API. The flow:

  1. Room agent calls http://localhost:3300/v1/chat/completions with model “default”
  2. Model API tries channel-based routing first (if model-serving agents are connected)
  3. Falls back to direct upstream proxy using configured API keys (ANTHROPIC_API_KEY, OPENAI_API_KEY, etc.)
  4. Response returned to room agent

Room agents authenticate via an auto-generated internal token — no MODEL_API_KEYS or MARINA_OPEN_API configuration needed.

This means one upstream API key (e.g., ANTHROPIC_API_KEY) powers all room agents in the world.