Point at a repo, auto-generate the BAML migration, ship a shareable report.
Project description
BAML Migration Scout
Point it at a GitHub repo, get a working BAML migration plus a shareable report. Built as the deliverable for the Basis Set Ventures fellowship Agentic GTM track, targeting BAML (YC W23).
Read the strategic memo first: STRATEGIC_MEMO.md.
What it does
- Clones a GitHub repo (or reads a local path / single
.pyfile). - AST-scans every Python file for LLM call sites:
openai.chat.completions.create/.parse/.responses.createinstructor.from_openai(...)/.patch(...)+ anyresponse_model=- LangChain
PydanticOutputParser/StructuredOutputParser json.loads(...)immediately after an LLM callanthropic.messages.create(..., tools=...)
- For each call site, asks the active LLM provider to generate the equivalent
.bamlfile. Default is Gemini 2.5 Flash on the free tier ($0 spent); OpenAI and Anthropic are supported via--providerfor users who knowingly opt in to paid APIs. - Validates every generated
.bamlwithbaml-cli check. Retries up to 2 times with the compiler error in context. Drops sites it can't translate cleanly rather than shipping broken BAML. - Runs
baml-cli generateagainst the finalbaml_src/to produce a working Pydantic client. - Optionally (
--benchmark) runs 5-trial head-to-head trials on the active provider comparing JSON-Schema-in-prompt vs BAML compact-hint formats. Measures tokens, latency, and schema-validity rate. - Renders a markdown migration report with before/after diffs, the generated BAML inline, measured deltas, and a tweet-ready summary.
Install
pip install baml-scout # core, Gemini-only ($0 path)
pip install 'baml-scout[openai]' # add OpenAI adapter
pip install 'baml-scout[anthropic]' # add Anthropic adapter
pip install 'baml-scout[all]' # all providers
npm install -g @boundaryml/baml # provides baml-cli (required for validation)
echo "GEMINI_API_KEY=your-free-key" > .env # get one at https://aistudio.google.com/
For rate-limit resilience, comma-separate multiple keys: GEMINI_API_KEY=key1,key2,key3. The scout rotates on 429s and exits cleanly when all keys are exhausted — never silently switches to a paid provider.
From source (development)
git clone https://github.com/Khangdang1690/vc-tasks.git
cd vc-tasks/gtm
uv sync --dev
uv run pytest # 63 tests, no network
uv run baml-scout --help
Switching to a paid provider
A first run on a paid provider hits an explicit "this is a PAID API" confirmation; pass --yes to acknowledge.
echo "OPENAI_API_KEY=sk-..." >> .env
baml-scout <repo> --provider openai --yes
echo "ANTHROPIC_API_KEY=sk-ant-..." >> .env
baml-scout <repo> --provider anthropic --yes
baml-scout <repo> --provider openai --model gpt-4o --yes
Usage
baml-scout <repo-url-or-path> [--scan-only] [--benchmark] [--out ./output]
[--provider {gemini,openai,anthropic}] [--model NAME] [--yes]
[--verbose]
Also available: python -m baml_scout <args> if you didn't install the entry point.
Examples:
# Just detect call sites, no LLM calls
baml-scout https://github.com/jxnl/n-levels-of-rag --scan-only
# Full migration + report (default: Gemini free tier)
baml-scout https://github.com/jxnl/n-levels-of-rag
# Full migration + report + measured benchmark
baml-scout https://github.com/jxnl/n-levels-of-rag --benchmark
# Local file or directory
baml-scout ./path/to/some_file.py
# Switch provider (paid — see above)
baml-scout <repo> --provider anthropic --yes
Library use
The CLI is a thin wrapper around an importable Python API. Useful if you want to build the scout into a CI step, a Slack bot, or a hosted demo.
from pathlib import Path
from baml_scout import (
scan_repo, # AST detection
get_provider, LLMClient, # provider abstraction
seed_baml_examples, translate_site, # translation
validate_baml_file, # baml-cli check
build_context, render_report, # markdown report
)
sites = scan_repo(Path("./my-project"))
provider = get_provider("gemini") # or "openai", "anthropic"
client = LLMClient(["YOUR-KEY"], provider=provider)
examples = seed_baml_examples() # wheel-bundled, no first-run shell-out
for site in sites:
baml, fn_name = translate_site(client, site, examples)
result = validate_baml_file(baml)
if result.ok:
print(fn_name, "→ valid BAML")
Every public name in src/baml_scout/init.py is part of the stable surface; module-private names (_…) may change without notice.
Output lands in output/<repo-name>/:
output/<repo-name>/
├── migration_report.md # the artifact — read this
├── baml_src/ # generated .baml files, ready to drop in
│ ├── clients.baml
│ ├── generators.baml
│ └── <function>.baml # one per migrated call site
├── baml_client/ # output of baml-cli generate
└── patch.diff # additive diff of the new baml_src/ files
Three live reports
| Repo | Sites | Report |
|---|---|---|
| jxnl/n-levels-of-rag | 3 | output/n-levels-of-rag/migration_report.md |
| daveebbelaar/ai-cookbook (intro) | 7 | output/1-introduction/migration_report.md |
| daveebbelaar/ai-cookbook (workflow patterns) | 11 | output/2-workflow-patterns/migration_report.md |
Project layout
src/baml_scout/
├── __init__.py Public library API (re-exports the stable surface)
├── __main__.py Lets `python -m baml_scout` work without an install
├── cli.py CLI entry point + orchestration (was scout.py)
├── scanner.py AST visitor — detects LLM call sites
├── translator.py Provider-agnostic LLMClient + prompt template
├── providers.py Provider adapters (Gemini default, OpenAI / Anthropic opt-in)
├── validator.py baml-cli check + generate subprocess wrappers
├── benchmark.py Optional --benchmark mode (head-to-head trials)
├── reporter.py Delta estimation + Jinja rendering
├── config.py Central config (model, temps, retries, timeouts, skip-dirs)
├── utils.py Shared helpers (fence-strip, token estimator, logger setup)
├── baml_examples.md Wheel-bundled few-shot seed
└── templates/
└── migration_report.md.j2
tests/ pytest suite (63 tests, no network)
pyproject.toml Hatchling build; src layout; scripts entry → baml-scout
Constraints honored
- $0 budget by default. Gemini 2.5 Flash free tier is the only required runtime dependency. Multi-key rotation; clean exit on exhaustion. Paid providers (OpenAI, Anthropic) require explicit
--provider+--yes. - Every generated
.bamlis validated against the compiler before shipping. Honest about translations that fail. - Voice for the report: senior engineer's post-mortem. No marketing language. No emojis except in the bottom tweet-ready section.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file baml_scout-0.2.0.tar.gz.
File metadata
- Download URL: baml_scout-0.2.0.tar.gz
- Upload date:
- Size: 54.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.7.8
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6cee1eeb2014358da0f17f152b4a8fcdfc63a7a3ff0ed91886035980fc276c5f
|
|
| MD5 |
08016c91681a11511764d35873a9c637
|
|
| BLAKE2b-256 |
8ded71781ab8321a6ff3af6e203d4a8c6fbf9de501664576d53cb85775e33cde
|
File details
Details for the file baml_scout-0.2.0-py3-none-any.whl.
File metadata
- Download URL: baml_scout-0.2.0-py3-none-any.whl
- Upload date:
- Size: 48.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.7.8
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
cd852a08aedad83dda51eae067422a84be2e12313d7c93065eaaf273bf963f1a
|
|
| MD5 |
83e786be345d57ee0d769fb76e7e16e5
|
|
| BLAKE2b-256 |
7f84695925359ea47730c94330a78b1840a2f316a83c9740d835c1056394c995
|