-
Notifications
You must be signed in to change notification settings - Fork 467
fix(stt): improve readability and reliability in apps/api/src/stt #2118
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Wrap normalizeWsData call in try/catch to prevent unhandled promise rejections from crashing the process. Errors are now captured to Sentry and the connection is gracefully closed. Co-Authored-By: yujonglee <[email protected]>
Validate that user-provided upstream URLs are valid and use ws: or wss: protocol to prevent SSRF attacks and invalid URL errors. Provides clear error messages for debugging. Co-Authored-By: yujonglee <[email protected]>
Standardize error handling across all STT providers by adding explicit API key validation to Deepgram. Now all three providers (Deepgram, AssemblyAI, Soniox) throw descriptive errors when API keys are missing. Co-Authored-By: yujonglee <[email protected]>
Add explicit validation for the provider query parameter instead of silently falling back to deepgram for unknown providers. Now throws a descriptive error listing valid providers when an invalid provider is specified. Co-Authored-By: yujonglee <[email protected]>
Add comment explaining why clientSocket must be set before ensureUpstreamSocket() to prevent race conditions when the upstream connection becomes ready during initialization. Co-Authored-By: yujonglee <[email protected]>
Reset the hasTransformedFirst flag when closing connections to ensure the transform is applied correctly if the connection instance is reused. This prevents stale state from affecting subsequent connections. Co-Authored-By: yujonglee <[email protected]>
Add explanatory comments for magic numbers and rename timeout constants to include _MS suffix for clarity: - UPSTREAM_ERROR_TIMEOUT -> UPSTREAM_ERROR_GRACE_MS - UPSTREAM_CONNECT_TIMEOUT -> UPSTREAM_CONNECT_TIMEOUT_MS Co-Authored-By: yujonglee <[email protected]>
Rename methods to better describe what they're flushing: - flushPendingMessages -> flushUpstreamQueue - flushDownstreamMessages -> flushDownstreamQueue This makes it clearer that these methods flush queues in different directions (to upstream server vs to downstream client). Co-Authored-By: yujonglee <[email protected]>
Add comment explaining why binary payloads are cloned - WebSocket message buffers may be reused or invalidated after the event handler returns, so copying prevents use-after-free issues when payloads are queued. Co-Authored-By: yujonglee <[email protected]>
🤖 Devin AI EngineerI'll be helping with this pull request! Here's what you should know: ✅ I will automatically:
Note: I can only respond to comments from users who have write access to this repository. ⚙️ Control Options:
|
✅ Deploy Preview for hyprnote ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
✅ Deploy Preview for hyprnote-storybook ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
📝 WalkthroughWalkthroughThe PR refactors and enhances the STT proxy system with improved error handling, validation, and resource management. Changes include renaming queue methods, adding timing constants, validating provider configuration and upstream URLs, enforcing environment variable requirements, and implementing binary payload cloning to prevent use-after-free conditions. Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes
Possibly related PRs
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this 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
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
apps/api/src/stt/utils.ts (1)
12-22: ArrayBuffer path still returns a non‑cloned view; use cloneBinaryPayload to fully avoid buffer reuse issues.
cloneBinaryPayloadcorrectly deep‑copiesArrayBuffer/views, butnormalizeWsDataonly uses it forUint8ArrayandArrayBuffer.isView(...). For plainArrayBufferyou still donew Uint8Array(data), which creates a new view over the same underlying buffer rather than copying it. SinceWsProxyConnectionsetsbinaryType = "arraybuffer"for the upstream socket,event.datawill typically be anArrayBuffer, so queued payloads can still alias a buffer that the WebSocket implementation may later reuse.To make the “ownership” guarantee in the comment accurate and avoid subtle corruption, route the
ArrayBuffercase throughcloneBinaryPayloadas well:if (data instanceof Uint8Array) { return cloneBinaryPayload(data); } if (data instanceof ArrayBuffer) { - return new Uint8Array(data); + return cloneBinaryPayload(data); } if (ArrayBuffer.isView(data)) { return cloneBinaryPayload(data); }Optionally, you could simplify further by having
cloneBinaryPayloadhandle all non‑string/non‑Blob binary payloads and call it unconditionally for those.Also applies to: 32-43
🧹 Nitpick comments (3)
apps/api/src/stt/index.ts (2)
17-25: Provider whitelist and type guard are aligned with SttProvider.Defining
VALID_PROVIDERSasreadonly SttProvider[]plusisValidProvidergives you a single source of truth for allowed providers while keeping nice type‑level narrowing. This matches theSttProviderunion and keeps the switch below exhaustive.
35-47: Upstream override URL parsing and protocol enforcement improve safety.Wrapping
new URL(upstreamOverride)in a try/catch and explicitly checking forws:/wss:prevents invalid or non‑WebSocket URLs from being proxied, which is a good hardening step for this header‑driven override.apps/api/src/stt/connection.ts (1)
11-18: Upstream lifecycle, error handling, and queue management refactor look solid.
- New timing constants (
UPSTREAM_ERROR_GRACE_MS,UPSTREAM_CONNECT_TIMEOUT_MS) and the grace‑period timeout around upstream errors are well‑scoped and cleared reliably.- Resetting
hasTransformedFirstincloseConnectionsavoids stale state when reusing a WsProxyConnection.- Splitting queue flushing into
flushUpstreamQueueandflushDownstreamQueue, and calling both on upstream"open"(and when upstream is already ready ininitializeUpstream), fixes the race where the upstream could become ready beforeclientSocketwas set and ensures queued messages are drained as soon as both ends are usable.- Wrapping
normalizeWsData(event.data)in a try/catch with Sentry capture and closing on"message_normalize_failed"improves observability and avoids silent corruption when upstream sends malformed or unsupported payload types.preconnectUpstream’s timeout handling is straightforward and cleans up the timer in all paths; errors correctly triggercloseConnections("upstream_connect_failed")before rethrowing.- First‑message transformation logic in
sendToUpstreamis now idempotent and only applied once, and control vs data messages are queued separately with a clear backpressure limit for the upstream side.If you want to harden further in the future, you might consider adding a similar size budget for
pendingDownstreamMessagesto guard against a misbehaving upstream when the client is slow or disconnected, but that’s an optional enhancement—not a blocker for this PR.Also applies to: 66-75, 188-189, 191-244, 269-279, 281-293, 326-365, 368-431
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
apps/api/src/stt/connection.ts(7 hunks)apps/api/src/stt/deepgram.ts(1 hunks)apps/api/src/stt/index.ts(3 hunks)apps/api/src/stt/utils.ts(1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.ts
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.ts: Agent implementations should use TypeScript and follow the established architectural patterns defined in the agent framework
Agent communication should use defined message protocols and interfaces
Files:
apps/api/src/stt/deepgram.tsapps/api/src/stt/utils.tsapps/api/src/stt/index.tsapps/api/src/stt/connection.ts
**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{ts,tsx}: Avoid creating a bunch of types/interfaces if they are not shared. Especially for function props, just inline them instead.
Never do manual state management for form/mutation. Use useForm (from tanstack-form) and useQuery/useMutation (from tanstack-query) instead for 99% of cases. Avoid patterns like setError.
If there are many classNames with conditional logic, usecn(import from@hypr/utils). It is similar toclsx. Always pass an array and split by logical grouping.
Usemotion/reactinstead offramer-motion.
Files:
apps/api/src/stt/deepgram.tsapps/api/src/stt/utils.tsapps/api/src/stt/index.tsapps/api/src/stt/connection.ts
🧬 Code graph analysis (1)
apps/api/src/stt/connection.ts (1)
apps/api/src/stt/utils.ts (1)
normalizeWsData(5-30)
🪛 GitHub Actions: .github/workflows/api_ci.yaml
apps/api/src/stt/index.ts
[error] 64-64: TypeScript error during typecheck. TS2322: Type 'string | null' is not assignable to type 'SttProvider'. Command failed: 'pnpm -F @hypr/api typecheck'.
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
- GitHub Check: Redirect rules - hyprnote
- GitHub Check: Header rules - hyprnote
- GitHub Check: Pages changed - hyprnote
- GitHub Check: fmt
- GitHub Check: Devin
🔇 Additional comments (1)
apps/api/src/stt/deepgram.ts (1)
21-24: API key guard and cached header usage look good.Validating
env.DEEPGRAM_API_KEYup front and reusing the cachedapiKeyin the Authorization header improves reliability and avoids silent misconfiguration. Just make sure callers surface this error in a controlled way (e.g., returning a 5xx) rather than crashing the whole process.Also applies to: 29-30
Fix type narrowing issue where providerParam could be null when assigned to provider variable. Now properly checks providerParam is truthy before using the type guard result. Co-Authored-By: yujonglee <[email protected]>
fix(stt): improve readability and reliability in apps/api/src/stt
Summary
This PR improves the STT (speech-to-text) proxy module with better error handling, input validation, and code readability.
Reliability improvements:
hasTransformedFirstflag incloseConnectionsto prevent stale state if connection is reusedReadability improvements:
_MSsuffix for clarityflushPendingMessages→flushUpstreamQueue,flushDownstreamMessages→flushDownstreamQueueinitializeUpstreamUpdates since last revision
providerParamcould benullwhen assigned toprovidervariable (required for CI to pass)Review & Testing Checklist for Human
providerquery params silently fell back to deepgram. Now throws an error. Verify no existing clients pass invalid provider values.DEEPGRAM_API_KEYis missing. Previously would passundefinedto Authorization header. Verify this is acceptable.ws:orwss:protocol. Verify no legitimate use cases are broken.Recommended test plan:
providerquery param and verify it returns an error (not silent fallback)DEEPGRAM_API_KEYconfigured before mergingNotes
The
WsProxyConnectionclass has no integration test coverage, so these changes rely on code review and manual testing. The existing unit tests for utility functions pass.Skipped larger refactoring (grouping state variables, extracting common flush logic) as too risky without test coverage.
Link to Devin run: https://app.devin.ai/sessions/ed7a91a96e0a463e8617293fbadeb715
Requested by: yujonglee ([email protected]) (@yujonglee)