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

Assignees

No one assigned

    Labels

    pythonUsage: [Issues, PRs], Target: PythonreproducedUsage: [Issues], Target: all issues that can be reproduced by the triage workflowtriageUsage: [Issues], Target: All issues that still need to be triaged

    Type

    No fields configured for Bug.

    Projects

    Status
    No status

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions