Skip to content

[Bug]:OllamaChatModel.doStream ignores ModelCreationContext.stream, returns empty response when consumed via blockLast() #2414

Description

@shinebigfool

Describe the bug

OllamaChatModel.doStream ignores the ModelCreationContext.stream flag and always uses Ollama's streaming API, regardless of what the caller requests. When a caller consumes the model via model.stream(...).blockLast() (a common pattern for single-turn clients and inside ReActAgent reasoning), it receives an empty response with no error thrown.

Because Model.stream(...) is final in ChatModelBase and always dispatches to doStream, there is no way for a caller to opt into the non-streaming path — the stream=false setting silently has no effect.

To Reproduce

Steps to reproduce the behavior:

  1. Your code — resolve an Ollama model with stream=false, then consume via blockLast():
import io.agentscope.core.model.Model;
import io.agentscope.core.model.ModelCreationContext;
import io.agentscope.core.model.ModelRegistry;
import io.agentscope.core.model.ChatResponse;
import io.agentscope.core.message.Msg;
import io.agentscope.core.message.MsgRole;
import java.util.List;

ModelCreationContext ctx = ModelCreationContext.builder()
        .baseUrl("http://localhost:11434")
        .stream(false)                       // request NON-streaming
        .build();
Model model = ModelRegistry.resolve("ollama:qwen2.5", ctx);

ChatResponse resp = model.stream(
        List.of(Msg.builder().role(MsgRole.USER).textContent("Hello").build()),
        null, null).blockLast();

System.out.println("content = " + resp.getContent());
  1. How to execute — with a real Ollama server running at localhost:11434 (or against a mocked HttpTransport returning Ollama's chunked streaming format). The snippet above hits the model's stream() entry point.

  2. See error — no exception is thrown, but the printed content is empty:

content = []

This is because under streaming mode, blockLast() returns the last streaming chunk, which for Ollama is the trailing {"done":true} marker carrying no message.content. The full reply is silently lost.

Expected behavior

When ModelCreationContext.stream is false, OllamaChatModel should use Ollama's non-streaming API (/api/chat with stream:false, returning one complete response) so that model.stream(...).blockLast() returns the full reply — mirroring how OpenAIChatModel already honors this flag (in OpenAIChatModel.doStream0, stream=false returns Flux.just(fullResponse)).

When the flag is null/unset, current streaming behavior should be preserved (backward compatibility).

Error messages

No error message is thrown — this is a silent failure (empty content), which is what makes it hard to diagnose. The root cause is visible in the source:

// OllamaChatModel.java — doStream hardcodes the streaming branch
@Override
protected Flux<ChatResponse> doStream(
        List<Msg> messages, List<ToolSchema> tools, GenerateOptions options) {
    return streamWithHttpClient(
            messages, tools,
            options != null ? options.getToolChoice() : null,
            OllamaOptions.fromGenerateOptions(options),
            true);   // ← hardcoded; ignores ModelCreationContext.stream
}

Environment (please complete the following information):

  • AgentScope-Java Version: 2.0.1-SNAPSHOT (confirmed also reproducible on current main branch)
  • Java Version: 17
  • OS: Windows (reproduced locally); also confirmed on Linux CI

Additional context

ModelCreationContext documents stream as a provider-neutral field ("common enough to be understood by all providers"), and OpenAIChatModel honors it end-to-end. OllamaChatModel currently does not — four gaps combine to break the contract:

  1. OllamaModelProvider.create never reads context.getStream()
  2. OllamaChatModel has no stream field
  3. OllamaChatModel.Builder has no stream(boolean) method
  4. doStream hardcodes the literal true

Notably, the non-streaming branch already exists in streamWithHttpClient(..., false) (it calls httpClient.chat() and returns a single complete response) — it is simply unreachable from doStream.

Impact: silent failure (no exception, empty content) for any caller consuming OllamaChatModel via stream().blockLast(), including single-turn bots and ReActAgent reasoning internals.

I have a fix ready that mirrors the OpenAIChatModel implementation (honor the flag end-to-end: provider reads context.getStream() → builder field → doStream dispatches on it) and will open a PR linked to this issue.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions