Skip to main content

data-harness — The controlled data-agent SDK

Python, not bash. Large data stays in a cache as handles, never in the prompt.
Every run is logged — and eval-backed.

PyPI Python 3.10+ License: MIT Docs


Most data-agent tooling makes you pick between giving a model a shell (unsafe, irreproducible) and single-shot code-gen (no state, no multi-step). data-harness is the controlled middle path: the model works through a constrained Python interpreter, large objects live in a SessionCache and are exposed as compact handle snapshots — so a 100k-row table never hits the context window — every run is recorded as an append-only session tree, and a built-in evaluation harness measures quality and cost across providers.

Principles

  • Python, not bash — one controlled execution surface: no shell side-effects, no destructive commands, reproducible runs.
  • Handles, not payloads — large data lives in the cache; only snapshots reach the model, so context (and cost) stay flat as data grows.
  • Measured, not vibes — a first-class eval harness with programmatic graders, multi-turn cases, cost, and tracked leaderboards.

Features

  • One-linerask(df, "...") in Python, or dh "..." data.csv from the shell.
  • Charts & SQL — automatic matplotlib capture; a DuckDB / SQLAlchemy sql_query tool.
  • Many providers, one key — OpenAI, Anthropic, DeepSeek, Qwen, Google, Z.ai… via OpenRouter.
  • MCP bridge — connect any MCP server (Postgres, SQLite, filesystem…) and use its tools, with progressive disclosure + handle/snapshot.
  • Production controls — subprocess sandbox, an approval gate, and a zero-token replay cache.
  • Evaluation — bespoke / hard / large-data suites + WikiTableQuestions, with multi-turn cases, cost, and JSON-tracked results.
  • Composableask/Chat over Agent over Harness; async + streaming; subagents; progressive connectors.

Install

pip install data-harness          # core
pip install "data-harness[all]"   # + openai, charts, duckdb, sqlalchemy, notebook, eval

Pick individual extras as needed: [openai], [viz], [duckdb], [sql], [notebook], [eval]. Requires Python 3.10+.


Quickstart

Ask a question about a DataFrame in one line. ask() resolves a provider from your environment, loads the data into the session cache, runs the agent, and returns a RunResult:

import pandas as pd
from data_harness import ask

df = pd.read_csv("sales.csv")
result = ask(df, "What was total revenue, and which month was highest?")

print(result.text)      # the written answer
print(result.value)     # the structured result the model computed via answer()
result.charts           # any charts it rendered (notebook-friendly)

Reach many providers through one key with OpenRouter — a provider/model id auto-routes there. Set OPENROUTER_API_KEY:

ask(df, "plot revenue by month", model="deepseek/deepseek-v4-flash")
ask(df, "summarise the data",   model="google/gemini-2.5-flash-lite")
ask(df, "which region grew fastest?", model="qwen/qwen3.5-flash-02-23")

Without OpenRouter, ask() falls back to ANTHROPIC_API_KEY / OPENAI_API_KEY / DEEPSEEK_API_KEY. In a notebook, the returned RunResult renders prose, the value, and charts inline (there's also a %%ask magic via %load_ext data_harness.app.notebook).


Command line & demo

Ask from the shell with dh (also installed as data-harness) — point at one or more files, or pipe CSV via stdin:

dh "What was total revenue?" sales.csv
dh "Join these and find the top region" orders.csv customers.csv
cat sales.csv | dh "median order amount" --json

A Streamlit demo app (pip install "data-harness[demo]"):

uv run streamlit run examples/streamlit_app.py

data-harness Streamlit demo

Multi-turn chat

from data_harness import Chat

chat = Chat(df)
chat.ask("What was total revenue?")
chat.ask("Which month was highest?")   # remembers context (shared cache + history)

Charts & SQL

matplotlib runs inside the interpreter; open figures are captured automatically as artefacts — the image bytes live on disk and never enter the message history or logs (only a path does):

result = ask(df, "Plot revenue by region as a bar chart.")
result.charts[0]        # a ChartArtifact; renders inline in Jupyter

With DuckDB installed, ask exposes a sql_query tool over your DataFrames; point it at a real database with a SQLAlchemy URL:

ask(df, "Use SQL to get total revenue per region.")          # DuckDB, in-process

from data_harness import Agent
agent = Agent.from_dataframe(df).enable_sql(engine_url="postgresql://...")
agent.run("Top 5 customers by spend last quarter?")

Production controls

from data_harness import Agent, ExecutionCache

agent = Agent.from_dataframe(df).enable_cache(ExecutionCache("cache.json"))  # 0-token replays
sandboxed = Agent.from_dataframe(df, execution="subprocess")                 # isolated process
gated = Agent.from_dataframe(df, on_code=lambda code: (print(code), True)[1]) # approve code
preview = Agent.from_dataframe(df, code_only=True)                            # dry-run, never executes
  • Code-replay cache — a repeat question over the same data schema replays the recorded code with no model call (zero turns, zero tokens), and stays correct when the data changes.
  • Subprocess sandbox — interpreter code runs in a separate process with networking disabled and CPU/wall-clock limits; handles cross by value, results merge back.
  • Approval gateon_code sees every code block before execution and can block it; code_only=True returns the code without running it.

Evaluation

A first-class harness to measure how well an agent answers real data questions — across models, with programmatic grading that leans on the structured .value (no LLM judge needed for most cases).

from data_harness.eval import evaluate_matrix, fetch_openrouter_prices, hard_suite

models = ["deepseek/deepseek-v4-flash", "qwen/qwen3.5-flash-02-23",
          "openai/gpt-5-nano", "google/gemini-2.5-flash-lite"]
report = evaluate_matrix(hard_suite(), models)
print(report.to_markdown(fetch_openrouter_prices(models)))  # accuracy / turns / tokens / cost
  • Suitesbespoke_suite() (smoke), hard_suite() (multi-table joins, deep multi-step, stateful multi-turn), large_data_suite() (100k-row frames answerable only via the handle, with a snapshot trap), and load_wikitablequestions() (public table-QA, the model differentiator).
  • Case types — single-shot EvalCase and multi-turn ConversationCase (graded turns over one Chat session, testing SessionCache persistence).
  • Gradersnumeric, contains, exact, dataframe_equals, chart_produced, refuses, all_of/any_of.
  • Reporting — leaderboards with per-model cost, per-category breakdowns, and to_dict()/to_json() for results tracked in evals/results/.

Results are committed as readable leaderboards — see evals/results/SUMMARY.md (a table per suite: accuracy, turns, tokens, cost).

What the runs show: the structured/large/stateful suites saturate at ~100% across recent models — i.e. the design is robust (even small, cheap models handle 100k-row data via the handle for ~$0.002 and pass the snapshot trap). Model differentiation shows up on messy real-world data — WikiTableQuestions spreads recent models 64%→96%. See the Evaluation guide.


Lower-level Agent and Harness

ask/Chat are conveniences over Agent, itself a thin layer over Harness. Drop down for full control:

from data_harness import Agent

agent = Agent(system="You are a data analyst.", model="claude-sonnet-4-6")
print(agent.run("Compute the mean of [1, 2, 3, 4, 5]."))
Component Role
Harness The ReAct loop — messages, tool dispatch, reminders, session recording
SessionCache Handle-based store; keeps large objects out of message history
ProviderAdapter Translates provider SDK responses into harness types
python_interpreter The model's only execution surface
ConnectorRegistry Hides connector tools until the model loads them
Subagent Isolated worker with explicit state transfer

Async + streaming (AsyncAgent.run_stream), progressive connectors, and subagents are all supported — see examples/advanced_wiring.py and the docs.


Examples & tests

uv run python examples/live_demo.py          # ask()/charts/SQL on a cheap model
uv run python examples/eval_demo.py --suite hard   # multi-model eval leaderboard (cost)
uv run python examples/cache_benchmark.py    # replay-cache benchmark (no API key)
uv run python -m pytest tests/ -m "not live"       # offline test suite

examples/demo.ipynb is an executed end-to-end notebook.


Sandbox disclaimer

The in-process interpreter uses AST checks and restricted globals to reduce accidental misuse — it is not a container sandbox. For stronger isolation use execution="subprocess" (separate process, no network, resource limits). Neither is hardened for untrusted input.


Links

Download files

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

Source Distribution

data_harness-1.2.0.tar.gz (3.2 MB view details)

Uploaded Source

Built Distribution

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

data_harness-1.2.0-py3-none-any.whl (127.6 kB view details)

Uploaded Python 3

File details

Details for the file data_harness-1.2.0.tar.gz.

File metadata

  • Download URL: data_harness-1.2.0.tar.gz
  • Upload date:
  • Size: 3.2 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.12.1 {"installer":{"name":"uv","version":"0.12.1","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 data_harness-1.2.0.tar.gz
Algorithm Hash digest
SHA256 216c2c91ff3a5cdae3c511db737de41a1b9bd28f4692702bb757ae7d6578aeb3
MD5 2f32ea7ec1c70abd84c54b6213766c1c
BLAKE2b-256 e7f8f2c99379b99a7991900d8036a31373df9b6ebd3a4dd1891281e10062bb92

See more details on using hashes here.

File details

Details for the file data_harness-1.2.0-py3-none-any.whl.

File metadata

  • Download URL: data_harness-1.2.0-py3-none-any.whl
  • Upload date:
  • Size: 127.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.12.1 {"installer":{"name":"uv","version":"0.12.1","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 data_harness-1.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 9cdca3feb5257f147259c8f59bc250f932449f3c488f4c76b619de28a075efab
MD5 9168583eb136e79129f436334835a8de
BLAKE2b-256 76f7ffe3f144767b86772ba61f4de9e9576f8a70e577cc1786b9fc45eb4b8091

See more details on using hashes here.

Release history Release notifications | RSS feed

1.3.4

2 files

1.3.3

2 files

1.3.2

2 files

1.3.1

2 files

1.3.0

2 files

This release

1.2.0 This release

2 files

1.1.0

2 files

1.0.0

2 files

0.13.0

2 files

0.12.0

2 files

0.11.0

2 files

0.10.0

2 files

0.9.0

2 files

0.8.0

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.3

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