Skip to content

QVAC-24488 feat[api]: close the SDK gaps against @qvac/bci-whispercpp 0.9.1 - #4565

Open
RamazTs wants to merge 5 commits into
mainfrom
feat/QVAC-24488-sdk-bci-gaps
Open

RamazTs wants to merge 5 commits into
mainfrom
feat/QVAC-24488-sdk-bci-gaps

Conversation

@RamazTs

@RamazTs RamazTs commented Sep 17, 2026

Copy link
Copy Markdown
Contributor

🎯 What problem does this PR solve?

Gap audit of @qvac/bci-whispercpp 0.9.1 against the SDK surface. Already pinned ^0.9.1, so parity work only — no version bump.

  • Streaming segments lost windowStartTimestep. lib/stream.js:148 attaches it to every segment emitted in emit: 'delta' mode, and the addon documents exactly why: a segment's own timestamps are window-local, so this is the only thing that maps them onto the stream timeline. It appeared nowhere in the SDK, leaving delta-mode output unplaceable.
  • Six emitted stats fields never reached callers: processCalls, totalTime, totalWallMs, whisperSampleMs, whisperBatchdMs, whisperPromptMs.
  • Five mapped fields the addon never emits: audioDurationMs, realTimeFactor, encoderMs, decoderMs, melSpecMs were declared on BciAddonResponse and conditionally spread, but BCIModel.cpp emits none of them — they belong to the asr-ggml engines. Harmless at runtime, but the interface promised a shape that cannot arrive.
  • No backend diagnostics, where the asr-ggml ops now have them.
  • detect_language was documented as rejected by the addon. It is in the addon's own validWhisperParams; the wording was inherited from the asr-ggml schema.

📝 How does it solve it?

  • windowStartTimestep is carried on the shared segment shape, and only when the engine sent it — batch segments do not gain a key that would be meaningless.
  • The stats interface is rewritten to the set BCIModel.cpp actually emits, and all six missing fields are mapped. The five phantom fields are gone.
  • Backend diagnostics ride the batch terminal frame, reusing the buildAsrBackendDiagnostics mapping from QVAC-24486 feat[api]: close the SDK gaps against @qvac/asr-ggml 0.5.3 #4564, and the frame attaches the profiling symbol so they land on the profiling event's backend. The streaming path is deliberately untouched: the addon does not populate response.stats for streams, so there is no verdict to derive.
  • detect_language's description now matches the validator.

Where the stats and diagnostics reach. They are on the batch bciTranscribe terminal frame, so RPC consumers see them (Python's bci_transcribe yields that frame) and profiling picks up the diagnostics. The JS convenience functions do not surface them: bciTranscribe() resolves to text or segments only, and neither BCI stream session (JS or Python) has a stats field. Exposing them there changes the public return shapes, so it is left as a follow-up rather than done here.

Verified already at parity and left unchanged: whisperConfig 16/16, contextParams 4/4, miscConfig 1/1, bciConfig 1/1 (day_idx), and the per-call stream options windowTimesteps / hopTimesteps / emit (including the hop < window refine that mirrors the addon's own check).

🧪 How was it tested?

  • inference: 146/146 suites green.
  • SDK typecheck, SDK test-types, and lunte on both packages: clean.
  • contract:export --check and sdk-python generate.py --check: both exit 0, with both zod copies pinned to the version CI resolves.
  • New coverage in bci-schemas.test.ts: windowStartTimestep survives the mapper and is absent on batch segments; the terminal frame accepts a diagnostics payload; the full emitted stats surface round-trips.
  • New bci-reachability.test.ts drives the real handlers rather than the schemas: the batch terminal frame feeds buildOperationEvent's backend, and delta-mode segments keep windowStartTimestep through the handler output. Each test fails against the code it guards (the handler without the symbol attach, and the mapper without the field).

🔌 API Changes

// Delta-mode segments can now be placed on the stream timeline.
for await (const seg of session) {
  if (seg.windowStartTimestep !== undefined) {
    const absoluteStartMs = seg.startMs + timestepsToMs(seg.windowStartTimestep)
  }
}
# The stats the native model actually reports, on the batch terminal frame.
async for frame in bci_transcribe(transport, request):
    if frame.done:
        print(frame.stats.total_wall_ms, frame.stats.process_calls, frame.diagnostics)

📋 Follow-ups

  • BCI stats in the JS API. bciTranscribe() drops the batch terminal frame's stats and diagnostics. Surfacing them means widening its return type, which is an API decision of its own. The stream sessions have nothing to surface until the addon reports stats for streams.
  • computeWER is exported by the addon and surfaced nowhere. Left out on purpose: it is a scoring helper rather than part of the inference path, so exposing it is an API-design call rather than a parity fix.
  • reload() is not a BCI gap. It exists on BCIInterface — the native binding wrapper exposed as the addon escape hatch — but the public BCIWhispercpp class has none, and the SDK's reload path calls model.reload(...). Widening the whisper-only reload union would not reach BCI.

@RamazTs
RamazTs requested review from a team as code owners September 17, 2026 20:13
@github-actions

Copy link
Copy Markdown
Contributor

License compliance — clean

No new dependency license findings in this PR.

Warn-only (shadow) mode — this check does not block merges yet.

Updated automatically by the canonical license compliance workflow.

NOTICE presence (advisory)

Missing NOTICE (advisory, does not block):

  • ./docs/website
  • ./packages/fabric/test/integration
  • ./packages/llm-llamacpp/benchmarks/server
  • ./packages/llm-llamacpp/benchmarks/performance
  • ./packages/inference-addon-cpp/mobile
  • ./packages/asr-ggml/benchmarks/server
  • ./packages/embed-llamacpp/benchmarks/server
  • ./packages/embed-llamacpp/benchmarks/performance
  • ./packages/sdk/e2e
  • ./packages/vla-ggml/sim/server
  • ./.github/actions/release-merge-guard

GustavoA1604
GustavoA1604 previously approved these changes Sep 18, 2026
mexxik
mexxik previously approved these changes Sep 18, 2026
Base automatically changed from feat/QVAC-24486-sdk-asr-ggml-gaps to main September 18, 2026 15:11
@RamazTs
RamazTs dismissed stale reviews from mexxik and GustavoA1604 September 18, 2026 15:11

The base branch was changed.

@RamazTs
RamazTs force-pushed the feat/QVAC-24488-sdk-bci-gaps branch from 31b4a40 to 908a20f Compare September 20, 2026 17:23
@github-actions

Copy link
Copy Markdown
Contributor

Review Status

Current Status: ❌ PENDING
Approvals so far: none

Pending reviews: Needs 1 Management or Team Lead, and 1 more from Management, Team Lead, or Member.

… 0.9.1

Audit of what bci-whispercpp 0.9.1 offers against what the SDK exposes. The
addon is already pinned ^0.9.1, so this is parity work only.

Streaming segments lost windowStartTimestep. lib/stream.js attaches it to every
segment emitted in `emit: 'delta'` mode, and it is the only thing that maps a
segment's window-local timestamps onto the stream timeline — without it a delta
consumer cannot place its own output.

Stats were diffed against BCIModel.cpp rather than against the SDK's own
expectations. The native model emits 15 fields; six never reached callers:
processCalls, totalTime, totalWallMs, whisperSampleMs, whisperBatchdMs and
whisperPromptMs. Five more were declared and mapped that the model never emits
at all — audioDurationMs, realTimeFactor, encoderMs, decoderMs and melSpecMs
belong to the asr-ggml engines, so the op was promising a shape that could not
arrive. Both directions are corrected.

Backend diagnostics now ride the batch terminal frame, reusing the mapping
added for asr-ggml. The streaming path is deliberately left alone: the addon
does not populate response.stats for streams, so there is nothing to derive a
verdict from.

detect_language was documented as "not supported natively (rejected by the
addon)". It is in the addon's own validWhisperParams; the description was
inherited from the asr-ggml schema.

Load config and per-call stream options were already at full parity —
whisperConfig 16/16, contextParams 4/4, miscConfig 1/1, bciConfig 1/1, and
windowTimesteps/hopTimesteps/emit — and are unchanged.
…ep reaches callers

The batch terminal frame carried diagnostics on the wire but never attached
the profiling symbol, so event.backend stayed empty for bciTranscribe. Attach
it the same way the ASR handlers do.

bci-reachability.test.ts drives the real handlers: the batch terminal must
feed buildOperationEvent's backend, and delta-mode segments must keep
windowStartTimestep through the handler output. Both fail against the
previous handler and mapper respectively.

Also document windowStartTimestep on the streaming session JSDoc.
@RamazTs
RamazTs force-pushed the feat/QVAC-24488-sdk-bci-gaps branch from 908a20f to e3a2dff Compare September 20, 2026 17:28
@RamazTs RamazTs added the test-e2e-smoke Triggers smoke e2e test suite [Currently SDK-only] label Sep 20, 2026
The BCI streaming terminal frame carried no modelExecutionMs, unlike every
sibling duplex handler: the stream op measured nothing and returned void. It
now times the model work and returns it, and the handler iterates by hand so
that return value reaches the terminal frame.

Neither BCI operation had a metrics config registered, so the timing and the
stats this branch maps were dropped before reaching an event — only the
backend diagnostics survived, because those are read unconditionally. Both
ops now register one.

The new tests fail without either half: one assertion without the timing
attach, five without the configs.
The segment type, its mapper and the backend-diagnostics helper are shared
with @qvac/bci-whispercpp since this branch, and the stats grouping comment
called the whisper.cpp stage timings whisper-only while BCI maps them too.

Renames the reachability test to the two names the ASR side established:
profiling-backend for the diagnostics, metadata-handlers for the segment.
@github-actions

Copy link
Copy Markdown
Contributor

QVAC E2E — base run in progress

Running: run 35544949877 · suite smoke · sha 11de6e8

The test-e2e-rerun-failed label is rejected until this run finishes and its failure set is recorded below.

@github-actions

Copy link
Copy Markdown
Contributor

QVAC E2E — tests changed by this PR

This smoke run additionally covers 2 test(s) that are not tagged smoke, adding roughly 2.2 min.

Catalog: 526 tests · smoke: 108 · touched by this PR: 3 (1 already in smoke)

Changed file Matched via Tests
packages/inference/src/plugins/builtin/bci-whispercpp-transcription inference engine 3
packages/sdk/src/client/api/bci-transcribe.ts sdk api 3
2 added test id(s)
bci-transcribe-stream
bci-transcribe-error-missing-file

@github-actions

github-actions Bot commented Sep 20, 2026

Copy link
Copy Markdown
Contributor

QVAC E2E — windows⚠️ no results

Config: suite=smoke · filter=(none) · exclude=(none)
Inference: branch:11de6e81699cf8c5cc644b73a3caf7d801b735c4:@qvac/inference@0.19.1 · Test-suite: branch:11de6e81699cf8c5cc644b73a3caf7d801b735c4:@qvac/test-suite@0.11.3
View run

The test job did not produce a results artifact (e.g. no device started within the start-timeout, or a job-level failure). Check the run and the device pool status above.

@github-actions

github-actions Bot commented Sep 20, 2026

Copy link
Copy Markdown
Contributor

QVAC E2E — android⚠️ no results

Config: suite=smoke · filter=(none) · exclude=(none)
Inference: branch:11de6e81699cf8c5cc644b73a3caf7d801b735c4:@qvac/inference@0.19.1 · Test-suite: branch:11de6e81699cf8c5cc644b73a3caf7d801b735c4:@qvac/test-suite@0.11.3
View run

The test job did not produce a results artifact (e.g. no device started within the start-timeout, or a job-level failure). Check the run and the device pool status above.

@github-actions

github-actions Bot commented Sep 20, 2026

Copy link
Copy Markdown
Contributor

QVAC E2E — ios⚠️ no results

Config: suite=smoke · filter=(none) · exclude=(none)
Inference: branch:11de6e81699cf8c5cc644b73a3caf7d801b735c4:@qvac/inference@0.19.1 · Test-suite: branch:11de6e81699cf8c5cc644b73a3caf7d801b735c4:@qvac/test-suite@0.11.3
View run

The test job did not produce a results artifact (e.g. no device started within the start-timeout, or a job-level failure). Check the run and the device pool status above.

@github-actions

github-actions Bot commented Sep 20, 2026

Copy link
Copy Markdown
Contributor

QVAC E2E — linux⚠️ no results

Config: suite=smoke · filter=(none) · exclude=(none)
Inference: branch:11de6e81699cf8c5cc644b73a3caf7d801b735c4:@qvac/inference@0.19.1 · Test-suite: branch:11de6e81699cf8c5cc644b73a3caf7d801b735c4:@qvac/test-suite@0.11.3
View run

The test job did not produce a results artifact (e.g. no device started within the start-timeout, or a job-level failure). Check the run and the device pool status above.

@github-actions

github-actions Bot commented Sep 20, 2026

Copy link
Copy Markdown
Contributor

QVAC E2E — macos⚠️ no results

Config: suite=smoke · filter=(none) · exclude=(none)
Inference: branch:11de6e81699cf8c5cc644b73a3caf7d801b735c4:@qvac/inference@0.19.1 · Test-suite: branch:11de6e81699cf8c5cc644b73a3caf7d801b735c4:@qvac/test-suite@0.11.3
View run

The test job did not produce a results artifact (e.g. no device started within the start-timeout, or a job-level failure). Check the run and the device pool status above.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

test-e2e-smoke Triggers smoke e2e test suite [Currently SDK-only]

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants