Repository: ollama
Issue: Role field should not be repeated in streamed response chunks
Labels: bug,api

### What is the issue?

The streamed chat-completion response from ollama's openai-compatible API repeats `"role": "assistant"` in all returned chunks. This is different to OpenAI's API which just has this in the first chunk. This breaks compatibility with the `client.beta.chat.completions.stream` helper from the openai package. See also this issue https://github.com/pydantic/logfire/pull/545#discussion_r1837660027. Ollama should omit the "role" field or return `"role": None` for all chunks after the first one.

---

OpenAI chunks: "role" only in first chunk

```python
from openai import OpenAI

client = Client()
response = client.chat.completions.create(
    model="gpt-4",
    messages=[{"role": "user", "content": "Just say: The answer is secret."}],
    stream=True,
)
for chunk in response:
    print(chunk.model_dump_json(exclude_none=True))
{"id":"chatcmpl-ASgaImINIA8gwsca92CCgES2VldF8","choices":[{"delta":{"content":"","role":"assistant"},"index":0}],"created":1731400242,"model":"gpt-4-0613","object":"chat.completion.chunk"}
{"id":"chatcmpl-ASgaImINIA8gwsca92CCgES2VldF8","choices":[{"delta":{"content":"The"},"index":0}],"created":1731400242,"model":"gpt-4-0613","object":"chat.completion.chunk"}
{"id":"chatcmpl-ASgaImINIA8gwsca92CCgES2VldF8","choices":[{"delta":{"content":" answer"},"index":0}],"created":1731400242,"model":"gpt-4-0613","object":"chat.completion.chunk"}
{"id":"chatcmpl-ASgaImINIA8gwsca92CCgES2VldF8","choices":[{"delta":{"content":" is"},"index":0}],"created":1731400242,"model":"gpt-4-0613","object":"chat.completion.chunk"}
{"id":"chatcmpl-ASgaImINIA8gwsca92CCgES2VldF8","choices":[{"delta":{"content":" secret"},"index":0}],"created":1731400242,"model":"gpt-4-0613","object":"chat.completion.chunk"}
{"id":"chatcmpl-ASgaImINIA8gwsca92CCgES2VldF8","choices":[{"delta":{"content":"."},"index":0}],"created":1731400242,"model":"gpt-4-0613","object":"chat.completion.chunk"}
{"id":"chatcmpl-ASgaImINIA8gwsca92CCgES2VldF8","choices":[{"delta":{},"finish_reason":"stop","index":0}],"created":1731400242,"model":"gpt-4-0613","object":"chat.completion.chunk"}
```

Ollama chunks: "role" provided in every chunk

```python
from openai import OpenAI

client = OpenAI(
    base_url="http://localhost:11434/v1",
    api_key="ollama",
)
response = client.chat.completions.create(
    model="llama3.1",
    messages=[{"role": "user", "content": "Just say: The answer is secret."}],
    stream=True,
    # stream_options={"include_usage": True},
    # max_tokens=1,
)
for chunk in response:
    print(chunk.model_dump_json(exclude_none=True))
{"id":"chatcmpl-230","choices":[{"delta":{"content":"The","role":"assistant"},"index":0}],"created":1731400290,"model":"llama3.1","object":"chat.completion.chunk","system_fingerprint":"fp_ollama"}
{"id":"chatcmpl-230","choices":[{"delta":{"content":" answer","role":"assistant"},"index":0}],"created":1731400290,"model":"llama3.1","object":"chat.completion.chunk","system_fingerprint":"fp_ollama"}
{"id":"chatcmpl-230","choices":[{"delta":{"content":" is","role":"assistant"},"index":0}],"created":1731400290,"model":"llama3.1","object":"chat.completion.chunk","system_fingerprint":"fp_ollama"}
{"id":"chatcmpl-230","choices":[{"delta":{"content":" secret","role":"assistant"},"index":0}],"created":1731400290,"model":"llama3.1","object":"chat.completion.chunk","system_fingerprint":"fp_ollama"}
{"id":"chatcmpl-230","choices":[{"delta":{"content":".","role":"assistant"},"index":0}],"created":1731400290,"model":"llama3.1","object":"chat.completion.chunk","system_fingerprint":"fp_ollama"}
{"id":"chatcmpl-230","choices":[{"delta":{"content":"","role":"assistant"},"finish_reason":"stop","index":0}],"created":1731400290,"model":"llama3.1","object":"chat.completion.chunk","system_fingerprint":"fp_ollama"}
```

Using `client.beta.chat.completions.stream` with ollama results in `"role": "assistantassistant...`.
openai docs: https://github.com/openai/openai-python/blob/646a579cdb305a9d3fba6c5f9a96011c5e2c2882/helpers.md#chat-completions-api

```python
from openai import OpenAI

client = OpenAI(
    base_url="http://localhost:11434/v1",
    api_key="ollama",
)
with client.beta.chat.completions.stream(
    model="llama3.1",
    messages=[{"role": "user", "content": "Just say: The answer is secret."}],
) as stream:
    for event in stream:
        pass

print(stream.get_final_completion().model_dump_json(indent=2))
{
  "id": "chatcmpl-653",
  "choices": [
    {
      "finish_reason": "stop",
      "index": 0,
      "logprobs": null,
      "message": {
        "content": "The answer is secret.",
        "refusal": null,
        "role": "assistantassistantassistantassistantassistantassistant",
        "audio": null,
        "function_call": null,
        "tool_calls": [],
        "parsed": null
      }
    }
  ],
  "created": 1731400312,
  "model": "llama3.1",
  "object": "chat.completion",
  "service_tier": null,
  "system_fingerprint": "fp_ollama",
  "usage": null
}
```

### OS

macOS

### GPU

_No response_

### CPU

_No response_

### Ollama version

0.4.1