From 471945c3483591a8c3868289de8e9b7510d26c37 Mon Sep 17 00:00:00 2001 From: Sam Lijin Date: Sat, 26 Sep 2026 13:39:53 -0700 Subject: [PATCH] Embed SDK bytecode as a single-line blob decoded natively by bridges MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Generated SDKs embed the compiled program as one string literal: base64 of an LZ4 frame (`baml_artifact::encode_embedded`). The host language never decodes it: generated code hands the literal (or its bytes) to the runtime entry point, renamed from `initialize_runtime_from_bytecode` to `initialize_runtime_from_blob` (`initializeRuntimeFromBlob` / `InitializeRuntimeFromBlob` in the bridges), and `bridge_cffi` decodes it natively (`baml_artifact::decode_embedded`). Raw artifacts, recognized by their `BAMLART` magic, still pass through, so the Rust SDK's `include_bytes!` `.bin` and the C#/C++ carriers are unchanged. - Python embeds `BYTECODE: bytes = b"…"`, Go a `const bytecode`, Swift a single-line `bytecodeBase64`, Java the same text in `inlinedbaml.b64`. - TypeScript embeds `export const BYTECODE = "…"`; the Node and web `initializeRuntimeFromBlob` also accept a string. Co-Authored-By: Claude Opus 5.5 --- .../workflows/verify-rust-sdk.reusable.yaml | 2 +- baml_language/Cargo.lock | 7 +- baml_language/Cargo.toml | 2 + baml_language/crates/baml_artifact/Cargo.toml | 2 + baml_language/crates/baml_artifact/src/lib.rs | 94 ++++++++++++++++++- .../crates/baml_sdkgen_types/Cargo.toml | 1 + .../src/embedded_bytecode.rs | 15 +++ .../crates/baml_sdkgen_types/src/lib.rs | 2 + baml_language/crates/bridge_cffi/Cargo.toml | 1 + .../crates/bridge_cffi/cbindgen.toml | 4 +- .../crates/bridge_cffi/include/baml_cffi.h | 19 ++-- baml_language/crates/bridge_cffi/src/api.rs | 28 +++--- .../crates/bridge_cffi/src/ffi/runtime.rs | 16 ++-- baml_language/crates/bridge_cffi/src/lib.rs | 26 +++-- .../crates/bridge_cffi/src/lib_native.rs | 4 +- .../crates/bridge_cffi/tests/abi_assertions.h | 12 +-- .../crates/bridge_cffi/tests/abi_layout.c | 4 +- .../crates/bridge_cffi/tests/abi_layout.rs | 4 +- .../crates/bridge_cffi/tests/abi_smoke.c | 2 +- .../crates/bridge_cffi/tests/bytecode_skew.rs | 41 +++++--- .../ruby_sorbet/test/native/bridge_fixture.c | 8 +- .../customizable/host_callables.test.ts | 6 +- .../customizable/bridge_surface.test.ts | 2 +- .../sdk_tests/harness_runner/src/lib.rs | 19 ++-- .../bridge-ref/ref-java-examples.md | 9 +- .../bridge-ref/ref-java-type-mappings.md | 4 +- .../bridge_cpp/include/baml/detail/loader.h | 4 +- .../cpp/bridge_cpp/include/baml/runtime.h | 11 +-- .../cpp/bridge_cpp/tests/runtime_smoke.cc | 2 +- baml_language/sdks/cpp/sdkgen_cpp/src/lib.rs | 8 +- .../bridge_csharp/src/Cffi/NativeApi.cs | 8 +- .../bridge_csharp/src/Cffi/NativeTypes.cs | 6 +- .../Baml.Bridge.AbiLifetimeProbe/Program.cs | 12 +-- .../tests/Baml.Bridge.AbiProbe/Program.cs | 4 +- .../NativeBytecodeInitializer.cs | 8 +- .../NativeBridge.cs | 8 +- .../tests/Baml.Bridge.Tests/Program.cs | 12 +-- .../tests/native_fixtures/table_diagnostics.c | 2 +- .../baml_go/internal/cffi/include/baml_cffi.h | 19 ++-- baml_language/sdks/go/baml_go/native_unix.go | 10 +- .../sdks/go/baml_go/native_windows.go | 10 +- baml_language/sdks/go/sdkgen_go/src/lib.rs | 29 +++--- .../sdks/java/bridge_java/src/lib.rs | 7 +- .../sdks/java/sdkgen_java/src/lib.rs | 58 +++--------- .../src/baml_core/baml_py/__init__.pyi | 2 +- .../python/rust/bridge_python/src/runtime.rs | 7 +- .../rust/sdkgen_python_pydantic2/src/lib.rs | 46 +++------ .../sdks/python/src/baml_bridge/baml_py.pyi | 2 +- .../sdks/python/tests/test_engine.py | 2 +- .../bridge_ruby/lib/baml/bridge/native.rb | 10 +- .../sdks/rust/bridge_rust/src/capi.rs | 24 ++--- .../sdks/rust/bridge_rust/src/runtime.rs | 4 +- .../sdks/swift/Sources/BamlBridge/Api.swift | 6 +- .../swift/Sources/BamlBridge/Runtime.swift | 4 +- .../Sources/CBamlBridge/include/baml_cffi.h | 19 ++-- .../sdks/swift/rust/sdkgen_swift/Cargo.toml | 2 +- .../sdks/swift/rust/sdkgen_swift/src/lib.rs | 59 ++++++------ .../bridge_typescript/dist/index.d.ts | 2 +- .../bridge_typescript/dist/index.d.ts.map | 2 +- .../bridge_typescript/dist/index.js | 6 +- .../bridge_typescript/dist/index.js.map | 2 +- .../bridge_typescript/dist/native.d.ts | 8 +- .../bridge_typescript/src/runtime.rs | 19 ++-- .../bridge_typescript/typescript_src/index.ts | 9 +- .../typescript_src/native.d.ts | 8 +- .../bridge_typescript_web/src/runtime.rs | 13 ++- .../tests/runtime_errors.test.ts | 2 +- .../typescript_src/index.ts | 4 +- .../typescript_src/native.ts | 2 +- .../typescript_src/wasm/bridge_web_core.d.ts | 9 +- .../sdkgen_typescript_shared/Cargo.toml | 4 +- .../sdkgen_typescript_shared/src/leaf.rs | 16 ++-- .../sdkgen_typescript_shared/src/lib.rs | 87 +++++++---------- release/bridge-cffi-public-exports.txt | 4 +- 74 files changed, 513 insertions(+), 423 deletions(-) create mode 100644 baml_language/crates/baml_sdkgen_types/src/embedded_bytecode.rs diff --git a/.github/workflows/verify-rust-sdk.reusable.yaml b/.github/workflows/verify-rust-sdk.reusable.yaml index b0371f91e77..4010d0f7b05 100644 --- a/.github/workflows/verify-rust-sdk.reusable.yaml +++ b/.github/workflows/verify-rust-sdk.reusable.yaml @@ -276,7 +276,7 @@ jobs: # GNU/Linux invocation; on macOS the functional smokes above already # prove the ABI (the consumer calls through every symbol). if [[ "$RUNNER_OS" == "Linux" ]]; then - for sym in version free_buffer create_baml_runtime initialize_runtime_from_bytecode \ + for sym in version free_buffer create_baml_runtime initialize_runtime_from_blob \ register_callback new_function_call call_function; do nm -D "${{ steps.art.outputs.dylib }}" | grep -qw "$sym" || { echo "::error::dylib missing exported symbol $sym"; exit 1; } diff --git a/baml_language/Cargo.lock b/baml_language/Cargo.lock index 17ccf4be18c..37e3e545877 100644 --- a/baml_language/Cargo.lock +++ b/baml_language/Cargo.lock @@ -597,8 +597,10 @@ name = "baml_artifact" version = "0.0.0-beta" dependencies = [ "baml_version", + "base64", "borsh", "log", + "lz4_flex", "sha2", "thiserror 2.0.18", ] @@ -1091,6 +1093,7 @@ dependencies = [ name = "baml_sdkgen_types" version = "0.0.0-beta" dependencies = [ + "baml_artifact", "baml_base", "baml_type", "fs2", @@ -6680,19 +6683,19 @@ dependencies = [ name = "sdkgen_swift" version = "0.0.0-beta" dependencies = [ + "baml_artifact", "baml_base", "baml_sdkgen_types", "baml_version", - "base64", ] [[package]] name = "sdkgen_typescript_shared" version = "0.0.0-beta" dependencies = [ + "baml_artifact", "baml_base", "baml_sdkgen_types", - "base64", ] [[package]] diff --git a/baml_language/Cargo.toml b/baml_language/Cargo.toml index 03a4ebc826e..8e247be9a0a 100644 --- a/baml_language/Cargo.toml +++ b/baml_language/Cargo.toml @@ -233,6 +233,8 @@ fancy-regex = { version = "0.19" } # gitignore-aware directory walking (workspace project discovery in the LSP) ignore = { version = "0.4" } base64 = "0.22" +# LZ4 frames compress the bytecode that generated SDKs embed. +lz4_flex = { version = "0.13" } mime_guess = "2" bytecount = "0.6" reqwest = { version = "0.13.1", default-features = false, features = [ diff --git a/baml_language/crates/baml_artifact/Cargo.toml b/baml_language/crates/baml_artifact/Cargo.toml index 7e5f4dc13f4..5b0faf8d35f 100644 --- a/baml_language/crates/baml_artifact/Cargo.toml +++ b/baml_language/crates/baml_artifact/Cargo.toml @@ -23,7 +23,9 @@ workspace = true [dependencies] baml_version = { workspace = true } +base64 = { workspace = true } borsh = { workspace = true } log = { workspace = true } +lz4_flex = { workspace = true } sha2 = { workspace = true } thiserror = { workspace = true } diff --git a/baml_language/crates/baml_artifact/src/lib.rs b/baml_language/crates/baml_artifact/src/lib.rs index 96608c859fa..26a425f6aa1 100644 --- a/baml_language/crates/baml_artifact/src/lib.rs +++ b/baml_language/crates/baml_artifact/src/lib.rs @@ -5,8 +5,12 @@ //! the release metadata check at bridge startup useful while adding an ABI //! check that runs even when generated metadata is unavailable. -use std::fmt; +use std::{ + fmt, + io::{Read as _, Write as _}, +}; +use base64::{Engine as _, engine::general_purpose::STANDARD as BASE64}; use borsh::{BorshDeserialize, BorshSerialize}; use sha2::{Digest, Sha256}; @@ -171,6 +175,54 @@ pub enum Error { Encode { kind: ArtifactKind, message: String }, #[error("failed to decode {kind}: {message}")] Decode { kind: ArtifactKind, message: String }, + #[error("failed to decode embedded {kind}: {message}")] + Embedded { kind: ArtifactKind, message: String }, +} + +/// Encode an artifact for embedding as a single-line string literal in +/// generated SDK source: an LZ4 frame, then standard padded base64. +/// [`decode_embedded`] reverses this; bridges call it so host languages never +/// decode the payload themselves. The encoding cannot begin with [`MAGIC`], so +/// bridges can accept either form. +pub fn encode_embedded(artifact: &[u8]) -> String { + let mut encoder = lz4_flex::frame::FrameEncoder::new(Vec::new()); + encoder + .write_all(artifact) + .expect("writing to a Vec cannot fail"); + BASE64.encode(encoder.finish().expect("writing to a Vec cannot fail")) +} + +/// Largest artifact [`decode_embedded`] will inflate. Generated programs are a +/// few MB; the cap keeps a malformed, highly compressible payload from +/// allocating unbounded memory before artifact validation can reject it. +pub const MAX_EMBEDDED_ARTIFACT_BYTES: u64 = 512 * 1024 * 1024; + +/// Decode an [`encode_embedded`] payload back into the artifact bytes. +pub fn decode_embedded(kind: ArtifactKind, encoded: &[u8]) -> Result, Error> { + decode_embedded_within(kind, encoded, MAX_EMBEDDED_ARTIFACT_BYTES) +} + +fn decode_embedded_within( + kind: ArtifactKind, + encoded: &[u8], + max_artifact_bytes: u64, +) -> Result, Error> { + let embedded_error = |message: String| Error::Embedded { kind, message }; + let compressed = BASE64 + .decode(encoded) + .map_err(|error| embedded_error(format!("invalid base64: {error}")))?; + let mut artifact = Vec::new(); + // Read one byte past the cap so an oversized artifact is detectable. + lz4_flex::frame::FrameDecoder::new(compressed.as_slice()) + .take(max_artifact_bytes.saturating_add(1)) + .read_to_end(&mut artifact) + .map_err(|error| embedded_error(format!("invalid LZ4 frame: {error}")))?; + if artifact.len() as u64 > max_artifact_bytes { + return Err(embedded_error(format!( + "decoded artifact exceeds {max_artifact_bytes} bytes" + ))); + } + Ok(artifact) } /// Serialize `value` and wrap it in a versioned artifact envelope. @@ -366,6 +418,46 @@ pub fn decode_payload(kind: ArtifactKind, bytes: &[u8]) -> Result<&[u8], Error> mod tests { use super::*; + #[test] + fn embedded_encoding_round_trips() { + let artifact = encode(ArtifactKind::Program, &vec![7_u32; 1024]).unwrap(); + let encoded = encode_embedded(&artifact); + assert!(encoded.len() < artifact.len()); + assert!(!encoded.contains('\n')); + assert!(!encoded.as_bytes().starts_with(MAGIC)); + let restored = decode_embedded(ArtifactKind::Program, encoded.as_bytes()).unwrap(); + assert_eq!(restored, artifact); + let decoded: Vec = decode(ArtifactKind::Program, &restored).unwrap(); + assert_eq!(decoded, vec![7_u32; 1024]); + } + + #[test] + fn oversized_embedded_artifacts_are_rejected() { + let artifact = encode(ArtifactKind::Program, &vec![0_u8; 4096]).unwrap(); + let encoded = encode_embedded(&artifact); + let limit = artifact.len() as u64; + assert_eq!( + decode_embedded_within(ArtifactKind::Program, encoded.as_bytes(), limit).unwrap(), + artifact + ); + let error = decode_embedded_within(ArtifactKind::Program, encoded.as_bytes(), limit - 1) + .unwrap_err(); + assert!( + matches!(&error, Error::Embedded { message, .. } if message.contains("exceeds")), + "{error}" + ); + } + + #[test] + fn corrupt_embedded_payloads_are_embedded_errors() { + let encoded = encode_embedded(&encode(ArtifactKind::Program, &vec![1_u32; 64]).unwrap()); + for corrupt in ["not base64!", &encoded[..(encoded.len() / 2) & !3]] { + let corrupt = corrupt.as_bytes(); + let error = decode_embedded(ArtifactKind::Program, corrupt).unwrap_err(); + assert!(matches!(error, Error::Embedded { .. }), "{error}"); + } + } + #[test] fn round_trips_payload() { let artifact = encode(ArtifactKind::Program, &vec![1_u32, 2, 3]).unwrap(); diff --git a/baml_language/crates/baml_sdkgen_types/Cargo.toml b/baml_language/crates/baml_sdkgen_types/Cargo.toml index cfe69b44f77..8ba16df70e7 100644 --- a/baml_language/crates/baml_sdkgen_types/Cargo.toml +++ b/baml_language/crates/baml_sdkgen_types/Cargo.toml @@ -17,6 +17,7 @@ doctest = false workspace = true [dependencies] +baml_artifact = { workspace = true } baml_base = { workspace = true } baml_type = { workspace = true } serde = { workspace = true } diff --git a/baml_language/crates/baml_sdkgen_types/src/embedded_bytecode.rs b/baml_language/crates/baml_sdkgen_types/src/embedded_bytecode.rs new file mode 100644 index 00000000000..96ac34776eb --- /dev/null +++ b/baml_language/crates/baml_sdkgen_types/src/embedded_bytecode.rs @@ -0,0 +1,15 @@ +//! The bytecode payload that generated SDKs embed in source. + +/// Encode compiled BAML bytecode for embedding as a single-line string +/// literal (see [`baml_artifact::encode_embedded`]). Generated SDKs hand the +/// string to their bridge's embedded-bytecode initializer unchanged; decoding +/// happens natively, never in the host language. +/// +/// Empty bytecode stays empty so the bridge still reports a missing payload +/// rather than a corrupt one. +pub fn embedded_bytecode_base64(bytecode: &[u8]) -> String { + if bytecode.is_empty() { + return String::new(); + } + baml_artifact::encode_embedded(bytecode) +} diff --git a/baml_language/crates/baml_sdkgen_types/src/lib.rs b/baml_language/crates/baml_sdkgen_types/src/lib.rs index 835d3928910..4397660b46b 100644 --- a/baml_language/crates/baml_sdkgen_types/src/lib.rs +++ b/baml_language/crates/baml_sdkgen_types/src/lib.rs @@ -2,6 +2,7 @@ //! Attributes "don't exist" //! Union / Optional / Null invariance guaranteed +mod embedded_bytecode; mod errors; mod generator_fields; mod output_writer; @@ -10,6 +11,7 @@ mod string_literal; mod symbols; mod ty; mod type_queries; +pub use embedded_bytecode::embedded_bytecode_base64; pub use errors::*; pub use generator_fields::*; pub use output_writer::*; diff --git a/baml_language/crates/bridge_cffi/Cargo.toml b/baml_language/crates/bridge_cffi/Cargo.toml index 5d68d1b16a4..2665191c856 100644 --- a/baml_language/crates/bridge_cffi/Cargo.toml +++ b/baml_language/crates/bridge_cffi/Cargo.toml @@ -21,6 +21,7 @@ rustls = [ "sys_native/rustls" ] bundle-http = [ "sys_native/bundle-http" ] [dependencies] +baml_artifact = { workspace = true } baml_version = { workspace = true } bex_project = { workspace = true } bridge_ctypes = { workspace = true } diff --git a/baml_language/crates/bridge_cffi/cbindgen.toml b/baml_language/crates/bridge_cffi/cbindgen.toml index 8f08a14de4a..e2b60870b7a 100644 --- a/baml_language/crates/bridge_cffi/cbindgen.toml +++ b/baml_language/crates/bridge_cffi/cbindgen.toml @@ -73,7 +73,7 @@ include = [ "BamlHostReleaseCallback", "BamlUnhandledSpawnErrorCallback", "BamlVersionFn", - "BamlInitializeRuntimeFromBytecodeFn", + "BamlInitializeRuntimeFromBlobFn", "BamlFreeBufferFn", "BamlRegisterCallbackFn", "BamlCallFunctionFn", @@ -118,7 +118,7 @@ exclude = [ "flush_events", "version", "create_baml_runtime", - "initialize_runtime_from_bytecode", + "initialize_runtime_from_blob", "destroy_baml_runtime", "invoke_runtime_cli", ] diff --git a/baml_language/crates/bridge_cffi/include/baml_cffi.h b/baml_language/crates/bridge_cffi/include/baml_cffi.h index 5a3209a3964..70785791c30 100644 --- a/baml_language/crates/bridge_cffi/include/baml_cffi.h +++ b/baml_language/crates/bridge_cffi/include/baml_cffi.h @@ -178,8 +178,7 @@ typedef struct BamlBuffer { typedef struct BamlBuffer (*BamlVersionFn)(void); -typedef struct BamlBuffer (*BamlInitializeRuntimeFromBytecodeFn)(const uint8_t *bytecode, - size_t length); +typedef struct BamlBuffer (*BamlInitializeRuntimeFromBlobFn)(const uint8_t *bytecode, size_t length); typedef void (*BamlFreeBufferFn)(struct BamlBuffer buffer); @@ -298,9 +297,9 @@ typedef void (*BamlRegisterUnhandledSpawnErrorCallbackFn)(BamlUnhandledSpawnErro typedef struct BamlBuffer (*BamlShutdownRuntimeFn)(void); -typedef struct BamlBuffer (*BamlInitializeRuntimeFromBytecodeWithMetadataFn)(const uint8_t *bytecode, - size_t length, - const char *baml_toml); +typedef struct BamlBuffer (*BamlInitializeRuntimeFromBlobWithMetadataFn)(const uint8_t *bytecode, + size_t length, + const char *baml_toml); /** * First version of the shared BAML C API. @@ -350,7 +349,7 @@ typedef struct BamlApiV1 { * calls are serialized only while replacing the global runtime; calls * already in progress retain their previous runtime instance. */ - BamlInitializeRuntimeFromBytecodeFn initialize_runtime_from_bytecode; + BamlInitializeRuntimeFromBlobFn initialize_runtime_from_blob; /** * Release exactly one runtime-owned buffer returned through this table. * @@ -483,7 +482,7 @@ typedef struct BamlApiV1 { /** * Replace the runtime from bytecode after validating embedded generation metadata. */ - BamlInitializeRuntimeFromBytecodeWithMetadataFn initialize_runtime_from_bytecode_with_metadata; + BamlInitializeRuntimeFromBlobWithMetadataFn initialize_runtime_from_blob_with_metadata; } BamlApiV1; typedef const struct BamlApiV1 *(*BamlGetApiV1Fn)(void); @@ -515,9 +514,9 @@ BamlCffiStatus __testonly_seed_heap_handle(uint64_t slab_key, * Initialize generated bytecode after validating its embedded `baml.toml`. */ BAML_CFFI_API -struct BamlBuffer initialize_runtime_from_bytecode_with_metadata(const uint8_t *bytecode, - size_t length, - const char *baml_toml); +struct BamlBuffer initialize_runtime_from_blob_with_metadata(const uint8_t *bytecode, + size_t length, + const char *baml_toml); #ifdef __cplusplus } // extern "C" diff --git a/baml_language/crates/bridge_cffi/src/api.rs b/baml_language/crates/bridge_cffi/src/api.rs index af5fff6067d..8485a57a024 100644 --- a/baml_language/crates/bridge_cffi/src/api.rs +++ b/baml_language/crates/bridge_cffi/src/api.rs @@ -95,9 +95,9 @@ pub type BamlUnhandledSpawnErrorCallback = extern "C" fn(content: *const i8, length: usize, cancelled: i32); pub type BamlVersionFn = extern "C" fn() -> Buffer; -pub type BamlInitializeRuntimeFromBytecodeFn = +pub type BamlInitializeRuntimeFromBlobFn = extern "C" fn(bytecode: *const u8, length: usize) -> Buffer; -pub type BamlInitializeRuntimeFromBytecodeWithMetadataFn = +pub type BamlInitializeRuntimeFromBlobWithMetadataFn = extern "C" fn(bytecode: *const u8, length: usize, baml_toml: *const libc::c_char) -> Buffer; pub type BamlFreeBufferFn = extern "C" fn(buffer: Buffer); pub type BamlRegisterCallbackFn = extern "C" fn(callback: BamlResultCallback); @@ -167,7 +167,7 @@ pub struct BamlApiV1 { /// failure and must always be passed once to `free_buffer`. Concurrent /// calls are serialized only while replacing the global runtime; calls /// already in progress retain their previous runtime instance. - pub initialize_runtime_from_bytecode: BamlInitializeRuntimeFromBytecodeFn, + pub initialize_runtime_from_blob: BamlInitializeRuntimeFromBlobFn, /// Release exactly one runtime-owned buffer returned through this table. /// /// Do not use a different library instance's function, release a copied @@ -258,15 +258,14 @@ pub struct BamlApiV1 { /// Wait for spawned work, report unreachable errors, and release the runtime. pub shutdown_runtime: BamlShutdownRuntimeFn, /// Replace the runtime from bytecode after validating embedded generation metadata. - pub initialize_runtime_from_bytecode_with_metadata: - BamlInitializeRuntimeFromBytecodeWithMetadataFn, + pub initialize_runtime_from_blob_with_metadata: BamlInitializeRuntimeFromBlobWithMetadataFn, } static BAML_API_V1: BamlApiV1 = BamlApiV1 { abi_version: BAML_API_V1_ABI_VERSION, struct_size: std::mem::size_of::(), version: crate::version, - initialize_runtime_from_bytecode: crate::initialize_runtime_from_bytecode_ffi, + initialize_runtime_from_blob: crate::initialize_runtime_from_blob_ffi, free_buffer: crate::free_buffer, register_callback: crate::register_callback, call_function: crate::call_function, @@ -287,8 +286,7 @@ static BAML_API_V1: BamlApiV1 = BamlApiV1 { register_bridge: crate::register_bridge_ffi, register_unhandled_spawn_error_callback: crate::register_unhandled_spawn_error_callback, shutdown_runtime: crate::shutdown_runtime_ffi, - initialize_runtime_from_bytecode_with_metadata: - crate::initialize_runtime_from_bytecode_with_metadata, + initialize_runtime_from_blob_with_metadata: crate::initialize_runtime_from_blob_with_metadata, }; /// Return the immutable version-1 BAML C API function table. @@ -328,8 +326,8 @@ mod tests { let api = unsafe { &*baml_get_api_v1() }; assert_same_function!(api.version, crate::version); assert_same_function!( - api.initialize_runtime_from_bytecode, - crate::initialize_runtime_from_bytecode_ffi + api.initialize_runtime_from_blob, + crate::initialize_runtime_from_blob_ffi ); assert_same_function!(api.free_buffer, crate::free_buffer); assert_same_function!(api.register_callback, crate::register_callback); @@ -361,8 +359,8 @@ mod tests { ); assert_same_function!(api.shutdown_runtime, crate::shutdown_runtime_ffi); assert_same_function!( - api.initialize_runtime_from_bytecode_with_metadata, - crate::initialize_runtime_from_bytecode_with_metadata + api.initialize_runtime_from_blob_with_metadata, + crate::initialize_runtime_from_blob_with_metadata ); } @@ -370,7 +368,7 @@ mod tests { fn every_v1_field_retains_its_declared_function_type() { let api = unsafe { &*baml_get_api_v1() }; let _: BamlVersionFn = api.version; - let _: BamlInitializeRuntimeFromBytecodeFn = api.initialize_runtime_from_bytecode; + let _: BamlInitializeRuntimeFromBlobFn = api.initialize_runtime_from_blob; let _: BamlFreeBufferFn = api.free_buffer; let _: BamlRegisterCallbackFn = api.register_callback; let _: BamlCallFunctionFn = api.call_function; @@ -392,8 +390,8 @@ mod tests { let _: BamlRegisterUnhandledSpawnErrorCallbackFn = api.register_unhandled_spawn_error_callback; let _: BamlShutdownRuntimeFn = api.shutdown_runtime; - let _: BamlInitializeRuntimeFromBytecodeWithMetadataFn = - api.initialize_runtime_from_bytecode_with_metadata; + let _: BamlInitializeRuntimeFromBlobWithMetadataFn = + api.initialize_runtime_from_blob_with_metadata; let _: BamlGetApiV1Fn = baml_get_api_v1; } diff --git a/baml_language/crates/bridge_cffi/src/ffi/runtime.rs b/baml_language/crates/bridge_cffi/src/ffi/runtime.rs index 4c5f6ca6530..b0b53bdd6e1 100644 --- a/baml_language/crates/bridge_cffi/src/ffi/runtime.rs +++ b/baml_language/crates/bridge_cffi/src/ffi/runtime.rs @@ -5,7 +5,7 @@ use std::{collections::HashMap, ffi::CStr, panic::AssertUnwindSafe}; use super::super::panic::ffi_safe_ptr; use crate::{ BridgeInfo, BridgeLanguage, Buffer, initialize_runtime, - initialize_runtime_from_bytecode as initialize_runtime_from_bytecode_impl, register_bridge, + initialize_runtime_from_blob as initialize_runtime_from_blob_impl, register_bridge, }; /// Returns the BAML version as a Buffer containing raw UTF-8 bytes. @@ -185,20 +185,20 @@ pub extern "C" fn create_baml_runtime( /// it with [`crate::free_buffer`]. #[allow(clippy::not_unsafe_ptr_arg_deref)] #[unsafe(no_mangle)] -pub extern "C" fn initialize_runtime_from_bytecode(bytecode: *const u8, length: usize) -> Buffer { +pub extern "C" fn initialize_runtime_from_blob(bytecode: *const u8, length: usize) -> Buffer { if bytecode.is_null() && length != 0 { return Buffer::from(b"bytecode pointer is null but length is nonzero".to_vec()); } if let Some(error) = bytecode_preflight_failure(length) { return error; } - initialize_runtime_from_bytecode_inner(bytecode, length, None) + initialize_runtime_from_blob_inner(bytecode, length, None) } /// Initialize generated bytecode after validating its embedded `baml.toml`. #[allow(clippy::not_unsafe_ptr_arg_deref)] #[unsafe(no_mangle)] -pub extern "C" fn initialize_runtime_from_bytecode_with_metadata( +pub extern "C" fn initialize_runtime_from_blob_with_metadata( bytecode: *const u8, length: usize, baml_toml: *const libc::c_char, @@ -224,7 +224,7 @@ pub extern "C" fn initialize_runtime_from_bytecode_with_metadata( } } }; - initialize_runtime_from_bytecode_inner(bytecode, length, manifest) + initialize_runtime_from_blob_inner(bytecode, length, manifest) } fn bytecode_preflight_failure(length: usize) -> Option { @@ -233,7 +233,7 @@ fn bytecode_preflight_failure(length: usize) -> Option { .map(|error| Buffer::from(error.to_string().into_bytes())) } -fn initialize_runtime_from_bytecode_inner( +fn initialize_runtime_from_blob_inner( bytecode: *const u8, length: usize, embedded_baml_toml: Option<&str>, @@ -248,7 +248,7 @@ fn initialize_runtime_from_bytecode_inner( } else { unsafe { std::slice::from_raw_parts(bytecode, length) } }; - initialize_runtime_from_bytecode_impl(bytecode, embedded_baml_toml) + initialize_runtime_from_blob_impl(bytecode, embedded_baml_toml) .map(|_| ()) .map_err(|error| error.to_string()) })); @@ -409,7 +409,7 @@ mod tests { #[test] fn bytecode_initializer_rejects_null_pointer_with_nonzero_length() { - let buffer = initialize_runtime_from_bytecode(std::ptr::null(), 1); + let buffer = initialize_runtime_from_blob(std::ptr::null(), 1); let message = unsafe { std::slice::from_raw_parts(buffer.ptr.cast::(), buffer.len) }; assert_eq!( std::str::from_utf8(message).unwrap(), diff --git a/baml_language/crates/bridge_cffi/src/lib.rs b/baml_language/crates/bridge_cffi/src/lib.rs index e57ac7c1481..8bfc3472f8e 100644 --- a/baml_language/crates/bridge_cffi/src/lib.rs +++ b/baml_language/crates/bridge_cffi/src/lib.rs @@ -156,12 +156,15 @@ pub fn get_runtime() -> Result, BridgeError> { /// Initialize the global runtime from serialized BAML bytecode. /// -/// The payload is a versioned artifact containing `bex_vm_types::Program`. -/// Validation, decoding, and engine construction live behind +/// The payload is a versioned artifact containing `bex_vm_types::Program`, +/// either raw or in the embedded encoding generated SDKs carry +/// (`baml_artifact::encode_embedded`), which is decoded here so host languages +/// pass it through untouched. Validation, decoding, and engine construction +/// live behind /// `bex_project::new_from_bytecode` so the bridge stays on the `bex_project` /// surface rather than reaching into bex internals. Artifact validation runs /// even when the optional generated `baml.toml` metadata is absent. -pub fn initialize_runtime_from_bytecode_with_sys_ops( +pub fn initialize_runtime_from_blob_with_sys_ops( bytecode: &[u8], embedded_baml_toml: Option<&str>, sys_ops: sys_ops::SysOps, @@ -171,7 +174,7 @@ pub fn initialize_runtime_from_bytecode_with_sys_ops( let generated_toolchain_version = embedded_baml_toml .map(|manifest| validate_generated_metadata(manifest, bridge)) .transpose()?; - let runtime: Arc = bex_project::new_from_bytecode(bytecode, sys_ops).map_err(|error| { + let load_error = |error: &dyn std::fmt::Display| { let generated = generated_toolchain_version .as_deref() .map(|version| format!(" generated using BAML toolchain {version},")) @@ -180,7 +183,16 @@ pub fn initialize_runtime_from_bytecode_with_sys_ops( "BAML startup failed: generated SDK bytecode could not be loaded.\n\n`baml_sdk`{generated} could not be loaded by {} {}: {error}", bridge.bridge_runtime_name, bridge.bridge_runtime_version, )) - })?; + }; + let bytecode: std::borrow::Cow<'_, [u8]> = if bytecode.starts_with(baml_artifact::MAGIC) { + bytecode.into() + } else { + baml_artifact::decode_embedded(baml_artifact::ArtifactKind::Program, bytecode) + .map_err(|error| load_error(&error))? + .into() + }; + let runtime: Arc = + bex_project::new_from_bytecode(&bytecode, sys_ops).map_err(|error| load_error(&error))?; install_unhandled_spawn_error_handler(&runtime); platform::replace_runtime(runtime.clone())?; Ok(runtime) @@ -374,13 +386,13 @@ pub async fn shutdown_runtime() -> Result<(), BridgeError> { /// Initialize the native process-global runtime from serialized BAML bytecode. #[cfg(not(target_arch = "wasm32"))] -pub fn initialize_runtime_from_bytecode( +pub fn initialize_runtime_from_blob( bytecode: &[u8], embedded_baml_toml: Option<&str>, ) -> Result, BridgeError> { use sys_native::SysOpsExt as _; - initialize_runtime_from_bytecode_with_sys_ops( + initialize_runtime_from_blob_with_sys_ops( bytecode, embedded_baml_toml, sys_ops::SysOps::native(), diff --git a/baml_language/crates/bridge_cffi/src/lib_native.rs b/baml_language/crates/bridge_cffi/src/lib_native.rs index 3e893f542d2..92ae4137654 100644 --- a/baml_language/crates/bridge_cffi/src/lib_native.rs +++ b/baml_language/crates/bridge_cffi/src/lib_native.rs @@ -43,8 +43,8 @@ pub use ffi::{ objects::flush_events, runtime::{ BamlBridgeInfoV1, create_baml_runtime, destroy_baml_runtime, - initialize_runtime_from_bytecode as initialize_runtime_from_bytecode_ffi, - initialize_runtime_from_bytecode_with_metadata, invoke_runtime_cli, register_bridge_ffi, + initialize_runtime_from_blob as initialize_runtime_from_blob_ffi, + initialize_runtime_from_blob_with_metadata, invoke_runtime_cli, register_bridge_ffi, shutdown_runtime as shutdown_runtime_ffi, version, }, unhandled_spawn::register_unhandled_spawn_error_callback, diff --git a/baml_language/crates/bridge_cffi/tests/abi_assertions.h b/baml_language/crates/bridge_cffi/tests/abi_assertions.h index d3d55235f0a..b121802a5b0 100644 --- a/baml_language/crates/bridge_cffi/tests/abi_assertions.h +++ b/baml_language/crates/bridge_cffi/tests/abi_assertions.h @@ -112,8 +112,8 @@ BAML_STATIC_ASSERT( BAML_STATIC_ASSERT(offsetof(BamlApiV1, abi_version) == 0, "ABI version must be first"); BAML_ASSERT_AFTER(abi_version, struct_size); BAML_ASSERT_AFTER(struct_size, version); -BAML_ASSERT_AFTER(version, initialize_runtime_from_bytecode); -BAML_ASSERT_AFTER(initialize_runtime_from_bytecode, free_buffer); +BAML_ASSERT_AFTER(version, initialize_runtime_from_blob); +BAML_ASSERT_AFTER(initialize_runtime_from_blob, free_buffer); BAML_ASSERT_AFTER(free_buffer, register_callback); BAML_ASSERT_AFTER(register_callback, call_function); BAML_ASSERT_AFTER(call_function, new_function_call); @@ -133,13 +133,13 @@ BAML_ASSERT_AFTER(media_base64, media_mime_type); BAML_ASSERT_AFTER(media_mime_type, register_bridge); BAML_ASSERT_AFTER(register_bridge, register_unhandled_spawn_error_callback); BAML_ASSERT_AFTER(register_unhandled_spawn_error_callback, shutdown_runtime); -BAML_ASSERT_AFTER(shutdown_runtime, initialize_runtime_from_bytecode_with_metadata); +BAML_ASSERT_AFTER(shutdown_runtime, initialize_runtime_from_blob_with_metadata); BAML_STATIC_ASSERT( BAML_API_V1_MIN_SIZE == offsetof(BamlApiV1, register_unhandled_spawn_error_callback), "the appended lifecycle fields must follow the original V1 prefix"); BAML_ASSERT_FIELD_TYPE(version, BamlVersionFn) -BAML_ASSERT_FIELD_TYPE(initialize_runtime_from_bytecode, BamlInitializeRuntimeFromBytecodeFn) +BAML_ASSERT_FIELD_TYPE(initialize_runtime_from_blob, BamlInitializeRuntimeFromBlobFn) BAML_ASSERT_FIELD_TYPE(free_buffer, BamlFreeBufferFn) BAML_ASSERT_FIELD_TYPE(register_callback, BamlRegisterCallbackFn) BAML_ASSERT_FIELD_TYPE(call_function, BamlCallFunctionFn) @@ -161,7 +161,7 @@ BAML_ASSERT_FIELD_TYPE(register_bridge, BamlRegisterBridgeFn) BAML_ASSERT_FIELD_TYPE(register_unhandled_spawn_error_callback, BamlRegisterUnhandledSpawnErrorCallbackFn) BAML_ASSERT_FIELD_TYPE(shutdown_runtime, BamlShutdownRuntimeFn) -BAML_ASSERT_FIELD_TYPE(initialize_runtime_from_bytecode_with_metadata, - BamlInitializeRuntimeFromBytecodeWithMetadataFn) +BAML_ASSERT_FIELD_TYPE(initialize_runtime_from_blob_with_metadata, + BamlInitializeRuntimeFromBlobWithMetadataFn) #endif /* BAML_CFFI_TEST_ABI_ASSERTIONS_H */ diff --git a/baml_language/crates/bridge_cffi/tests/abi_layout.c b/baml_language/crates/bridge_cffi/tests/abi_layout.c index 6491138da91..950fda1ebb7 100644 --- a/baml_language/crates/bridge_cffi/tests/abi_layout.c +++ b/baml_language/crates/bridge_cffi/tests/abi_layout.c @@ -69,7 +69,7 @@ int main(void) { PRINT_OFFSET(BamlApiV1, abi_version); PRINT_OFFSET(BamlApiV1, struct_size); PRINT_OFFSET(BamlApiV1, version); - PRINT_OFFSET(BamlApiV1, initialize_runtime_from_bytecode); + PRINT_OFFSET(BamlApiV1, initialize_runtime_from_blob); PRINT_OFFSET(BamlApiV1, free_buffer); PRINT_OFFSET(BamlApiV1, register_callback); PRINT_OFFSET(BamlApiV1, call_function); @@ -90,6 +90,6 @@ int main(void) { PRINT_OFFSET(BamlApiV1, register_bridge); PRINT_OFFSET(BamlApiV1, register_unhandled_spawn_error_callback); PRINT_OFFSET(BamlApiV1, shutdown_runtime); - PRINT_OFFSET(BamlApiV1, initialize_runtime_from_bytecode_with_metadata); + PRINT_OFFSET(BamlApiV1, initialize_runtime_from_blob_with_metadata); return 0; } diff --git a/baml_language/crates/bridge_cffi/tests/abi_layout.rs b/baml_language/crates/bridge_cffi/tests/abi_layout.rs index 40417819a4b..50dbe2f0e12 100644 --- a/baml_language/crates/bridge_cffi/tests/abi_layout.rs +++ b/baml_language/crates/bridge_cffi/tests/abi_layout.rs @@ -250,7 +250,7 @@ fn rust_c_and_cpp_agree_on_the_complete_v1_abi() { field!("BamlApiV1", BamlApiV1, abi_version); field!("BamlApiV1", BamlApiV1, struct_size); field!("BamlApiV1", BamlApiV1, version); - field!("BamlApiV1", BamlApiV1, initialize_runtime_from_bytecode); + field!("BamlApiV1", BamlApiV1, initialize_runtime_from_blob); field!("BamlApiV1", BamlApiV1, free_buffer); field!("BamlApiV1", BamlApiV1, register_callback); field!("BamlApiV1", BamlApiV1, call_function); @@ -278,7 +278,7 @@ fn rust_c_and_cpp_agree_on_the_complete_v1_abi() { field!( "BamlApiV1", BamlApiV1, - initialize_runtime_from_bytecode_with_metadata + initialize_runtime_from_blob_with_metadata ); assert_eq!(actual, expected, "C and Rust ABI layouts differ"); diff --git a/baml_language/crates/bridge_cffi/tests/abi_smoke.c b/baml_language/crates/bridge_cffi/tests/abi_smoke.c index 649c7066842..a4431a55745 100644 --- a/baml_language/crates/bridge_cffi/tests/abi_smoke.c +++ b/baml_language/crates/bridge_cffi/tests/abi_smoke.c @@ -53,7 +53,7 @@ static void baml_close_library(BamlLibrary library) { #endif static int baml_required_functions_exist(const BamlApiV1 *api) { - return api->version != NULL && api->initialize_runtime_from_bytecode != NULL && + return api->version != NULL && api->initialize_runtime_from_blob != NULL && api->free_buffer != NULL && api->register_callback != NULL && api->call_function != NULL && api->new_function_call != NULL && api->cancel_function_call != NULL && api->register_host_dispatch_callback != NULL && diff --git a/baml_language/crates/bridge_cffi/tests/bytecode_skew.rs b/baml_language/crates/bridge_cffi/tests/bytecode_skew.rs index 9c073a6a153..fe317a0ee91 100644 --- a/baml_language/crates/bridge_cffi/tests/bytecode_skew.rs +++ b/baml_language/crates/bridge_cffi/tests/bytecode_skew.rs @@ -15,18 +15,35 @@ fn bridge_surfaces_bytecode_skew_without_generated_metadata() { ) .unwrap(); - let Err(error) = bridge_cffi::initialize_runtime_from_bytecode(&bytecode, None) else { - panic!("format-skewed bytecode must fail"); + // Raw artifacts and the embedded encoding generated SDKs carry decode to + // the same program, so both surface the same skew. + let embedded = baml_artifact::encode_embedded(&bytecode); + for payload in [bytecode.as_slice(), embedded.as_bytes()] { + let Err(error) = bridge_cffi::initialize_runtime_from_blob(payload, None) else { + panic!("format-skewed bytecode must fail"); + }; + assert_eq!(error.to_string(), expected_skew_error()); + } + + let Err(error) = bridge_cffi::initialize_runtime_from_blob(b"not an embedded program", None) + else { + panic!("corrupt embedded bytecode must fail"); }; - assert_eq!( - error.to_string(), - format!( - "BAML startup failed: generated SDK bytecode could not be loaded.\n\n`baml_sdk` could not be loaded by baml-bridge-test {}: Failed to deserialize BAML bytecode: generated bytecode: toolchain {} / format {}; this runtime: {} / format {} — regenerate baml_sdk and rebuild the bridge from the same commit", - baml_version::CANONICAL_VERSION, - baml_artifact::BUILD_FINGERPRINT, - baml_artifact::FORMAT_VERSION + 1, - baml_artifact::BUILD_FINGERPRINT, - baml_artifact::FORMAT_VERSION, - ) + assert!( + error + .to_string() + .contains("failed to decode embedded generated bytecode: invalid base64"), + "{error}" ); } + +fn expected_skew_error() -> String { + format!( + "BAML startup failed: generated SDK bytecode could not be loaded.\n\n`baml_sdk` could not be loaded by baml-bridge-test {}: Failed to deserialize BAML bytecode: generated bytecode: toolchain {} / format {}; this runtime: {} / format {} — regenerate baml_sdk and rebuild the bridge from the same commit", + baml_version::CANONICAL_VERSION, + baml_artifact::BUILD_FINGERPRINT, + baml_artifact::FORMAT_VERSION + 1, + baml_artifact::BUILD_FINGERPRINT, + baml_artifact::FORMAT_VERSION, + ) +} diff --git a/baml_language/sdk_tests/crates/ruby_sorbet/test/native/bridge_fixture.c b/baml_language/sdk_tests/crates/ruby_sorbet/test/native/bridge_fixture.c index 48c812f2875..f1c91f0edd1 100644 --- a/baml_language/sdk_tests/crates/ruby_sorbet/test/native/bridge_fixture.c +++ b/baml_language/sdk_tests/crates/ruby_sorbet/test/native/bridge_fixture.c @@ -215,7 +215,7 @@ static const BamlApiV1 default_api = { .abi_version = BAML_API_V1_ABI_VERSION, .struct_size = sizeof(BamlApiV1), .version = probe_version, - .initialize_runtime_from_bytecode = probe_initialize, + .initialize_runtime_from_blob = probe_initialize, .free_buffer = probe_free, .register_callback = probe_register_result, .call_function = probe_call, @@ -236,7 +236,7 @@ static const BamlApiV1 default_api = { .register_bridge = probe_register_bridge, .register_unhandled_spawn_error_callback = probe_register_unhandled_spawn_error, .shutdown_runtime = probe_shutdown_runtime, - .initialize_runtime_from_bytecode_with_metadata = probe_initialize_with_metadata, + .initialize_runtime_from_blob_with_metadata = probe_initialize_with_metadata, }; static BamlApiV1 probe_api; @@ -252,7 +252,7 @@ static void null_requested_field(const char *field) { return; } NULL_FIELD(version) - NULL_FIELD(initialize_runtime_from_bytecode) + NULL_FIELD(initialize_runtime_from_blob) NULL_FIELD(free_buffer) NULL_FIELD(register_callback) NULL_FIELD(call_function) @@ -273,7 +273,7 @@ static void null_requested_field(const char *field) { NULL_FIELD(register_bridge) NULL_FIELD(register_unhandled_spawn_error_callback) NULL_FIELD(shutdown_runtime) - NULL_FIELD(initialize_runtime_from_bytecode_with_metadata) + NULL_FIELD(initialize_runtime_from_blob_with_metadata) } BAML_CFFI_API const BamlApiV1 *baml_get_api_v1(void) { diff --git a/baml_language/sdk_tests/crates/typescript/function_calls/customizable/host_callables.test.ts b/baml_language/sdk_tests/crates/typescript/function_calls/customizable/host_callables.test.ts index 19f70f4a48c..060ec8c578f 100644 --- a/baml_language/sdk_tests/crates/typescript/function_calls/customizable/host_callables.test.ts +++ b/baml_language/sdk_tests/crates/typescript/function_calls/customizable/host_callables.test.ts @@ -1,5 +1,5 @@ import "./baml_sdk/index.js"; -import { BamlAbortError, BamlCallContext, BamlError, BamlPanic, initializeRuntimeFromBytecode } from "@boundaryml/baml-bridge"; +import { BamlAbortError, BamlCallContext, BamlError, BamlPanic, initializeRuntimeFromBlob } from "@boundaryml/baml-bridge"; import { describe, expect, it } from "vitest"; import { BYTECODE } from "./baml_sdk/_inlinedbaml.js"; import { @@ -227,7 +227,7 @@ describe("function_calls — generated SDK host callables", () => { const pending = call_with_callback_async(callback, 9); await wasDispatched; - initializeRuntimeFromBytecode(BYTECODE); + initializeRuntimeFromBlob(BYTECODE); resolveResult("after-replacement"); await expect(pending).resolves.toBe("after-replacement"); }); @@ -263,7 +263,7 @@ describe("function_calls — generated SDK host callables", () => { const pending = call_with_callback_async(callback, 12, { $ctx: ctx }); await wasDispatched; - initializeRuntimeFromBytecode(BYTECODE); + initializeRuntimeFromBlob(BYTECODE); ctx.abort(); await expect(pending).rejects.toBeInstanceOf(BamlAbortError); settle("late after replacement"); diff --git a/baml_language/sdk_tests/crates/typescript/type_shapes/customizable/bridge_surface.test.ts b/baml_language/sdk_tests/crates/typescript/type_shapes/customizable/bridge_surface.test.ts index 9321c59407a..817a97882cc 100644 --- a/baml_language/sdk_tests/crates/typescript/type_shapes/customizable/bridge_surface.test.ts +++ b/baml_language/sdk_tests/crates/typescript/type_shapes/customizable/bridge_surface.test.ts @@ -41,7 +41,7 @@ const packageRootExports = [ "getTypeMap", "getVersion", "initializeRuntime", - "initializeRuntimeFromBytecode", + "initializeRuntimeFromBlob", "lowerTypeToWireTy", "newFunctionCall", "reflectType", diff --git a/baml_language/sdk_tests/harness_runner/src/lib.rs b/baml_language/sdk_tests/harness_runner/src/lib.rs index 7d1eded7c44..188ef4b7730 100644 --- a/baml_language/sdk_tests/harness_runner/src/lib.rs +++ b/baml_language/sdk_tests/harness_runner/src/lib.rs @@ -437,23 +437,20 @@ pub fn assert_typescript_web_generated_esm(fixture: &str, runtime_dir: &str) { ); let inlined_bytecode = fs::read_to_string(generated.join("baml_sdk/_inlinedbaml.ts")) .unwrap_or_else(|e| panic!("{fixture}: read generated bytecode module: {e}")); - // The bytecode is emitted as base64 lines joined into one string; an empty - // program would still emit the export, so non-emptiness means at least one - // populated payload line. - let has_bytecode_payload = inlined_bytecode - .lines() - .any(|line| line.starts_with(" \"") && line.ends_with("\",") && line.len() > 5); + // The bytecode is emitted as a single string literal; an empty program + // would still emit the export, so non-emptiness means a populated literal. + let has_bytecode_payload = inlined_bytecode.lines().any(|line| { + line.starts_with("export const BYTECODE = \"") + && line.len() > "export const BYTECODE = \"\";".len() + }); assert!( - inlined_bytecode.contains("export const BYTECODE = decodeBytecode(BYTECODE_BASE64);") - && has_bytecode_payload, + has_bytecode_payload, "{fixture}: generated SDK must contain non-empty BAML bytecode" ); let root = fs::read_to_string(generated.join("baml_sdk/index.ts")) .unwrap_or_else(|e| panic!("{fixture}: read generated SDK root: {e}")); assert!( - root.contains( - "initializeRuntimeFromBytecode(_inlinedbaml.BYTECODE, _inlinedbaml.BAML_TOML)" - ), + root.contains("initializeRuntimeFromBlob(_inlinedbaml.BYTECODE, _inlinedbaml.BAML_TOML)"), "{fixture}: generated SDK root must initialize the web runtime from emitted bytecode and metadata" ); } diff --git a/baml_language/sdks/agent-docs/bridge-ref/ref-java-examples.md b/baml_language/sdks/agent-docs/bridge-ref/ref-java-examples.md index 87639fa5e3a..1a731197a48 100644 --- a/baml_language/sdks/agent-docs/bridge-ref/ref-java-examples.md +++ b/baml_language/sdks/agent-docs/bridge-ref/ref-java-examples.md @@ -111,8 +111,7 @@ public final class Baml { if (in == null) { throw new IllegalStateException("baml_sdk/inlinedbaml.b64 not found on the classpath — …"); } - byte[] b64 = in.readAllBytes(); - byte[] bytecode = java.util.Base64.getMimeDecoder().decode(b64); + byte[] bytecode = in.readAllBytes(); baml_bridge.BamlFfi.initFromBytecode(bytecode); } catch (java.io.IOException e) { throw new java.io.UncheckedIOException("failed to read embedded BAML bytecode", e); @@ -900,9 +899,9 @@ conditionally-always via `__traceback__`). The generated `Person` / The generated Java SDK root includes: -- **`inlinedbaml.b64`** — the compiler-produced bytecode as a base64 classpath - resource, decoded in `Baml.java`'s static initializer via - `Base64.getMimeDecoder()` (`lib.rs:11-13,320-326`; `Baml.java:450-460`); +- **`inlinedbaml.b64`** — the compiler-produced bytecode as a base64-encoded LZ4 + classpath resource, which `Baml.java`'s static initializer passes unchanged to + `BamlFfi.initFromBytecode` for native decoding; - **`Baml.java`** — the runtime anchor whose `static {}` block registers the type map (`registerClass` / `registerEnum` / `registerUnion`, carrying field declaration order + per-field decode descriptors) and calls diff --git a/baml_language/sdks/agent-docs/bridge-ref/ref-java-type-mappings.md b/baml_language/sdks/agent-docs/bridge-ref/ref-java-type-mappings.md index 02c6c52ddb7..ceb2c7b7c1f 100644 --- a/baml_language/sdks/agent-docs/bridge-ref/ref-java-type-mappings.md +++ b/baml_language/sdks/agent-docs/bridge-ref/ref-java-type-mappings.md @@ -329,8 +329,8 @@ Notes: expression is a fully-qualified name, the import-collection machinery the TS emitter needs (`TranslatedType { expr, imports }`) collapses to a plain `String` (translate_ty.rs:1–7). - **Runtime init via a root-holder static initializer.** Loading any generated class triggers - idempotent runtime initialization from **embedded bytecode** (base64/MIME-embedded, decoded with - `java.util.Base64.getMimeDecoder()`), the Java analog of Python's root-package import side effect + idempotent runtime initialization from **embedded bytecode** (a base64-encoded LZ4 resource whose + raw bytes go to `BamlFfi.initFromBytecode`, which decodes them natively), the Java analog of Python's root-package import side effect (conventions doc:84–87). There is no `_inlinedbaml.py` / `_typemap.py` / `py.typed` triplet; the BAML source and the FQN⇄class map live in `TypeRegistry` registrations run at init. - **Free functions → static methods on a per-namespace `Fns` holder** (`baml_sdk..Fns.()`; diff --git a/baml_language/sdks/cpp/bridge_cpp/include/baml/detail/loader.h b/baml_language/sdks/cpp/bridge_cpp/include/baml/detail/loader.h index 8e88899eea7..36879972f35 100644 --- a/baml_language/sdks/cpp/bridge_cpp/include/baml/detail/loader.h +++ b/baml_language/sdks/cpp/bridge_cpp/include/baml/detail/loader.h @@ -307,7 +307,7 @@ inline const loaded_api& load_api() { " bytes from " + chosen); } if (table->version == nullptr || - table->initialize_runtime_from_bytecode == nullptr || + table->initialize_runtime_from_blob == nullptr || table->free_buffer == nullptr || table->register_callback == nullptr || table->call_function == nullptr || table->new_function_call == nullptr || @@ -323,7 +323,7 @@ inline const loaded_api& load_api() { table->register_bridge == nullptr || table->register_unhandled_spawn_error_callback == nullptr || table->shutdown_runtime == nullptr || - table->initialize_runtime_from_bytecode_with_metadata == nullptr) { + table->initialize_runtime_from_blob_with_metadata == nullptr) { throw runtime_error( "BAML_RUNTIME_ABI_MISMATCH", "runtime ABI table contains a null required operation"); diff --git a/baml_language/sdks/cpp/bridge_cpp/include/baml/runtime.h b/baml_language/sdks/cpp/bridge_cpp/include/baml/runtime.h index 34216feceb9..33908951627 100644 --- a/baml_language/sdks/cpp/bridge_cpp/include/baml/runtime.h +++ b/baml_language/sdks/cpp/bridge_cpp/include/baml/runtime.h @@ -88,9 +88,8 @@ inline std::string version() { // payload generated SDKs embed). Registers this bridge (language + SDK // canonical version) first -- the contract-required ordering -- then boots. // Replaces any previously initialized runtime. -inline void initialize_runtime_from_bytecode(const uint8_t* bytecode, - size_t length, - const char* sdk_version) { +inline void initialize_runtime_from_blob(const uint8_t* bytecode, size_t length, + const char* sdk_version) { static_cast(sdk_version); detail::ensure_registered(toolchain_version(), kBridgeRuntimeName, bridge_runtime_version()); @@ -98,13 +97,13 @@ inline void initialize_runtime_from_bytecode(const uint8_t* bytecode, baml_cpp_unhandled_spawn_error_trampoline); install_shutdown_hook(); detail::owned_buffer failure{ - detail::api().initialize_runtime_from_bytecode(bytecode, length)}; + detail::api().initialize_runtime_from_blob(bytecode, length)}; if (!failure.empty()) { throw error(failure.to_string()); } } -inline void initialize_runtime_from_bytecode_with_metadata( +inline void initialize_runtime_from_blob_with_metadata( const uint8_t* bytecode, size_t length, const char* embedded_baml_toml) { detail::ensure_registered(toolchain_version(), kBridgeRuntimeName, bridge_runtime_version()); @@ -112,7 +111,7 @@ inline void initialize_runtime_from_bytecode_with_metadata( baml_cpp_unhandled_spawn_error_trampoline); install_shutdown_hook(); detail::owned_buffer failure{ - detail::api().initialize_runtime_from_bytecode_with_metadata( + detail::api().initialize_runtime_from_blob_with_metadata( bytecode, length, embedded_baml_toml)}; if (!failure.empty()) { throw error(failure.to_string()); diff --git a/baml_language/sdks/cpp/bridge_cpp/tests/runtime_smoke.cc b/baml_language/sdks/cpp/bridge_cpp/tests/runtime_smoke.cc index df8084f86e9..402aaca10ab 100644 --- a/baml_language/sdks/cpp/bridge_cpp/tests/runtime_smoke.cc +++ b/baml_language/sdks/cpp/bridge_cpp/tests/runtime_smoke.cc @@ -31,7 +31,7 @@ static void TestBytecodeInitRejectsGarbage() { bool threw = false; try { const std::string v = baml::version(); - baml::initialize_runtime_from_bytecode(garbage, sizeof(garbage), v.c_str()); + baml::initialize_runtime_from_blob(garbage, sizeof(garbage), v.c_str()); } catch (const baml::error&) { threw = true; } diff --git a/baml_language/sdks/cpp/sdkgen_cpp/src/lib.rs b/baml_language/sdks/cpp/sdkgen_cpp/src/lib.rs index 402b5454203..5fd958171ea 100644 --- a/baml_language/sdks/cpp/sdkgen_cpp/src/lib.rs +++ b/baml_language/sdks/cpp/sdkgen_cpp/src/lib.rs @@ -2444,7 +2444,7 @@ fn render_inlinedbaml( for (const BamlBytecodeChunk& chunk : kBamlBytecodeChunks) {{\n \ bytecode.append(chunk.data, chunk.len);\n \ }}\n \ - ::baml::initialize_runtime_from_bytecode(\n \ + ::baml::initialize_runtime_from_blob(\n \ reinterpret_cast(bytecode.data()), bytecode.size(),\n \ \"{version}\");\n \ }});\n\ @@ -2453,12 +2453,12 @@ fn render_inlinedbaml( ); if embedded_baml_toml.is_some() { let legacy_call = format!( - "::baml::initialize_runtime_from_bytecode(\n reinterpret_cast(bytecode.data()), bytecode.size(),\n \"{}\");", + "::baml::initialize_runtime_from_blob(\n reinterpret_cast(bytecode.data()), bytecode.size(),\n \"{}\");", baml_version::CANONICAL_VERSION ); buf = buf.replace( &legacy_call, - "::baml::initialize_runtime_from_bytecode_with_metadata(\n reinterpret_cast(bytecode.data()), bytecode.size(),\n kEmbeddedBamlToml);", + "::baml::initialize_runtime_from_blob_with_metadata(\n reinterpret_cast(bytecode.data()), bytecode.size(),\n kEmbeddedBamlToml);", ); } let _ = writeln!(buf, "}} // namespace {detail}"); @@ -2485,7 +2485,7 @@ mod bytecode_escape_tests { ); assert!(output.contains("const char kEmbeddedBamlToml[]")); - assert!(output.contains("initialize_runtime_from_bytecode_with_metadata(")); + assert!(output.contains("initialize_runtime_from_blob_with_metadata(")); assert!(output.contains("kEmbeddedBamlToml);")); } diff --git a/baml_language/sdks/csharp/bridge_csharp/src/Cffi/NativeApi.cs b/baml_language/sdks/csharp/bridge_csharp/src/Cffi/NativeApi.cs index 3f58cacf87a..7fe0cfba5db 100644 --- a/baml_language/sdks/csharp/bridge_csharp/src/Cffi/NativeApi.cs +++ b/baml_language/sdks/csharp/bridge_csharp/src/Cffi/NativeApi.cs @@ -65,8 +65,8 @@ internal void InitializeRuntime(ReadOnlySpan bytecode, string? embeddedBam fixed (byte* manifestPointer = manifest) { status = embeddedBamlToml is null - ? table->InitializeRuntimeFromBytecode(pointer, (nuint)bytecode.Length) - : table->InitializeRuntimeFromBytecodeWithMetadata( + ? table->InitializeRuntimeFromBlob(pointer, (nuint)bytecode.Length) + : table->InitializeRuntimeFromBlobWithMetadata( pointer, (nuint)bytecode.Length, manifestPointer); @@ -263,8 +263,8 @@ internal static void ValidateTable(BamlApiV1* api) } Require(api->Version is not null, "version"); - Require(api->InitializeRuntimeFromBytecode is not null, "initialize_runtime_from_bytecode"); - Require(api->InitializeRuntimeFromBytecodeWithMetadata is not null, "initialize_runtime_from_bytecode_with_metadata"); + Require(api->InitializeRuntimeFromBlob is not null, "initialize_runtime_from_blob"); + Require(api->InitializeRuntimeFromBlobWithMetadata is not null, "initialize_runtime_from_blob_with_metadata"); Require(api->FreeBuffer is not null, "free_buffer"); Require(api->RegisterCallback is not null, "register_callback"); Require(api->CallFunction is not null, "call_function"); diff --git a/baml_language/sdks/csharp/bridge_csharp/src/Cffi/NativeTypes.cs b/baml_language/sdks/csharp/bridge_csharp/src/Cffi/NativeTypes.cs index 36583d95e99..af402f31bd6 100644 --- a/baml_language/sdks/csharp/bridge_csharp/src/Cffi/NativeTypes.cs +++ b/baml_language/sdks/csharp/bridge_csharp/src/Cffi/NativeTypes.cs @@ -39,7 +39,7 @@ internal unsafe struct BamlApiV1 internal uint AbiVersion; internal nuint StructSize; internal delegate* unmanaged[Cdecl] Version; - internal delegate* unmanaged[Cdecl] InitializeRuntimeFromBytecode; + internal delegate* unmanaged[Cdecl] InitializeRuntimeFromBlob; internal delegate* unmanaged[Cdecl] FreeBuffer; internal delegate* unmanaged[Cdecl], void> RegisterCallback; internal delegate* unmanaged[Cdecl] CallFunction; @@ -60,12 +60,12 @@ internal unsafe struct BamlApiV1 internal delegate* unmanaged[Cdecl] RegisterBridge; internal delegate* unmanaged[Cdecl], void> RegisterUnhandledSpawnErrorCallback; internal delegate* unmanaged[Cdecl] ShutdownRuntime; - internal delegate* unmanaged[Cdecl] InitializeRuntimeFromBytecodeWithMetadata; + internal delegate* unmanaged[Cdecl] InitializeRuntimeFromBlobWithMetadata; } internal static unsafe class BamlApiV1Layout { - internal static readonly nuint RequiredPrefixSize = EndOf(nameof(BamlApiV1.InitializeRuntimeFromBytecodeWithMetadata)); + internal static readonly nuint RequiredPrefixSize = EndOf(nameof(BamlApiV1.InitializeRuntimeFromBlobWithMetadata)); private static nuint EndOf(string field) => checked((nuint)Marshal.OffsetOf(field) + (nuint)IntPtr.Size); diff --git a/baml_language/sdks/csharp/bridge_csharp/tests/Baml.Bridge.AbiLifetimeProbe/Program.cs b/baml_language/sdks/csharp/bridge_csharp/tests/Baml.Bridge.AbiLifetimeProbe/Program.cs index e4b75269500..c52efb3db5e 100644 --- a/baml_language/sdks/csharp/bridge_csharp/tests/Baml.Bridge.AbiLifetimeProbe/Program.cs +++ b/baml_language/sdks/csharp/bridge_csharp/tests/Baml.Bridge.AbiLifetimeProbe/Program.cs @@ -103,7 +103,7 @@ public static int Main(string[] args) string emptyBytecodeDiagnostic = ConsumeUtf8Buffer( api, - api->InitializeRuntimeFromBytecode(null, 0)); + api->InitializeRuntimeFromBlob(null, 0)); Require( emptyBytecodeDiagnostic.Length > 0, "empty bytecode unexpectedly initialized the runtime"); @@ -113,7 +113,7 @@ public static int Main(string[] args) { string diagnostic = ConsumeUtf8Buffer( api, - api->InitializeRuntimeFromBytecode( + api->InitializeRuntimeFromBlob( invalid, (nuint)invalidBytecode.Length)); Require( @@ -126,7 +126,7 @@ public static int Main(string[] args) { string diagnostic = ConsumeUtf8Buffer( api, - api->InitializeRuntimeFromBytecode( + api->InitializeRuntimeFromBlob( bytes, (nuint)bytecode.Length)); Require( @@ -854,8 +854,8 @@ private static void ValidateRequiredFunctions(BamlApiV1* api) { Require(api->Version is not null, "version is null"); Require( - api->InitializeRuntimeFromBytecode is not null, - "initialize_runtime_from_bytecode is null"); + api->InitializeRuntimeFromBlob is not null, + "initialize_runtime_from_blob is null"); Require(api->FreeBuffer is not null, "free_buffer is null"); Require(api->RegisterCallback is not null, "register_callback is null"); Require(api->CallFunction is not null, "call_function is null"); @@ -1035,7 +1035,7 @@ private readonly struct BamlApiV1 public readonly delegate* unmanaged[Cdecl]< byte*, nuint, - BamlBuffer> InitializeRuntimeFromBytecode; + BamlBuffer> InitializeRuntimeFromBlob; public readonly delegate* unmanaged[Cdecl] FreeBuffer; public readonly delegate* unmanaged[Cdecl]< delegate* unmanaged[Cdecl], diff --git a/baml_language/sdks/csharp/bridge_csharp/tests/Baml.Bridge.AbiProbe/Program.cs b/baml_language/sdks/csharp/bridge_csharp/tests/Baml.Bridge.AbiProbe/Program.cs index f15c4224660..06a9abefe02 100644 --- a/baml_language/sdks/csharp/bridge_csharp/tests/Baml.Bridge.AbiProbe/Program.cs +++ b/baml_language/sdks/csharp/bridge_csharp/tests/Baml.Bridge.AbiProbe/Program.cs @@ -92,7 +92,7 @@ private static string ConsumeBuffer(BamlApiV1* api, BamlBuffer buffer) private static void ValidateRequiredFunctions(BamlApiV1* api) { Require(api->Version is not null, "version is null"); - Require(api->InitializeRuntimeFromBytecode is not null, "initialize_runtime_from_bytecode is null"); + Require(api->InitializeRuntimeFromBlob is not null, "initialize_runtime_from_blob is null"); Require(api->FreeBuffer is not null, "free_buffer is null"); Require(api->RegisterCallback is not null, "register_callback is null"); Require(api->CallFunction is not null, "call_function is null"); @@ -143,7 +143,7 @@ private readonly struct BamlApiV1 public readonly uint AbiVersion; public readonly nuint StructSize; public readonly delegate* unmanaged[Cdecl] Version; - public readonly delegate* unmanaged[Cdecl] InitializeRuntimeFromBytecode; + public readonly delegate* unmanaged[Cdecl] InitializeRuntimeFromBlob; public readonly delegate* unmanaged[Cdecl] FreeBuffer; public readonly delegate* unmanaged[Cdecl], void> RegisterCallback; public readonly delegate* unmanaged[Cdecl] CallFunction; diff --git a/baml_language/sdks/csharp/bridge_csharp/tests/Baml.Bridge.ProgramBootstrapProbe/NativeBytecodeInitializer.cs b/baml_language/sdks/csharp/bridge_csharp/tests/Baml.Bridge.ProgramBootstrapProbe/NativeBytecodeInitializer.cs index acb188da646..bcabe3ab704 100644 --- a/baml_language/sdks/csharp/bridge_csharp/tests/Baml.Bridge.ProgramBootstrapProbe/NativeBytecodeInitializer.cs +++ b/baml_language/sdks/csharp/bridge_csharp/tests/Baml.Bridge.ProgramBootstrapProbe/NativeBytecodeInitializer.cs @@ -52,8 +52,8 @@ internal NativeBytecodeInitializer(string? nativeLibraryPath) $"truncated API table {api->StructSize}"); Require(api->Version is not null, "version is null"); Require( - api->InitializeRuntimeFromBytecode is not null, - "initialize_runtime_from_bytecode is null"); + api->InitializeRuntimeFromBlob is not null, + "initialize_runtime_from_blob is null"); Require(api->FreeBuffer is not null, "free_buffer is null"); ProductVersion = Consume(api->Version()); } @@ -74,7 +74,7 @@ internal string Initialize(byte[] bytes) fixed (byte* pointer = bytes) { return Consume( - api->InitializeRuntimeFromBytecode( + api->InitializeRuntimeFromBlob( pointer, (nuint)bytes.Length)); } @@ -132,7 +132,7 @@ private readonly struct BamlApiPrefix public readonly delegate* unmanaged[Cdecl]< byte*, nuint, - BamlBuffer> InitializeRuntimeFromBytecode; + BamlBuffer> InitializeRuntimeFromBlob; public readonly delegate* unmanaged[Cdecl] FreeBuffer; } diff --git a/baml_language/sdks/csharp/bridge_csharp/tests/Baml.Bridge.StreamMediaAbiProbe/NativeBridge.cs b/baml_language/sdks/csharp/bridge_csharp/tests/Baml.Bridge.StreamMediaAbiProbe/NativeBridge.cs index ad2fc49b993..7a1f14e3372 100644 --- a/baml_language/sdks/csharp/bridge_csharp/tests/Baml.Bridge.StreamMediaAbiProbe/NativeBridge.cs +++ b/baml_language/sdks/csharp/bridge_csharp/tests/Baml.Bridge.StreamMediaAbiProbe/NativeBridge.cs @@ -89,7 +89,7 @@ public NativeBridge( fixed (byte* pointer = bytes) { string diagnostic = ConsumeUtf8( - api->InitializeRuntimeFromBytecode( + api->InitializeRuntimeFromBlob( pointer, (nuint)bytes.Length)); Require( @@ -418,8 +418,8 @@ private static void ValidateRequiredFunctions(BamlApiV1* api) { Require(api->Version is not null, "version is null"); Require( - api->InitializeRuntimeFromBytecode is not null, - "initialize_runtime_from_bytecode is null"); + api->InitializeRuntimeFromBlob is not null, + "initialize_runtime_from_blob is null"); Require(api->FreeBuffer is not null, "free_buffer is null"); Require(api->RegisterCallback is not null, "register_callback is null"); Require(api->CallFunction is not null, "call_function is null"); @@ -539,7 +539,7 @@ private readonly struct BamlApiV1 public readonly delegate* unmanaged[Cdecl]< byte*, nuint, - BamlBuffer> InitializeRuntimeFromBytecode; + BamlBuffer> InitializeRuntimeFromBlob; public readonly delegate* unmanaged[Cdecl] FreeBuffer; public readonly delegate* unmanaged[Cdecl]< delegate* unmanaged[Cdecl], diff --git a/baml_language/sdks/csharp/bridge_csharp/tests/Baml.Bridge.Tests/Program.cs b/baml_language/sdks/csharp/bridge_csharp/tests/Baml.Bridge.Tests/Program.cs index aedab4029d2..1a4900fe5b6 100644 --- a/baml_language/sdks/csharp/bridge_csharp/tests/Baml.Bridge.Tests/Program.cs +++ b/baml_language/sdks/csharp/bridge_csharp/tests/Baml.Bridge.Tests/Program.cs @@ -1245,7 +1245,7 @@ private static void VerifyNativeAbiLayoutAndValidation() (nameof(BamlApiV1.AbiVersion), 0), (nameof(BamlApiV1.StructSize), 8), (nameof(BamlApiV1.Version), 16), - (nameof(BamlApiV1.InitializeRuntimeFromBytecode), 24), + (nameof(BamlApiV1.InitializeRuntimeFromBlob), 24), (nameof(BamlApiV1.FreeBuffer), 32), (nameof(BamlApiV1.RegisterCallback), 40), (nameof(BamlApiV1.CallFunction), 48), @@ -1266,7 +1266,7 @@ private static void VerifyNativeAbiLayoutAndValidation() (nameof(BamlApiV1.RegisterBridge), 168), (nameof(BamlApiV1.RegisterUnhandledSpawnErrorCallback), 176), (nameof(BamlApiV1.ShutdownRuntime), 184), - (nameof(BamlApiV1.InitializeRuntimeFromBytecodeWithMetadata), 192), + (nameof(BamlApiV1.InitializeRuntimeFromBlobWithMetadata), 192), ]; foreach ((string field, int offset) in layout) { @@ -2719,7 +2719,7 @@ private static BamlOutboundResult MediaHandleResult( AbiVersion = 2, StructSize = (nuint)sizeof(BamlApiV1), Version = &Version, - InitializeRuntimeFromBytecode = &Initialize, + InitializeRuntimeFromBlob = &Initialize, FreeBuffer = &FreeBuffer, RegisterCallback = &RegisterResult, CallFunction = &Call, @@ -2740,7 +2740,7 @@ private static BamlOutboundResult MediaHandleResult( RegisterBridge = &RegisterBridge, RegisterUnhandledSpawnErrorCallback = &RegisterUnhandledSpawnError, ShutdownRuntime = &Shutdown, - InitializeRuntimeFromBytecodeWithMetadata = &InitializeWithMetadata, + InitializeRuntimeFromBlobWithMetadata = &InitializeWithMetadata, }; private static void ExpectInvalidTable(BamlApiV1 table, bool passNull = false) @@ -2762,7 +2762,7 @@ private static void ClearRequiredFunction(ref BamlApiV1 table, int field) switch (field) { case 0: table.Version = null; break; - case 1: table.InitializeRuntimeFromBytecode = null; break; + case 1: table.InitializeRuntimeFromBlob = null; break; case 2: table.FreeBuffer = null; break; case 3: table.RegisterCallback = null; break; case 4: table.CallFunction = null; break; @@ -2783,7 +2783,7 @@ private static void ClearRequiredFunction(ref BamlApiV1 table, int field) case 19: table.RegisterBridge = null; break; case 20: table.RegisterUnhandledSpawnErrorCallback = null; break; case 21: table.ShutdownRuntime = null; break; - case 22: table.InitializeRuntimeFromBytecodeWithMetadata = null; break; + case 22: table.InitializeRuntimeFromBlobWithMetadata = null; break; default: throw new ArgumentOutOfRangeException(nameof(field)); } } diff --git a/baml_language/sdks/csharp/bridge_csharp/tests/native_fixtures/table_diagnostics.c b/baml_language/sdks/csharp/bridge_csharp/tests/native_fixtures/table_diagnostics.c index 1c83889738a..eb67f9c6726 100644 --- a/baml_language/sdks/csharp/bridge_csharp/tests/native_fixtures/table_diagnostics.c +++ b/baml_language/sdks/csharp/bridge_csharp/tests/native_fixtures/table_diagnostics.c @@ -110,7 +110,7 @@ static BamlApiV1 probe_api = { .abi_version = BAML_API_V1_ABI_VERSION, .struct_size = sizeof(BamlApiV1), .version = probe_version, - .initialize_runtime_from_bytecode = probe_initialize, + .initialize_runtime_from_blob = probe_initialize, .free_buffer = probe_free, .register_callback = probe_register_result, .call_function = probe_call, diff --git a/baml_language/sdks/go/baml_go/internal/cffi/include/baml_cffi.h b/baml_language/sdks/go/baml_go/internal/cffi/include/baml_cffi.h index 5a3209a3964..70785791c30 100644 --- a/baml_language/sdks/go/baml_go/internal/cffi/include/baml_cffi.h +++ b/baml_language/sdks/go/baml_go/internal/cffi/include/baml_cffi.h @@ -178,8 +178,7 @@ typedef struct BamlBuffer { typedef struct BamlBuffer (*BamlVersionFn)(void); -typedef struct BamlBuffer (*BamlInitializeRuntimeFromBytecodeFn)(const uint8_t *bytecode, - size_t length); +typedef struct BamlBuffer (*BamlInitializeRuntimeFromBlobFn)(const uint8_t *bytecode, size_t length); typedef void (*BamlFreeBufferFn)(struct BamlBuffer buffer); @@ -298,9 +297,9 @@ typedef void (*BamlRegisterUnhandledSpawnErrorCallbackFn)(BamlUnhandledSpawnErro typedef struct BamlBuffer (*BamlShutdownRuntimeFn)(void); -typedef struct BamlBuffer (*BamlInitializeRuntimeFromBytecodeWithMetadataFn)(const uint8_t *bytecode, - size_t length, - const char *baml_toml); +typedef struct BamlBuffer (*BamlInitializeRuntimeFromBlobWithMetadataFn)(const uint8_t *bytecode, + size_t length, + const char *baml_toml); /** * First version of the shared BAML C API. @@ -350,7 +349,7 @@ typedef struct BamlApiV1 { * calls are serialized only while replacing the global runtime; calls * already in progress retain their previous runtime instance. */ - BamlInitializeRuntimeFromBytecodeFn initialize_runtime_from_bytecode; + BamlInitializeRuntimeFromBlobFn initialize_runtime_from_blob; /** * Release exactly one runtime-owned buffer returned through this table. * @@ -483,7 +482,7 @@ typedef struct BamlApiV1 { /** * Replace the runtime from bytecode after validating embedded generation metadata. */ - BamlInitializeRuntimeFromBytecodeWithMetadataFn initialize_runtime_from_bytecode_with_metadata; + BamlInitializeRuntimeFromBlobWithMetadataFn initialize_runtime_from_blob_with_metadata; } BamlApiV1; typedef const struct BamlApiV1 *(*BamlGetApiV1Fn)(void); @@ -515,9 +514,9 @@ BamlCffiStatus __testonly_seed_heap_handle(uint64_t slab_key, * Initialize generated bytecode after validating its embedded `baml.toml`. */ BAML_CFFI_API -struct BamlBuffer initialize_runtime_from_bytecode_with_metadata(const uint8_t *bytecode, - size_t length, - const char *baml_toml); +struct BamlBuffer initialize_runtime_from_blob_with_metadata(const uint8_t *bytecode, + size_t length, + const char *baml_toml); #ifdef __cplusplus } // extern "C" diff --git a/baml_language/sdks/go/baml_go/native_unix.go b/baml_language/sdks/go/baml_go/native_unix.go index 6f106839f0b..ef223a1418b 100644 --- a/baml_language/sdks/go/baml_go/native_unix.go +++ b/baml_language/sdks/go/baml_go/native_unix.go @@ -81,13 +81,13 @@ static const char *baml_open_library(const char *path) { dlclose(handle); return baml_loader_error; } - const size_t required_size = offsetof(BamlApiV1, initialize_runtime_from_bytecode_with_metadata) + sizeof(api->initialize_runtime_from_bytecode_with_metadata); + const size_t required_size = offsetof(BamlApiV1, initialize_runtime_from_blob_with_metadata) + sizeof(api->initialize_runtime_from_blob_with_metadata); if (api->struct_size < required_size) { snprintf(baml_loader_error, sizeof(baml_loader_error), "truncated BAML ABI v1 table: got %zu bytes, need at least %zu", api->struct_size, required_size); dlclose(handle); return baml_loader_error; } - if (api->version == NULL || api->initialize_runtime_from_bytecode == NULL || + if (api->version == NULL || api->initialize_runtime_from_blob == NULL || api->free_buffer == NULL || api->register_callback == NULL || api->call_function == NULL || api->new_function_call == NULL || api->cancel_function_call == NULL || @@ -101,7 +101,7 @@ static const char *baml_open_library(const char *path) { api->register_bridge == NULL || api->register_unhandled_spawn_error_callback == NULL || api->shutdown_runtime == NULL || - api->initialize_runtime_from_bytecode_with_metadata == NULL) { + api->initialize_runtime_from_blob_with_metadata == NULL) { snprintf(baml_loader_error, sizeof(baml_loader_error), "BAML ABI v1 table contains a NULL required function"); dlclose(handle); return baml_loader_error; @@ -135,10 +135,10 @@ static BamlBuffer baml_register_go_bridge(const uint8_t *runtime_name, size_t ru return baml_api->register_bridge(&info); } static BamlBuffer baml_initialize(const uint8_t *bytecode, size_t length) { - return baml_api->initialize_runtime_from_bytecode(bytecode, length); + return baml_api->initialize_runtime_from_blob(bytecode, length); } static BamlBuffer baml_initialize_with_metadata(const uint8_t *bytecode, size_t length, const char *baml_toml) { - return baml_api->initialize_runtime_from_bytecode_with_metadata(bytecode, length, baml_toml); + return baml_api->initialize_runtime_from_blob_with_metadata(bytecode, length, baml_toml); } static void baml_free_buffer(BamlBuffer buffer) { baml_api->free_buffer(buffer); } static void baml_register_go_callback(void) { diff --git a/baml_language/sdks/go/baml_go/native_windows.go b/baml_language/sdks/go/baml_go/native_windows.go index 973018f28a4..74e9685cffa 100644 --- a/baml_language/sdks/go/baml_go/native_windows.go +++ b/baml_language/sdks/go/baml_go/native_windows.go @@ -59,13 +59,13 @@ static const char *baml_open_library(const wchar_t *path) { FreeLibrary(handle); return baml_loader_error; } - const size_t required_size = offsetof(BamlApiV1, initialize_runtime_from_bytecode_with_metadata) + sizeof(api->initialize_runtime_from_bytecode_with_metadata); + const size_t required_size = offsetof(BamlApiV1, initialize_runtime_from_blob_with_metadata) + sizeof(api->initialize_runtime_from_blob_with_metadata); if (api->struct_size < required_size) { snprintf(baml_loader_error, sizeof(baml_loader_error), "truncated BAML ABI v1 table: got %zu bytes, need at least %zu", api->struct_size, required_size); FreeLibrary(handle); return baml_loader_error; } - if (api->version == NULL || api->initialize_runtime_from_bytecode == NULL || + if (api->version == NULL || api->initialize_runtime_from_blob == NULL || api->free_buffer == NULL || api->register_callback == NULL || api->call_function == NULL || api->new_function_call == NULL || api->cancel_function_call == NULL || @@ -79,7 +79,7 @@ static const char *baml_open_library(const wchar_t *path) { api->register_bridge == NULL || api->register_unhandled_spawn_error_callback == NULL || api->shutdown_runtime == NULL || - api->initialize_runtime_from_bytecode_with_metadata == NULL) { + api->initialize_runtime_from_blob_with_metadata == NULL) { snprintf(baml_loader_error, sizeof(baml_loader_error), "BAML ABI v1 table contains a NULL required function"); FreeLibrary(handle); return baml_loader_error; @@ -109,8 +109,8 @@ static BamlBuffer baml_register_go_bridge(const uint8_t *runtime_name, size_t ru }; return baml_api->register_bridge(&info); } -static BamlBuffer baml_initialize(const uint8_t *bytecode, size_t length) { return baml_api->initialize_runtime_from_bytecode(bytecode, length); } -static BamlBuffer baml_initialize_with_metadata(const uint8_t *bytecode, size_t length, const char *baml_toml) { return baml_api->initialize_runtime_from_bytecode_with_metadata(bytecode, length, baml_toml); } +static BamlBuffer baml_initialize(const uint8_t *bytecode, size_t length) { return baml_api->initialize_runtime_from_blob(bytecode, length); } +static BamlBuffer baml_initialize_with_metadata(const uint8_t *bytecode, size_t length, const char *baml_toml) { return baml_api->initialize_runtime_from_blob_with_metadata(bytecode, length, baml_toml); } static void baml_free_buffer(BamlBuffer buffer) { baml_api->free_buffer(buffer); } static void baml_register_go_callback(void) { baml_api->register_callback(baml_go_result_callback); diff --git a/baml_language/sdks/go/sdkgen_go/src/lib.rs b/baml_language/sdks/go/sdkgen_go/src/lib.rs index a2bef8522db..88263933c83 100644 --- a/baml_language/sdks/go/sdkgen_go/src/lib.rs +++ b/baml_language/sdks/go/sdkgen_go/src/lib.rs @@ -5032,24 +5032,23 @@ fn render_bootstrap(bytecode: &[u8], embedded_baml_toml: Option<&str>) -> String out.push_str("import (\n\t\"sync\"\n\n"); let _ = writeln!(out, "\t\"{BAML_GO_MODULE}\"\n)\n"); out.push_str("var (\n\tonce sync.Once\n\tinitializeErr error\n)\n\n"); - out.push_str("var bytecode = []byte{\n"); - for chunk in bytecode.chunks(20) { - let line = chunk - .iter() - .map(u8::to_string) - .collect::>() - .join(", "); - let _ = writeln!(out, "\t{line},"); - } - out.push_str("}\n\n"); + // One line of encoded bytecode, passed to the runtime as bytes; the + // bridge decodes it natively. + let _ = writeln!( + out, + "const bytecode = \"{}\"\n", + baml_sdkgen_types::embedded_bytecode_base64(bytecode) + ); if let Some(embedded_baml_toml) = embedded_baml_toml { let _ = writeln!(out, "const embeddedBamlToml = {embedded_baml_toml:?}\n"); } out.push_str("func Ensure() error {\n"); if embedded_baml_toml.is_some() { - out.push_str("\tonce.Do(func() { initializeErr = baml_go.InitializeWithMetadata(bytecode, embeddedBamlToml) })\n"); + out.push_str("\tonce.Do(func() { initializeErr = baml_go.InitializeWithMetadata([]byte(bytecode), embeddedBamlToml) })\n"); } else { - out.push_str("\tonce.Do(func() { initializeErr = baml_go.Initialize(bytecode) })\n"); + out.push_str( + "\tonce.Do(func() { initializeErr = baml_go.Initialize([]byte(bytecode)) })\n", + ); } out.push_str("\treturn initializeErr\n}\n"); out @@ -5323,8 +5322,10 @@ mod tests { assert!(functions.contains("\"user.echo_text\"")); assert!(functions.contains("baml_go.String(value)")); assert!( - files[&PathBuf::from("internal/bootstrap/bootstrap.go")] - .contains("var bytecode = []byte{\n\t1, 2, 3,") + files[&PathBuf::from("internal/bootstrap/bootstrap.go")].contains(&format!( + "const bytecode = \"{}\"\n", + baml_sdkgen_types::embedded_bytecode_base64(&[1, 2, 3]) + )) ); } diff --git a/baml_language/sdks/java/bridge_java/src/lib.rs b/baml_language/sdks/java/bridge_java/src/lib.rs index b5500f98017..00befac87e4 100644 --- a/baml_language/sdks/java/bridge_java/src/lib.rs +++ b/baml_language/sdks/java/bridge_java/src/lib.rs @@ -55,12 +55,12 @@ fn call_sync_to_bytes(args_proto: &[u8]) -> Vec { /// `baml_bridge.BamlFfi.nativeInitFromBytecode(byte[] bytecode, String metadata, String runtimeVersion, String toolchainVersion)`. /// /// Initialize the process-global runtime from serialized BAML bytecode -/// (`bridge_cffi::initialize_runtime_from_bytecode`, the same path +/// (`bridge_cffi::initialize_runtime_from_blob`, the same path /// `bridge_python` uses). Idempotent in the same sense as Python: the /// single-slot singleton is replaced, so a second call swaps the runtime. /// A setup failure is thrown as an unchecked `RuntimeException` (this is a /// handle-returning site with no envelope to ride, like Python's -/// `initialize_runtime_from_bytecode` raising). +/// `initialize_runtime_from_blob` raising). #[unsafe(no_mangle)] pub extern "system" fn Java_baml_1bridge_BamlFfi_nativeInitFromBytecode( mut env: JNIEnv<'_>, @@ -142,8 +142,7 @@ pub extern "system" fn Java_baml_1bridge_BamlFfi_nativeInitFromBytecode( } }; - if let Err(e) = - bridge_cffi::initialize_runtime_from_bytecode(&bytes, embedded_baml_toml.as_deref()) + if let Err(e) = bridge_cffi::initialize_runtime_from_blob(&bytes, embedded_baml_toml.as_deref()) { throw_runtime_exception_exact(&mut env, &e.to_string()); } diff --git a/baml_language/sdks/java/sdkgen_java/src/lib.rs b/baml_language/sdks/java/sdkgen_java/src/lib.rs index f5d310b55be..27b86130625 100644 --- a/baml_language/sdks/java/sdkgen_java/src/lib.rs +++ b/baml_language/sdks/java/sdkgen_java/src/lib.rs @@ -8,7 +8,7 @@ //! file), a per-package `Fns` holder binding free functions to the //! `baml_bridge.BamlFfi` runtime entry points, minted sealed-union //! types, a root `Baml.java` runtime anchor, and the compiled BAML -//! bytecode as a base64 classpath resource (`inlinedbaml.b64` — Java +//! bytecode as a base64 LZ4 classpath resource (`inlinedbaml.b64` — Java //! caps a static initializer at 64KB of JVM bytecode, so a //! `new byte[]{...}` literal like TS's `_inlinedbaml.ts` cannot work). //! @@ -394,7 +394,7 @@ fn to_source_code_internal( )); } let anchor_body = format!( - "/**\n * Runtime anchor for the generated SDK: loading this class registers\n * the type map (BAML FQN \u{2194} generated class, with field declaration\n * order) and initializes the BAML runtime from the embedded bytecode\n * resource (idempotent) \u{2014} the Java analog of Python's root-package\n * import side effect. Every generated binding holder forces this via\n * {{@link #ensure()}}.\n */\npublic final class {anchor_ident} {{\n private {anchor_ident}() {{}}\n\n static {{\n{registrations} try (java.io.InputStream in = {anchor_ident}.class.getResourceAsStream(\"/baml_sdk/inlinedbaml.b64\")) {{\n if (in == null) {{\n throw new IllegalStateException(\n \"baml_sdk/inlinedbaml.b64 not found on the classpath \u{2014} is the generated resource root registered?\");\n }}\n byte[] b64 = in.readAllBytes();\n byte[] bytecode = java.util.Base64.getMimeDecoder().decode(b64);\n baml_bridge.BamlFfi.initFromBytecode(bytecode);\n }} catch (java.io.IOException e) {{\n throw new java.io.UncheckedIOException(\"failed to read embedded BAML bytecode\", e);\n }}\n }}\n\n /** Forces class initialization (and thus runtime init). No-op afterwards. */\n public static void ensure() {{}}\n}}\n" + "/**\n * Runtime anchor for the generated SDK: loading this class registers\n * the type map (BAML FQN \u{2194} generated class, with field declaration\n * order) and initializes the BAML runtime from the embedded bytecode\n * resource (idempotent) \u{2014} the Java analog of Python's root-package\n * import side effect. Every generated binding holder forces this via\n * {{@link #ensure()}}.\n */\npublic final class {anchor_ident} {{\n private {anchor_ident}() {{}}\n\n static {{\n{registrations} try (java.io.InputStream in = {anchor_ident}.class.getResourceAsStream(\"/baml_sdk/inlinedbaml.b64\")) {{\n if (in == null) {{\n throw new IllegalStateException(\n \"baml_sdk/inlinedbaml.b64 not found on the classpath \u{2014} is the generated resource root registered?\");\n }}\n byte[] bytecode = in.readAllBytes();\n baml_bridge.BamlFfi.initFromBytecode(bytecode);\n }} catch (java.io.IOException e) {{\n throw new java.io.UncheckedIOException(\"failed to read embedded BAML bytecode\", e);\n }}\n }}\n\n /** Forces class initialization (and thus runtime init). No-op afterwards. */\n public static void ensure() {{}}\n}}\n" ); let anchor_body = if embedded_baml_toml.is_some() { anchor_body.replace( @@ -411,13 +411,15 @@ fn to_source_code_internal( with_package(&root, &anchor_body), ); - // Compiled BAML bytecode as a base64 text resource. Base64 keeps - // the emitter's `HashMap` contract (raw bytecode - // is not valid UTF-8) and dodges Java's 64KB static-initializer / - // constant-pool limits that rule out a byte-array literal. + // Compiled BAML bytecode as an embedded-bytecode text resource (base64 + // of an LZ4 frame), passed to the bridge as raw bytes and decoded + // natively. Text keeps the emitter's + // `HashMap` contract (raw bytecode is not valid + // UTF-8) and dodges Java's 64KB static-initializer / constant-pool + // limits that rule out a byte-array literal. out.insert( PathBuf::from("inlinedbaml.b64"), - base64_encode(baml_bytecode), + baml_sdkgen_types::embedded_bytecode_base64(baml_bytecode), ); if let Some(embedded_baml_toml) = embedded_baml_toml { out.insert( @@ -603,35 +605,6 @@ fn with_package(pkg: &PackagePath, body: &str) -> String { format!("package {};\n\n{body}", pkg.java_package()) } -/// Standard-alphabet base64 with padding, dependency-free. The -/// bytecode payload is written once per fixture at build time, so -/// encoder throughput is irrelevant. -fn base64_encode(data: &[u8]) -> String { - const ALPHABET: &[u8; 64] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - let mut out = String::with_capacity(data.len().div_ceil(3) * 4); - for chunk in data.chunks(3) { - let b = [ - chunk[0], - chunk.get(1).copied().unwrap_or(0), - chunk.get(2).copied().unwrap_or(0), - ]; - let n = (u32::from(b[0]) << 16) | (u32::from(b[1]) << 8) | u32::from(b[2]); - out.push(ALPHABET[(n >> 18) as usize & 63] as char); - out.push(ALPHABET[(n >> 12) as usize & 63] as char); - out.push(if chunk.len() > 1 { - ALPHABET[(n >> 6) as usize & 63] as char - } else { - '=' - }); - out.push(if chunk.len() > 2 { - ALPHABET[n as usize & 63] as char - } else { - '=' - }); - } - out -} - #[cfg(test)] mod tests { use baml_base::Name as BaseName; @@ -1668,15 +1641,10 @@ mod tests { &[0, 1, 2, 253, 254, 255], NamingConvention::PreserveCase, ); - assert_eq!(out[&PathBuf::from("inlinedbaml.b64")], "AAEC/f7/"); - } - - #[test] - fn base64_padding() { - assert_eq!(base64_encode(b""), ""); - assert_eq!(base64_encode(b"f"), "Zg=="); - assert_eq!(base64_encode(b"fo"), "Zm8="); - assert_eq!(base64_encode(b"foo"), "Zm9v"); + assert_eq!( + out[&PathBuf::from("inlinedbaml.b64")], + baml_sdkgen_types::embedded_bytecode_base64(&[0, 1, 2, 253, 254, 255]) + ); } // -- explicit generics: emitter surface ---------------------------------- diff --git a/baml_language/sdks/python/rust/bridge_python/src/baml_core/baml_py/__init__.pyi b/baml_language/sdks/python/rust/bridge_python/src/baml_core/baml_py/__init__.pyi index 1552e1a4c0c..ea2010286dc 100644 --- a/baml_language/sdks/python/rust/bridge_python/src/baml_core/baml_py/__init__.pyi +++ b/baml_language/sdks/python/rust/bridge_python/src/baml_core/baml_py/__init__.pyi @@ -179,7 +179,7 @@ class BamlRuntime: * `files` - Map of filename to file content """ @staticmethod - def initialize_runtime_from_bytecode(bytecode: bytes, embedded_baml_toml: typing.Optional[builtins.str] = None) -> BamlRuntime: + def initialize_runtime_from_blob(bytecode: bytes, embedded_baml_toml: typing.Optional[builtins.str] = None) -> BamlRuntime: r""" Initialize the process-global runtime from serialized BAML bytecode. """ diff --git a/baml_language/sdks/python/rust/bridge_python/src/runtime.rs b/baml_language/sdks/python/rust/bridge_python/src/runtime.rs index 13e64e7a523..dd316e0e3fa 100644 --- a/baml_language/sdks/python/rust/bridge_python/src/runtime.rs +++ b/baml_language/sdks/python/rust/bridge_python/src/runtime.rs @@ -57,14 +57,11 @@ impl BamlRuntime { /// * `bytecode` - borsh-encoded BAML bytecode program #[staticmethod] #[pyo3(signature = (bytecode, embedded_baml_toml=None))] - fn initialize_runtime_from_bytecode( + fn initialize_runtime_from_blob( bytecode: Vec, embedded_baml_toml: Option, ) -> PyResult { - match bridge_cffi::initialize_runtime_from_bytecode( - &bytecode, - embedded_baml_toml.as_deref(), - ) { + match bridge_cffi::initialize_runtime_from_blob(&bytecode, embedded_baml_toml.as_deref()) { Ok(_bex) => Ok(BamlRuntime), Err(e) => Err(crate::errors::bridge_error_to_initialization_error(e)), } diff --git a/baml_language/sdks/python/rust/sdkgen_python_pydantic2/src/lib.rs b/baml_language/sdks/python/rust/sdkgen_python_pydantic2/src/lib.rs index 15b82c56d65..cafc72e2d33 100644 --- a/baml_language/sdks/python/rust/sdkgen_python_pydantic2/src/lib.rs +++ b/baml_language/sdks/python/rust/sdkgen_python_pydantic2/src/lib.rs @@ -613,7 +613,7 @@ fn render_root_init(top_children: &BTreeSet, use_bytecode: bool) -> Stri out.push_str("from . import _inlinedbaml\n"); out.push_str("from ._typemap import _TYPE_MAP\n\n"); if use_bytecode { - out.push_str("BamlRuntime.initialize_runtime_from_bytecode(_inlinedbaml.BYTECODE, _inlinedbaml.EMBEDDED_BAML_TOML)\n\n"); + out.push_str("BamlRuntime.initialize_runtime_from_blob(_inlinedbaml.BYTECODE, _inlinedbaml.EMBEDDED_BAML_TOML)\n\n"); } else { out.push_str("BamlRuntime.initialize_runtime(\n"); out.push_str(" \"baml_src\", _inlinedbaml.FILES\n"); @@ -736,9 +736,11 @@ fn render_baml_source_files(files: &[UserBamlFile]) -> String { } fn render_inlinedbaml_bytecode(bytecode: &[u8], embedded_baml_toml: Option<&str>) -> String { - let mut out = String::from("from __future__ import annotations\n\nBYTECODE: bytes = "); - out.push_str(&py_bytes(bytecode)); - out.push_str("\nEMBEDDED_BAML_TOML: str | None = "); + // One line of encoded bytecode (ASCII), passed to the bridge as bytes; the + // bridge decodes it natively. + let mut out = String::from("from __future__ import annotations\n\nBYTECODE: bytes = b\""); + out.push_str(&baml_sdkgen_types::embedded_bytecode_base64(bytecode)); + out.push_str("\"\nEMBEDDED_BAML_TOML: str | None = "); out.push_str( &embedded_baml_toml .map(py_string) @@ -753,32 +755,6 @@ fn render_inlinedbaml_bytecode(bytecode: &[u8], embedded_baml_toml: Option<&str> /// round-trips through `ast.literal_eval` and is byte-identical. pub(crate) use baml_sdkgen_types::quoted_string as py_string; -/// Render bytes as adjacent Python bytes literals. Chunking keeps generated -/// lines manageable without adding any runtime decode step. -pub(crate) fn py_bytes(bytes: &[u8]) -> String { - const CHUNK_SIZE: usize = 80; - - if bytes.is_empty() { - return "b\"\"".to_string(); - } - - let mut out = String::from("(\n"); - for chunk in bytes.chunks(CHUNK_SIZE) { - out.push_str(" b\""); - for &byte in chunk { - match byte { - b'\\' => out.push_str("\\\\"), - b'"' => out.push_str("\\\""), - 0x20..=0x7e => out.push(byte as char), - _ => write!(out, "\\x{byte:02x}").unwrap(), - } - } - out.push_str("\"\n"); - } - out.push(')'); - out -} - /// Return the `cg::Name` that keys a given symbol in the pool. Not /// load-bearing today (we iterate `pool.keys()` directly), but kept /// near the emitter for symmetry with `SymbolPool`. @@ -1773,15 +1749,17 @@ mod tests { let root = &out[&PathBuf::from("__init__.py")]; assert!( - root.contains("BamlRuntime.initialize_runtime_from_bytecode(_inlinedbaml.BYTECODE, _inlinedbaml.EMBEDDED_BAML_TOML)") + root.contains("BamlRuntime.initialize_runtime_from_blob(_inlinedbaml.BYTECODE, _inlinedbaml.EMBEDDED_BAML_TOML)") ); assert!(!root.contains("BamlRuntime.initialize_runtime(")); assert!(root.contains("def get_baml_source_files() -> dict[str, str]:")); let inl = &out[&PathBuf::from("_inlinedbaml.py")]; assert!(inl.starts_with(HEADER)); - assert!(inl.contains("BYTECODE: bytes = (")); - assert!(inl.contains("b\"\\x00BAML\\\"\\x0a\\xff\"")); + assert!(inl.contains(&format!( + "BYTECODE: bytes = b\"{}\"\n", + baml_sdkgen_types::embedded_bytecode_base64(bytecode) + ))); assert!(!inl.contains("FILES: dict[str, str]")); let sources = &out[&PathBuf::from("_baml_sources.py")]; @@ -1810,7 +1788,7 @@ mod tests { ); let inlined = &out[&PathBuf::from("_inlinedbaml.py")]; - assert!(inlined.contains("BYTECODE: bytes = (")); + assert!(inlined.contains("BYTECODE: bytes = b\"")); assert!(inlined.contains("EMBEDDED_BAML_TOML: str | None = ")); assert!(!inlined.contains("function a()")); diff --git a/baml_language/sdks/python/src/baml_bridge/baml_py.pyi b/baml_language/sdks/python/src/baml_bridge/baml_py.pyi index 2401232687b..1558abcfb5e 100644 --- a/baml_language/sdks/python/src/baml_bridge/baml_py.pyi +++ b/baml_language/sdks/python/src/baml_bridge/baml_py.pyi @@ -179,7 +179,7 @@ class BamlRuntime: * `files` - Map of filename to file content """ @staticmethod - def initialize_runtime_from_bytecode(bytecode: typing.Sequence[builtins.int], embedded_baml_toml: typing.Optional[builtins.str] = None) -> BamlRuntime: + def initialize_runtime_from_blob(bytecode: typing.Sequence[builtins.int], embedded_baml_toml: typing.Optional[builtins.str] = None) -> BamlRuntime: r""" Initialize the process-global runtime from serialized BAML bytecode. diff --git a/baml_language/sdks/python/tests/test_engine.py b/baml_language/sdks/python/tests/test_engine.py index bb34a6e0251..e323cbbbb34 100644 --- a/baml_language/sdks/python/tests/test_engine.py +++ b/baml_language/sdks/python/tests/test_engine.py @@ -185,7 +185,7 @@ def test_generated_bytecode_version_skew_fails_before_deserialization(self): """ with pytest.raises(RuntimeError) as exc_info: - BamlRuntime.initialize_runtime_from_bytecode( + BamlRuntime.initialize_runtime_from_blob( b"\x00", embedded_baml_toml ) diff --git a/baml_language/sdks/ruby/bridge_ruby/lib/baml/bridge/native.rb b/baml_language/sdks/ruby/bridge_ruby/lib/baml/bridge/native.rb index 5b13a194d19..3bf3724e586 100644 --- a/baml_language/sdks/ruby/bridge_ruby/lib/baml/bridge/native.rb +++ b/baml_language/sdks/ruby/bridge_ruby/lib/baml/bridge/native.rb @@ -30,7 +30,7 @@ class BridgeInfoV1 < FFI::Struct class ApiV1 < FFI::Struct FUNCTION_FIELDS = %i[ version - initialize_runtime_from_bytecode + initialize_runtime_from_blob free_buffer register_callback call_function @@ -51,7 +51,7 @@ class ApiV1 < FFI::Struct register_bridge register_unhandled_spawn_error_callback shutdown_runtime - initialize_runtime_from_bytecode_with_metadata + initialize_runtime_from_blob_with_metadata ].freeze layout :abi_version, :uint32, @@ -61,7 +61,7 @@ class ApiV1 < FFI::Struct FUNCTION_SIGNATURES = { version: [Buffer.by_value, []], - initialize_runtime_from_bytecode: [ + initialize_runtime_from_blob: [ Buffer.by_value, %i[pointer size_t], { blocking: true } @@ -97,8 +97,8 @@ def register_result_callback(callback) def initialize_runtime(bytecode) pointer = memory_for(bytecode) diagnostic = read_owned_utf8( - function(:initialize_runtime_from_bytecode).call(pointer, bytecode.bytesize), - "initialize_runtime_from_bytecode" + function(:initialize_runtime_from_blob).call(pointer, bytecode.bytesize), + "initialize_runtime_from_blob" ) return if diagnostic.empty? diff --git a/baml_language/sdks/rust/bridge_rust/src/capi.rs b/baml_language/sdks/rust/bridge_rust/src/capi.rs index 91ce5a2570a..e9262ca66dc 100644 --- a/baml_language/sdks/rust/bridge_rust/src/capi.rs +++ b/baml_language/sdks/rust/bridge_rust/src/capi.rs @@ -45,8 +45,8 @@ pub(crate) struct Api { unsafe extern "C" fn(*const c_char, *const c_char) -> *const c_void, /// Returns a status buffer: empty on success, otherwise a UTF-8 error /// message. Read it with [`Api::take_status`]. - pub(crate) initialize_runtime_from_bytecode: unsafe extern "C" fn(*const u8, usize) -> Buffer, - pub(crate) initialize_runtime_from_bytecode_with_metadata: + pub(crate) initialize_runtime_from_blob: unsafe extern "C" fn(*const u8, usize) -> Buffer, + pub(crate) initialize_runtime_from_blob_with_metadata: unsafe extern "C" fn(*const u8, usize, *const c_char) -> Buffer, pub(crate) register_callback: unsafe extern "C" fn(CallbackFn), pub(crate) new_function_call: unsafe extern "C" fn() -> u64, @@ -139,7 +139,7 @@ struct BamlApiV1 { abi_version: u32, struct_size: usize, version: Option Buffer>, - initialize_runtime_from_bytecode: Option Buffer>, + initialize_runtime_from_blob: Option Buffer>, free_buffer: Option, register_callback: Option, call_function: Option, @@ -166,7 +166,7 @@ struct BamlApiV1 { register_unhandled_spawn_error_callback: Option, shutdown_runtime: Option Buffer>, - initialize_runtime_from_bytecode_with_metadata: + initialize_runtime_from_blob_with_metadata: Option Buffer>, } @@ -241,14 +241,14 @@ fn load_inner(env: &loader::LoaderEnv) -> Result { } let version = required_slot(table.version, "version", &path)?; - let initialize_runtime_from_bytecode = required_slot( - table.initialize_runtime_from_bytecode, - "initialize_runtime_from_bytecode", + let initialize_runtime_from_blob = required_slot( + table.initialize_runtime_from_blob, + "initialize_runtime_from_blob", &path, )?; - let initialize_runtime_from_bytecode_with_metadata = required_slot( - table.initialize_runtime_from_bytecode_with_metadata, - "initialize_runtime_from_bytecode_with_metadata", + let initialize_runtime_from_blob_with_metadata = required_slot( + table.initialize_runtime_from_blob_with_metadata, + "initialize_runtime_from_blob_with_metadata", &path, )?; let free_buffer = required_slot(table.free_buffer, "free_buffer", &path)?; @@ -287,8 +287,8 @@ fn load_inner(env: &loader::LoaderEnv) -> Result { let api = Api { // Not part of BamlApiV1 (a legacy direct export); resolved directly. create_baml_runtime: sym(&library, b"create_baml_runtime\0")?, - initialize_runtime_from_bytecode, - initialize_runtime_from_bytecode_with_metadata, + initialize_runtime_from_blob, + initialize_runtime_from_blob_with_metadata, register_callback, new_function_call, call_function, diff --git a/baml_language/sdks/rust/bridge_rust/src/runtime.rs b/baml_language/sdks/rust/bridge_rust/src/runtime.rs index 74cc3cbfb3a..4bd4b1ffd24 100644 --- a/baml_language/sdks/rust/bridge_rust/src/runtime.rs +++ b/baml_language/sdks/rust/bridge_rust/src/runtime.rs @@ -37,12 +37,12 @@ pub fn initialize_from_bytecode_with_metadata( #[expect(unsafe_code)] let status = unsafe { match manifest.as_ref() { - Some(manifest) => (api.initialize_runtime_from_bytecode_with_metadata)( + Some(manifest) => (api.initialize_runtime_from_blob_with_metadata)( bytecode.as_ptr(), bytecode.len(), manifest.as_ptr(), ), - None => (api.initialize_runtime_from_bytecode)(bytecode.as_ptr(), bytecode.len()), + None => (api.initialize_runtime_from_blob)(bytecode.as_ptr(), bytecode.len()), } }; api.take_status(status).map_err(SdkError::new) diff --git a/baml_language/sdks/swift/Sources/BamlBridge/Api.swift b/baml_language/sdks/swift/Sources/BamlBridge/Api.swift index 73238b37d89..d78922ea5b8 100644 --- a/baml_language/sdks/swift/Sources/BamlBridge/Api.swift +++ b/baml_language/sdks/swift/Sources/BamlBridge/Api.swift @@ -28,7 +28,7 @@ enum BamlApi { }() static let version = v1.version! - static let initializeRuntimeFromBytecode = v1.initialize_runtime_from_bytecode! + static let initializeRuntimeFromBlob = v1.initialize_runtime_from_blob! static let freeBuffer = v1.free_buffer! static let registerCallback = v1.register_callback! static let callFunction = v1.call_function! @@ -49,8 +49,8 @@ enum BamlApi { static let registerBridge = v1.register_bridge! static let registerUnhandledSpawnErrorCallback = v1.register_unhandled_spawn_error_callback! static let shutdownRuntime = v1.shutdown_runtime! - static let initializeRuntimeFromBytecodeWithMetadata = - v1.initialize_runtime_from_bytecode_with_metadata! + static let initializeRuntimeFromBlobWithMetadata = + v1.initialize_runtime_from_blob_with_metadata! /// Copy a runtime-owned buffer to a `Data` and release it exactly /// once via the table's `free_buffer`. A zero-length buffer may have diff --git a/baml_language/sdks/swift/Sources/BamlBridge/Runtime.swift b/baml_language/sdks/swift/Sources/BamlBridge/Runtime.swift index 437450885fb..217c36865b7 100644 --- a/baml_language/sdks/swift/Sources/BamlBridge/Runtime.swift +++ b/baml_language/sdks/swift/Sources/BamlBridge/Runtime.swift @@ -133,14 +133,14 @@ public final class BamlRuntime: @unchecked Sendable { let errorBuffer = bytecode.withUnsafeBytes { buf -> BamlBuffer in if let embeddedBamlToml { return embeddedBamlToml.withCString { manifest in - BamlApi.initializeRuntimeFromBytecodeWithMetadata( + BamlApi.initializeRuntimeFromBlobWithMetadata( buf.baseAddress?.assumingMemoryBound(to: UInt8.self), buf.count, manifest ) } } - return BamlApi.initializeRuntimeFromBytecode( + return BamlApi.initializeRuntimeFromBlob( buf.baseAddress?.assumingMemoryBound(to: UInt8.self), buf.count) } let initError = String(decoding: BamlApi.takeBuffer(errorBuffer), as: UTF8.self) diff --git a/baml_language/sdks/swift/Sources/CBamlBridge/include/baml_cffi.h b/baml_language/sdks/swift/Sources/CBamlBridge/include/baml_cffi.h index 5a3209a3964..70785791c30 100644 --- a/baml_language/sdks/swift/Sources/CBamlBridge/include/baml_cffi.h +++ b/baml_language/sdks/swift/Sources/CBamlBridge/include/baml_cffi.h @@ -178,8 +178,7 @@ typedef struct BamlBuffer { typedef struct BamlBuffer (*BamlVersionFn)(void); -typedef struct BamlBuffer (*BamlInitializeRuntimeFromBytecodeFn)(const uint8_t *bytecode, - size_t length); +typedef struct BamlBuffer (*BamlInitializeRuntimeFromBlobFn)(const uint8_t *bytecode, size_t length); typedef void (*BamlFreeBufferFn)(struct BamlBuffer buffer); @@ -298,9 +297,9 @@ typedef void (*BamlRegisterUnhandledSpawnErrorCallbackFn)(BamlUnhandledSpawnErro typedef struct BamlBuffer (*BamlShutdownRuntimeFn)(void); -typedef struct BamlBuffer (*BamlInitializeRuntimeFromBytecodeWithMetadataFn)(const uint8_t *bytecode, - size_t length, - const char *baml_toml); +typedef struct BamlBuffer (*BamlInitializeRuntimeFromBlobWithMetadataFn)(const uint8_t *bytecode, + size_t length, + const char *baml_toml); /** * First version of the shared BAML C API. @@ -350,7 +349,7 @@ typedef struct BamlApiV1 { * calls are serialized only while replacing the global runtime; calls * already in progress retain their previous runtime instance. */ - BamlInitializeRuntimeFromBytecodeFn initialize_runtime_from_bytecode; + BamlInitializeRuntimeFromBlobFn initialize_runtime_from_blob; /** * Release exactly one runtime-owned buffer returned through this table. * @@ -483,7 +482,7 @@ typedef struct BamlApiV1 { /** * Replace the runtime from bytecode after validating embedded generation metadata. */ - BamlInitializeRuntimeFromBytecodeWithMetadataFn initialize_runtime_from_bytecode_with_metadata; + BamlInitializeRuntimeFromBlobWithMetadataFn initialize_runtime_from_blob_with_metadata; } BamlApiV1; typedef const struct BamlApiV1 *(*BamlGetApiV1Fn)(void); @@ -515,9 +514,9 @@ BamlCffiStatus __testonly_seed_heap_handle(uint64_t slab_key, * Initialize generated bytecode after validating its embedded `baml.toml`. */ BAML_CFFI_API -struct BamlBuffer initialize_runtime_from_bytecode_with_metadata(const uint8_t *bytecode, - size_t length, - const char *baml_toml); +struct BamlBuffer initialize_runtime_from_blob_with_metadata(const uint8_t *bytecode, + size_t length, + const char *baml_toml); #ifdef __cplusplus } // extern "C" diff --git a/baml_language/sdks/swift/rust/sdkgen_swift/Cargo.toml b/baml_language/sdks/swift/rust/sdkgen_swift/Cargo.toml index 24a57d49b66..8859fb740ec 100644 --- a/baml_language/sdks/swift/rust/sdkgen_swift/Cargo.toml +++ b/baml_language/sdks/swift/rust/sdkgen_swift/Cargo.toml @@ -14,7 +14,6 @@ license = { workspace = true } baml_base = { workspace = true } baml_sdkgen_types = { workspace = true } baml_version = { workspace = true } -base64 = { workspace = true } [lib] doctest = false @@ -23,3 +22,4 @@ doctest = false workspace = true [dev-dependencies] +baml_artifact = { workspace = true } diff --git a/baml_language/sdks/swift/rust/sdkgen_swift/src/lib.rs b/baml_language/sdks/swift/rust/sdkgen_swift/src/lib.rs index b1bc37ad510..9dc58c261fd 100644 --- a/baml_language/sdks/swift/rust/sdkgen_swift/src/lib.rs +++ b/baml_language/sdks/swift/rust/sdkgen_swift/src/lib.rs @@ -37,7 +37,6 @@ use std::{ use baml_base::qualified_name::AI_STREAM_STREAM; use baml_sdkgen_types::{Class, Name, Symbol, SymbolPool, Ty, TypeAlias}; pub use baml_sdkgen_types::{NamingConvention, OutputType}; -use base64::Engine as _; use emit::{ FnKind, RenderedField, indent_lines, render_callable, render_class, render_enum, render_type_alias, sort_key, @@ -691,36 +690,30 @@ fn render_ns_enum( out } -/// The borsh bytecode payload, base64-encoded, as ONE multiline string -/// literal. Two rejected alternatives, both fatal at engine sizes: a -/// `[UInt8]` literal type-checks element-by-element, and a `"…" + "…"` -/// chunk chain builds a `+` expression whose type-check is -/// super-linear in the number of chunks (observed: 55+ minutes for a -/// multi-MB payload). A `"""…"""` literal is a single token — instant — -/// and the embedded newlines are skipped by the base64 decoder via -/// `.ignoreUnknownCharacters`. +/// The bytecode payload (base64 of an LZ4 frame, which the bridge decodes +/// natively) as ONE single-line string literal, handed to the bridge as its +/// UTF-8 bytes. Two rejected alternatives, both fatal at engine sizes: a +/// `[UInt8]` literal type-checks element-by-element, and a `"…" + "…"` chunk +/// chain builds a `+` expression whose type-check is super-linear in the +/// number of chunks (observed: 55+ minutes for a multi-MB payload). A single +/// literal is a single token — instant. fn render_inlined_baml(baml_bytecode: &[u8], embedded_baml_toml: Option<&str>) -> String { - // Fixed-width lines inside the literal for editor/diff friendliness. - const CHUNK: usize = 96; - let b64 = base64::engine::general_purpose::STANDARD.encode(baml_bytecode); let mut out = String::from( "// Generated by BAML. DO NOT EDIT.\n\ import Foundation\n\n\ - enum _BamlInlined {\n static let bytecodeBase64: String = \"\"\"\n", + enum _BamlInlined {\n", + ); + let _ = writeln!( + out, + " static let bytecodeBase64: String = \"{}\"", + baml_sdkgen_types::embedded_bytecode_base64(baml_bytecode) ); - for chunk in b64.as_bytes().chunks(CHUNK) { - out.push_str(" "); - out.push_str(std::str::from_utf8(chunk).expect("base64 is ascii")); - out.push('\n'); - } - out.push_str(" \"\"\"\n"); let manifest = embedded_baml_toml .map(|manifest| format!("Optional.some({manifest:?})")) .unwrap_or_else(|| "Optional.none".to_string()); let _ = writeln!(out, " static let embeddedBamlToml: String? = {manifest}"); out.push_str( - "\n static var bytecode: Data {\n \ - Data(base64Encoded: bytecodeBase64, options: .ignoreUnknownCharacters)!\n }\n}\n", + "\n static var bytecode: Data {\n Data(bytecodeBase64.utf8)\n }\n}\n", ); out } @@ -730,23 +723,25 @@ mod tests { use super::*; #[test] - fn bytecode_payload_round_trips_via_base64() { + fn bytecode_payload_round_trips_through_embedded_encoding() { let pool = SymbolPool::default(); let bytecode = vec![0u8, 1, 2, 250, 251, 252]; let files = to_source_code_with_bytecode(&pool, &bytecode, NamingConvention::PreserveCase); let inlined = &files[&PathBuf::from("_InlinedBaml.swift")]; - // Collect the bare base64 lines between the `"""` delimiters. - let b64: String = inlined + let encoded = inlined .lines() - .skip_while(|l| !l.contains("\"\"\"")) - .skip(1) - .take_while(|l| !l.contains("\"\"\"")) - .map(str::trim) - .collect(); - let decoded = base64::engine::general_purpose::STANDARD - .decode(b64) - .expect("valid base64"); + .find_map(|l| { + l.trim() + .strip_prefix("static let bytecodeBase64: String = \"") + }) + .and_then(|l| l.strip_suffix('"')) + .expect("single-line bytecode literal"); + let decoded = baml_artifact::decode_embedded( + baml_artifact::ArtifactKind::Program, + encoded.as_bytes(), + ) + .expect("valid embedded bytecode"); assert_eq!(decoded, bytecode); assert!(files[&PathBuf::from("BamlRoot.swift")].contains("public enum Baml")); diff --git a/baml_language/sdks/typescript/bridge_typescript/dist/index.d.ts b/baml_language/sdks/typescript/bridge_typescript/dist/index.d.ts index 230e939ad14..d7a7f72d0c8 100644 --- a/baml_language/sdks/typescript/bridge_typescript/dist/index.d.ts +++ b/baml_language/sdks/typescript/bridge_typescript/dist/index.d.ts @@ -31,7 +31,7 @@ export declare function initializeRuntime(srcDir: string, files: Record): BamlRuntime - /** Initialize the process-global runtime from precompiled BAML bytecode. */ - static initializeRuntimeFromBytecode(bytecode: Buffer, embeddedBamlToml?: string | undefined | null): BamlRuntime + /** + * Initialize the process-global runtime from precompiled BAML bytecode: + * a raw artifact, or the encoded string generated SDKs embed (decoded + * natively, never in JavaScript). + */ + static initializeRuntimeFromBlob(bytecode: string | Buffer, embeddedBamlToml?: string | undefined | null): BamlRuntime /** Call a BAML function synchronously (blocking). */ callFunctionSync(argsProto: Buffer, ctx?: HostSpanManager | undefined | null): Buffer /** Call a BAML function asynchronously. */ diff --git a/baml_language/sdks/typescript/bridge_typescript/src/runtime.rs b/baml_language/sdks/typescript/bridge_typescript/src/runtime.rs index ef89d372fdc..051fe3116fd 100644 --- a/baml_language/sdks/typescript/bridge_typescript/src/runtime.rs +++ b/baml_language/sdks/typescript/bridge_typescript/src/runtime.rs @@ -35,16 +35,19 @@ impl BamlRuntime { } } - /// Initialize the process-global runtime from precompiled BAML bytecode. - #[napi(factory, js_name = "initializeRuntimeFromBytecode")] - pub fn initialize_runtime_from_bytecode( - bytecode: Buffer, + /// Initialize the process-global runtime from precompiled BAML bytecode: + /// a raw artifact, or the encoded string generated SDKs embed (decoded + /// natively, never in JavaScript). + #[napi(factory, js_name = "initializeRuntimeFromBlob")] + pub fn initialize_runtime_from_blob( + bytecode: Either, embedded_baml_toml: Option, ) -> napi::Result { - match bridge_cffi::initialize_runtime_from_bytecode( - bytecode.as_ref(), - embedded_baml_toml.as_deref(), - ) { + let bytecode: &[u8] = match &bytecode { + Either::A(encoded) => encoded.as_bytes(), + Either::B(bytes) => bytes.as_ref(), + }; + match bridge_cffi::initialize_runtime_from_blob(bytecode, embedded_baml_toml.as_deref()) { Ok(_bex) => Ok(BamlRuntime {}), Err(e) => Err(bridge_error_to_napi(e)), } diff --git a/baml_language/sdks/typescript/bridge_typescript/typescript_src/index.ts b/baml_language/sdks/typescript/bridge_typescript/typescript_src/index.ts index 2f53cdce8c8..7973ca8e339 100644 --- a/baml_language/sdks/typescript/bridge_typescript/typescript_src/index.ts +++ b/baml_language/sdks/typescript/bridge_typescript/typescript_src/index.ts @@ -58,8 +58,13 @@ export function initializeRuntime(srcDir: string, files: Record) * Free-function runtime initializer used by generated `baml_sdk/index.ts` when * codegen embeds precompiled BAML bytecode. */ -export function initializeRuntimeFromBytecode(bytecode: Buffer | Uint8Array, embeddedBamlToml?: string): void { - BamlRuntime.initializeRuntimeFromBytecode(Buffer.from(bytecode), embeddedBamlToml); +export function initializeRuntimeFromBlob(bytecode: string | Buffer | Uint8Array, embeddedBamlToml?: string): void { + // Generated SDKs pass their embedded bytecode string through untouched; + // the native bridge decodes it. + BamlRuntime.initializeRuntimeFromBlob( + typeof bytecode === "string" ? bytecode : Buffer.from(bytecode), + embeddedBamlToml, + ); } export { BamlAbortError, diff --git a/baml_language/sdks/typescript/bridge_typescript/typescript_src/native.d.ts b/baml_language/sdks/typescript/bridge_typescript/typescript_src/native.d.ts index 6d64d13e648..c69f7e6f1aa 100644 --- a/baml_language/sdks/typescript/bridge_typescript/typescript_src/native.d.ts +++ b/baml_language/sdks/typescript/bridge_typescript/typescript_src/native.d.ts @@ -100,8 +100,12 @@ export declare class BamlRuntime { * the `initializeRuntime(...)` import the spec docs use. */ static initializeRuntime(rootPath: string, files: Record): BamlRuntime - /** Initialize the process-global runtime from precompiled BAML bytecode. */ - static initializeRuntimeFromBytecode(bytecode: Buffer, embeddedBamlToml?: string | undefined | null): BamlRuntime + /** + * Initialize the process-global runtime from precompiled BAML bytecode: + * a raw artifact, or the encoded string generated SDKs embed (decoded + * natively, never in JavaScript). + */ + static initializeRuntimeFromBlob(bytecode: string | Buffer, embeddedBamlToml?: string | undefined | null): BamlRuntime /** Call a BAML function synchronously (blocking). */ callFunctionSync(argsProto: Buffer, ctx?: HostSpanManager | undefined | null): Buffer /** Call a BAML function asynchronously. */ diff --git a/baml_language/sdks/typescript/bridge_typescript_web/src/runtime.rs b/baml_language/sdks/typescript/bridge_typescript_web/src/runtime.rs index d32f546ef12..521f0071ebc 100644 --- a/baml_language/sdks/typescript/bridge_typescript_web/src/runtime.rs +++ b/baml_language/sdks/typescript/bridge_typescript_web/src/runtime.rs @@ -4,16 +4,23 @@ use std::collections::HashMap; use wasm_bindgen::prelude::*; +/// Stage a runtime from precompiled BAML bytecode: a raw artifact +/// (`Uint8Array`), or the encoded string generated SDKs embed (decoded +/// natively, never in JavaScript). #[wasm_bindgen(js_name = stageRuntimeBytecode)] #[allow(clippy::needless_pass_by_value)] // wasm-bindgen requires an owned optional string at the JS boundary. pub fn stage_runtime_bytecode( - bytecode: &[u8], + bytecode: JsValue, embedded_baml_toml: Option, ) -> Result<(), JsValue> { + let bytecode = match bytecode.as_string() { + Some(encoded) => encoded.into_bytes(), + None => js_sys::Uint8Array::new(&bytecode).to_vec(), + }; let sys_ops = sys_wasm::build() .map_err(|error| crate::errors::setup_error(crate::errors::CLIENT, error))?; - bridge_cffi::initialize_runtime_from_bytecode_with_sys_ops( - bytecode, + bridge_cffi::initialize_runtime_from_blob_with_sys_ops( + &bytecode, embedded_baml_toml.as_deref(), sys_ops, ) diff --git a/baml_language/sdks/typescript/bridge_typescript_web/tests/runtime_errors.test.ts b/baml_language/sdks/typescript/bridge_typescript_web/tests/runtime_errors.test.ts index 6077fd8d499..8c2b067b19d 100644 --- a/baml_language/sdks/typescript/bridge_typescript_web/tests/runtime_errors.test.ts +++ b/baml_language/sdks/typescript/bridge_typescript_web/tests/runtime_errors.test.ts @@ -63,7 +63,7 @@ describe("Web runtime and setup errors", () => { expect((rawError as Error & { code?: string }).code).toBe("invalid_argument"); expect(() => BamlRuntime.initializeRuntime(".", { "": SOURCE_A })).toThrow(BamlInvalidArgumentError); - expect(() => BamlRuntime.initializeRuntimeFromBytecode(new Uint8Array([1, 2, 3]))).toThrow(/Failed to deserialize BAML bytecode/); + expect(() => BamlRuntime.initializeRuntimeFromBlob(new Uint8Array([1, 2, 3]))).toThrow(/Failed to deserialize BAML bytecode/); }); it("initializes from sources and replaces the singleton atomically", async () => { diff --git a/baml_language/sdks/typescript/bridge_typescript_web/typescript_src/index.ts b/baml_language/sdks/typescript/bridge_typescript_web/typescript_src/index.ts index 1fee901e4e0..0bab3fad16c 100644 --- a/baml_language/sdks/typescript/bridge_typescript_web/typescript_src/index.ts +++ b/baml_language/sdks/typescript/bridge_typescript_web/typescript_src/index.ts @@ -18,8 +18,8 @@ export { BamlAbortError, BamlCancelledError, BamlClientError, BamlError, BamlInv export { BamlPrompt, decodeCallResult, encodeCallArgs } from "./shared/proto.js"; export type { BamlPromptCallOptions, BamlPromptMessage } from "./shared/proto.js"; -export function initializeRuntimeFromBytecode(bytecode: Uint8Array, embeddedBamlToml?: string): void { - BamlRuntime.initializeRuntimeFromBytecode(bytecode, embeddedBamlToml); +export function initializeRuntimeFromBlob(bytecode: string | Uint8Array, embeddedBamlToml?: string): void { + BamlRuntime.initializeRuntimeFromBlob(bytecode, embeddedBamlToml); } export function initializeRuntime(srcDir: string, files: Record): void { diff --git a/baml_language/sdks/typescript/bridge_typescript_web/typescript_src/native.ts b/baml_language/sdks/typescript/bridge_typescript_web/typescript_src/native.ts index 6677e3ac0bc..164ea4221ce 100644 --- a/baml_language/sdks/typescript/bridge_typescript_web/typescript_src/native.ts +++ b/baml_language/sdks/typescript/bridge_typescript_web/typescript_src/native.ts @@ -377,7 +377,7 @@ export class HostSpanManager { } export class BamlRuntime { - static initializeRuntimeFromBytecode(bytecode: Uint8Array, embeddedBamlToml?: string): BamlRuntime { + static initializeRuntimeFromBlob(bytecode: string | Uint8Array, embeddedBamlToml?: string): BamlRuntime { ensureWebSysopsConfigured(); try { stageRuntimeBytecode(bytecode, embeddedBamlToml); diff --git a/baml_language/sdks/typescript/bridge_typescript_web/typescript_src/wasm/bridge_web_core.d.ts b/baml_language/sdks/typescript/bridge_typescript_web/typescript_src/wasm/bridge_web_core.d.ts index 3ce75312222..a0eff38ada4 100644 --- a/baml_language/sdks/typescript/bridge_typescript_web/typescript_src/wasm/bridge_web_core.d.ts +++ b/baml_language/sdks/typescript/bridge_typescript_web/typescript_src/wasm/bridge_web_core.d.ts @@ -125,7 +125,12 @@ export function seedFunctionRefHandle(global_index: number): bigint; export function seedGenericMediaHandle(): bigint; -export function stageRuntimeBytecode(bytecode: Uint8Array, embedded_baml_toml?: string | null): void; +/** + * Stage a runtime from precompiled BAML bytecode: a raw artifact + * (`Uint8Array`), or the encoded string generated SDKs embed (decoded + * natively, never in JavaScript). + */ +export function stageRuntimeBytecode(bytecode: any, embedded_baml_toml?: string | null): void; export function stageRuntimeSources(root_path: string, files: any): void; @@ -154,7 +159,7 @@ export interface InitOutput { readonly mediaUrl: (a: bigint, b: number) => [number, number, number, number]; readonly callFunction: (a: number, b: number) => any; readonly callFunctionSync: (a: number, b: number) => [number, number]; - readonly stageRuntimeBytecode: (a: number, b: number, c: number, d: number) => [number, number]; + readonly stageRuntimeBytecode: (a: any, b: number, c: number) => [number, number]; readonly stageRuntimeSources: (a: number, b: number, c: any) => [number, number]; readonly _testHandleTableEntryCount: () => [number, number, number]; readonly cloneHandle: (a: bigint) => [bigint, number, number]; diff --git a/baml_language/sdks/typescript/sdkgen_typescript_shared/Cargo.toml b/baml_language/sdks/typescript/sdkgen_typescript_shared/Cargo.toml index 540692f9d2d..50670bdbcd3 100644 --- a/baml_language/sdks/typescript/sdkgen_typescript_shared/Cargo.toml +++ b/baml_language/sdks/typescript/sdkgen_typescript_shared/Cargo.toml @@ -13,10 +13,12 @@ license = { workspace = true } [dependencies] baml_base = { workspace = true } baml_sdkgen_types = { workspace = true } -base64 = { workspace = true } [lib] doctest = false [lints] workspace = true + +[dev-dependencies] +baml_artifact = { workspace = true } diff --git a/baml_language/sdks/typescript/sdkgen_typescript_shared/src/leaf.rs b/baml_language/sdks/typescript/sdkgen_typescript_shared/src/leaf.rs index ecbcc61f773..44c99fc74ea 100644 --- a/baml_language/sdks/typescript/sdkgen_typescript_shared/src/leaf.rs +++ b/baml_language/sdks/typescript/sdkgen_typescript_shared/src/leaf.rs @@ -599,16 +599,14 @@ fn write_preamble_ts( if is_root { out.push_str(&runtime_import_line( state, - &["initializeRuntimeFromBytecode", "setTypeMap"], + &["initializeRuntimeFromBlob", "setTypeMap"], runtime_package, )); out.push_str("import * as _inlinedbaml from \"./_inlinedbaml.js\";\n"); out.push_str("import { _TYPE_MAP } from \"./_typemap.js\";\n"); out.push_str(&cross_leaf_imports(state, &body.leaf)); out.push('\n'); - out.push_str( - "initializeRuntimeFromBytecode(_inlinedbaml.BYTECODE, _inlinedbaml.BAML_TOML);\n", - ); + out.push_str("initializeRuntimeFromBlob(_inlinedbaml.BYTECODE, _inlinedbaml.BAML_TOML);\n"); out.push_str("setTypeMap(_TYPE_MAP);\n"); if !kids.is_empty() { out.push('\n'); @@ -1373,12 +1371,14 @@ mod tests { let mut kids = BTreeSet::new(); kids.insert("lorem".to_string()); let ts = render_index_ts(&b, &kids, true, TEST_RUNTIME_PACKAGE); - assert!(ts.contains( - "initializeRuntimeFromBytecode(_inlinedbaml.BYTECODE, _inlinedbaml.BAML_TOML);" - )); + assert!( + ts.contains( + "initializeRuntimeFromBlob(_inlinedbaml.BYTECODE, _inlinedbaml.BAML_TOML);" + ) + ); assert!(ts.contains("setTypeMap(_TYPE_MAP);")); assert!(ts.contains("export * as lorem from \"./lorem/index.js\";")); assert!(ts.contains("export const make_foo = defineFunction(")); - assert!(ts.contains("import { defineFunction, initializeRuntimeFromBytecode, setTypeMap, type BamlCallContext } from \"@boundaryml/baml-bridge\";")); + assert!(ts.contains("import { defineFunction, initializeRuntimeFromBlob, setTypeMap, type BamlCallContext } from \"@boundaryml/baml-bridge\";")); } } diff --git a/baml_language/sdks/typescript/sdkgen_typescript_shared/src/lib.rs b/baml_language/sdks/typescript/sdkgen_typescript_shared/src/lib.rs index 8c1ab888f00..434ae644a69 100644 --- a/baml_language/sdks/typescript/sdkgen_typescript_shared/src/lib.rs +++ b/baml_language/sdks/typescript/sdkgen_typescript_shared/src/lib.rs @@ -32,7 +32,6 @@ use std::{ use baml_sdkgen_types::{Name, SymbolPool, public_interface_tokens}; pub use baml_sdkgen_types::{NamingConvention, OutputType}; -use base64::{Engine as _, engine::general_purpose::STANDARD as BASE64_STANDARD}; use crate::{ emit::{build_emitted, typemap_file::render_typemap_module}, @@ -221,43 +220,21 @@ fn init_ts_path(dir: &[String]) -> PathBuf { path } -/// Base64 characters per emitted source line. Long enough that the line count -/// stays small, short enough that the file is still diffable. -const BYTECODE_BASE64_LINE: usize = 4096; - fn render_inlinedbaml(bytecode: &[u8], embedded_baml_toml: Option<&str>) -> String { - // The bytecode ships as base64 rather than a decimal `new Uint8Array([...])` - // literal. That literal cost up to 5 source characters per byte (~3.8 on - // average, measured); base64 costs 1.33, which shrinks this module by ~65%. + // The bytecode ships as one line of encoded bytecode (see + // `baml_sdkgen_types::embedded_bytecode_base64`) rather than a decimal + // `new Uint8Array([...])` literal, which cost up to 5 source characters + // per byte (~3.8 on average, measured). BYTECODE is passed to + // `initializeRuntimeFromBlob` as a string, and the bridge decodes it + // natively. // // Size matters here beyond disk: bundlers and the Cloudflare Workers test // pool move this module around as a unit, and workerd caps a single // WebSocket message at 1 MiB — a large enough program made the pool drop // the connection with a 1009 "Message is too large". - // - // `atob` is the one decoder available everywhere this runs: browsers, - // Workers, and Node (global since 16). BYTECODE stays a Uint8Array, so - // `initializeRuntimeFromBytecode` is unaffected. - let encoded = BASE64_STANDARD.encode(bytecode); - let mut out = String::with_capacity(encoded.len() + encoded.len() / 256 + 512); - out.push_str("const BYTECODE_BASE64 = [\n"); - for chunk in encoded.as_bytes().chunks(BYTECODE_BASE64_LINE) { - out.push_str(" \""); - // base64 output is ASCII, so the chunk is always valid UTF-8 and needs - // no escaping. - out.push_str(std::str::from_utf8(chunk).expect("base64 output is ASCII")); - out.push_str("\",\n"); - } - out.push_str("].join(\"\");\n\n"); - out.push_str("function decodeBytecode(encoded: string): Uint8Array {\n"); - out.push_str(" const binary = atob(encoded);\n"); - out.push_str(" const bytes = new Uint8Array(binary.length);\n"); - out.push_str(" for (let i = 0; i < binary.length; i += 1) {\n"); - out.push_str(" bytes[i] = binary.charCodeAt(i);\n"); - out.push_str(" }\n"); - out.push_str(" return bytes;\n"); - out.push_str("}\n\n"); - out.push_str("export const BYTECODE = decodeBytecode(BYTECODE_BASE64);\n"); + let encoded = baml_sdkgen_types::embedded_bytecode_base64(bytecode); + let mut out = String::with_capacity(encoded.len() + 512); + let _ = writeln!(out, "export const BYTECODE = \"{encoded}\";"); let manifest = embedded_baml_toml .map(ts_string) .unwrap_or_else(|| "undefined".to_string()); @@ -365,9 +342,11 @@ mod tests { assert!(!out.keys().any(|p| p.to_string_lossy().ends_with(".d.ts"))); let root = &out[&PathBuf::from("index.ts")]; assert!(root.contains(HEADER_LEN_MARKER)); - assert!(root.contains( - "initializeRuntimeFromBytecode(_inlinedbaml.BYTECODE, _inlinedbaml.BAML_TOML);" - )); + assert!( + root.contains( + "initializeRuntimeFromBlob(_inlinedbaml.BYTECODE, _inlinedbaml.BAML_TOML);" + ) + ); assert!(root.contains("setTypeMap(_TYPE_MAP);")); assert!(root.contains("export * as baml from \"./baml/index.js\";")); assert!(!root.contains("export const b")); @@ -469,25 +448,23 @@ mod tests { GeneratorConfig::new("@boundaryml/baml-bridge"), ); let inlined = &out[&PathBuf::from("_inlinedbaml.ts")]; - assert!(inlined.contains("export const BYTECODE = decodeBytecode(BYTECODE_BASE64);")); - // The exact base64 for those six bytes, so a change to the encoding + // The exact payload for those six bytes, so a change to the encoding // fails here rather than at runtime in a generated SDK. assert!( inlined.contains(&format!( - " \"{}\",\n", - BASE64_STANDARD.encode([0, 1, 2, 253, 254, 255]) + "export const BYTECODE = \"{}\";\n", + baml_sdkgen_types::embedded_bytecode_base64(&[0, 1, 2, 253, 254, 255]) )), "{inlined}" ); - // The decoder must be self-contained: no import, and byte-exact. - assert!(inlined.contains("const binary = atob(encoded);")); + // Decoding is the bridge's job, never the generated module's. + assert!(!inlined.contains("atob")); } #[test] - fn inlinedbaml_base64_round_trips_and_wraps_long_bytecode() { - // Enough bytes to span several emitted lines, with every byte value - // represented so a mangled alphabet or a bad chunk boundary shows up. - let bytecode: Vec = (0..(BYTECODE_BASE64_LINE * 3)) + fn inlinedbaml_payload_is_one_line_that_round_trips() { + // Every byte value represented so a mangled alphabet shows up. + let bytecode: Vec = (0..(3 * 4096)) .map(|i| u8::try_from(i % 256).expect("i % 256 fits in u8")) .collect(); let out = to_source_code( @@ -498,17 +475,23 @@ mod tests { ); let inlined = &out[&PathBuf::from("_inlinedbaml.ts")]; - let joined: String = inlined + let payloads: Vec<&str> = inlined .lines() - .filter(|l| l.starts_with(" \"") && l.ends_with("\",")) - .map(|l| &l[3..l.len() - 2]) + .filter_map(|l| l.strip_prefix("export const BYTECODE = \"")) + .map(|l| { + l.strip_suffix("\";") + .expect("payload line ends the literal") + }) .collect(); + assert_eq!(payloads.len(), 1, "{inlined}"); assert_eq!( - BASE64_STANDARD - .decode(joined) - .expect("emitted base64 decodes"), + baml_artifact::decode_embedded( + baml_artifact::ArtifactKind::Program, + payloads[0].as_bytes() + ) + .expect("emitted payload decodes"), bytecode, - "emitted base64 must round-trip to the original bytecode" + "emitted payload must round-trip to the original bytecode" ); } diff --git a/release/bridge-cffi-public-exports.txt b/release/bridge-cffi-public-exports.txt index 93613172f44..7d62377f63c 100644 --- a/release/bridge-cffi-public-exports.txt +++ b/release/bridge-cffi-public-exports.txt @@ -18,8 +18,8 @@ create_baml_runtime destroy_baml_runtime flush_events free_buffer -initialize_runtime_from_bytecode -initialize_runtime_from_bytecode_with_metadata +initialize_runtime_from_blob +initialize_runtime_from_blob_with_metadata invoke_runtime_cli new_function_call register_callback