Skip to content

Python: [Bug]: OllamaChatClient: response_format as a Pydantic model class isn't converted to a JSON schema (structured output fails; breaks harness plan mode) #6781

Description

@antsok

Description

OllamaChatClient forwards response_format straight to Ollama's format parameter without converting a Pydantic model class to its JSON schema. Ollama's format only accepts '', 'json', or a JSON-schema dict, so passing a model class — the form OpenAIChatClient/FoundryChatClient accept, and the form create_harness_agent's plan mode uses — raises a validation error at request-construction time, before the request is ever sent.

Net effect: structured output via a model class doesn't work on Ollama, and harness plan mode (create_harness_agent + PlanningOutputObserver) crashes on every Ollama model.

Code Sample

import asyncio
from agent_framework import Message
from agent_framework.ollama import OllamaChatClient
from pydantic import BaseModel


class Plan(BaseModel):
    summary: str
    steps: list[str]


async def main() -> None:
    client = OllamaChatClient(model="llama3.2")
    msgs = [Message(role="user", contents=["Give a 2-step plan to host a static site on Azure."])]

    # FAILS: response_format as a Pydantic model class (what the harness plan mode passes)
    await client.get_response(msgs, options={"response_format": Plan})

    # WORKS: response_format as a JSON-schema dict
    # await client.get_response(msgs, options={"response_format": Plan.model_json_schema()})


asyncio.run(main())

Error Messages / Stack Traces

Ollama chat request failed : 2 validation errors for ChatRequest
format.literal['','json']
  Input should be '' or 'json' [type=literal_error, input_value=<class '__main__.Plan'>, input_type=ModelMetaclass]
format.dict
  Input should be a valid dictionary [type=dict_type, ...]

Package Versions

agent-framework-core:1.9.0, agent-framework-ollama:1.0.0b260521

Python Version

Python 3.12.10

Additional Context

Expected

Passing a Pydantic model class as response_format produces structured output, consistent with OpenAIChatClient / FoundryChatClient (which accept a model class) and with create_harness_agent plan mode, which sets response_format to a model class.

Actual

ChatClientException wrapping a pydantic ValidationError for the ollama-python ChatRequest.format:

Ollama chat request failed : 2 validation errors for ChatRequest
format.literal['','json']
  Input should be '' or 'json' [type=literal_error, input_value=<class '__main__.Plan'>, input_type=ModelMetaclass]
format.dict
  Input should be a valid dictionary [type=dict_type, ...]

It fails while building the request, so it's model-independent (a larger/cloud model behaves identically), and no call reaches Ollama.

Root cause

In python/packages/ollama/agent_framework_ollama/_chat_client.py, the option is remapped and the value passed through unchanged:

OLLAMA_OPTION_TRANSLATIONS: dict[str, str] = {
    "response_format": "format",
}
...
# in _prepare_options:
translated_key = OLLAMA_OPTION_TRANSLATIONS.get(key, key)
run_options[translated_key] = value   # value may be a Pydantic model class

Ollama-python's ChatRequest.format is typed as Literal['', 'json'] | JsonSchemaValue (a dict). A Pydantic model class is neither, hence the validation error. (Ollama's documented structured-output usage is format=Model.model_json_schema() — see https://ollama.com/blog/structured-outputs.)

Impact

  • Any caller passing a Pydantic model class as response_format to OllamaChatClient.
  • create_harness_agent plan mode: PlanningOutputObserver sets response_format to a model class, so the harness console sample (python/samples/02-agents/harness) errors on every turn when run against Ollama.
  • Inconsistent behavior across providers (OpenAI/Foundry accept a model class; Ollama does not).

Suggested fix

When mapping response_formatformat, convert a Pydantic model class (or instance type) to its JSON schema, keeping the existing '' / 'json' / dict pass-through. For example, in _prepare_options:

from pydantic import BaseModel

value = options.get("response_format")
if isinstance(value, type) and issubclass(value, BaseModel):
    value = value.model_json_schema()
# then assign `value` to run_options["format"]

Convert only the outgoing format; keep the original response_format on the value used to parse the response back so typed parsing (ChatResponse.value) still works.

Workaround

Subclass OllamaChatClient and override _prepare_options to perform the conversion before delegating to super(). Verified: with the conversion, a model-class response_format returns valid structured JSON and parses back into the model; without it, the error above occurs.

Metadata

Metadata

Labels

pythonUsage: [Issues, PRs], Target: PythonreproducedUsage: [Issues], Target: all issues that can be reproduced by the triage workflow

Type

Projects

Status
Done

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions