In this article
An Ollama application may receive three substantially different forms of data: incremental chat fields, arrays of numbers, or audio bytes. Response handling should therefore follow the requested operation rather than assume every result is a completed text string.
- Streaming chat returns a sequence of structured chunks.
- Embeddings return JSON containing a two-dimensional array of numeric vectors.
- Speech returns WAV or raw PCM audio.
This comparison covers Ollama’s documented response contracts. It does not evaluate latency, model quality, vector-search methods, storage choices, audio quality, playback compatibility, or undocumented SDK behavior.
The three response paths
| Operation | Caller submits | Ollama returns | Application handles |
|---|---|---|---|
| Streaming chat | Messages and stream: true |
Incremental message chunks | Partial content, optional thinking, and possible tool_calls |
| Embeddings | A model and one string or an array of strings | JSON with embeddings as number[][] and metadata |
Nested numeric arrays and response fields |
| Speech | A model, input text, and voice | WAV or raw PCM audio | Binary data interpreted according to its format |
These contracts call for separate handlers: a streaming loop, a JSON vector parser, and an audio-byte consumer.
Path 1: Accumulate streamed chat fields
With streaming enabled, Ollama returns partial assistant messages that an application can process as they arrive. The official Python example uses qwen3, a multiplication prompt, and stream=True. Ollama streaming documentation
from ollama import chat
stream = chat(
model="qwen3",
messages=[{"role": "user", "content": "What is 17 × 23?"}],
stream=True,
)
The caller iterates over the stream and inspects each chunk’s message fields. For thinking-capable models, Ollama documents a thinking field alongside regular content; this does not mean every model or every chunk provides thinking data. Ollama streaming documentation
The documented example accumulates the two fields separately:
thinking = ""
content = ""
for chunk in stream:
if chunk.message.thinking:
thinking += chunk.message.thinking
elif chunk.message.content:
content += chunk.message.content
The complete example also tracks whether it is printing thinking or answer text. After streaming, it constructs an assistant message containing the accumulated thinking and content for use in the next request. This is an example pattern, not a requirement to use the same variables or control flow.
Streamed chunks may also expose tool_calls. Ollama’s documented flow is to watch for them, execute the requested tool, and append the tool output to the conversation. Ollama streaming documentation In other words, the model response can request a call, while the application performs the operation and returns its result. The documentation cited here does not establish security or authorization behavior.
A streaming-chat handler may need to:
- Iterate until the stream ends.
- Inspect each chunk for
thinking,content, ortool_calls. - Accumulate partial fields when a complete record is needed.
- Keep thinking and answer content separate when both are used.
- Route tool calls through application logic.
Path 2: Preserve the embedding array structure
The /api/embed endpoint accepts the required input field as either one string or an array of strings. Ollama’s embeddinggemma example sends one string. Ollama embed API documentation
curl http://localhost:11434/api/embed -d '{
"model": "embeddinggemma",
"input": "Why is the sky blue?"
}'
Unlike chat, the primary result is not prose. The response contains embeddings with the documented type number[][]: an array of numeric vectors. Even the single-input example returns a nested array. Ollama embed API documentation
{
"model": "embeddinggemma",
"embeddings": [
[
0.010071029,
-0.0017594862,
0.05007221
]
],
"total_duration": 14143917,
"load_duration": 1019500,
"prompt_eval_count": 8
}
The application should preserve that nested structure rather than treat the result as generated text or assume it is one flat numeric array.
The request schema also documents these optional fields: Ollama embed API documentation
truncate: Defaults totrue. Inputs exceeding the context window are truncated; when set tofalse, an over-limit input returns an error.dimensions: The number of embedding dimensions to generate.keep_alive: The model keep-alive duration.
The documentation does not specify model-by-model constraints for dimensions or the complete error body produced when truncation is disabled.
Alongside the vectors, the response documents:
model: The model that produced the embeddings.total_duration: Total generation time in nanoseconds.load_duration: Load time in nanoseconds.prompt_eval_count: The number of input tokens processed.
These fields are response metadata, not independently measured performance results. The response contract also does not establish vector quality, similarity methods, or storage design.
Path 3: Consume speech as audio
Ollama documents speech generation with phonon through both the CLI and /v1/audio/speech. Ollama speech documentation
The CLI can write generated audio to a file or accept piped text:
ollama run phonon "Save this to a file." > speech.wav
echo "Text from another program works too." | ollama run phonon
The API example submits a model, input, and voice:
curl http://localhost:11434/v1/audio/speech \
-H "Content-Type: application/json" \
-d '{
"model": "phonon",
"input": "Hello from Ollama.",
"voice": "default"
}' --output speech.wav
The result is audio data, not a chat message. Ollama documents two output formats: Ollama speech documentation
| Format | Documented output |
|---|---|
wav |
Default; 16-bit, mono, 24 kHz WAV |
pcm |
Raw 16-bit little-endian, mono, 24 kHz samples without a header |
For WAV output, the header is written before the final length is known, so its size fields contain the maximum value. This is part of the documented format and does not imply compatibility with every decoder. Raw PCM has no container header, so the consumer must interpret its bytes using the documented bit depth, byte order, channel count, and sample rate.
The speech API accepts up to 4,096 input characters per request. The ollama run command splits longer text at paragraph and sentence breaks. Ollama also says that text is spoken as written, while emoji, bullet characters, and emphasis markers are skipped. Ollama speech documentation
Documented HTTP 400 cases include:
- Unsupported options, including unsupported formats, speeds, or instructions.
- An unknown voice; the error lists the available voices.
- Sending chat, completion, or embedding requests to a speech-only model.
Route responses by contract
The handling map reduces to three branches:

streaming chat → iterate chunks → inspect fields → accumulate as needed
embedding → parse JSON → preserve embeddings[][] → read metadata
speech → consume bytes → interpret as WAV or documented PCM
Routing by operation avoids three basic mistakes: expecting one completed string from a stream, flattening an embedding response, or attempting to parse speech audio as JSON.
No comments yet