From ebb2509d0a5cf87e6a66e6993c584a9adb35bcb7 Mon Sep 17 00:00:00 2001 From: Jhin Lee Date: Mon, 8 Jun 2026 11:14:04 -0400 Subject: [PATCH 1/2] Rename StreamProxy helper to LiteRtLmBridge --- README.md | 31 ++-- docs/platform_strategy.md | 15 +- native/bridge/BUILD.bazel | 42 +++++ .../litert_lm_bridge.c} | 14 +- tools/build_upstream_runtime.py | 156 ++++-------------- tools/package_ios_runtime.py | 12 +- 6 files changed, 113 insertions(+), 157 deletions(-) create mode 100644 native/bridge/BUILD.bazel rename native/{stream_proxy/stream_proxy.c => bridge/litert_lm_bridge.c} (77%) diff --git a/README.md b/README.md index 4db3010..7244420 100644 --- a/README.md +++ b/README.md @@ -13,8 +13,8 @@ Responsibilities: - Track upstream `google-ai-edge/LiteRT-LM` releases. - Fetch or build native LiteRT-LM libraries per platform. - Preserve upstream LiteRT-LM's C runtime ABI as the FFI boundary. -- Embed the small StreamProxy callback-copy helper into runtime libraries used - by asynchronous FFI clients. +- Embed the small LiteRtLmBridge callback helper into runtime libraries used by + asynchronous FFI clients. - Package web assets around official LiteRT-LM/LiteRT.js distribution paths. - Publish Apple Swift Package Manager XCFramework zip assets built from the same bridge runtimes as the native release payload. @@ -53,11 +53,13 @@ GPU/NPU validation; web should use JavaScript interop instead of FFI. - `web/`: web package scaffold for JS/Wasm/WebGPU/WebNN integration. - `tools/fetch_upstream.py`: resolves and downloads upstream release assets. - `tools/build_upstream_runtime.py`: builds upstream LiteRT-LM C runtime - libraries from tagged source with Bazel/Bazelisk, embeds StreamProxy symbols - into source-built runtime libraries, and stages them for release. + libraries from tagged source with Bazel/Bazelisk through the repo-owned + `native/bridge` Bazel package, embeds LiteRtLmBridge symbols into + source-built runtime libraries without patching upstream source files, and + stages them for release. - `tools/package_ios_runtime.py`: extracts official upstream `CLiteRTLM.xcframework` slices and stages `LiteRtLm.framework` plus - `CLiteRTLM.framework`, with the wrapper embedding StreamProxy symbols and + `CLiteRTLM.framework`, with the wrapper embedding LiteRtLmBridge symbols and re-exporting `CLiteRTLM`. - `tools/package_apple_xcframeworks.py`: packages iOS framework wrappers, a macOS `LiteRtLm.framework` wrapper around the source-built runtime, and macOS @@ -93,11 +95,11 @@ python3 tools/validate_artifacts.py - `Validate`: validates package metadata and checks Python/web tooling on pushes and pull requests. - `Native Build & Release`: manually packages a selected upstream LiteRT-LM tag. - It builds upstream C runtime libraries with embedded StreamProxy symbols for + It builds upstream C runtime libraries with embedded LiteRtLmBridge symbols for Android arm64/x64, macOS arm64/x64, Linux x64/arm64, and Windows x64, copies upstream `prebuilt/` companion libraries for Android, Apple, Linux, and Windows, converts official upstream `CLiteRTLM.xcframework` slices into iOS - framework runtime archives with an embedded-StreamProxy wrapper, packages + framework runtime archives with an embedded LiteRtLmBridge wrapper, packages Apple SPM XCFramework zips from the same runtime payloads, includes the official upstream release assets, then publishes a GitHub release with `manifest.json` and `SHA256SUMS`. @@ -115,14 +117,18 @@ package hooks and Swift Package manifests. When moving to a new LiteRT-LM tag: 2. Verify the release contains runtime archives, official upstream assets, Apple SPM XCFramework zips, `manifest.json`, and `SHA256SUMS`. 3. Update downstream `llamadart` hook pins, SPM URLs, and SPM checksums - together so native-assets and SPM consumers use the same StreamProxy-enabled + together so native-assets and SPM consumers use the same bridge-enabled runtime build. The release workflow uses upstream's public C API (`c/engine.h`) as the production FFI boundary. Downstream loaders should bind directly to the runtime -library for the selected platform. StreamProxy symbols are embedded into the -same runtime library surface; no standalone StreamProxy runtime artifact is part -of the release contract. +library for the selected platform. Source-built native runtimes are assembled +from a repo-owned Bazel package selected ahead of the upstream source tree with +`--package_path`, so the workflow does not edit upstream LiteRT-LM files. +LiteRtLmBridge symbols are embedded into the same runtime library surface; no +standalone bridge runtime artifact is part of the release contract. The bridge +currently exports the `stream_proxy_*` compatibility symbols used by +asynchronous callback loaders. Apple SPM consumers should depend on the release's direct `litert-lm-native-apple-*-xcframework-.zip` assets. The `LiteRtLm` @@ -142,4 +148,5 @@ architecture, runtime kind (`native` or `web`), and accelerator metadata, then verify checksums before bundling or loading the files. Upstream LiteRT-LM's native C ABI is the compatibility boundary. This repository -does not add a second wrapper ABI unless a future upstream change requires it. +does not add a second model wrapper ABI unless a future upstream change requires +it; bridge helpers remain narrow FFI utilities around that runtime surface. diff --git a/docs/platform_strategy.md b/docs/platform_strategy.md index b927d48..be5f8b6 100644 --- a/docs/platform_strategy.md +++ b/docs/platform_strategy.md @@ -9,21 +9,24 @@ avoids publishing a second model wrapper ABI that does not add behavior. The release automation publishes these runtime artifact groups: - upstream LiteRT-LM C runtime libraries built from the tagged source archive, - with StreamProxy symbols embedded for downstream FFI streaming on source-built - platforms + with LiteRtLmBridge symbols embedded by the repo-owned `native/bridge` Bazel + package for downstream FFI streaming on source-built platforms - upstream `prebuilt/` companion libraries copied from the tagged source archive - official upstream release assets, including the iOS `CLiteRTLM.xcframework` archive when Google publishes it - iOS framework-style runtime wrappers derived from `CLiteRTLM.xcframework`, - with StreamProxy symbols embedded in `LiteRtLm.framework/LiteRtLm` and + with LiteRtLmBridge symbols embedded in `LiteRtLm.framework/LiteRtLm` and upstream symbols re-exported from `CLiteRTLM.framework/CLiteRTLM` - Apple Swift Package Manager XCFramework zips produced from the same iOS wrappers, macOS source-built runtime, and macOS companion dylibs used by the native-assets payloads -The upstream C runtime is the production FFI target for downstream packages. If -we later need a repo-owned compatibility layer, it should be introduced as a real -wrapper over upstream LiteRT-LM rather than a scaffold library. +The upstream C runtime is the production FFI target for downstream packages. +LiteRtLmBridge is limited to narrow FFI helpers around that runtime surface. It +currently exports the `stream_proxy_*` compatibility symbols used by downstream +streaming callbacks. Source-built native runtimes use Bazel `--package_path` to +build the repo-owned bridge package alongside upstream LiteRT-LM, without +patching upstream source files. SPM artifacts are intentionally split by binary target. `LiteRtLm` carries the primary iOS runtime/wrapper and a macOS framework wrapper around the diff --git a/native/bridge/BUILD.bazel b/native/bridge/BUILD.bazel new file mode 100644 index 0000000..71b7adc --- /dev/null +++ b/native/bridge/BUILD.bazel @@ -0,0 +1,42 @@ +package(default_visibility = ["//visibility:public"]) + +cc_library( + name = "litert_lm_bridge", + srcs = ["litert_lm_bridge.c"], + alwayslink = True, + linkopts = select({ + "@platforms//os:android": ["-ldl"], + "@platforms//os:linux": ["-ldl"], + "//conditions:default": [], + }), +) + +cc_binary( + name = "libLiteRtLm.so", + linkshared = True, + deps = [ + ":litert_lm_bridge", + "//c:engine", + "//schema/capabilities:capabilities_c", + ], +) + +cc_binary( + name = "libLiteRtLm.dylib", + linkshared = True, + deps = [ + ":litert_lm_bridge", + "//c:engine", + "//schema/capabilities:capabilities_c", + ], +) + +cc_binary( + name = "LiteRtLm.dll", + linkshared = True, + deps = [ + ":litert_lm_bridge", + "//c:engine", + "//schema/capabilities:capabilities_c", + ], +) diff --git a/native/stream_proxy/stream_proxy.c b/native/bridge/litert_lm_bridge.c similarity index 77% rename from native/stream_proxy/stream_proxy.c rename to native/bridge/litert_lm_bridge.c index bf8304a..cf3bff5 100644 --- a/native/stream_proxy/stream_proxy.c +++ b/native/bridge/litert_lm_bridge.c @@ -3,13 +3,15 @@ #include #include +// LiteRtLmBridge hosts downstream FFI bridge helpers. The stream_proxy_* exports +// are the current streaming callback ABI and stay stable for compatibility. #if defined(_WIN32) #define WIN32_LEAN_AND_MEAN #include -#define STREAM_PROXY_EXPORT __declspec(dllexport) +#define LITERT_LM_BRIDGE_EXPORT __declspec(dllexport) #else #include -#define STREAM_PROXY_EXPORT __attribute__((visibility("default"))) +#define LITERT_LM_BRIDGE_EXPORT __attribute__((visibility("default"))) #endif typedef void (*stream_proxy_callback_t)( @@ -57,7 +59,7 @@ static void stream_proxy_forward( error_copy); } -STREAM_PROXY_EXPORT void *stream_proxy_load_global(const char *path) { +LITERT_LM_BRIDGE_EXPORT void *stream_proxy_load_global(const char *path) { if (path == NULL) { return NULL; } @@ -69,7 +71,7 @@ STREAM_PROXY_EXPORT void *stream_proxy_load_global(const char *path) { #endif } -STREAM_PROXY_EXPORT void *stream_proxy_create( +LITERT_LM_BRIDGE_EXPORT void *stream_proxy_create( stream_proxy_callback_t dart_callback, void *dart_data, stream_proxy_callback_t *out_proxy_callback) { @@ -89,10 +91,10 @@ STREAM_PROXY_EXPORT void *stream_proxy_create( return context; } -STREAM_PROXY_EXPORT void stream_proxy_delete(void *callback_data) { +LITERT_LM_BRIDGE_EXPORT void stream_proxy_delete(void *callback_data) { free(callback_data); } -STREAM_PROXY_EXPORT void stream_proxy_free_string(char *value) { +LITERT_LM_BRIDGE_EXPORT void stream_proxy_free_string(char *value) { free(value); } diff --git a/tools/build_upstream_runtime.py b/tools/build_upstream_runtime.py index 9a5adae..134a921 100644 --- a/tools/build_upstream_runtime.py +++ b/tools/build_upstream_runtime.py @@ -14,7 +14,7 @@ REPO_ROOT = Path(__file__).resolve().parents[1] BIN_DIR = REPO_ROOT / "bin" -STREAM_PROXY_SOURCE = REPO_ROOT / "native" / "stream_proxy" / "stream_proxy.c" +BRIDGE_PACKAGE_ROOT = REPO_ROOT / "native" UPSTREAM_REPO = "google-ai-edge/LiteRT-LM" MACOS_MINIMUM_OS = "14.0" UPSTREAM_ARCHIVE_URL = ( @@ -23,57 +23,57 @@ RUNTIME_TARGETS = { ("android", "arm64"): { - "bazel_target": "//c:libLiteRtLm.so", + "bazel_target": "//bridge:libLiteRtLm.so", "bazel_config": "android_arm64", "bazel_options": [ "--linkopt=-Wl,-z,max-page-size=16384", "--linkopt=-Wl,-z,common-page-size=16384", ], - "output": "bazel-bin/c/libLiteRtLm.so", + "output": "bazel-bin/bridge/libLiteRtLm.so", "library": "libLiteRtLm.so", }, ("android", "x64"): { - "bazel_target": "//c:libLiteRtLm.so", + "bazel_target": "//bridge:libLiteRtLm.so", "bazel_config": "android_x86_64", "bazel_options": [ "--linkopt=-Wl,-z,max-page-size=16384", "--linkopt=-Wl,-z,common-page-size=16384", ], - "output": "bazel-bin/c/libLiteRtLm.so", + "output": "bazel-bin/bridge/libLiteRtLm.so", "library": "libLiteRtLm.so", }, ("linux", "arm64"): { - "bazel_target": "//c:libLiteRtLm.so", + "bazel_target": "//bridge:libLiteRtLm.so", "bazel_configs": ["linux", "linux_arm64"], - "output": "bazel-bin/c/libLiteRtLm.so", + "output": "bazel-bin/bridge/libLiteRtLm.so", "library": "libLiteRtLm.so", }, ("linux", "x64"): { - "bazel_target": "//c:libLiteRtLm.so", + "bazel_target": "//bridge:libLiteRtLm.so", "bazel_config": "linux", - "output": "bazel-bin/c/libLiteRtLm.so", + "output": "bazel-bin/bridge/libLiteRtLm.so", "library": "libLiteRtLm.so", }, ("macos", "arm64"): { - "bazel_target": "//c:libLiteRtLm.dylib", + "bazel_target": "//bridge:libLiteRtLm.dylib", "bazel_configs": ["macos", "macos_arm64"], - "output": "bazel-bin/c/libLiteRtLm.dylib", + "output": "bazel-bin/bridge/libLiteRtLm.dylib", "library": "libLiteRtLm.dylib", }, ("macos", "x64"): { - "bazel_target": "//c:libLiteRtLm.dylib", + "bazel_target": "//bridge:libLiteRtLm.dylib", "bazel_config": "macos", "bazel_options": [ "--cpu=darwin_x86_64", "--platforms=@build_bazel_apple_support//platforms:darwin_x86_64", ], - "output": "bazel-bin/c/libLiteRtLm.dylib", + "output": "bazel-bin/bridge/libLiteRtLm.dylib", "library": "libLiteRtLm.dylib", }, ("windows", "x64"): { - "bazel_target": "//c:LiteRtLm.dll", + "bazel_target": "//bridge:LiteRtLm.dll", "bazel_config": "windows", - "output": "bazel-bin/c/LiteRtLm.dll", + "output": "bazel-bin/bridge/LiteRtLm.dll", "library": "LiteRtLm.dll", }, } @@ -85,59 +85,13 @@ b"litert_lm_conversation_send_message_stream", ] -REQUIRED_STREAM_PROXY_SYMBOLS = [ +REQUIRED_BRIDGE_SYMBOLS = [ b"stream_proxy_load_global", b"stream_proxy_create", b"stream_proxy_delete", b"stream_proxy_free_string", ] -SHARED_TARGETS = """ - -# Added by litert-lm-native packaging. Upstream publishes the C API as a static -# cc_library for most platforms; Dart/Flutter FFI needs a loadable library. -cc_library( - name = "stream_proxy", - srcs = ["stream_proxy.c"], - alwayslink = True, - linkopts = select({ - "@platforms//os:android": ["-ldl"], - "@platforms//os:linux": ["-ldl"], - "//conditions:default": [], - }), -) - -cc_binary( - name = "libLiteRtLm.so", - linkshared = True, - deps = [ - ":engine", - ":stream_proxy", - "//schema/capabilities:capabilities_c", - ], -) - -cc_binary( - name = "libLiteRtLm.dylib", - linkshared = True, - deps = [ - ":engine", - ":stream_proxy", - "//schema/capabilities:capabilities_c", - ], -) - -cc_binary( - name = "LiteRtLm.dll", - linkshared = True, - deps = [ - ":engine", - ":stream_proxy", - "//schema/capabilities:capabilities_c", - ], -) -""" - def run(command: list[str], cwd: Path, env: dict[str, str] | None = None) -> None: printable = " ".join(command) @@ -161,63 +115,6 @@ def download_upstream(tag: str, work_dir: Path) -> Path: return candidates[0] -def patch_upstream_build(source_root: Path) -> None: - stream_proxy_target = source_root / "c" / "stream_proxy.c" - shutil.copy2(STREAM_PROXY_SOURCE, stream_proxy_target) - - build_path = source_root / "c" / "BUILD" - text = build_path.read_text(encoding="utf-8") - insert_before = "\ncc_test(\n name = \"engine_test\"," - packaging_marker = "# Added by litert-lm-native packaging." - packaging_start = text.find(packaging_marker) - if packaging_start != -1: - packaging_end = text.find(insert_before, packaging_start) - if packaging_end == -1: - raise RuntimeError("Could not find packaging block end in upstream c/BUILD") - text = text[:packaging_start].rstrip() + text[packaging_end:] - if insert_before not in text: - raise RuntimeError("Could not find insertion point in upstream c/BUILD") - patched_build = text.replace(insert_before, SHARED_TARGETS + insert_before, 1) - build_path.write_text(patched_build, encoding="utf-8") - - workspace_path = source_root / "WORKSPACE" - workspace_text = workspace_path.read_text(encoding="utf-8") - zlib_url = 'url = "https://zlib.net/fossils/zlib-1.3.1.tar.gz",' - if zlib_url in workspace_text: - workspace_path.write_text( - workspace_text.replace( - zlib_url, - """urls = [ - "https://github.com/madler/zlib/releases/download/v1.3.1/zlib-1.3.1.tar.gz", - "https://zlib.net/fossils/zlib-1.3.1.tar.gz", - ],""", - ), - encoding="utf-8", - ) - - rules_rust_patch_path = source_root / "PATCH.rules_rust" - rules_rust_patch_text = rules_rust_patch_path.read_text(encoding="utf-8") - if '+ "x86_64-apple-darwin",' not in rules_rust_patch_text: - hunk_header = "@@ -28,6 +28,9 @@\n" - if hunk_header in rules_rust_patch_text: - rules_rust_patch_text = rules_rust_patch_text.replace( - hunk_header, - "@@ -28,6 +28,10 @@\n", - 1, - ) - marker = ' "aarch64-apple-darwin",\n' - if marker not in rules_rust_patch_text: - raise RuntimeError("Could not find rules_rust triple insertion point") - rules_rust_patch_path.write_text( - rules_rust_patch_text.replace( - marker, - marker + '+ "x86_64-apple-darwin",\n', - 1, - ), - encoding="utf-8", - ) - - def bazel_command() -> list[str]: if shutil.which("bazelisk"): return ["bazelisk"] @@ -241,8 +138,10 @@ def build_runtime(source_root: Path, platform: str, arch: str, jobs: str | None) if not os.path.isabs(output_user_root): output_user_root = str((REPO_ROOT / output_user_root).resolve()) command.append(f"--output_user_root={output_user_root}") + package_path = f"{BRIDGE_PACKAGE_ROOT}{os.pathsep}{source_root}" command += [ "build", + f"--package_path={package_path}", *configs, *target.get("bazel_options", []), target["bazel_target"], @@ -272,11 +171,16 @@ def stage_runtime(output: Path, platform: str, arch: str) -> Path: stage_dir = BIN_DIR / platform / arch stage_dir.mkdir(parents=True, exist_ok=True) staged = stage_dir / target["library"] - shutil.copy2(output, staged) + copy_artifact(output, staged) print(f"Staged {staged}", flush=True) return staged +def copy_artifact(source: Path, destination: Path) -> None: + destination.unlink(missing_ok=True) + shutil.copy2(source, destination) + + def stage_runtime_dependencies( output: Path, source_root: Path, @@ -316,7 +220,7 @@ def stage_runtime_dependencies( f"{current} depends on {library_name}, but that library " "was not found in the Bazel output tree." ) - shutil.copy2(dependency, destination) + copy_artifact(dependency, destination) print(f"Staged runtime dependency {destination}", flush=True) queued.append(destination) @@ -357,7 +261,7 @@ def stage_macho_runtime_dependencies( f"{current} depends on {install_name}, but {library_name} " "was not found in the Bazel output tree." ) - shutil.copy2(dependency, destination) + copy_artifact(dependency, destination) print(f"Staged runtime dependency {destination}", flush=True) queued.append(destination) @@ -414,7 +318,7 @@ def find_runtime_dependency( def validate_exported_symbols(output: Path) -> None: data = output.read_bytes() - required_symbols = REQUIRED_C_API_SYMBOLS + REQUIRED_STREAM_PROXY_SYMBOLS + required_symbols = REQUIRED_C_API_SYMBOLS + REQUIRED_BRIDGE_SYMBOLS missing = [ symbol.decode("ascii") for symbol in required_symbols @@ -422,10 +326,10 @@ def validate_exported_symbols(output: Path) -> None: ] if missing: raise RuntimeError( - f"{output} does not contain required LiteRT-LM/StreamProxy symbols: " + f"{output} does not contain required LiteRT-LM/bridge symbols: " + ", ".join(missing) ) - print(f"Validated LiteRT-LM and StreamProxy symbols in {output}", flush=True) + print(f"Validated LiteRT-LM and bridge symbols in {output}", flush=True) def main() -> int: @@ -446,7 +350,6 @@ def main() -> int: if args.source_root: source_root = args.source_root.resolve() - patch_upstream_build(source_root) output = build_runtime(source_root, args.platform, args.arch, args.jobs) validate_exported_symbols(output) stage_runtime(output, args.platform, args.arch) @@ -458,7 +361,6 @@ def main() -> int: ignore_cleanup_errors=os.name == "nt", ) as tmp: source_root = download_upstream(args.upstream_tag, Path(tmp)) - patch_upstream_build(source_root) output = build_runtime(source_root, args.platform, args.arch, args.jobs) validate_exported_symbols(output) stage_runtime(output, args.platform, args.arch) diff --git a/tools/package_ios_runtime.py b/tools/package_ios_runtime.py index be17be4..6ff42d9 100644 --- a/tools/package_ios_runtime.py +++ b/tools/package_ios_runtime.py @@ -12,7 +12,7 @@ REPO_ROOT = Path(__file__).resolve().parents[1] BIN_DIR = REPO_ROOT / "bin" DIST_OFFICIAL_DIR = REPO_ROOT / "dist" / "official" -STREAM_PROXY_SOURCE = REPO_ROOT / "native" / "stream_proxy" / "stream_proxy.c" +BRIDGE_SOURCE = REPO_ROOT / "native" / "bridge" / "litert_lm_bridge.c" DEFAULT_IOS_MINIMUM_OS = "15.0" REQUIRED_C_API_SYMBOLS = [ @@ -22,7 +22,7 @@ b"litert_lm_conversation_send_message_stream", ] -REQUIRED_STREAM_PROXY_SYMBOLS = [ +REQUIRED_BRIDGE_SYMBOLS = [ b"stream_proxy_load_global", b"stream_proxy_create", b"stream_proxy_delete", @@ -50,12 +50,12 @@ def validate_exported_symbols(output: Path) -> None: data = output.read_bytes() missing = [ symbol.decode("ascii") - for symbol in REQUIRED_STREAM_PROXY_SYMBOLS + for symbol in REQUIRED_BRIDGE_SYMBOLS if symbol not in data ] if missing: raise RuntimeError( - f"{output} does not contain required StreamProxy symbols: " + f"{output} does not contain required LiteRtLmBridge symbols: " + ", ".join(missing) ) if CLITERTLM_REEXPORT_NAME not in data: @@ -63,7 +63,7 @@ def validate_exported_symbols(output: Path) -> None: f"{output} does not re-export " f"{CLITERTLM_REEXPORT_NAME.decode('ascii')}" ) - print(f"Validated StreamProxy wrapper symbols in {output}", flush=True) + print(f"Validated LiteRtLmBridge wrapper symbols in {output}", flush=True) def validate_upstream_symbols(output: Path) -> None: @@ -190,7 +190,7 @@ def build_wrapper(spec: dict, framework_dir: Path, upstream: Path) -> Path: "-Wl,-reexport_library," + str(upstream), "-o", str(output), - str(STREAM_PROXY_SOURCE), + str(BRIDGE_SOURCE), ]) validate_exported_symbols(output) return output From 61dcc966c3f3eeda159b7cbc3a09cf57a0ec7c72 Mon Sep 17 00:00:00 2001 From: Jhin Lee Date: Mon, 8 Jun 2026 15:09:06 -0400 Subject: [PATCH 2/2] Update LiteRT-LM upstream snapshot --- docs/current_upstream.md | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/docs/current_upstream.md b/docs/current_upstream.md index 25b357a..92fb064 100644 --- a/docs/current_upstream.md +++ b/docs/current_upstream.md @@ -1,19 +1,23 @@ # Current Upstream Snapshot -As of the first scaffold, the latest upstream release resolved by +As of `2026-06-08`, the latest upstream release resolved by `tools/fetch_upstream.py --latest --metadata-only` is: - Repository: `google-ai-edge/LiteRT-LM` -- Tag: `v0.12.0` -- Published: `2026-05-18T20:53:57Z` -- Release URL: `https://github.com/google-ai-edge/LiteRT-LM/releases/tag/v0.12.0` -- Asset: `CLiteRTLM.xcframework.zip` +- Tag: `v0.13.1` +- Published: `2026-06-03T20:52:11Z` +- Release URL: `https://github.com/google-ai-edge/LiteRT-LM/releases/tag/v0.13.1` +- Assets: + - `CLiteRTLM.xcframework.zip` + - `CLiteRTLM_mac.xcframework.zip` -Older release `v0.11.0` exposed standalone CLI assets for Android, iOS -simulator, Linux x64, macOS arm64, and Windows x64. The native repo should not -assume that upstream releases always publish the same asset matrix. +Older releases have exposed different asset matrices. For example, `v0.11.0` +included standalone CLI assets for Android, iOS simulator, Linux x64, macOS +arm64, and Windows x64, while `v0.13.1` publishes Apple XCFramework archives. +The native repo should not assume that upstream releases always publish the same +asset matrix. -The first production implementation should support both modes: +Production packaging should support both modes: - consume official upstream artifacts when they exist and expose the required ABI/API surface