Skip to content

Commit ebe40db

Browse files
authored
Merge branch 'main' into pr/qwen3vl-multitile-batching
2 parents 238ae39 + 233cf43 commit ebe40db

57 files changed

Lines changed: 3691 additions & 1845 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
name: Verify prebuild symbols
2+
description: >-
3+
Static guard that prebuilt addon modules contain no unresolvable engine
4+
symbols (ggml_/gguf_/llama_/whisper_/...) -- the @qvac/tts-ggml@0.2.1
5+
dlopen-on-device crash class -- and (optionally) do not leak engine symbols
6+
into their export table (cross-addon interposition risk). Runs
7+
scripts/verify-prebuild-symbols.mjs against the workdir's prebuilds/ tree.
8+
No emulator or device required; runs straight off the build artifacts.
9+
10+
inputs:
11+
platform:
12+
description: Build platform (linux, android, darwin, ios, win32)
13+
required: true
14+
workdir:
15+
description: Directory containing the prebuilds/ folder
16+
required: true
17+
enforce-exports:
18+
description: >-
19+
Fail (instead of warn) when a module exports engine symbols at default
20+
visibility. Default false during rollout; flip to true per package once
21+
it ships a symbols.map / -Wl,--exclude-libs,ALL.
22+
required: false
23+
default: "false"
24+
25+
runs:
26+
using: composite
27+
steps:
28+
- if: ${{ inputs.platform == 'android' }}
29+
name: Verify symbols (Android)
30+
shell: bash
31+
working-directory: ${{ inputs.workdir }}
32+
env:
33+
ENFORCE_EXPORTS: ${{ inputs.enforce-exports }}
34+
run: |
35+
set -euo pipefail
36+
TOOLCHAIN="${ANDROID_TOOLCHAIN_ROOT:-${ANDROID_NDK_HOME}/toolchains/llvm/prebuilt/linux-x86_64}"
37+
export READELF="${TOOLCHAIN}/bin/llvm-readelf"
38+
export NM="${TOOLCHAIN}/bin/llvm-nm"
39+
EXTRA=""
40+
if [ "$ENFORCE_EXPORTS" = "true" ]; then EXTRA="--enforce-exports"; fi
41+
node "$GITHUB_WORKSPACE/scripts/verify-prebuild-symbols.mjs" --dir prebuilds --platform android $EXTRA
42+
43+
- if: ${{ inputs.platform == 'linux' || inputs.platform == 'darwin' || inputs.platform == 'ios' }}
44+
name: Verify symbols (native)
45+
shell: bash
46+
working-directory: ${{ inputs.workdir }}
47+
env:
48+
ENFORCE_EXPORTS: ${{ inputs.enforce-exports }}
49+
PLATFORM: ${{ inputs.platform }}
50+
run: |
51+
set -euo pipefail
52+
EXTRA=""
53+
if [ "$ENFORCE_EXPORTS" = "true" ]; then EXTRA="--enforce-exports"; fi
54+
node "$GITHUB_WORKSPACE/scripts/verify-prebuild-symbols.mjs" --dir prebuilds --platform "$PLATFORM" $EXTRA
55+
56+
- if: ${{ inputs.platform == 'win32' }}
57+
name: Verify symbols (skip on Windows)
58+
shell: bash
59+
run: |
60+
echo "verify-prebuild-symbols skipped on win32 (PE format is not analyzed by the ELF/Mach-O gate)."
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
# Multi-addon co-load smoke (ggml addons).
2+
#
3+
# Per-addon CI loads exactly one addon per run, so it cannot catch the class of
4+
# bug where addon A and addon B each pass alone but crash when both are
5+
# dlopen'd into one process (the @qvac/tts-ggml@0.2.1 ggml_backend_is_cpu
6+
# crash). This workflow runs packages/ggml-coload-smoke, which require()s
7+
# several @qvac ggml addons into ONE Bare process, across a matrix of
8+
# combinations (see packages/ggml-coload-smoke/scripts/coload-combinations.mjs):
9+
# the full set, each ggml stack, cross-stack pairs, and -- on PRs -- combos
10+
# focused on the addon(s) the PR changed.
11+
#
12+
# IMPORTANT: it installs the *published* addons from npm and co-loads those, so
13+
# on a PR it validates the current REGISTRY BASELINE, not the PR's own diff (a
14+
# PR's freshly-built change is guarded by the Phase-1 prebuild symbol gate).
15+
# Follow-up: overlay the PR's freshly-built prebuild for the changed addon
16+
# (download its prebuild artifact into node_modules/@qvac/<addon>/prebuilds) so
17+
# the co-load also exercises unpublished changes; the run stays model-free/cheap.
18+
19+
name: Co-load smoke (ggml)
20+
21+
on:
22+
pull_request:
23+
paths:
24+
- "packages/tts-ggml/**"
25+
- "packages/transcription-parakeet/**"
26+
- "packages/transcription-whispercpp/**"
27+
- "packages/bci-whispercpp/**"
28+
- "packages/llm-llamacpp/**"
29+
- "packages/embed-llamacpp/**"
30+
- "packages/classification-ggml/**"
31+
- "packages/vla-ggml/**"
32+
- "packages/ocr-ggml/**"
33+
- "packages/translation-nmtcpp/**"
34+
- "packages/diffusion-cpp/**"
35+
- "packages/ggml-coload-smoke/**"
36+
- ".github/workflows/coload-smoke-ggml.yml"
37+
workflow_dispatch:
38+
inputs:
39+
mode:
40+
description: "full (every combo) or changed (focus on --addons)"
41+
type: choice
42+
options: [full, changed]
43+
default: full
44+
addons:
45+
description: "changed-mode: comma-separated addon short names (e.g. tts-ggml,llm-llamacpp)"
46+
required: false
47+
type: string
48+
49+
permissions:
50+
contents: read
51+
packages: read
52+
53+
jobs:
54+
prepare:
55+
runs-on: ubuntu-latest
56+
outputs:
57+
combos: ${{ steps.gen.outputs.combos }}
58+
steps:
59+
- name: Checkout
60+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # 6.0.2
61+
with:
62+
fetch-depth: 0
63+
64+
- name: Setup Node.js
65+
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # 6.3.0
66+
with:
67+
node-version: lts/*
68+
69+
- name: Compute co-load combinations
70+
id: gen
71+
shell: bash
72+
env:
73+
EVENT_NAME: ${{ github.event_name }}
74+
DISPATCH_MODE: ${{ inputs.mode }}
75+
DISPATCH_ADDONS: ${{ inputs.addons }}
76+
BASE_SHA: ${{ github.event.pull_request.base.sha }}
77+
run: |
78+
set -euo pipefail
79+
SCRIPT=packages/ggml-coload-smoke/scripts/coload-combinations.mjs
80+
if [ "$EVENT_NAME" = "workflow_dispatch" ]; then
81+
if [ "$DISPATCH_MODE" = "changed" ] && [ -n "$DISPATCH_ADDONS" ]; then
82+
node "$SCRIPT" --changed "$DISPATCH_ADDONS"
83+
else
84+
node "$SCRIPT"
85+
fi
86+
else
87+
git diff --name-only "$BASE_SHA" HEAD > /tmp/coload-changed.txt || true
88+
node "$SCRIPT" --changed-files /tmp/coload-changed.txt
89+
fi
90+
91+
coload:
92+
needs: prepare
93+
if: ${{ needs.prepare.outputs.combos != '' && needs.prepare.outputs.combos != '[]' }}
94+
# github-hosted runner: released @qvac addons resolve from public npm with no
95+
# secrets, so this runs on fork PRs too. It co-loads the PUBLISHED addons;
96+
# overlaying the PR's freshly built prebuild is a documented follow-up.
97+
runs-on: ubuntu-latest
98+
timeout-minutes: 60
99+
permissions:
100+
contents: read
101+
packages: read
102+
strategy:
103+
fail-fast: false
104+
matrix:
105+
combo: ${{ fromJSON(needs.prepare.outputs.combos) }}
106+
name: coload-${{ matrix.combo.name }}
107+
steps:
108+
- name: Setup Node.js
109+
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # 6.3.0
110+
with:
111+
node-version: lts/*
112+
113+
- name: Checkout
114+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # 6.0.2
115+
116+
- name: Install bare tooling
117+
shell: bash
118+
run: npm install -g --force bare bare-make
119+
120+
- name: Install co-load harness deps
121+
working-directory: packages/ggml-coload-smoke
122+
shell: bash
123+
run: npm install
124+
125+
- name: Install addons under test (published)
126+
working-directory: packages/ggml-coload-smoke
127+
shell: bash
128+
env:
129+
ADDONS: ${{ matrix.combo.addons }}
130+
run: |
131+
set -euo pipefail
132+
PKGS=""
133+
for a in ${ADDONS//,/ }; do PKGS="$PKGS @qvac/$a"; done
134+
echo "Installing combo '${{ matrix.combo.name }}': $PKGS"
135+
npm install --no-save $PKGS
136+
137+
- name: Co-load smoke
138+
working-directory: packages/ggml-coload-smoke
139+
shell: bash
140+
env:
141+
COLOAD_ADDONS: ${{ matrix.combo.addons }}
142+
run: bare test/coload.test.js
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
# Multi-addon co-load smoke on Android (AWS Device Farm).
2+
#
3+
# The @qvac/tts-ggml@0.2.1 dlopen crash only reproduced once MANY ggml addons
4+
# were co-loaded into one process on a real Android device -- exactly what the
5+
# SDK consumer does at bootstrap. This workflow reuses the SDK mobile bundle
6+
# machinery (test-android-sdk.yml) to build a consumer that bundles a chosen
7+
# subset of plugins (packages/ggml-coload-smoke/scripts/coload-combinations.mjs
8+
# emits the SDK plugin specifiers per combo) and runs it on Device Farm.
9+
#
10+
# Signal: the PRIMARY co-load check is that the consumer BOOTSTRAPS (co-loads
11+
# the bundled plugins) without a dlopen SIGABRT; test-android-sdk.yml fails the
12+
# run if the consumer dies. On PRs we run ONLY the full bundle ("all"), which is
13+
# the exact all-addon Android bootstrap that crashed in 0.2.1 and keeps the
14+
# signal clean. Subset combos (for isolating which addon/stack regressed) are
15+
# available via workflow_dispatch; note that the smoke suite may surface
16+
# secondary feature-test failures for a feature whose plugin is not in the
17+
# subset -- the co-load/bootstrap result is the signal that matters there.
18+
#
19+
# Gated behind the `verified` label (Device Farm is expensive) AND an in-repo
20+
# branch -- a `pull_request` from a fork cannot read the Device Farm secrets, so
21+
# fork PRs rely on the desktop co-load plus workflow_dispatch. iOS statically
22+
# links ggml (the dlopen class does not bite there); an iOS variant is a
23+
# symmetric follow-up once test-ios-sdk.yml grows the same `plugins` input.
24+
25+
name: Co-load smoke (ggml, mobile)
26+
27+
on:
28+
pull_request:
29+
paths:
30+
- "packages/tts-ggml/**"
31+
- "packages/transcription-parakeet/**"
32+
- "packages/transcription-whispercpp/**"
33+
- "packages/llm-llamacpp/**"
34+
- "packages/embed-llamacpp/**"
35+
- "packages/vla-ggml/**"
36+
- "packages/translation-nmtcpp/**"
37+
- "packages/diffusion-cpp/**"
38+
- "packages/ggml-coload-smoke/**"
39+
- ".github/workflows/coload-smoke-mobile-ggml.yml"
40+
workflow_dispatch:
41+
inputs:
42+
mode:
43+
description: "full (every mobile-capable combo) or changed (focus on --addons)"
44+
type: choice
45+
options: [full, changed]
46+
default: full
47+
addons:
48+
description: "changed-mode: comma-separated addon short names (e.g. tts-ggml,llm-llamacpp)"
49+
required: false
50+
type: string
51+
52+
permissions:
53+
actions: read
54+
id-token: write
55+
contents: read
56+
pull-requests: write
57+
packages: read
58+
59+
jobs:
60+
gate:
61+
# Device Farm needs base-repo secrets, which a `pull_request` from a fork
62+
# cannot access. Only run on explicit dispatch, or on a `verified` PR from an
63+
# in-repo branch. Fork PRs are covered by the desktop co-load and can still
64+
# be exercised on demand via workflow_dispatch from the base repo.
65+
if: ${{ github.event_name == 'workflow_dispatch' || (contains(github.event.pull_request.labels.*.name, 'verified') && github.event.pull_request.head.repo.full_name == github.repository) }}
66+
runs-on: ubuntu-latest
67+
outputs:
68+
combos: ${{ steps.gen.outputs.combos }}
69+
steps:
70+
- name: Checkout
71+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # 6.0.2
72+
with:
73+
fetch-depth: 0
74+
75+
- name: Setup Node.js
76+
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # 6.3.0
77+
with:
78+
node-version: lts/*
79+
80+
- name: Compute mobile co-load combinations
81+
id: gen
82+
shell: bash
83+
env:
84+
EVENT_NAME: ${{ github.event_name }}
85+
DISPATCH_MODE: ${{ inputs.mode }}
86+
DISPATCH_ADDONS: ${{ inputs.addons }}
87+
run: |
88+
set -euo pipefail
89+
SCRIPT=packages/ggml-coload-smoke/scripts/coload-combinations.mjs
90+
if [ "$EVENT_NAME" = "workflow_dispatch" ]; then
91+
if [ "$DISPATCH_MODE" = "changed" ] && [ -n "$DISPATCH_ADDONS" ]; then
92+
node "$SCRIPT" --mobile --changed "$DISPATCH_ADDONS"
93+
else
94+
node "$SCRIPT" --mobile
95+
fi
96+
else
97+
# PRs: keep the signal clean -- only the full bundle (all plugins),
98+
# the exact all-addon Android bootstrap that crashed in 0.2.1.
99+
node "$SCRIPT" --mobile --only all
100+
fi
101+
102+
mobile-coload:
103+
needs: gate
104+
if: ${{ needs.gate.outputs.combos != '' && needs.gate.outputs.combos != '[]' }}
105+
permissions:
106+
actions: read
107+
id-token: write
108+
contents: read
109+
pull-requests: write
110+
packages: read
111+
strategy:
112+
fail-fast: false
113+
matrix:
114+
combo: ${{ fromJSON(needs.gate.outputs.combos) }}
115+
name: coload-${{ matrix.combo.name }}
116+
uses: ./.github/workflows/test-android-sdk.yml
117+
with:
118+
project-directory: "packages/sdk"
119+
working-directory: "packages/sdk/e2e"
120+
plugins: ${{ matrix.combo.plugins }}
121+
suite: smoke
122+
consumer-timeout: 1200
123+
device-farm-timeout: 90
124+
device-farm-project-arn: ${{ vars.SDK_DEVICE_FARM_PROJECT_ARN }}
125+
device-pools: ${{ vars.SDK_DEVICE_FARM_POOLS }}
126+
secrets: inherit

0 commit comments

Comments
 (0)