infoxtractor/tests/live/test_ollama_client_live.py
Dirk Riemann 90e46b707d
All checks were successful
tests / test (push) Successful in 1m10s
tests / test (pull_request) Successful in 1m5s
feat(genai): OllamaClient — structured-output /api/chat backend (spec §6)
Real GenAIClient for the production pipeline. Sends `format=<pydantic JSON
schema>`, `stream=false`, and mapped options (`temperature`; drops
`reasoning_effort`). Content-parts lists joined to a single string since
MVP models don't speak native content-parts. Error mapping per spec:
connection/timeout/5xx → IX_002_000, schema violations → IX_002_001.
`selfcheck()` probes /api/tags with a fixed 5 s timeout for /healthz.

Tests: 10 hermetic pytest-httpx unit tests; 2 live tests gated on
IX_TEST_OLLAMA=1 (never run in CI).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 11:58:15 +02:00

70 lines
2.3 KiB
Python

"""Live tests for :class:`OllamaClient` — gated on ``IX_TEST_OLLAMA=1``.
Never runs in CI (Forgejo runner has no LAN access to Ollama). Run locally::
IX_TEST_OLLAMA=1 uv run pytest tests/live/test_ollama_client_live.py -v
Assumes the Ollama server at ``http://192.168.68.42:11434`` already has
``gpt-oss:20b`` pulled.
"""
from __future__ import annotations
import os
import pytest
from ix.genai.ollama_client import OllamaClient
from ix.use_cases.bank_statement_header import BankStatementHeader
pytestmark = [
pytest.mark.live,
pytest.mark.skipif(
os.environ.get("IX_TEST_OLLAMA") != "1",
reason="live: IX_TEST_OLLAMA=1 required",
),
]
_OLLAMA_URL = "http://192.168.68.42:11434"
_MODEL = "gpt-oss:20b"
async def test_structured_output_round_trip() -> None:
"""Real Ollama returns a parsed BankStatementHeader instance."""
client = OllamaClient(base_url=_OLLAMA_URL, per_call_timeout_s=300.0)
result = await client.invoke(
request_kwargs={
"model": _MODEL,
"messages": [
{
"role": "system",
"content": (
"You extract bank statement header fields. "
"Return valid JSON matching the given schema. "
"Do not invent values."
),
},
{
"role": "user",
"content": (
"Bank: Deutsche Kreditbank (DKB)\n"
"Currency: EUR\n"
"IBAN: DE89370400440532013000\n"
"Period: 2025-01-01 to 2025-01-31"
),
},
],
},
response_schema=BankStatementHeader,
)
assert isinstance(result.parsed, BankStatementHeader)
assert isinstance(result.parsed.bank_name, str)
assert result.parsed.bank_name # non-empty
assert isinstance(result.parsed.currency, str)
assert result.model_name # server echoes a model name
async def test_selfcheck_ok_against_real_server() -> None:
"""``selfcheck`` returns ``ok`` when the target model is pulled."""
client = OllamaClient(base_url=_OLLAMA_URL, per_call_timeout_s=5.0)
assert await client.selfcheck(expected_model=_MODEL) == "ok"