Skip to content

feat(engine): minimal vendor profile for baml_py + CLI#3936

Draft
hellovai wants to merge 12 commits into
canaryfrom
better-for-vendoring
Draft

feat(engine): minimal vendor profile for baml_py + CLI#3936
hellovai wants to merge 12 commits into
canaryfrom
better-for-vendoring

Conversation

@hellovai

@hellovai hellovai commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

What

Adds a feature-gated vendor profile: a way to build baml_py and the baml CLI with a much smaller dependency tree, for environments that vendor all third-party crates (where every external crate must be imported and reviewed).

Building either crate with --no-default-features drops the optional surface; everything is on by default, so published artifacts (PyPI / NPM / gems / release CLI) are unchanged.

cargo build -p baml-python-ffi --no-default-features            # ~283 crates
cargo build -p baml-python-ffi --no-default-features --features vertex   # ~289

(vs ~460+ for the published wheel; ring is the only native-code crate on Linux.)

Feature gates (all default-on)

Feature Gates Drops
lsp baml lsp lsp-server/types, tonic+prost, 2nd axum, tokio-tungstenite, schemars, tar
bedrock AWS Bedrock provider the aws-*/smithy tree
serve / dev baml serve / dev axum, notify
repl baml repl dirs, supports-color
tui / optimize interactive UIs, GEPA ratatui, crossterm, cassowary, vte, …
cloud baml auth/login/deploy dialoguer, indicatif, open, jsonwebtoken-adjacent
studio Boundary Studio tracing publisher baml-rpc (ts-rs, serde_with), blake3, flate2
vertex Vertex AI provider auth gcp_auth

A provider compiled out (e.g. vertex, bedrock) still exists in the language; it fails at request time with a clear "built without the X feature" message rather than disappearing.

baml-http (new crate)

Shipping HTTP now goes through baml-http, which exposes the reqwest API subset BAML uses:

  • native: implemented directly on hyper + hyper-util + hyper-rustls (rustls + ring, bundled roots). reqwest is no longer in the native dependency tree.
  • wasm32: re-exports reqwest (browser fetch backend).

The LLM send path was already funneled through one client factory + two send sites, and the SSE decoder was already reqwest-agnostic, so the swap is contained. reqwest remains for non-shipping code (LSP CORS proxy, playground, sandbox, tests).

Known deltas vs reqwest: no env proxies (HTTP_PROXY), read_timeout is an idle-between-chunks timeout. See baml-http/src/lib.rs.

Dependency hygiene (affects all builds)

  • Replace async-std (a second async runtime, 3 trivial uses) with tokio/wasmtimer + tokio::fs.
  • Drop dead deps: sha2 (baml-rpc, baml-runtime), whoami (baml-core).
  • Move test-log / pretty_assertions to dev-deps where they belong.
  • Gate jsonish's 304KB test corpus behind #[cfg(test)] (it was pub mod tests;, shipping in every build). 483 tests still run.

CI

New vendor-profile job in primary.yml: compiles both --no-default-features builds (with and without vertex) and fails if reqwest re-enters the native tree.

Verification

  • Full compile matrix (default + minimal) across baml-python-ffi, baml-cli, TS/Ruby/CFFI clients, language-server; wasm32; runtime tests — all green.
  • clippy (CI flags) + rustfmt (repo config) clean.
  • Live end-to-end: vendor-built CLI ran initgeneratebaml test hitting OpenAI over hyper (401), and with --features vertex, resolved ADC credentials via gcp_auth and reached Vertex through hyper (real 403 for a fake project).

Docs: engine/VENDORING.md, engine/scripts/vendor-audit-coverage.py.

🤖 Generated with Claude Code

Add a feature-gated "vendor profile" so `baml_py` and the `baml` CLI can be
built with a much smaller dependency tree for environments that vendor all
third-party crates. Building either with --no-default-features drops the
LSP, Boundary cloud (auth/login/deploy), serve/dev servers, REPL,
optimize/TUI, the Studio tracing publisher, and AWS Bedrock — each behind an
on-by-default feature, so published artifacts (PyPI/NPM/gems/release CLI)
are unchanged.

Highlights:
- New feature gates: lsp, serve, dev, repl, tui, optimize, studio, vertex,
  cloud (plus existing bedrock). Providers compiled out fail at request time
  with a clear message rather than vanishing from the language.
- New `baml-http` crate: the reqwest API subset BAML uses, implemented on
  hyper + hyper-util + hyper-rustls (native) and re-exporting reqwest on
  wasm32. Shipping code no longer pulls reqwest into the native tree.
- Dependency hygiene: replace async-std usage with tokio/wasmtimer, drop
  dead deps (sha2, whoami, test-log/pretty_assertions moved to dev-deps),
  gate jsonish's test corpus behind cfg(test).
- Vendor profile is ~283 crates (~289 with vertex) vs ~460+ for the wheel;
  ring is the only native-code crate on Linux.
- CI: new `vendor-profile` job compiles the --no-default-features builds
  (with and without vertex) and asserts reqwest stays out of the native tree.
- Docs: engine/VENDORING.md + scripts/vendor-audit-coverage.py.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@vercel

vercel Bot commented Jul 7, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
beps Ready Ready Preview, Comment Jul 8, 2026 9:23pm
promptfiddle Ready Ready Preview, Comment Jul 8, 2026 9:23pm
promptfiddle2 Ready Ready Preview, Comment Jul 8, 2026 9:23pm

Request Review

@coderabbitai

coderabbitai Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: ee0afe5e-a9fd-4439-8aa2-a0510b919e9b

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch better-for-vendoring

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

github-actions Bot commented Jul 7, 2026

Copy link
Copy Markdown

⏭️ Performance benchmarks were skipped

Perf benchmarks (CodSpeed) are opt-in on pull requests — they no longer run on every push. They always run automatically after merge to canary/main.

To run them on this PR, do any of the following, then push a commit (or re-run CI):

  • Add RUN_CODSPEED=1 to the PR description, or
  • Include run-perf or /perf in the PR title or any commit message.

Bumps pyo3, pyo3-async-runtimes, and pyo3-build-config to 0.28 so the Python
binding layer matches newer pyo3 versions (smaller import/review surface for
vendoring consumers that already carry a recent pyo3).

Mechanical migration, no behavior change intended:
- Python::with_gil -> Python::attach, allow_threads -> detach (0.26 rename).
- pyo3::PyObject was removed from the crate root; re-provided as a crate-local
  `type PyObject = Py<PyAny>` alias so signatures are unchanged.
- downcast/downcast_bound -> cast/cast_bound.
- #[pyclass] Clone types opt into the FromPyObject derive explicitly via
  #[pyclass(from_py_object)] to preserve current behavior.
- abi3-py38 retained (package already requires Python >=3.10).

Compiles clean (0 warnings) on both default and --no-default-features. To
retarget 0.26 instead, only the three version strings change.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ixes

Addresses vendoring friction for monorepos that ban `ring` and mandate the
system/BoringSSL crypto stack.

- baml-http: TLS backend is now a cargo feature. `native-tls` (default) uses
  the platform TLS stack (SChannel / Security.framework / system OpenSSL) via
  hyper-tls, reads the OS trust store, and keeps `ring` out of the tree.
  `rustls-tls` (opt-in) restores the static rustls + ring + webpki stack with
  HTTP/2. `native-tls-vendored` statically vendors OpenSSL for portable Linux
  wheels. native-tls connections use HTTP/1.1 (hyper-tls does not forward the
  ALPN h2 hint; SSE and LLM calls are unaffected).
- baml-runtime: remove a dead direct `ring` dependency (only referenced in a
  comment). With native-tls default, the base vendor profile is now ring-free.
  Note: the `vertex` feature still pulls ring via gcp_auth 0.12.
- cli: clap-cargo 0.14 -> 0.12, which 6 -> 7 (align with commonly-hosted
  monorepo versions).

Verified: full compile matrix (both TLS backends, default + vendor profile +
vertex), wasm, clippy (-D warnings) on both backends, and a live HTTPS call to
OpenAI over native-tls returning a clean 401.

⚠ native-tls as the default means prebuilt Linux wheels now need a system
libssl (or the native-tls-vendored feature). Revisit before release if that
regresses manylinux packaging.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The `vertex` feature pulled `ring` via `gcp_auth 0.12`, which used it for both
TLS (hyper-rustls) and JWT RS256 signing — the last ring source in the vendor
profile. gcp_auth is a community crate (github.com/djc/gcp_auth), not a Google
project, so its crypto choices are not load-bearing.

Vendor it into engine/vendored/gcp-auth (MIT, like minijinja) with two
localized changes in src/types.rs:
- HTTPS client: hyper-rustls -> hyper-tls (platform/native TLS, OS trust
  store, HTTP/1.1).
- JWT signer: ring::signature RSA_PKCS1_SHA256 -> the pure-Rust `rsa` + `sha2`
  crates. A unit test asserts the signature is byte-identical to
  `openssl dgst -sha256 -sign`, so Google's token endpoint accepts it exactly
  as before.

Result: the vendor profile is now ring-free WITH `--features vertex` (0 ring
crates in the tree). To use a different signing backend (e.g. BoringSSL),
replace the ~15-line Signer impl.

Verified: full compile matrix + wasm, the byte-equality signer test, and a
live Vertex call — ADC token obtained over native-tls, request reached
aiplatform.googleapis.com (403 CONSUMER_INVALID for a fake project = auth
succeeded).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
hellovai and others added 2 commits July 7, 2026 22:18
1.0 requires each derive's feature to be listed explicitly (0.99 enabled all by
default). The workspace uses only the `From` and `Constructor` derives, so the
dep becomes `features = ["from", "constructor"]`. No source changes needed;
0.99 is fully gone from the tree.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The vendored gcp-auth fork is a crate we modified (ring-free TLS + RSA signer),
so it should get the same CI coverage as first-party code rather than sitting
outside the workspace as an excluded path dep. Promote it to a workspace member:

- fmt to the workspace style (imports_granularity=Crate, StdExternalCrate).
- clippy is clean under -D warnings.
- its one network test (`gcloud`) is already #[ignore]d, so
  `cargo test --workspace` stays hermetic; the signer byte-equality test now
  runs as part of the workspace suite.
- no separate Cargo.lock (uses the workspace lockfile).

minijinja stays excluded: it is a large fork tracked close to upstream, and
its test suite / dev-deps should not enter our build graph.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Adds a manually-dispatchable workflow that builds the minimal vendor profile
(--no-default-features --features vertex,native-tls-vendored) as an x86_64
extension that runs in ultra-hermetic sandboxes (old GLIBC, no libgcc_s.so.1):

- built on manylinux2014 (GLIBC 2.17),
- statically linked libgcc/libstdc++ via RUSTFLAGS,
- vendored OpenSSL (no system libssl).

The build fails unless the artifact has no libgcc_s dependency and requires
GLIBC <= 2.17, then uploads baml_py.so as an artifact to drop into third_party.

Also adds the `native-tls-vendored` feature forwarding
(baml-http -> baml-runtime -> baml-cli -> baml-python-ffi) that the vendored
OpenSSL build needs.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…TFLAGS)

Adds -static-libgcc/-static-libstdc++ to the x86_64-unknown-linux-gnu target in
engine/.cargo/config.toml so the built extension has no libgcc_s.so.1 dynamic
dependency. Also makes normal x86_64 release wheels hermetic for the same
sandboxes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Target-specific rustflags are ignored for host builds (cargo#8607), which is how
maturin builds in the manylinux container. Use CARGO_BUILD_RUSTFLAGS (applies to
host builds) and forward it into the container via maturin-action docker-options.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… only that)

maturin re-encodes .cargo/config.toml [build].rustflags into
CARGO_ENCODED_RUSTFLAGS, overriding env-based RUSTFLAGS. Patch the runner-local
config before the build; the committed config is untouched so other platforms'
release wheels are unaffected.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant