Skip to content

Comments

Release 2026-02-09: Merge staging with profiling toggle fix#34

Merged
ysdede merged 26 commits intoreleasefrom
staging/release-2026-02-09
Feb 9, 2026
Merged

Release 2026-02-09: Merge staging with profiling toggle fix#34
ysdede merged 26 commits intoreleasefrom
staging/release-2026-02-09

Conversation

@ysdede
Copy link
Owner

@ysdede ysdede commented Feb 9, 2026

Merges staging/release-2026-02-09 into release branch.

Includes:

All tests passing (77 tests).

Summary by CodeRabbit

  • New Features

    • Added profiling toggle control to demo applications to enable/disable performance monitoring at runtime.
    • Added cache management controls: configurable incremental cache size limit and explicit cache clearing capability.
  • Bug Fixes

    • Improved WASM runtime version detection for better compatibility.
    • Enhanced tokenizer performance with optimized token decoding.
  • Performance

    • Optimized incremental mel processor with incremental computation support.
    • Reduced memory allocations in cache operations.

ysdede and others added 26 commits February 7, 2026 20:34
The `transcribe` method is now fully implemented in `ParakeetModel`, supporting various features like streaming and timestamps. The TODO comment indicating it was an early scaffold was outdated and misleading. This commit removes that comment to improve documentation accuracy.

Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
globalThis.ort is sufficient as globalThis covers self in modern environments.
This simplifies the code and removes an unnecessary check.

Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
- Replace dynamic `import(...)` with static top-level imports for `ParakeetModel`, `getParakeetModel`, and `MODELS`.
- Remove `await` on imports.
- Update `fromUrls` and `fromHub` to use imported modules.
- Add `tests/index.test.mjs` to verify `fromUrls` and `fromHub` behavior.
- Ensure `package-lock.json` is consistent with `package.json`.

Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
- Updated `transcribe` method in `src/parakeet.js` to accept `enableProfiling` option.
- Changed performance logging logic to depend on `debug` or `enableProfiling` options.
- Added test case `tests/transcribe_perf.test.mjs` to verify conditional metrics collection.

Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
…wMel` and `normalizeFeatures`

This change eliminates duplicated logic in the `process` method of `JsPreprocessor` by reusing the existing `computeRawMel` and `normalizeFeatures` methods. This improves maintainability and ensures consistency between full and incremental processing pipelines.

The logic for handling empty audio or zero features is preserved. All tests in `tests/mel.test.mjs` and `tests/test_mel.mjs` pass.

Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
Consolidates redundant method implementations in IncrementalMelProcessor.
The clear() method now calls reset() to ensure consistent behavior and
reduce code duplication.

Verified with updated tests in tests/mel.test.mjs.

Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
Add `maxIncrementalCacheSize` option (default 50) to ParakeetModel constructor and
`clearIncrementalCache()` method. Implement LRU eviction logic in `transcribe` to
prevent unbounded growth of `_incrementalCache` during long-running sessions.

Fixes potential memory leak where unique cache keys would accumulate indefinitely.

Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
Replaced hardcoded version string in `src/backend.js` with `ort.env.versions.common`.
This ensures that the WASM binary URL always matches the loaded JS runtime version, improving maintainability and preventing version mismatches.
Added error handling if the version cannot be detected.

Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
Replaced `Array.prototype.slice()` with `TypedArray.prototype.subarray()` for `tokenLogits` and `durLogits` extraction.
This creates a view on the existing buffer instead of copying data, resulting in a ~22x speedup for this operation in microbenchmarks (25ms -> 1ms for 10M iterations).

Verified with `bun test` and benchmark script.

Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
- Modify `JsPreprocessor.computeRawMel` to accept `startFrame` to skip redundant STFT/Mel computations for cached prefixes.
- Update `IncrementalMelProcessor.process` to utilize `startFrame` for a ~3x speedup in streaming scenarios (26ms vs 78ms).
- Refactor `IncrementalMelProcessor.process` non-cached path to avoid double computation, yielding a ~2x speedup (78ms vs 153ms).

Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
- Pre-compute sanitized tokens (SentencePiece marker replacement) in `ParakeetTokenizer` constructor.
- Use `this.sanitizedTokens` lookup in `decode` method instead of repeated regex replacement.
- Optimize blank token check to use integer comparison (`id === this.blankId`) instead of string comparison.
- Benchmark showed ~36% speedup (748ms -> 477ms for 1M tokens).

Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
…893802667249' into staging/release-2026-02-09
…93746588552' into staging/release-2026-02-09
…53576085822775' into staging/release-2026-02-09
…5019006915315' into staging/release-2026-02-09
…2663042696932520' into staging/release-2026-02-09
…163951053442' into staging/release-2026-02-09
…74266680745745899' into staging/release-2026-02-09
…0997344201347' into staging/release-2026-02-09
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@tests/transcribe_perf.test.mjs`:
- Around line 22-24: The mock encoder output shape doesn't match how parakeet.js
extracts the tensor: parakeet code does enc = encOut['outputs'] ??
Object.values(encOut)[0] and then uses enc.dims, so update the test's
mockEncoderSession.run to resolve to a tensor-like object (i.e., the resolved
value should be an object whose top-level properties are dims and data) or
ensure the 'outputs' key itself is the tensor-like object; modify
mockEncoderSession.run.mockResolvedValue accordingly so it returns either {
dims: [1,64,10], data: new Float32Array(640) } or { outputs: { dims: [...],
data: ... } } to match how enc is consumed in parakeet.js (referencing
mockEncoderSession.run, encOut and enc).
🧹 Nitpick comments (2)
tests/index.test.mjs (2)

31-51: Consider adding test coverage for model key resolution.

The fromHub function resolves model keys via MODELS[repoIdOrModelKey]?.repoId (see src/index.js line 33), but this test only covers the pass-through case where the input is not a known model key. Consider adding a test case that verifies the model key resolution path:

it('fromHub should resolve model key to repo ID', async () => {
  // You'd need to mock MODELS or use an actual key from MODELS
  // to verify that the resolved repoId is passed to getParakeetModel
});

24-52: Add mock cleanup between tests.

If additional tests are added later, mock state from previous tests could leak and cause flaky behavior. Consider adding:

 describe('index.js', () => {
+  beforeEach(() => {
+    vi.clearAllMocks();
+  });
+
   it('fromUrls should call ParakeetModel.fromUrls', async () => {

Comment on lines +22 to +24
const mockEncoderSession = {
run: vi.fn().mockResolvedValue({ outputs: { dims: [1, 64, 10], data: new Float32Array(640) } }),
};
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Mock encoder output format may not match actual usage.

The mock returns { outputs: { dims: [...], data: ... } }, but in parakeet.js line 543, the code does enc = encOut['outputs'] ?? Object.values(encOut)[0], then later accesses enc.dims. This means enc should be a tensor-like object directly, not a wrapper with dims inside.

Proposed fix for mock format
 const mockEncoderSession = {
-  run: vi.fn().mockResolvedValue({ outputs: { dims: [1, 64, 10], data: new Float32Array(640) } }),
+  run: vi.fn().mockResolvedValue({
+    outputs: new mockOrt.Tensor('float32', new Float32Array(640), [1, 64, 10])
+  }),
 };
🤖 Prompt for AI Agents
In `@tests/transcribe_perf.test.mjs` around lines 22 - 24, The mock encoder output
shape doesn't match how parakeet.js extracts the tensor: parakeet code does enc
= encOut['outputs'] ?? Object.values(encOut)[0] and then uses enc.dims, so
update the test's mockEncoderSession.run to resolve to a tensor-like object
(i.e., the resolved value should be an object whose top-level properties are
dims and data) or ensure the 'outputs' key itself is the tensor-like object;
modify mockEncoderSession.run.mockResolvedValue accordingly so it returns either
{ dims: [1,64,10], data: new Float32Array(640) } or { outputs: { dims: [...],
data: ... } } to match how enc is consumed in parakeet.js (referencing
mockEncoderSession.run, encOut and enc).

@ysdede
Copy link
Owner Author

ysdede commented Feb 9, 2026

Post-merge review of all PRs included in this staging branch

All PRs in this branch (#18-#31) were created by Google Jules (coding agent). I reviewed every diff for correctness, backward compatibility, and potential risks. Below is the full assessment.


Safe -- No Issues Found

PR Change Verdict
#18 Remove outdated TODO comment Harmless cleanup, zero risk
#19 Remove redundant self.ort assignment Harmless cleanup, zero risk
#23 clear() becomes alias for reset() Trivial DRY improvement, test updated
#22 Refactor JsPreprocessor.process to reuse computeRawMel + normalizeFeatures Good refactor, eliminates 97 lines of duplication
#30 Pre-compute sanitized tokens in tokenizer constructor Clean hot-path optimization, measurable speedup
#29 Optimize incremental mel with startFrame parameter Best of 3 competing mel PRs, correct and well-tested
#31 Reuse LCS buffer in merger Correct typed-array reuse, reduces GC pressure

Needs Attention -- Issues Filed

#28 - slice() to subarray() in decoding loop (Low-Medium risk)
subarray() returns a view into the original buffer, not a copy. Currently safe (read-only use), but if anyone later mutates tokenLogits or durLogits in-place (temperature scaling, softmax, etc.), they will silently corrupt the joiner output buffer. Needs a safety comment at the call site.

#20 - Dynamic imports replaced with static imports (Medium risk)
The original dynamic import() calls enabled lazy loading / code-splitting. Static top-level imports mean all modules load eagerly. Fine for bundlers with tree-shaking, but loses code-splitting for non-bundled ESM, CDN usage, or serverless. The agent did not acknowledge this trade-off.

#21 - Performance logging made conditional (Medium-High risk, handled by @ysdede)
perfEnabled changed from true (always) to debug || enableProfiling (off by default). This is a breaking API change: result.metrics is now null by default. Any code reading result.metrics.total_ms without a null check will crash. Fixed separately via the chore/demo-profiling-toggle branch merged into this staging.

#25 - LRU eviction for incremental decoder cache (Medium risk)
Good fix for unbounded memory growth, but eviction is completely silent. No log or warning when entries are dropped. For multi-stream apps, this can cause invisible performance degradation as the incremental path falls back to full recomputation. Default of 50 may also need tuning guidance.

#26 - Derive ort version from runtime (Medium-High risk)
Replaces hardcoded version with ort.env.versions?.common. If that property doesn't exist (older ort versions, custom builds), the code throws, completely blocking initialization. The old hardcoded value always worked. Needs a fallback + warning instead of a hard throw.


Excluded PRs (correct decisions)

PR Reason
#33 CodeRabbit auto-generated tests -- tests were failing
#24 Superseded by #29 (same mel optimization, less complete)
#27 Superseded by #29 (same mel optimization, less complete)

Overall Assessment

The agent produced reasonable, well-tested changes. Most are genuinely useful (mel optimization, tokenizer speedup, LRU cache, deduplication). However, it was too eager to "improve" things without considering backward compatibility:

Recommendation: Address issues #35, #36, #37, #38 before merging this staging branch into release. #36 (ort version fallback) is the highest priority since it can cause hard failures.

@ysdede ysdede merged commit 189afbb into release Feb 9, 2026
1 check passed
Repository owner deleted a comment from coderabbitai bot Feb 17, 2026
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.

1 participant