Metadata-Version: 2.5
Name: batchgrid
Version: 0.2.0b1
Summary: Run a prompt, or a whole pipeline, over every row of a DataFrame or spreadsheet - with cost estimates, retries and resumable runs.
Project-URL: Homepage, https://github.com/mertguvencli/batchgrid
Project-URL: Source, https://github.com/mertguvencli/batchgrid/tree/main/python
Project-URL: Issues, https://github.com/mertguvencli/batchgrid/issues
Author: Mert Guvencli
License-Expression: MIT
Keywords: anthropic,batch,csv,dataframe,gemini,llm,openai,pandas
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Science/Research
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Requires-Python: >=3.9
Provides-Extra: dev
Requires-Dist: pandas>=1.5; extra == 'dev'
Requires-Dist: pytest>=7; extra == 'dev'
Provides-Extra: pandas
Requires-Dist: pandas>=1.5; extra == 'pandas'
Provides-Extra: progress
Requires-Dist: tqdm>=4.60; extra == 'progress'
Description-Content-Type: text/markdown

# batchgrid for Python

Run a prompt, or a whole pipeline, over every row of a DataFrame or spreadsheet. You get a cost
estimate before anything runs, parallel calls with retries and rate-limit backoff, and runs that
resume after an interruption.

```python
import pandas as pd
import batchgrid

df = pd.read_csv("reviews.csv")
result = batchgrid.run(df, "classify the sentiment and extract keywords", max_cost=5)
result.data  # df with the new columns
```

> **Beta.** This package drives the [batchgrid CLI](https://www.npmjs.com/package/batchgrid), so it
> needs **Node.js 22 or newer**. It uses a `batchgrid` on your PATH, or fetches the CLI with `npx`
> on first use.

## Install

```bash
pip install "batchgrid[pandas,progress]"
```

`pandas` is needed for DataFrame input and `progress` adds a tqdm progress bar. File paths work
without either.

Set the key of the provider you use, either as an environment variable (`OPENAI_API_KEY`,
`ANTHROPIC_API_KEY`, `GOOGLE_API_KEY`, `OPENROUTER_API_KEY`, `XAI_API_KEY`) or once with
`npx batchgrid config --set-key openai:sk-…`.

## Plan, check, run

Planning asks a model, so the same request can produce a slightly different plan each time. Look at
the plan and its cost first, then run it:

```python
plan = batchgrid.plan(df, "classify the sentiment of each review")
print(plan)            # the steps, the columns they write, the estimated cost
plan.cost_usd          # 0.08

result = batchgrid.run(df, plan=plan)
```

A pipeline that must behave the same on every run should save the plan once and run that file.
Running a saved plan does not ask the planner again:

```python
plan.save("sentiment.json")

# later, in the pipeline
result = batchgrid.run("next_week.csv", plan="sentiment.json", max_cost=10)
```

The file is the same one `batchgrid --save-plan` writes and `batchgrid --plan` reads.

## In the browser

`batchgrid.ui()` opens the batchgrid workspace in your browser with your data in the sheet. You can
chat, check the plan and run it there, while the log prints in the notebook cell or terminal as it
happens:

```python
df2 = batchgrid.ui(df)
```

```
batchgrid is open at http://127.0.0.1:4817

❯ classify the sentiment of each review
  ✔ Reading data.csv - 1,000 rows · 1 column: review
  ✔ Asking gpt-5.6 for a plan - 1 step · 2.1k tokens
  ✔ Estimating the cost - About ~$0.08 for 1,000 rows
batchgrid: 100%|██████████| 1000/1000 [00:42<00:00, failed=0]
  ✔ 1000/1000 rows, 0 failed
  ↳ Sheet synced: 1,000 rows × 2 columns

Finished in the browser.
```

To come back, press **Finish** in the tab, close the tab, or interrupt the cell. `ui()` returns the
sheet as the tab last showed it. For a DataFrame, that is a copy of yours with the new columns and
any cells you edited; untouched columns keep their dtypes. `ui()` with no data starts on an empty
sheet.

The workspace runs on your machine and uses the keys from `batchgrid config` or your environment. Your
browser has to be able to reach the machine running Python, so this does not work for a notebook on
a remote server.

From a terminal, the `batchgrid` command this package installs does the same:

```bash
batchgrid ui data.csv     # or: batchgrid --ui data.csv
```

## Reference

### `batchgrid.run(data, prompt=None, *, plan=None, ...) -> Result`

| Argument | Meaning |
|---|---|
| `data` | A CSV, TSV or Excel path, a pandas DataFrame, or `None` when the request brings its own rows |
| `prompt` | What to do, in plain language. Pass either this or `plan` |
| `plan` | A `Plan`, its dict, or a saved JSON file |
| `model` | `"gpt-5.6"` or `"provider:model"`, e.g. `"anthropic:claude-sonnet-5"`. The default is the saved choice |
| `rows` | Run only the first N rows, a cheap way to try a plan |
| `concurrency` | The most requests running at once |
| `output` | Where to write the result. The extension picks the format |
| `max_cost` | In US dollars. The plan is priced first and nothing runs above it, or when the model has no price list |
| `progress` | Show a progress bar when tqdm is installed. Defaults to on |
| `on_event` | Called with each event the CLI reports: plan, progress, result… |

`Result` has `status` (`done` or `stopped`), `total`, `success`, `failed`, `input_tokens`,
`output_tokens`, `duration_ms`, `output_path`, `error_log_path`, and `data`. For DataFrame input,
`data` is a copy of your DataFrame with the new columns added, with its index and dtypes kept. If a
plan filters rows or drops columns, `data` holds the output as it was written.

Rows that still fail after their retries do not raise an error. Check `result.failed`, and look in
`result.error_log_path` for the reasons.

### `batchgrid.plan(data, prompt, *, model=None, rows=None) -> Plan`

Plans the request and prices it without running anything. `Plan` has `title`, `summary`,
`cost_usd`, `cost_formatted`, `rows`, `missing_secrets`, the raw `steps`, and `save(path)` /
`Plan.load(path)`.

### `batchgrid.resume(data) -> Result`

Ctrl+C (or a Jupyter interrupt) stops a run and keeps the rows that finished. Call `resume` with the
same file or DataFrame to finish it. Only the unfinished rows are sent to the model.

```python
try:
    result = batchgrid.run(df, plan="sentiment.json")
except KeyboardInterrupt:
    result = batchgrid.resume(df)
```

### `batchgrid.ui(data=None, *, open_browser=True, port=None, log=True) -> DataFrame | None`

See [In the browser](#in-the-browser). `on_event` receives the same events as in `run()`, plus `ui`,
`sheet`, `browser_error` and `closed`.

### Errors

Everything raises `batchgrid.BatchgridError`, and its `code` says what went wrong:

- `no_api_key`: no provider key is set.
- `invalid_plan`: the plan does not fit the data.
- `no_plan`: the planner asked a question instead of returning a plan. This raises `NoPlanError`,
  and its `reply` holds the question.
- `missing_secrets`: the plan needs keys that are not saved. This raises `MissingSecretsError`,
  and its `names` lists them.
- `cost_limit`: the run would go over `max_cost`. This raises `CostLimitError`, and its `plan`
  holds the priced plan.
- `run_failed`: the run itself failed.

### Choosing the CLI

The `batchgrid` command pip installs hands everything to the Node CLI, so `batchgrid config`,
`batchgrid ui` and the rest work the same whether batchgrid came from pip or npm. The package looks
for the CLI in this order:

1. the `cli=[...]` argument
2. the `BATCHGRID_CLI` environment variable, e.g. `node /path/to/cli/dist/index.js`
3. the npm `batchgrid` on the PATH (never this package's own command)
4. `npx "batchgrid@>=0.3.0 <1"`

## Development

```bash
pnpm --filter batchgrid build   # the tests drive the real CLI
cd python
uv venv && uv pip install -e ".[dev,progress]"
.venv/bin/python -m pytest
```

The tests replace only the model: a local server stands in for the OpenAI API.
