Skip to main content
Version: 0.11

Chat Completions API

The chat completions endpoint generates responses from a conversation. It's OpenAI-compatible, so if you've used the OpenAI API before, this works the same way.

POST /v1/chat/completions

Basic Request

curl http://localhost:11434/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "in-memory::meta-llama/Llama-3.2-3B-Instruct-Q4_K_M",
"messages": [
{"role": "user", "content": "What is the capital of France?"}
]
}'

Response:

{
"id": "chatcmpl-abc123",
"object": "chat.completion",
"created": 1700000000,
"model": "meta-llama/Llama-3.2-3B-Instruct-Q4_K_M",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": "The capital of France is Paris."
},
"finish_reason": "stop"
}
],
"usage": {
"prompt_tokens": 12,
"completion_tokens": 8,
"total_tokens": 20
}
}

Request Parameters

ParameterTypeDefaultDescription
modelstringrequiredModel identifier (e.g., in-memory::meta-llama/Llama-3.2-3B-Instruct)
messagesarrayrequiredConversation messages (see below)
streambooleanfalseEnable token-by-token streaming
temperaturefloat1.0Sampling temperature. Lower = more deterministic, higher = more creative
max_tokensintegermodel defaultMaximum tokens to generate
response_formatobjectnullConstrain output format (see Structured Generation)
enable_thinkingbooleannullToggle the extended-thinking span on models that support it (Qwen 3.5/3.6, SmolLM3). Ignored — with a warning — on other models. See Thinking / Reasoning.

Message Roles

Each message in the messages array has a role and content:

RolePurpose
systemSets the AI's behavior, personality, or constraints. Placed first.
userThe human's message.
assistantThe AI's previous response. Used for multi-turn context.

System Prompts

System prompts define how the model behaves. They're essential for game NPCs, assistants, or any specialized behavior:

from openai import OpenAI

client = OpenAI(base_url="http://localhost:11434/v1", api_key="not-needed")

response = client.chat.completions.create(
model="in-memory::meta-llama/Llama-3.2-3B-Instruct-Q4_K_M",
messages=[
{
"role": "system",
"content": "You are a ship AI aboard a deep-space freighter. You speak formally, "
"address the player as Captain, and provide status reports when asked. "
"You are concerned about a recent anomaly in sector 7.",
},
{"role": "user", "content": "Status report."},
],
temperature=0.7,
)

print(response.choices[0].message.content)

Multi-Turn Conversations

Include previous messages to give the model context of the conversation:

response = client.chat.completions.create(
model="in-memory::meta-llama/Llama-3.2-3B-Instruct-Q4_K_M",
messages=[
{"role": "system", "content": "You are a helpful tavern keeper named Boris."},
{"role": "user", "content": "What do you have on the menu?"},
{"role": "assistant", "content": "We have roasted boar, mushroom stew, and fresh bread. The stew is my specialty!"},
{"role": "user", "content": "I'll have the stew. Any rumors lately?"},
],
)

print(response.choices[0].message.content)

Streaming

Set "stream": true to receive tokens as they're generated. This is ideal for typewriter-style dialogue UI.

SSE Format: Each token arrives as a Server-Sent Event. The stream ends with data: [DONE].

data: {"id":"chatcmpl-abc","object":"chat.completion.chunk","choices":[{"index":0,"delta":{"role":"assistant"},"finish_reason":null}]}
data: {"id":"chatcmpl-abc","object":"chat.completion.chunk","choices":[{"index":0,"delta":{"content":"Once"},"finish_reason":null}]}
data: {"id":"chatcmpl-abc","object":"chat.completion.chunk","choices":[{"index":0,"delta":{"content":" upon"},"finish_reason":null}]}
...
data: {"id":"chatcmpl-abc","object":"chat.completion.chunk","choices":[{"index":0,"delta":{},"finish_reason":"stop"}]}
data: [DONE]
stream = client.chat.completions.create(
model="in-memory::meta-llama/Llama-3.2-3B-Instruct-Q4_K_M",
messages=[
{"role": "system", "content": "You are a narrator for a fantasy RPG."},
{"role": "user", "content": "Describe the entrance to the dungeon."},
],
stream=True,
)

for chunk in stream:
content = chunk.choices[0].delta.content
if content:
print(content, end="", flush=True)
print()

Temperature

Temperature controls randomness. For game applications:

Use CaseTemperatureWhy
Factual responses, game rules0.1 - 0.3Consistent, predictable
NPC dialogue, general conversation0.6 - 0.8Natural variation
Creative writing, storytelling0.9 - 1.2More surprising, diverse

Finish Reasons

The finish_reason field tells you why generation stopped:

ValueMeaning
stopModel finished naturally (end of response)
lengthHit the max_tokens limit

length means the output was truncated — common with structured/JSON output. When you see it, raise max_tokens and regenerate; for a best-effort salvage of a cut-off JSON response, see repair_json.

Thinking / Reasoning

Some models support an extended "thinking" (chain-of-thought) phase before the final answer. How a model handles this is a per-model capability, and the enable_thinking request field only has a defined meaning for one group:

Model familyBehavior with enable_thinking
Qwen 3.5, Qwen 3.6, SmolLM3Honor the flag: true gates a <think>…</think> reasoning span; false skips it.
Gemma 4Reasons in its own native format, driven by the model/template — not by enable_thinking. Passing true is ignored and can degrade output; pass false.
All other models (Llama, etc.)No extended-thinking mode. enable_thinking: true is ignored.

If you pass enable_thinking: true to a model that doesn't honor the flag (Gemma 4 or any non-thinking model), the engine logs a warning and runs the request anyway — the flag is simply ignored. The safe default is to omit enable_thinking (or set it false) unless you're targeting a model in the first row.

:::warning Gemma 4 known issue Gemma 4's native reasoning can diverge during long agentic / tool-use decoding. See the Gemma-4 agentic decoding divergence known-issue note. :::

Error Handling

Errors return standard HTTP status codes with an OpenAI-compatible error body:

{
"error": {
"message": "Model 'nonexistent-model' not found",
"type": "invalid_request_error",
"param": "model",
"code": null
}
}
StatusMeaning
400Invalid request (bad model name, malformed JSON)
500Server error (model failed to load, inference error)