Compare commits
No commits in common. "65670af78ffb01268d25971649dec9802f9ce9d4" and "4c0746950ef7119cf38aa8059a1f41bf804d0c91" have entirely different histories.
65670af78f
...
4c0746950e
2 changed files with 2 additions and 58 deletions
|
|
@ -168,9 +168,7 @@ class OllamaClient:
|
||||||
"model": request_kwargs.get("model"),
|
"model": request_kwargs.get("model"),
|
||||||
"messages": messages,
|
"messages": messages,
|
||||||
"stream": False,
|
"stream": False,
|
||||||
"format": _sanitise_schema_for_ollama(
|
"format": response_schema.model_json_schema(),
|
||||||
response_schema.model_json_schema()
|
|
||||||
),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
options: dict[str, Any] = {}
|
options: dict[str, Any] = {}
|
||||||
|
|
@ -202,52 +200,4 @@ class OllamaClient:
|
||||||
return out
|
return out
|
||||||
|
|
||||||
|
|
||||||
def _sanitise_schema_for_ollama(schema: Any) -> Any:
|
|
||||||
"""Strip null branches from ``anyOf`` unions.
|
|
||||||
|
|
||||||
Ollama 0.11.8's llama.cpp structured-output implementation segfaults on
|
|
||||||
Pydantic v2's standard Optional pattern::
|
|
||||||
|
|
||||||
{"anyOf": [{"type": "string"}, {"type": "null"}]}
|
|
||||||
|
|
||||||
We collapse any ``anyOf`` that includes a ``{"type": "null"}`` entry to
|
|
||||||
its non-null branch — single branch becomes that branch inline; multiple
|
|
||||||
branches keep the union without null. This only narrows what the LLM is
|
|
||||||
*told* it may emit; Pydantic still validates the real response and can
|
|
||||||
accept ``None`` at parse time if the field is ``Optional``.
|
|
||||||
|
|
||||||
Walk is recursive and structure-preserving. Other ``anyOf`` shapes (e.g.
|
|
||||||
polymorphic unions without null) are left alone.
|
|
||||||
"""
|
|
||||||
if isinstance(schema, dict):
|
|
||||||
cleaned: dict[str, Any] = {}
|
|
||||||
for key, value in schema.items():
|
|
||||||
if key == "anyOf" and isinstance(value, list):
|
|
||||||
non_null = [
|
|
||||||
_sanitise_schema_for_ollama(branch)
|
|
||||||
for branch in value
|
|
||||||
if not (isinstance(branch, dict) and branch.get("type") == "null")
|
|
||||||
]
|
|
||||||
if len(non_null) == 1:
|
|
||||||
# Inline the single remaining branch; merge its keys into the
|
|
||||||
# parent so siblings like ``default``/``title`` are preserved.
|
|
||||||
only = non_null[0]
|
|
||||||
if isinstance(only, dict):
|
|
||||||
for ok, ov in only.items():
|
|
||||||
cleaned.setdefault(ok, ov)
|
|
||||||
else:
|
|
||||||
cleaned[key] = non_null
|
|
||||||
elif len(non_null) == 0:
|
|
||||||
# Pathological: nothing left. Fall back to a permissive type.
|
|
||||||
cleaned["type"] = "string"
|
|
||||||
else:
|
|
||||||
cleaned[key] = non_null
|
|
||||||
else:
|
|
||||||
cleaned[key] = _sanitise_schema_for_ollama(value)
|
|
||||||
return cleaned
|
|
||||||
if isinstance(schema, list):
|
|
||||||
return [_sanitise_schema_for_ollama(item) for item in schema]
|
|
||||||
return schema
|
|
||||||
|
|
||||||
|
|
||||||
__all__ = ["OllamaClient"]
|
__all__ = ["OllamaClient"]
|
||||||
|
|
|
||||||
|
|
@ -79,13 +79,7 @@ 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 the pydantic schema with Optional `anyOf [T, null]`
|
assert body_json["format"] == _Schema.model_json_schema()
|
||||||
# patterns collapsed to just T — Ollama 0.11.8 segfaults on the
|
|
||||||
# anyOf+null shape, so we sanitise before sending.
|
|
||||||
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"] == [
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue