Compare commits

..

No commits in common. "9c7389531813bb4363071dd8a9be395b6ff29c49" and "f6ce97d7fd7cc5d74deb1f6d0443016939147fae" have entirely different histories.

2 changed files with 11 additions and 20 deletions

View file

@ -159,21 +159,7 @@ class OllamaClient:
request_kwargs: dict[str, Any], request_kwargs: dict[str, Any],
response_schema: type[BaseModel], response_schema: type[BaseModel],
) -> dict[str, Any]: ) -> dict[str, Any]:
"""Map provider-neutral kwargs to Ollama's /api/chat body. """Map provider-neutral kwargs to Ollama's /api/chat body."""
Schema strategy for Ollama 0.11.8: we pass ``format="json"`` (loose
JSON mode) rather than the full Pydantic schema. The llama.cpp
structured-output implementation in 0.11.8 segfaults on schemas
involving ``anyOf``, ``$ref``, or ``pattern`` which Pydantic v2
emits for Optional / nested-model / Decimal fields.
In loose JSON mode Ollama guarantees only syntactically-valid
JSON; we enforce the schema on our side by catching the Pydantic
``ValidationError`` at parse time and raising IX_002_001. The
system prompt (built upstream in GenAIStep) already tells the
model what JSON shape to emit, so loose mode is the right
abstraction layer here.
"""
messages = self._translate_messages( messages = self._translate_messages(
list(request_kwargs.get("messages") or []) list(request_kwargs.get("messages") or [])
@ -182,7 +168,9 @@ class OllamaClient:
"model": request_kwargs.get("model"), "model": request_kwargs.get("model"),
"messages": messages, "messages": messages,
"stream": False, "stream": False,
"format": "json", "format": _sanitise_schema_for_ollama(
response_schema.model_json_schema()
),
} }
options: dict[str, Any] = {} options: dict[str, Any] = {}

View file

@ -79,10 +79,13 @@ class TestInvokeHappyPath:
body_json = json.loads(body) body_json = json.loads(body)
assert body_json["model"] == "gpt-oss:20b" assert body_json["model"] == "gpt-oss:20b"
assert body_json["stream"] is False assert body_json["stream"] is False
# format is "json" (loose mode): Ollama 0.11.8 segfaults on full # Format is the pydantic schema with Optional `anyOf [T, null]`
# Pydantic schemas. We pass the schema via the system prompt # patterns collapsed to just T — Ollama 0.11.8 segfaults on the
# upstream and validate on parse. # anyOf+null shape, so we sanitise before sending.
assert body_json["format"] == "json" fmt = body_json["format"]
assert fmt["properties"]["bank_name"] == {"title": "Bank Name", "type": "string"}
assert fmt["properties"]["account_number"]["type"] == "string"
assert "anyOf" not in fmt["properties"]["account_number"]
assert body_json["options"]["temperature"] == 0.2 assert body_json["options"]["temperature"] == 0.2
assert "reasoning_effort" not in body_json assert "reasoning_effort" not in body_json
assert body_json["messages"] == [ assert body_json["messages"] == [