Ollama

aimock implements Ollama's native /api/chat, /api/generate, and /api/tags endpoints with NDJSON streaming, matching Ollama's wire format including its key differences from OpenAI.

To send events or fields aimock does not model yet, use a raw passthrough fixture with wire: "ollama" for /api/chat or wire: "ollama-generate" for /api/generate.

Endpoints

Method Path Description
POST /api/chat Chat completions (multi-turn, tool calls)
POST /api/generate Single-prompt text generation (no tool calls)
POST /api/embed Current embeddings API: input text or batch, embeddings matrix
POST /api/embeddings Legacy embeddings API: prompt text, single embedding vector
GET /api/tags List available models (derived from fixtures)

If the client hangs up before the reply is complete, aimock stops delivery and journals the request with interruptReason: "client aborted" (see Client Disconnects). To delay "stream": false replies, set nonStreamingTimingAllProviders: true with a streamingProfile or recordedTimings (see All Providers).

Embeddings

Use the Ollama SDK's embed({ model, input }) with a string or array of strings. /api/embed returns one vector per input in embeddings, in input order. Unmatched inputs get separate, deterministic vectors; dimensions sets their width (1–100,000, default 1,536). An embedding fixture repeats its supplied vector for each input; match inputText against the inputs joined by spaces. Recorded batches keep every vector and the upstream JSON metadata for replay.

New /api/embed recordings also store match.embeddingRequest with the operation, exact input shape and requested dimensions, plus match.model. A batch such as ["first", "second"] therefore does not match the string "first second", a different width, or another embedding operation. Re-record older captures to add these keys. New legacy Ollama and OpenAI embedding recordings use the same operation-specific match field. Handwritten embedding fixtures without embeddingRequest keep their broad matching behavior.

The legacy SDK method embeddings({ model, prompt }) still uses /api/embeddings and returns a single embedding array.

Key Differences from OpenAI

Quick Start

ollama-quick-start.ts ts
import { LLMock } from "@copilotkit/aimock";

const mock = new LLMock();
mock.onMessage("hello", { content: "Hi from Ollama!" });
await mock.start();

// Point the Ollama SDK at aimock
const res = await fetch(`${mock.url}/api/chat`, {
  method: "POST",
  body: JSON.stringify({
    model: "llama3",
    messages: [{ role: "user", content: "hello" }],
    stream: false,
  }),
});

Streaming Response Format (NDJSON)

When stream is true (the default), each line is a complete JSON object separated by newlines:

/api/chat streaming output ndjson
{"model":"llama3","message":{"role":"assistant","content":"Hi"},"done":false}
{"model":"llama3","message":{"role":"assistant","content":" there"},"done":false}
{"model":"llama3","message":{"role":"assistant","content":""},"done":true,"done_reason":"stop","total_duration":0,"load_duration":0,"prompt_eval_count":0,"prompt_eval_duration":0,"eval_count":0,"eval_duration":0}

Non-Streaming Response

/api/chat non-streaming output json
{
  "model": "llama3",
  "message": { "role": "assistant", "content": "Hi there!" },
  "done": true,
  "done_reason": "stop",
  "total_duration": 0,
  "load_duration": 0,
  "prompt_eval_count": 0,
  "prompt_eval_duration": 0,
  "eval_count": 0,
  "eval_duration": 0
}

Tool Calls

Tool calls in Ollama send arguments as a parsed object (not a JSON string). aimock automatically converts fixture arguments strings into objects for the Ollama wire format.

ollama-tool-call-fixture.json json
{
  "fixtures": [
    {
      "match": { "userMessage": "weather" },
      "response": {
        "toolCalls": [
          { "name": "get_weather", "arguments": { "city": "NYC" } }
        ]
      }
    }
  ]
}

The Ollama streaming response wraps tool calls in a single chunk:

Tool call NDJSON output ndjson
{"model":"llama3","message":{"role":"assistant","content":"","tool_calls":[{"function":{"name":"get_weather","arguments":{"city":"NYC"}}}]},"done":false}
{"model":"llama3","message":{"role":"assistant","content":""},"done":true,"done_reason":"stop","total_duration":0,"load_duration":0,"prompt_eval_count":0,"prompt_eval_duration":0,"eval_count":0,"eval_duration":0}

/api/generate Endpoint

The /api/generate endpoint takes a prompt string instead of a messages array. The prompt is internally converted to a single user message for fixture matching. Only text responses are supported (no tool calls).

/api/generate request json
{
  "model": "llama3",
  "prompt": "Tell me a joke",
  "stream": false
}
/api/generate response json
{
  "model": "llama3",
  "created_at": "2025-01-01T00:00:00.000Z",
  "response": "Why did the chicken cross the road?",
  "done": true,
  "done_reason": "stop",
  "total_duration": 0,
  "load_duration": 0,
  "prompt_eval_count": 0,
  "prompt_eval_duration": 0,
  "eval_count": 0,
  "eval_duration": 0,
  "context": []
}

/api/tags Endpoint

GET /api/tags returns a list of available models, derived from the model fields across all loaded fixtures. This lets Ollama clients discover which models the mock server supports.

Request Translation

aimock internally translates Ollama requests to a unified ChatCompletionRequest format for fixture matching. The ollamaToCompletionRequest() function maps Ollama's options.temperature to temperature and options.num_predict to max_tokens, so the same fixtures work across all providers.

Ordered blocks (tool-first)

A combined content + toolCalls fixture accepts an optional blocks array to control stream order — see Ordered blocks. Ollama support is partial: a tool_calls chunk can be emitted before content on the NDJSON wire, but some clients reassemble positionally, so tool-first is best-effort on this provider.