Skip to content

feat(ai): migrate localNlpService onto WorkerBus v2, delete v1 inference worker - #290

Open
qnbs wants to merge 2 commits into
feat/worker-v2-embedding-migrationfrom
feat/worker-v2-nlp-migration
Open

feat(ai): migrate localNlpService onto WorkerBus v2, delete v1 inference worker#290
qnbs wants to merge 2 commits into
feat/worker-v2-embedding-migrationfrom
feat/worker-v2-nlp-migration

Conversation

@qnbs

@qnbs qnbs commented Jul 29, 2026

Copy link
Copy Markdown
Owner

Summary

Fourth and final migration PR of the worker-generation consolidation (ADR-0014). Stacked on #288#287#286. Plan: .claude/plans/worker-generation-v1-vs-enumerated-fox.md.

  • services/ai/localNlpService.ts's public API (analyzeSentiment/summarizeText/classifyWritingTopic) is unchanged — confirmed zero real production callers beyond its own tests and a barrel re-export in services/ai/index.ts, so this is the lowest-blast-radius of the three service migrations. Internals swapped from a dedicated new Worker(inference.worker.ts) instance to the shared WorkerBus v2 'inference' pool via ensureInferencePool() (added in feat(ai): migrate localEmbeddingService onto WorkerBus v2 #288), routing to the 'inference.text' capability.
  • analyzeSentiment/summarizeText already had their own app-level graceful-degrade behavior on failure (NEUTRAL sentiment / truncated text) — preserved unchanged via try/catch around the new throw-on-failure requestInference() call.
  • With both localEmbeddingService.ts (feat(ai): migrate localEmbeddingService onto WorkerBus v2 #288) and this migrated, workers/inference.worker.ts (v1) has no remaining callers — deleted, along with its dedicated test (tests/unit/inferenceWorker.test.ts).
  • tests/unit/localNlpService.test.ts rewritten against a workerBusManager mock; tests/unit/services/localNlpService.test.ts deleted as a near-total duplicate (same module, overlapping classifyWritingTopic/analyzeSentiment coverage the rewritten suite already has).

Verified via typecheck and a full local pnpm run build:edge reproduction of Vercel's exact build command — the lesson from #288's missing-export incident (typecheck/Vitest alone didn't catch that one; only the real build did).

Test plan

  • pnpm exec vitest run tests/unit/localNlpService.test.ts tests/unit/inferenceWorkerHandlerV2.test.ts — 30/30 passing
  • pnpm run typecheck (exact CI command) — clean
  • NODE_OPTIONS=--max-old-space-size=3072 pnpm run build:edge (exact Vercel build command) — clean, verified end-to-end
  • pnpm run lint (Biome) — clean via pre-commit hook
  • CI quality gate green

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes
    • Improved reliability of local sentiment analysis and text summarization.
    • Added graceful fallbacks when local language processing is unavailable or encounters an error.
    • Preserved input limits to support more consistent processing and performance.
    • Improved sentiment handling for unrecognized results by defaulting to a neutral classification.
    • Updated the local processing infrastructure for more stable inference execution.

…nce worker

Fourth and final migration PR of the worker-generation consolidation
(ADR-0014). services/ai/localNlpService.ts's public API
(analyzeSentiment/summarizeText/classifyWritingTopic) is unchanged —
confirmed zero real production callers beyond its own tests and a
barrel re-export in services/ai/index.ts, so this is the
lowest-blast-radius of the three migrations. Internals swapped from a
dedicated `new Worker(inference.worker.ts)` instance to the shared
WorkerBus v2 'inference' pool via ensureInferencePool() (added in the
previous PR), routing to the 'inference.text' capability.

analyzeSentiment/summarizeText already had their own app-level
graceful-degrade behavior on failure (NEUTRAL sentiment / truncated
text) — that's preserved unchanged via try/catch around the new
throw-on-failure requestInference() call, replacing the old
ok:false-check.

With both localEmbeddingService.ts (previous PR) and this migrated,
workers/inference.worker.ts (v1) has no remaining callers — deleted,
along with its dedicated test (tests/unit/inferenceWorker.test.ts).
tests/unit/localNlpService.test.ts rewritten against a
workerBusManager mock; tests/unit/services/localNlpService.test.ts
deleted as a near-total duplicate of the rewritten suite (same module,
overlapping classifyWritingTopic/analyzeSentiment coverage, the
comprehensive file already had everything it did).

Verified via typecheck and a full local `pnpm run build:edge`
reproduction of Vercel's exact build command (the lesson from the
previous PR's missing-export incident) — both clean.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@vercel

vercel Bot commented Jul 29, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
worldscript-studio Ready Ready Preview Jul 29, 2026 10:38pm

@coderabbitai

coderabbitai Bot commented Jul 29, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: f8f0a7e6-edae-44b0-96da-21163172f90b

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Comment @coderabbitai help to get the list of available commands.

@codeant-ai

codeant-ai Bot commented Jul 29, 2026

Copy link
Copy Markdown

🏁 CodeAnt Quality Gate Results

Commit: 45191803
Scan Time: 2026-07-29 22:17:10 UTC

✅ Overall Status: PASSED

Quality Gate Details

Quality Gate Status Details
Secrets ✅ PASSED 0 secrets found
Duplicate Code ✅ PASSED 0.0% duplicated
SAST ✅ PASSED No security issues
Bugs ✅ PASSED Rating S: No bugs
IAC ✅ PASSED No IAC issues

View Full Results

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (2)
tests/unit/localNlpService.test.ts (1)

45-100: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Solid coverage of sentiment parsing/fallback; missing a dedicated "pool unavailable" test.

The suite exercises label parsing, truncation, and enqueue wiring well. It doesn't add a test for ensureInferencePool() resolving to null (the !bus throw branch in requestInference), which is a distinct, real-world path (e.g., unsupported environment) beyond a generic rejected task. It's implicitly covered via the shared catch behavior, but an explicit test would pin down that specific contract.

🧪 Example addition
+  it('returns NEUTRAL fallback when the inference pool is unavailable', async () => {
+    mockEnsureInferencePool.mockResolvedValue(null);
+    const r = await analyzeSentiment('anything');
+    expect(r.label).toBe('NEUTRAL');
+  });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/unit/localNlpService.test.ts` around lines 45 - 100, Add a dedicated
test in the analyzeSentiment suite that makes ensureInferencePool resolve to
null, exercising requestInference’s !bus throw branch. Assert that
analyzeSentiment preserves the same NEUTRAL fallback contract as task rejection,
including score 0.5 and normalized 0.
services/ai/localNlpService.ts (1)

51-54: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Catch blocks silently swallow all inference failures without logging.

Both fallbacks catch every error (OOM, pool unavailable, malformed result, etc.) and return a static fallback with no call into services/logger.ts. Per the **/*.{ts,tsx,js,jsx} guideline, "technical details may only be emitted through services/logger.ts using Biome-compliant warn or error" and silent swallowing is only permitted for "documented aborts." Additionally, summarizeText's catch (lines 65-67) lacks the QNBS-v3 doc comment that analyzeSentiment's catch (line 52) has, so it isn't even textually documented.

Add a logger.warn (with the caught error, no payload/PII) in both catches, and give summarizeText's catch a QNBS-v3 comment matching the one on analyzeSentiment.

♻️ Example
   } catch {
+    // QNBS-v3: [Graceful degrade — same fallback v1 returned on any worker failure.]
     return text.slice(0, 280); // graceful degrade
   }

Also applies to: 65-67

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@services/ai/localNlpService.ts` around lines 51 - 54, Update the catch blocks
in analyzeSentiment and summarizeText to accept the caught error and call
services/logger.ts through logger.warn with the error only, avoiding payload or
PII. Add the matching QNBS-v3 graceful-degrade comment to summarizeText’s catch
while preserving both existing fallback results.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@services/ai/localNlpService.ts`:
- Around line 15-32: Update requestInference so the enqueue payload omits
inferenceOptions when it is undefined, while preserving the property when
options are provided; adjust the payload construction around bus.enqueue and
keep the existing request type contract intact.

---

Nitpick comments:
In `@services/ai/localNlpService.ts`:
- Around line 51-54: Update the catch blocks in analyzeSentiment and
summarizeText to accept the caught error and call services/logger.ts through
logger.warn with the error only, avoiding payload or PII. Add the matching
QNBS-v3 graceful-degrade comment to summarizeText’s catch while preserving both
existing fallback results.

In `@tests/unit/localNlpService.test.ts`:
- Around line 45-100: Add a dedicated test in the analyzeSentiment suite that
makes ensureInferencePool resolve to null, exercising requestInference’s !bus
throw branch. Assert that analyzeSentiment preserves the same NEUTRAL fallback
contract as task rejection, including score 0.5 and normalized 0.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 8cc231a7-0b99-4d4c-8a6e-61a62455d021

📥 Commits

Reviewing files that changed from the base of the PR and between 776ff4e and 4519180.

📒 Files selected for processing (7)
  • services/ai/localNlpService.ts
  • services/ai/pipelineLruCache.ts
  • services/voice/wasmSttEngine.ts
  • tests/unit/inferenceWorker.test.ts
  • tests/unit/localNlpService.test.ts
  • tests/unit/services/localNlpService.test.ts
  • workers/inference.worker.ts
💤 Files with no reviewable changes (3)
  • tests/unit/inferenceWorker.test.ts
  • tests/unit/services/localNlpService.test.ts
  • workers/inference.worker.ts

Comment thread services/ai/localNlpService.ts
…tOptionalPropertyTypes)

localNlpService.requestInference() spread inferenceOptions into the enqueue
payload via object-literal shorthand, so calls without a 4th arg (analyzeSentiment)
set the key to an explicit `undefined`. With exactOptionalPropertyTypes the
optional payload field only accepts the property being absent, not present-with-undefined.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
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