Skip to content

feat: add stdlib building blocks for custom LLM providers - #4164

Merged
aaronvg merged 8 commits into
canaryfrom
codex/upstream-custom-llm-builtins
Jul 24, 2026
Merged

aaronvg merged 8 commits into
canaryfrom
codex/upstream-custom-llm-builtins

Conversation

@aaronvg

@aaronvg aaronvg commented Jul 23, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • add provider-facing capability error interfaces, public schema-aligned parsing, JSON Schema lowering, typed JSON paths, prompt flattening, and a text-oriented WebSocket surface
  • implement native schema/prompt/WebSocket operations and resource lifecycle support
  • allow the magic direct cleanup method to satisfy an in-body interface implementation while preserving finalizer registration
  • add focused BAML regressions for provider helpers and interface cleanup dispatch

Why

BAML-authored LLM providers need reusable stdlib primitives for parsing, request/schema adaptation, realtime transport, and provider-neutral failure handling. These APIs are the minimal built-in and runtime support needed for the custom-provider fixtures to type-check with a freshly built CLI.

Validation

  • cargo build -p baml_cli
  • baml check against an isolated baml_src_temp2 project: 109 files, no diagnostics
  • focused BAML runtime tests: 4 passed (provider stdlib plus direct cleanup interface dispatch)
  • cargo nextest run -p baml_tests --test cleanup: 5 passed
  • cargo nextest run -p sys_ops: 5 passed
  • focused baml_compiler2_tir nextest interface tests: 3 passed
  • cargo clippy for all changed Rust packages with all targets/features and warnings denied

Note

Medium Risk
New public I/O surfaces (WebSockets, schema lowering) and dependency growth affect shipped CLI/pack sizes and runtime networking; changes are covered by tests and size gates but expand the attack/ops surface for providers.

Overview
Introduces stdlib building blocks for BAML-authored LLM providers: jq-style baml.json.path / path_or with JsonPathError, public baml.sap.parse / parse_type (via new baml.llm.__new_stream_cache), baml.schema.json_schema (Rust I/O), and a text baml.ws client (connect, send, next, close). Also adds baml.errors.UnknownError for errors without a known capability channel.

Registers the new builtin modules in baml_builtins2, wires WebSocket support through tokio-tungstenite / rustls in Cargo.lock, and adds provider stdlib BAML tests plus compiler/CLI snapshot updates.

Size-gate baselines and per-platform ceilings in .cargo/size-gate.toml and .ci/size-gate/* are bumped (notably packed-program grows on all native targets).

Reviewed by Cursor Bugbot for commit 147a55f. Bugbot is set up for automated code reviews on this repo. Configure here.

Summary by CodeRabbit

  • New Features
    • Added jq-style JSON-path navigation with typed extraction and fallback values.
    • Added JSON Schema generation from BAML types, including recursive types, unions, maps, and optional fields.
    • Added schema-aligned parsing helpers for converting text into typed results.
    • Added WebSocket connectivity with text messaging, receiving, timeouts, headers, and graceful closing.
    • Added prompt-to-text conversion for rendered prompts.
    • Added clearer handling for unknown errors and JSON-path failures.

@vercel

vercel Bot commented Jul 23, 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 24, 2026 10:42pm
promptfiddle Ready Ready Preview, Comment Jul 24, 2026 10:42pm
promptfiddle2 Ready Ready Preview, Comment Jul 24, 2026 10:42pm

Request Review

@github-actions

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.

@vercel
vercel Bot temporarily deployed to Preview – beps July 23, 2026 21:19 Inactive
@coderabbitai

coderabbitai Bot commented Jul 23, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

The PR adds JSON-path navigation, JSON Schema lowering, SAP parsing, prompt-to-text conversion, and unknown-error builtins. It also adds feature-gated native WebSocket support with registry-backed streams, plus updated native artifact size baselines.

Changes

Provider-oriented standard library surfaces

Layer / File(s) Summary
Provider builtin contracts
baml_language/crates/baml_builtins2/baml_std/baml/ns_{json,llm,sap,schema,ws}/*, baml_language/crates/baml_builtins2/src/lib.rs
BAML declarations add JSON paths, stream-cache creation, prompt flattening, SAP parsing, JSON Schema lowering, UnknownError, and builtin registration.
Provider sysop implementations
baml_language/crates/sys_ops/Cargo.toml, baml_language/crates/sys_ops/src/lib.rs
Sysops lower runtime types to JSON Schema and render PromptAst values into text.
Provider behavior tests
baml_language/crates/baml_tests/baml_src/ns_provider_stdlib/provider_stdlib.baml
Tests cover reflected schema extraction, JSON-path bracket keys, SAP parsing, and prompt flattening.

Native WebSocket support

Layer / File(s) Summary
WebSocket resource and builtin contracts
baml_language/crates/baml_builtins2/baml_std/baml/ns_ws/ws.baml, baml_language/crates/bex_resource_types/src/lib.rs, baml_language/crates/sys_native/Cargo.toml
Defines timeout-aware WebSocket APIs, the WsStream resource type, and optional Tokio Tungstenite support.
WebSocket registry storage
baml_language/crates/sys_native/src/registry.rs
Adds registry entries, resource storage, registration, and sink/source lookup for WebSocket streams.
WebSocket native operations
baml_language/crates/sys_native/src/io_impls.rs, baml_language/crates/sys_ops/src/lib.rs
Implements native connection, text sending, frame reading, ping handling, closing, cleanup, and unsupported default operations.

Artifact size gate updates

Layer / File(s) Summary
Artifact size baselines
baml_language/.cargo/size-gate.toml, baml_language/.ci/size-gate/*
Updates platform-specific ceilings, build metadata, and recorded sizes for baml-cli and packed-program.

Estimated code review effort: 5 (Critical) | ~90 minutes

Sequence Diagram(s)

sequenceDiagram
  participant ProviderTest
  participant baml_schema
  participant IoNamespaceSchema
  participant schema_json_schema
  ProviderTest->>baml_schema: provider_schema()
  baml_schema->>IoNamespaceSchema: json_schema(ProviderSchemaExample)
  IoNamespaceSchema->>schema_json_schema: lower RuntimeTy
  schema_json_schema-->>IoNamespaceSchema: JSON Schema value
  IoNamespaceSchema-->>ProviderTest: reflected JSON schema
  ProviderTest->>baml_schema: baml.json.path(schema, selector)
  baml_schema-->>ProviderTest: typed schema value
Loading
sequenceDiagram
  participant ProviderTest
  participant baml_sap
  participant baml_llm
  participant prompt_to_text
  ProviderTest->>baml_sap: parse<int>("42")
  baml_sap->>baml_llm: __sap_parse_final(text, stream cache)
  baml_llm-->>ProviderTest: parsed integer
  ProviderTest->>prompt_to_text: prompt_to_text(PromptAst)
  prompt_to_text-->>ProviderTest: flattened text
Loading

Possibly related PRs

Poem

A rabbit hops through paths of JSON bright,
Finds schema carrots tucked just right.
WebSockets ping and streams reply,
Prompts turn to text and errors fly.
Size gates stretch beneath the moon—
New builtins burrow in soon!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed Docstring coverage is 81.36% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: new stdlib building blocks for custom LLM providers.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/upstream-custom-llm-builtins

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.

Comment thread baml_language/crates/sys_native/src/io_impls.rs Outdated
Comment thread baml_language/crates/sys_ops/src/lib.rs Outdated
@vercel
vercel Bot temporarily deployed to Preview – beps July 23, 2026 21:23 Inactive
@vercel
vercel Bot temporarily deployed to Preview – beps July 23, 2026 21:27 Inactive
@vercel
vercel Bot temporarily deployed to Preview – beps July 23, 2026 21:29 Inactive
Comment thread baml_language/crates/baml_builtins2/baml_std/baml/ns_json/json.baml
@vercel
vercel Bot temporarily deployed to Preview – promptfiddle2 July 23, 2026 21:36 Inactive

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (3)
baml_language/crates/sys_native/src/io_impls.rs (2)

2486-2488: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

Binary frames are silently lossy-decoded to string, despite the "text-oriented" contract.

ws.baml documents this surface as "text-oriented" and next() as returning "the next text frame", but Message::Binary payloads are silently converted via from_utf8_lossy, which replaces invalid UTF-8 with replacement characters — corrupting data instead of surfacing an error for callers that don't expect binary frames.

♻️ Proposed fix: treat unexpected binary frames as an error
-                    Some(Ok(Message::Binary(bytes))) => {
-                        return Ok(Some(String::from_utf8_lossy(&bytes).into_owned()));
-                    }
+                    Some(Ok(Message::Binary(bytes))) => {
+                        return Err(VmBamlError::Io {
+                            message: format!(
+                                "received unexpected binary WebSocket frame ({} bytes) on a text-oriented stream",
+                                bytes.len()
+                            ),
+                        }
+                        .into());
+                    }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@baml_language/crates/sys_native/src/io_impls.rs` around lines 2486 - 2488,
Update the Message::Binary branch in the websocket next() handling to return an
error for unexpected binary frames instead of converting bytes with
String::from_utf8_lossy. Preserve the existing successful text-frame behavior
and use the module’s established error type or constructor to report the
binary-frame condition.

2525-2542: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

close() never sends a WebSocket Close frame.

It only removes the registry entry and lets the sink/source drop; the underlying TCP connection is torn down without a WS-level close handshake, so the remote peer sees an abnormal closure instead of a clean one. Consider sending a Message::Close(None) (and flushing) on the sink before/while removing from the registry.

Separately, note that close() only unregisters the resource — any send()/next() call that already resolved its own Arc clone via get_ws_stream() before this runs will keep operating on the still-alive connection until it naturally completes, so close() doesn't forcibly interrupt in-flight operations.

♻️ Proposed fix to send a Close frame
     fn close(
         &self,
         _heap: &Arc<BexHeap>,
         _call_id: CallId,
         stream: owned::ws::WsStream,
         _ctx: &SysOpContext,
     ) -> SysOpOutput<()> {
         use bex_resource_types::ResourceRegistryRef;
 
-        if let Ok(handle) = stream
-            ._handle
-            .downcast::<bex_resource_types::ResourceHandle>()
-        {
-            crate::registry::REGISTRY.remove(handle.key());
-        }
-        SysOpOutput::ok(())
+        if let Ok(handle) = stream
+            ._handle
+            .downcast::<bex_resource_types::ResourceHandle>()
+        {
+            if let Some((sink, _)) = crate::registry::REGISTRY.get_ws_stream(handle.key()) {
+                use futures::SinkExt;
+                let _ = sink.lock().await.send(tokio_tungstenite::tungstenite::Message::Close(None)).await;
+            }
+            crate::registry::REGISTRY.remove(handle.key());
+        }
+        SysOpOutput::ok(())
     }

(sketch only — close would need to become async / return SysOpOutput::async_op for the .await, mirroring send/next.)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@baml_language/crates/sys_native/src/io_impls.rs` around lines 2525 - 2542,
Update the WebSocket close flow in close to become asynchronous as needed, send
Message::Close(None) through the stream’s sink, and flush it before removing the
resource from REGISTRY. Preserve the existing successful SysOpOutput result and
resource unregistration behavior; do not attempt to forcibly interrupt in-flight
operations using previously resolved Arc clones.
baml_language/crates/sys_ops/src/lib.rs (1)

683-960: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add in-crate Rust unit tests for the schema lowering.

The module carries non-trivial logic (cycle handling, union/null widening, $ref/$defs, JSON-pointer escaping) but is only exercised via the .baml integration test. Direct unit tests would pin edge cases (self-referential/mutually-recursive classes, nullable unions, enum aliases).

As per coding guidelines: "Prefer writing Rust unit tests over integration tests where possible".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@baml_language/crates/sys_ops/src/lib.rs` around lines 683 - 960, Add in-crate
Rust unit tests within the schema module covering json_schema lowering for
self-referential and mutually recursive classes, nullable unions, JSON-pointer
escaping in class references, and enum aliases. Build the required SysOpContext
definitions using existing test helpers or fixtures, and assert the generated
$ref/$defs structure and representative schema values directly.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@baml_language/crates/sys_native/src/io_impls.rs`:
- Around line 2581-2586: Wrap the tokio_tungstenite::connect_async future in the
existing timeout_from_nanos helper within SysOpOutput::async_op, passing the
request timeout and preserving the established timeout error mapping. Keep the
existing VmBamlError::Io handling for handshake failures, while ensuring an
incomplete TCP/TLS/WebSocket handshake cannot exceed the configured request
timeout.

In `@baml_language/crates/sys_ops/src/lib.rs`:
- Around line 786-793: The TypeAlias branch in ty_schema recursively resolves
aliases without detecting cycles, causing the builtin json alias to overflow the
stack. Add visited alias-key tracking across recursive ty_schema resolution, and
on re-entry emit the existing $ref/Unsupported schema outcome; add a Rust unit
test covering the builtin recursive json alias.

---

Nitpick comments:
In `@baml_language/crates/sys_native/src/io_impls.rs`:
- Around line 2486-2488: Update the Message::Binary branch in the websocket
next() handling to return an error for unexpected binary frames instead of
converting bytes with String::from_utf8_lossy. Preserve the existing successful
text-frame behavior and use the module’s established error type or constructor
to report the binary-frame condition.
- Around line 2525-2542: Update the WebSocket close flow in close to become
asynchronous as needed, send Message::Close(None) through the stream’s sink, and
flush it before removing the resource from REGISTRY. Preserve the existing
successful SysOpOutput result and resource unregistration behavior; do not
attempt to forcibly interrupt in-flight operations using previously resolved Arc
clones.

In `@baml_language/crates/sys_ops/src/lib.rs`:
- Around line 683-960: Add in-crate Rust unit tests within the schema module
covering json_schema lowering for self-referential and mutually recursive
classes, nullable unions, JSON-pointer escaping in class references, and enum
aliases. Build the required SysOpContext definitions using existing test helpers
or fixtures, and assert the generated $ref/$defs structure and representative
schema values directly.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 7a3c0456-138a-4baa-a3fe-a52f3aab224f

📥 Commits

Reviewing files that changed from the base of the PR and between 6713441 and 726094b.

⛔ Files ignored due to path filters (1)
  • baml_language/Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (18)
  • baml_language/crates/baml_builtins2/baml_std/baml/ns_errors/capability.baml
  • baml_language/crates/baml_builtins2/baml_std/baml/ns_json/json.baml
  • baml_language/crates/baml_builtins2/baml_std/baml/ns_llm/llm.baml
  • baml_language/crates/baml_builtins2/baml_std/baml/ns_llm/llm_types.baml
  • baml_language/crates/baml_builtins2/baml_std/baml/ns_sap/sap.baml
  • baml_language/crates/baml_builtins2/baml_std/baml/ns_schema/schema.baml
  • baml_language/crates/baml_builtins2/baml_std/baml/ns_ws/ws.baml
  • baml_language/crates/baml_builtins2/src/lib.rs
  • baml_language/crates/baml_compiler2_emit/src/lib.rs
  • baml_language/crates/baml_compiler2_tir/src/interfaces/impl_rules.rs
  • baml_language/crates/baml_tests/baml_src/ns_interface_direct_methods/interface_direct_methods.baml
  • baml_language/crates/baml_tests/baml_src/ns_provider_stdlib/provider_stdlib.baml
  • baml_language/crates/bex_resource_types/src/lib.rs
  • baml_language/crates/sys_native/Cargo.toml
  • baml_language/crates/sys_native/src/io_impls.rs
  • baml_language/crates/sys_native/src/registry.rs
  • baml_language/crates/sys_ops/Cargo.toml
  • baml_language/crates/sys_ops/src/lib.rs

Comment thread baml_language/crates/sys_native/src/io_impls.rs Outdated
Comment thread baml_language/crates/sys_ops/src/lib.rs Outdated
@vercel
vercel Bot temporarily deployed to Preview – promptfiddle July 23, 2026 21:42 Inactive
@vercel
vercel Bot temporarily deployed to Preview – beps July 23, 2026 21:43 Inactive
Comment thread baml_language/crates/sys_native/src/io_impls.rs
@vercel
vercel Bot temporarily deployed to Preview – promptfiddle2 July 23, 2026 21:50 Inactive
@github-actions

github-actions Bot commented Jul 23, 2026 •

Copy link
Copy Markdown

Binary size checks passed

✅ 7 passed

Artifact Platform File Gzip Gated on Baseline Delta Status
✅ baml-cli Linux 🔒 27.2 MB 11.6 MB file 27.1 MB +154.9 KB (+0.6%) OK
✅ packed-program Linux 🔒 17.7 MB 7.3 MB file 17.6 MB +11.3 KB (+0.1%) OK
✅ baml-cli macOS 🔒 21.2 MB 10.1 MB file 21.0 MB +149.1 KB (+0.7%) OK
✅ packed-program macOS 🔒 13.8 MB 6.4 MB file 13.8 MB +33.1 KB (+0.2%) OK
✅ baml-cli Windows 🔒 22.8 MB 10.3 MB file 22.7 MB +128.0 KB (+0.6%) OK
✅ packed-program Windows 🔒 14.7 MB 6.5 MB file 14.7 MB +6.7 KB (+0.0%) OK
✅ bridge_wasm WASM 16.5 MB 🔒 4.5 MB gzip 4.5 MB +19.4 KB (+0.4%) OK

🔒 = the size this artifact is GATED on (ceiling + delta). Binaries gate on file size (installed binary); WASM gates on gzip (download size). The other size is shown for information only.


Generated by cargo size-gate · workflow run

@vercel
vercel Bot temporarily deployed to Preview – beps July 23, 2026 21:59 Inactive
@vercel
vercel Bot temporarily deployed to Preview – promptfiddle2 July 23, 2026 22:06 Inactive
@aaronvg
aaronvg force-pushed the codex/upstream-custom-llm-builtins branch from 4bd3ea0 to 236c051 Compare July 23, 2026 22:13
@vercel
vercel Bot temporarily deployed to Preview – beps July 23, 2026 22:14 Inactive
@aaronvg
aaronvg force-pushed the codex/upstream-custom-llm-builtins branch from 236c051 to 10f6e4b Compare July 23, 2026 22:18
@vercel
vercel Bot temporarily deployed to Preview – beps July 23, 2026 22:19 Inactive
@vercel
vercel Bot temporarily deployed to Preview – promptfiddle2 July 23, 2026 22:26 Inactive
@vercel
vercel Bot temporarily deployed to Preview – promptfiddle July 23, 2026 22:41 Inactive
@aaronvg
aaronvg force-pushed the codex/upstream-custom-llm-builtins branch from 10f6e4b to c1424c5 Compare July 23, 2026 22:49
@vercel
vercel Bot temporarily deployed to Preview – beps July 23, 2026 22:50 Inactive

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit c1424c5. Configure here.

Comment thread baml_language/crates/baml_builtins2/baml_std/baml/ns_llm/llm_types.baml Outdated
@vercel
vercel Bot temporarily deployed to Preview – promptfiddle2 July 23, 2026 22:58 Inactive
@vercel
vercel Bot temporarily deployed to Preview – beps July 23, 2026 23:00 Inactive
@aaronvg
aaronvg enabled auto-merge July 23, 2026 23:03
@vercel
vercel Bot temporarily deployed to Preview – promptfiddle2 July 23, 2026 23:08 Inactive
@vercel
vercel Bot temporarily deployed to Preview – promptfiddle July 23, 2026 23:20 Inactive
@aaronvg
aaronvg force-pushed the codex/upstream-custom-llm-builtins branch from 69c69a5 to 147a55f Compare July 24, 2026 22:21
@vercel
vercel Bot temporarily deployed to Preview – beps July 24, 2026 22:22 Inactive
@aaronvg
aaronvg disabled auto-merge July 24, 2026 22:27
@aaronvg
aaronvg enabled auto-merge July 24, 2026 22:27
@vercel
vercel Bot temporarily deployed to Preview – promptfiddle2 July 24, 2026 22:29 Inactive
@aaronvg
aaronvg added this pull request to the merge queue Jul 24, 2026
@vercel
vercel Bot temporarily deployed to Preview – promptfiddle July 24, 2026 22:42 Inactive
Merged via the queue into canary with commit a58c4b2 Jul 24, 2026
81 checks passed
@aaronvg
aaronvg deleted the codex/upstream-custom-llm-builtins branch July 24, 2026 22:45

This branch was previously deployed

3 inactive deployments
Preview – promptfiddle — 147a55f0 Deployed Jul 24, 2026 by vercel[bot]
Preview – promptfiddle2 — 147a55f0 Deployed Jul 24, 2026 by vercel[bot]
Preview – beps — 147a55f0 Deployed Jul 24, 2026 by vercel[bot]
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