Evaluate a hosted model¶
Run a hosted, API-served model as the system under test. The solver is a PromptSolver that
calls the model through litellm — set the model id and its
credentials, and it runs.
Prerequisites¶
Write the eval¶
The solver is a PromptSolver(model=...). Create test_hosted_ai.py:
"""Hosted-AI text-to-SQL example evals: a `PromptSolver` calling a hosted model."""
import os
import tempfile
from collections.abc import Iterator
from pathlib import Path
import duckdb
import pytest
from evaldata import EvalCase, ResultSetEquivalence, assert_eval, eval_case
from evaldata.platforms import duckdb_platform
from evaldata.solvers import PromptSolver
_DB_PATH = Path(tempfile.mkdtemp(prefix="evaldata_ex03_")) / "shop.duckdb"
_PLATFORM = duckdb_platform(name="examples-hosted-ai", path=str(_DB_PATH))
_MODEL = os.getenv("EVALDATA_HOSTED_MODEL", "openai/gpt-4o-mini")
@pytest.fixture(scope="module", autouse=True)
def _seed_db() -> Iterator[None]:
con = duckdb.connect(str(_DB_PATH))
con.execute("CREATE TABLE customers (id INTEGER, name VARCHAR, country VARCHAR)")
con.execute("INSERT INTO customers VALUES (1, 'Ada', 'GB'), (2, 'Bo', 'US'), (3, 'Cy', 'US')")
con.execute("CREATE TABLE orders (id INTEGER, customer_id INTEGER, amount DECIMAL(10, 2))")
con.execute("INSERT INTO orders VALUES (1, 1, 10.00), (2, 1, 5.50), (3, 2, 20.00), (4, 2, 7.25)")
con.close()
yield
@eval_case(
input="How many orders are there? Name the output column order_count.",
expected={"rows": [{"order_count": 4}]},
platform=_PLATFORM,
)
def test_order_count(case: EvalCase) -> None:
"""Hosted model counts orders; scored on exact rows."""
solver = PromptSolver(model=_MODEL, timeout=120, temperature=0)
assert_eval(case, solver, scorers=[ResultSetEquivalence()])
@eval_case(
input=("How many distinct customers placed an order? Name the output column customer_count."),
expected={"rows": [{"customer_count": 2}]},
platform=_PLATFORM,
)
def test_distinct_customers(case: EvalCase) -> None:
"""Hosted model counts distinct ordering customers; scored on exact rows."""
solver = PromptSolver(model=_MODEL, timeout=120, temperature=0)
assert_eval(case, solver, scorers=[ResultSetEquivalence()])
The example reads the model id from EVALDATA_HOSTED_MODEL and passes it to
PromptSolver(model=...) — that's just the model argument, so you can pass a literal instead.
litellm reads your provider credentials from the environment, e.g. OPENAI_API_KEY.
Run it against the live model¶
Or run it deterministically (no key, no network)¶
To run this as a CI plumbing check without a live call, mock the model reply. Add a
conftest.py next to your test that returns the structured {"sql": ...} the solver expects,
matched per question — so the eval exercises the full path except the network:
"""Mocked model replies for the hosted-AI example, so it runs without a key or network."""
from typing import Any
import litellm
import pytest
_SQL_BY_QUESTION = {
"order_count": "SELECT count(*) AS order_count FROM orders",
"customer_count": "SELECT count(DISTINCT customer_id) AS customer_count FROM orders",
}
def _mock_sql(messages: list[dict[str, Any]]) -> str:
"""Pick the correct SQL for a request by matching its question text.
Args:
messages: The chat messages of the request, whose prompt names the expected
output column.
Returns:
The raw SQL reply for the matched question.
Raises:
AssertionError: When no known question is present in the messages.
"""
prompt = " ".join(m.get("content", "") for m in messages)
for marker, sql in _SQL_BY_QUESTION.items():
if marker in prompt:
return sql
msg = f"no mock SQL for prompt: {prompt!r}" # pragma: no cover
raise AssertionError(msg) # pragma: no cover
@pytest.fixture(autouse=True)
def _mock_completion(monkeypatch: pytest.MonkeyPatch) -> None:
"""Patch model calls to return a deterministic reply per question, with no network."""
real_completion = litellm.completion
def fake(**kwargs: Any) -> Any:
return real_completion(**kwargs, mock_response=_mock_sql(kwargs["messages"]))
monkeypatch.setattr("litellm.completion", fake)
With the mock in place it runs offline:
Remove the conftest.py (and set a real OPENAI_API_KEY) to evaluate the live model instead.
Run it from a clone
This is the bundled examples/03_hosted_ai/ example. If you've cloned the repo, run it
directly with uv run pytest examples/03_hosted_ai — it runs mocked, with no key needed.
Next steps¶
- Evaluate against Databricks — run an eval on a real Databricks SQL warehouse.
- Concepts — solvers, scorers, and expected-types in depth.