Skip to main content

evaldata

CI Coverage License: Apache 2.0

Evaluate AI-generated SQL with pytest.

evaldata runs text-to-SQL evals in your existing test suite.

It checks semantic equivalence of SQL queries, diffs result sets in your warehouse, and uses an LLM judge for ambiguous cases.

Why evaldata

  • Semantic equivalence. Parse both queries, normalize their ASTs, and compare canonical forms. It doesn't execute queries or call an LLM. When it can't confirm equivalence, it returns unknown.
  • Execution in your warehouse. Run the query on DuckDB, Postgres, Databricks, Snowflake, or BigQuery and compare the results, accounting for row order, NULLs, float tolerance, and types.
  • It's just pytest. Every eval is a test, run in your suite and your CI on every PR. No new runner, notebook, or dashboard.
  • An LLM judge when you need one. For ambiguous questions, missing reference answers, or explanations to grade, use a grader model with explicit criteria.

Quickstart

uv add evaldata   # core, includes the DuckDB adapter

An eval is a pytest test: a case (a question and its expected answer), a solver (the system under test that writes the SQL), and a scorer (how the answer is judged).

Below, the AI's SQL has reordered predicates and different casing, but means the same thing as the reference query. observed_equivalence() confirms the match with AST normalization; no query runs.

from evaldata import CallableSolver, EvalCase, assert_eval, eval_case, observed_equivalence
from evaldata.platforms import duckdb_platform

platform = duckdb_platform(name="shop", path="shop.duckdb")


@eval_case(
    input="Name the US customers with an id above 1.",
    expected={"kind": "gold_query", "sql": "SELECT name FROM customers WHERE country = 'US' AND id > 1"},
    platform=platform,
)
def test_us_customers(case: EvalCase) -> None:
    solver = CallableSolver(lambda c: "select NAME from customers where id > 1 and country = 'US'")
    assert_eval(case, solver, scorers=[observed_equivalence()])
uv run pytest
 case               result   detail
 ──────────────────────────────────
 test_us_customers  PASS

 1 passed, 0 failed

The full runnable version is in examples/01_deterministic/test_showcase.py.

To test a real model instead of fixed SQL, swap the solver for PromptSolver(model="openai/gpt-4o-mini") (needs the evaldata[litellm] extra). To judge equivalence without a warehouse, swap the scorer for judged_equivalence(model).

More use cases

Install

uv add evaldata                # core (includes the DuckDB adapter)
uv add "evaldata[postgres]"    # + Postgres adapter
uv add "evaldata[databricks]"  # + Databricks adapter
uv add "evaldata[snowflake]"   # + Snowflake adapter
uv add "evaldata[bigquery]"    # + BigQuery adapter
uv add "evaldata[cortex]"      # + Snowflake Cortex Analyst solver
uv add "evaldata[litellm]"     # + litellm, to call a model from PromptSolver
uv add "evaldata[pydantic-evals]"  # + Pydantic Evals integration

DuckDB, Postgres, Databricks, Snowflake, and BigQuery are the adapters available today.

Documentation

Full documentation: monospaceai.github.io/evaldata

Examples

Runnable examples in examples/:

Example Shows
Showcase Compare SQL meaning and results on DuckDB; no setup
Deterministic Score SQL with expected results, reference queries, and data expectations on DuckDB
Local AI Text-to-SQL evaluation with a local Ollama model
Hosted AI Text-to-SQL evaluation with a hosted model
Databricks Score SQL with expected results, reference queries, and data expectations on Databricks SQL Warehouse
LLM judge Use an LLM to judge SQL equivalence
Benchmark Measure text-to-SQL execution accuracy with Spider or BIRD datasets
Snowflake Score SQL with expected results, reference queries, and data expectations on Snowflake
Cortex Analyst Score Snowflake Cortex Analyst queries against expected results
BigQuery Score SQL with expected results, reference queries, and data expectations on BigQuery
dbt Text-to-SQL evaluation for a dbt project with fixed model responses
dbt Semantic Layer dbt Semantic Layer (MetricFlow) queries, scored locally on DuckDB
Pydantic Evals Add execution-based SQL scoring to a Pydantic Evals dataset

See examples/README.md for details.

Contributing

git clone https://github.com/monospaceai/evaldata.git
cd evaldata
uv sync                       # core + dev tooling
uv run pre-commit install
just check                    # lint + typecheck + tests with coverage (runs everything)

just check runs lint, typecheck, and tests with coverage (held at 100%). See the justfile for the full set of commands.

Platform e2e tests

Adapter conformance for real platforms is marked e2e. CI provisions Postgres as a service container and runs the suite on every push, so the Postgres adapter is exercised against a real engine on every change.

Run it locally against Postgres with:

docker compose up -d                  # postgres:17 on localhost:5432
uv run --extra postgres pytest -m e2e # connection via POSTGRES_TEST_* env (defaults match compose)

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

evaldata-0.8.0.tar.gz (116.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

evaldata-0.8.0-py3-none-any.whl (150.9 kB view details)

Uploaded Python 3

File details

Details for the file evaldata-0.8.0.tar.gz.

File metadata

  • Download URL: evaldata-0.8.0.tar.gz
  • Upload date:
  • Size: 116.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.30 {"installer":{"name":"uv","version":"0.11.30","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for evaldata-0.8.0.tar.gz
Algorithm Hash digest
SHA256 5c1e3c52771ab9dd874976c511f96e7722ba9e8c58bf09404e79f446117b58b3
MD5 f9174b17ed0097c49038beb9bfa58fa1
BLAKE2b-256 4c59dc0ad30dbd71cbbafb9d1dd734e02c9fc71633c566f5caa5603830b733c4

See more details on using hashes here.

File details

Details for the file evaldata-0.8.0-py3-none-any.whl.

File metadata

  • Download URL: evaldata-0.8.0-py3-none-any.whl
  • Upload date:
  • Size: 150.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.30 {"installer":{"name":"uv","version":"0.11.30","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for evaldata-0.8.0-py3-none-any.whl
Algorithm Hash digest
SHA256 aeb6ddd7a30d81efbb90128e8b9f3415b9976ed9e883ab26b614c8f54fe81a6f
MD5 a0b751fc6a57d12974bb142f27a1fab8
BLAKE2b-256 5fc4586176e683804e86e3b78aafddb11cb3e9b9747dd5d8b58227a47983133b

See more details on using hashes here.

Release history Release notifications | RSS feed

0.9.0

2 files

This release

0.8.0 This release

2 files

0.7.0

2 files

0.6.0

2 files

0.5.0

2 files

0.4.0

2 files

0.3.0

2 files

0.2.0

2 files

0.1.0

2 files

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page