Skip to content

chore(0.10.6): bump every demo to leap-sdk 0.10.6 + adopt LMD-only iOS import#48

Open
iamstuffed wants to merge 3 commits into
mainfrom
feat/desktop-examples
Open

chore(0.10.6): bump every demo to leap-sdk 0.10.6 + adopt LMD-only iOS import#48
iamstuffed wants to merge 3 commits into
mainfrom
feat/desktop-examples

Conversation

@iamstuffed
Copy link
Copy Markdown
Contributor

@iamstuffed iamstuffed commented Apr 29, 2026

Summary

Three logical commits, bundled because they ship together. Rebased onto main after the security-hardening PR #49 merged — earlier 41-commit + per-version iteration history flattened.

1. feat: add JVM/Linux/Windows desktop chat-cli demos (907e1d0)

Three new desktop chat REPL CLIs under JVM/, Linux/, Windows/ — REPL chat apps that download the default LFM2.5-350M (Q4_0) bundle on first run, cache it under ./leap_models/, and stream assistant responses to stdout. All three share the same SDK API surface — LeapDownloader().loadModel(modelName, quantizationType, progress)runner.createConversation(systemPrompt)conversation.generateResponse(line).collect { … } — so the demos illustrate the SDK's manifest-resolution flow rather than requiring users to provide a model bundle path.

  • JVM uses ai.liquid.leap:leap-sdk-jvm. The published JAR bundles JNI binaries for Linux x86_64 / aarch64 / macOS / Windows via NativeLibLoader, so one artifact runs on every desktop OS.
  • Linux uses Kotlin/Native (linuxX64) + leap-sdk-linuxx64 and the upstream ai.liquid.leap.nativelibs Gradle plugin to auto-extract the per-target .so set (umbrella + the runtime-dispatched libggml-cpu-{alderlake,…,zen4}.so backends) into build/bin/linuxX64/releaseExecutable/ so the cinterop $ORIGIN rpath finds them at runtime.
  • Windows is the mingwX64 mirror; same plugin auto-extracts the equivalent .dll set.

For Kotlin/Native (Linux + Windows): the leap-sdk's bundled Ktor CIO engine doesn't support TLS on Native, so each demo injects HttpClient(Curl) { install(ContentNegotiation) { json(...) } } into LeapDownloader. The per-target Ktor Curl artifacts statically link libcurl + openssl, so no system libraries are needed at build/run time.

Also adds:

  • .github/workflows/{jvm,linux,windows}-leap-chat-cli-test.yml — three build + smoke-test workflows. Top-level permissions: contents: read, persist-credentials: false on checkout, gradle/actions/setup-gradle@50e97c2c # v6.1.0 (SHA-pinned), and a Sonatype-snapshot regex whitelist on the cache-purge step. Concurrency group keyed by workflow + ref so a fast-following push cancels the in-flight run.
  • scripts/refresh-spm-if-needed.sh — local script for iOS/macOS demos: detects when the upstream leap-sdk SPM tag has been force-pushed (compares local Package.resolved revisions against git ls-remote) and clears the relevant SPM caches + Package.resolved files. Idempotent.
  • Root README.md adds JVM, Linux, Windows sections and Quick Start entries for each platform.

2. ci: add concurrency guard + snapshot purge to android workflow (70a3afc)

Bring android-leap-chat-test.yml up to parity with the desktop CLI workflows:

  • Concurrency group keyed by workflow + ref.
  • Snapshot-purge step that compares Sonatype's <lastUpdated> against a marker in ~/.gradle/caches/leap-snapshot-marker and only wipes the cache when a SNAPSHOT pin was force-republished. Whitelists the parsed SDK_VERSION shape before interpolating into the curl URL. No-op now that we're on stable.
  • --refresh-dependencies on the gradle invocations so Gradle's metadata TTL doesn't mask a republished snapshot.

3. chore(0.10.6): bump every existing demo to leap-sdk 0.10.6, swap to LFM2.5-350M, and adopt the LMD-only iOS import (b7492ab)

Tracks ai.liquid.leap:leap-sdk:0.10.6 on Maven Central + SPM v0.10.6 on the leap-sdk release (both live as of 2026-05-12).

Android (eight demos):

  • bump leapSdk = "0.10.6" across all gradle/libs.versions.toml
  • drop the Sonatype Central Portal Snapshots maven block from every settings.gradle.kts — stable releases live on Maven Central proper
  • switch the demo model from LFM2-350M (Q8_0) to LFM2.5-350M (Q4_0) — smaller (~250 MB vs 370 MB), faster cold start, exercises 0.10.6's CPU-dispatched llamacpp backend that picks the right libggml-cpu-<arch> for the runner's CPU. Updates the corresponding MainActivity / ViewModel model-id strings.

iOS (six demos):

  • pin SPM revision: v0.10.6 in every project.yml
  • switch the five demos that use the downloader path (LeapChat, LeapAudio, LeapSlogan, LeapVLM, RecipeGenerator) from import LeapSDK to import LeapModelDownloader. In 0.10.6 the LMD K/N framework re-exports every leap-sdk Kotlin type via its ObjC binding + SKIE-bundled Swift overlay, so one import covers both the download surface and the SDK types those demos use.
  • swap each of those demos' OTHER_LDFLAGS / LD_RUNPATH_SEARCH_PATHS / Sign nested *.dylib post-build scripts from LeapSDK.framework/Frameworks to LeapModelDownloader.framework/Frameworks and add -lie_zip for the download path.
  • LeapVoiceAssistantDemo stays on the LeapUI SPM product — LeapUI doesn't export(project(":leap-sdk")) in its K/N framework binary, so dual-importing LeapSDK + LeapUi here is unambiguous. Top-of-file comment in its project.yml documents the asymmetry.
  • adopt the renamed LeapDownloader.loadModel(modelName:, quantizationType:, …) kwargs (was model: / quantization:).
  • swap demo model to LFM2.5-350M (Q4_0), same rationale as Android.

macOS / Web:

  • macOS/LeapVoiceAssistantDemo/project.yml, macOS/LeapVLMExample/project.yml — SPM revision: v0.10.6.
  • Web/LeapVoiceAssistantDemo/gradle/libs.versions.tomlleapSdk = "0.10.6".
  • (JVM/Linux/Windows CLIs are introduced at 0.10.6 directly in commit 1.)

Local-test apparatus (new):

  • scripts/use-local-sdk.sh rewrites each iOS demo's pbxproj from the remote https://github.com/Liquid4All/leap-sdk SPM ref to an XCLocalSwiftPackageReference pointing at a leap-android-sdk worktree. Run after xcodegen generate (which would otherwise overwrite the local ref each time). Idempotent.
  • iOS/LOCAL_TESTING.md documents the workflow end-to-end (build XCFrameworks → link symlinks → run the rewrite script) for contributors iterating on unpublished SDK changes.

Docs:

  • iOS/README.md + Android/RecipeGenerator/README.md updated for the new release URL + model name.

Test plan

  • All 0.10.5 references replaced with 0.10.6 across gradle/libs.versions.toml, project.yml, and READMEs (verified by grep -r "0\.10\.5" returning empty).
  • iOS/macOS pin bump applies to project.yml only (no Xcode CI in this repo); manual Xcode resolve required.
  • Manual end-to-end on a Windows host with MinGW runtime DLLs available (out of scope for the demo's redistributable bundling — JVM demo is the portable equivalent for Windows users today).

Copilot AI review requested due to automatic review settings April 29, 2026 13:02
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds three new desktop chat CLI examples (JVM, Linux, Windows) that load a local LeapSDK .bundle and stream assistant responses in a simple REPL, plus links from the root README so they’re discoverable alongside existing demos.

Changes:

  • Introduce JVM/LeapChatCli (Kotlin/JVM + Gradle Application) using LeapSDK 0.10.1.
  • Introduce Linux/LeapChatCli and Windows/LeapChatCli (Kotlin/Native linuxX64 / mingwX64) using the ai.liquid.leap.nativelibs Gradle plugin to extract native inference libs next to the produced binary.
  • Update root README.md to document the new desktop examples and quick-start commands.

Reviewed changes

Copilot reviewed 31 out of 34 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
README.md Adds JVM/Linux/Windows example sections + quick start commands and updates LeapSDK positioning to include desktop platforms.
JVM/LeapChatCli/src/main/kotlin/ai/liquid/leap/cli/Main.kt Implements JVM REPL that flushes stdout for true token streaming and prints stats.
JVM/LeapChatCli/build.gradle.kts Sets up Kotlin/JVM app packaging and main class entry.
JVM/LeapChatCli/settings.gradle.kts + wrapper/catalog files Standalone Gradle project setup for the JVM CLI.
Linux/LeapChatCli/src/linuxX64Main/kotlin/ai/liquid/leap/cli/Main.kt Implements Linux Kotlin/Native REPL (streaming + stats).
Linux/LeapChatCli/build.gradle.kts Configures Kotlin/Native linuxX64 executable and Leap native-libs plugin wiring.
Linux/LeapChatCli/README.md Documents Linux build/run steps and how native libs are provided.
Linux/LeapChatCli/settings.gradle.kts + wrapper/catalog files Standalone Gradle project setup for the Linux CLI.
Windows/LeapChatCli/src/mingwX64Main/kotlin/ai/liquid/leap/cli/Main.kt Implements Windows Kotlin/Native REPL (streaming + stats).
Windows/LeapChatCli/build.gradle.kts Configures Kotlin/Native mingwX64 executable and Leap native-libs plugin wiring.
Windows/LeapChatCli/README.md Documents Windows build/run steps and how native libs are provided.
Windows/LeapChatCli/settings.gradle.kts + wrapper/catalog files Standalone Gradle project setup for the Windows CLI.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +51 to +58
conversation.generateResponse(line).collect { response ->
when (response) {
is MessageResponse.Chunk -> print(response.text)
is MessageResponse.Complete -> {
println()
response.stats?.let { stats ->
println("[${stats.completionTokens} tok, ${stats.tokenPerSecond} tok/s]")
}
Comment thread Linux/LeapChatCli/README.md Outdated
## Build

```bash
JAVA_HOME="$(/usr/libexec/java_home -v 21)" ./gradlew linkReleaseExecutableLinuxX64
Comment thread Windows/LeapChatCli/README.md Outdated
Comment on lines +9 to +17
- Windows x86_64 host (Windows 10/11 tested; or build via WSL2 / Linux host
with mingw cross-toolchain)
- JDK 21 to run Gradle (Zulu recommended)
- A LeapSDK model bundle on local disk (e.g. `LFM2-1.2B-Q4_0.bundle`)

> **Note on cross-compile**: Kotlin/Native can compile `mingwX64` Kotlin code
> from any host (verified on macOS), but cross-linking the final `.exe`
> against `inference_engine.dll` from non-Windows hosts isn't supported by
> Kotlin/Native. Build + run from Windows.
Comment on lines +51 to +58
conversation.generateResponse(line).collect { response ->
when (response) {
is MessageResponse.Chunk -> print(response.text)
is MessageResponse.Complete -> {
println()
response.stats?.let { stats ->
println("[${stats.completionTokens} tok, ${stats.tokenPerSecond} tok/s]")
}
iamstuffed added a commit that referenced this pull request Apr 29, 2026
- Linux + Windows Main.kt: flush stdout after every Chunk and after the
  prompt. Kotlin/Native print()/println() route through stdio which is
  fully buffered when stdout is not a TTY (and only line-buffered when
  it is) — without an explicit flush the streaming chunks would appear
  in one burst on Complete, defeating the REPL. Use fflush(NULL) which
  flushes all open output streams per POSIX, avoiding per-target stdout
  symbol lookup. File-level @OptIn(ExperimentalForeignApi::class)
  required for the cinterop fflush(null) call.

- Linux README: replaced /usr/libexec/java_home (a macOS-only command)
  in the build instructions with a Linux-native JAVA_HOME example
  pointing at /usr/lib/jvm/java-21-openjdk-amd64 and a SDKMAN
  alternative.

- Windows README: removed the conflicting "build via WSL2 / Linux host
  with mingw cross-toolchain" prerequisite. Kotlin/Native cross-link
  from non-Windows isn't supported (already noted in the cross-compile
  callout); WSL2 is Linux under the hood and hits the same limitation.
  Reworded the callout to make this explicit.

Both Native targets recompile cleanly with the changes.
@iamstuffed iamstuffed force-pushed the feat/desktop-examples branch from 42e4bbf to 3e17d55 Compare April 29, 2026 16:58
iamstuffed added a commit that referenced this pull request Apr 29, 2026
- Linux + Windows Main.kt: flush stdout after every Chunk and after the
  prompt. Kotlin/Native print()/println() route through stdio which is
  fully buffered when stdout is not a TTY (and only line-buffered when
  it is) — without an explicit flush the streaming chunks would appear
  in one burst on Complete, defeating the REPL. Use fflush(NULL) which
  flushes all open output streams per POSIX, avoiding per-target stdout
  symbol lookup. File-level @OptIn(ExperimentalForeignApi::class)
  required for the cinterop fflush(null) call.

- Linux README: replaced /usr/libexec/java_home (a macOS-only command)
  in the build instructions with a Linux-native JAVA_HOME example
  pointing at /usr/lib/jvm/java-21-openjdk-amd64 and a SDKMAN
  alternative.

- Windows README: removed the conflicting "build via WSL2 / Linux host
  with mingw cross-toolchain" prerequisite. Kotlin/Native cross-link
  from non-Windows isn't supported (already noted in the cross-compile
  callout); WSL2 is Linux under the hood and hits the same limitation.
  Reworded the callout to make this explicit.

Both Native targets recompile cleanly with the changes.
@iamstuffed iamstuffed changed the base branch from refactor/reorg-platform-dirs to main April 29, 2026 16:58
@iamstuffed iamstuffed changed the title feat: add JVM/Linux/Windows desktop chat CLI examples feat: JVM/Linux/Windows desktop chat CLIs + bump all examples to 0.10.4.1 May 7, 2026
@iamstuffed iamstuffed changed the title feat: JVM/Linux/Windows desktop chat CLIs + bump all examples to 0.10.4.1 feat: JVM/Linux/Windows desktop chat CLIs + KV cache + bump to 0.10.4.4 May 7, 2026
@iamstuffed iamstuffed changed the title feat: JVM/Linux/Windows desktop chat CLIs + KV cache + bump to 0.10.4.4 feat: JVM/Linux/Windows desktop chat CLIs + KV cache + bump to 0.10.4.5 May 8, 2026
iamstuffed added a commit that referenced this pull request May 8, 2026
Address PR #49 review. Copilot reviewer flagged that setup-java's
cache: 'gradle' uses the Actions cache API and the "no GitHub API
access needed" comment was imprecise. Empirically the cache works
under contents: read (cache save succeeded on the prior run), but
gradle/actions/setup-gradle is the proper modern path — matches the
desktop-examples worktree (PR #48) and cleanly separates JDK setup
from build-cache wiring.

- Drop `cache: 'gradle'` from actions/setup-java.
- Add gradle/actions@50e97c2
  (v6.1.0, SHA-pinned).
- Refine the permissions-block comment: the Actions cache is gated by
  the ACTIONS_RUNTIME_TOKEN, not GITHUB_TOKEN, so contents: read is
  sufficient.
@iamstuffed iamstuffed force-pushed the feat/desktop-examples branch from c618176 to 037ddd4 Compare May 8, 2026 20:43
@iamstuffed iamstuffed changed the title feat: JVM/Linux/Windows desktop chat CLIs + KV cache + bump to 0.10.4.5 feat: JVM/Linux/Windows desktop chat-cli demos at leap-sdk 0.10.4.5 May 8, 2026
@iamstuffed iamstuffed changed the title feat: JVM/Linux/Windows desktop chat-cli demos at leap-sdk 0.10.4.5 chore(0.10.6): bump every demo to leap-sdk 0.10.6 + adopt LMD-only iOS import May 12, 2026
Three new desktop chat REPL CLIs under `JVM/`, `Linux/`, `Windows/` — REPL chat apps that download the default `LFM2.5-350M` (Q4_0) bundle on first run, cache it under `./leap_models/`, and stream assistant responses to stdout. All three share the same SDK API surface — `LeapDownloader().loadModel(modelName, quantizationType, progress)` → `runner.createConversation(systemPrompt)` → `conversation.generateResponse(line).collect { … }` — so the demos illustrate the SDK's manifest-resolution flow rather than requiring users to provide a model bundle path.

- **JVM** uses `ai.liquid.leap:leap-sdk-jvm`. The published JAR bundles JNI binaries for Linux x86_64 / aarch64 / macOS / Windows via `NativeLibLoader`, so one artifact runs on every desktop OS.
- **Linux** uses Kotlin/Native (`linuxX64`) + `leap-sdk-linuxx64` and the upstream `ai.liquid.leap.nativelibs` Gradle plugin to auto-extract the per-target `.so` set (umbrella + the runtime-dispatched `libggml-cpu-{alderlake,…,zen4}.so` backends) into `build/bin/linuxX64/releaseExecutable/` so the cinterop `\$ORIGIN` rpath finds them at runtime.
- **Windows** is the `mingwX64` mirror; same plugin auto-extracts the equivalent `.dll` set.

For Kotlin/Native (Linux + Windows): the leap-sdk's bundled Ktor CIO engine doesn't support TLS on Native, so each demo injects `HttpClient(Curl) { install(ContentNegotiation) { json(...) } }` into `LeapDownloader`. The per-target Ktor Curl artifacts statically link libcurl + openssl, so no system libraries are needed at build/run time.

Also adds:

- `.github/workflows/{jvm,linux,windows}-leap-chat-cli-test.yml` — three build + smoke-test workflows. Top-level `permissions: contents: read`, `persist-credentials: false` on checkout, `gradle/actions/setup-gradle@50e97c2c # v6.1.0` (SHA-pinned), and a Sonatype-snapshot regex whitelist on the cache-purge step. Concurrency group keyed by `workflow + ref` so a fast-following push cancels the in-flight run.
- `scripts/refresh-spm-if-needed.sh` — local script for iOS/macOS demos: detects when the upstream `leap-sdk` SPM tag has been force-pushed (compares local `Package.resolved` revisions against `git ls-remote`) and clears the relevant SPM caches + `Package.resolved` files. Idempotent.
- Root `README.md` adds JVM, Linux, Windows sections and Quick Start entries for each platform.
Bring `android-leap-chat-test.yml` up to parity with the desktop CLI workflows:

- Concurrency group keyed by workflow + ref.
- Snapshot-purge step that compares Sonatype's `<lastUpdated>` against a marker in `~/.gradle/caches/leap-snapshot-marker` and only wipes the cache when a SNAPSHOT pin was force-republished. Whitelists the parsed `SDK_VERSION` shape before interpolating into the `curl` URL. No-op now that we're on stable.
- `--refresh-dependencies` on the `gradle` invocations so Gradle's metadata TTL doesn't mask a republished snapshot.
…FM2.5-350M, and adopt the LMD-only iOS import

Tracks `ai.liquid.leap:leap-sdk:0.10.6` on Maven Central + SPM `v0.10.6` on the leap-sdk release (both live as of 2026-05-12).

Android (eight demos):
- bump `leapSdk = "0.10.6"` across all `gradle/libs.versions.toml`
- drop the Sonatype Central Portal Snapshots maven block from every `settings.gradle.kts` — stable releases live on Maven Central proper
- switch the demo model from `LFM2-350M (Q8_0)` to `LFM2.5-350M (Q4_0)` — smaller (~250 MB vs 370 MB), faster cold start, exercises 0.10.6's CPU-dispatched llamacpp backend that picks the right `libggml-cpu-<arch>` for the runner's CPU. Updates the corresponding `MainActivity` / `ViewModel` model-id strings

iOS (six demos):
- pin SPM `revision: v0.10.6` in every `project.yml`
- switch the five demos that use the downloader path (LeapChat, LeapAudio, LeapSlogan, LeapVLM, RecipeGenerator) from `import LeapSDK` to `import LeapModelDownloader`. In 0.10.6 the LMD K/N framework re-exports every leap-sdk Kotlin type via its ObjC binding + SKIE-bundled Swift overlay, so one import covers both the download surface and the SDK types those demos use
- swap each of those demos' `OTHER_LDFLAGS` / `LD_RUNPATH_SEARCH_PATHS` / `Sign nested *.dylib` post-build scripts from `LeapSDK.framework/Frameworks` to `LeapModelDownloader.framework/Frameworks` and add `-lie_zip` for the download path
- `LeapVoiceAssistantDemo` stays on the `LeapUI` SPM product — `LeapUI` doesn't `export(project(":leap-sdk"))` in its K/N framework binary, so dual-importing `LeapSDK` + `LeapUi` here is unambiguous. Top-of-file comment in its `project.yml` documents the asymmetry
- adopt the renamed `LeapDownloader.loadModel(modelName:, quantizationType:, …)` kwargs (was `model:` / `quantization:`)
- swap demo model to `LFM2.5-350M` (Q4_0), same rationale as Android

macOS / Web / desktop CLIs:
- `macOS/LeapVoiceAssistantDemo/project.yml`, `macOS/LeapVLMExample/project.yml` — SPM `revision: v0.10.6`
- `Web/LeapVoiceAssistantDemo/gradle/libs.versions.toml` — `leapSdk = "0.10.6"`
- (JVM/Linux/Windows CLIs are introduced at 0.10.6 directly in the previous feat commit)

Local-test apparatus (new):
- `scripts/use-local-sdk.sh` rewrites each iOS demo's pbxproj from the remote `https://github.com/Liquid4All/leap-sdk` SPM ref to an `XCLocalSwiftPackageReference` pointing at a leap-android-sdk worktree. Run after `xcodegen generate` (which would otherwise overwrite the local ref each time). Idempotent
- `iOS/LOCAL_TESTING.md` documents the workflow end-to-end (build XCFrameworks → link symlinks → run the rewrite script) for contributors iterating on unpublished SDK changes

Docs:
- `iOS/README.md` + `Android/RecipeGenerator/README.md` updated for the new release URL + model name
@iamstuffed iamstuffed force-pushed the feat/desktop-examples branch from bce4b0b to b7492ab Compare May 12, 2026 23:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants