added 60DB services#14
Conversation
📝 WalkthroughWalkthroughThis pull request integrates 60db Speech-to-Text as a new ASR provider into DeLive. It adds vendor types, a WebSocket proxy that bridges client requests to 60db's upstream API, a frontend provider implementation, registration infrastructure, and a UI logo component. Changes60db Speech-to-Text Provider
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ 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.
Actionable comments posted: 3
🧹 Nitpick comments (1)
frontend/src/providers/implementations/SixtydbProvider.ts (1)
199-202: 💤 Low valueGuard against race condition in async Blob conversion.
If the WebSocket closes during the async
arrayBuffer()call,this.wscould become null, causing the optional chaining to silently drop audio. Consider checking readiness after the await.Proposed fix
if (data instanceof Blob) { data.arrayBuffer().then(buffer => { - this.ws?.send(buffer) + if (this.ws && this.wsReady) { + this.ws.send(buffer) + } }) } else {🤖 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 `@frontend/src/providers/implementations/SixtydbProvider.ts` around lines 199 - 202, In SixtydbProvider (the method sending audio blobs), avoid the race by awaiting the Blob.arrayBuffer() call instead of using .then, then re-check that this.ws exists and is open (WebSocket.readyState === WebSocket.OPEN) before calling this.ws.send; also wrap the await/send in try/catch to handle errors and avoid silently dropping audio when the socket closes during conversion.
🤖 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 `@frontend/src/providers/implementations/SixtydbProvider.ts`:
- Around line 92-98: The connect method in SixtydbProvider (connect(config:
ProviderConfig)) calls this.emitError(this.createError(...)) when apiKey is
missing but never rejects or throws, leaving the caller hanging; after emitting
the error you should reject the Promise by throwing the created error (or
returning Promise.reject) so callers receive the failure. Update the connect
implementation to create the error via this.createError('MISSING_API_KEY', '60db
API key is required'), call this.emitError(error), then throw that error (or
return Promise.reject(error)) so the Promise is settled.
- Around line 138-142: In the 'final' switch branch inside SixtydbProvider (the
case handling final messages) remove the call to this.emitFinished() so that
emitFinal(msg.text || '') is emitted without signaling session end; instead
ensure emitFinished() is only invoked from real session-termination paths (e.g.,
the session_stopped/disconnect handler or wherever session lifecycle is
explicitly closed) so multiple 'final' transcripts during continuous
transcription don't trigger end-of-session callbacks.
In `@shared/sixtydbProxyCore.ts`:
- Around line 197-201: The clientWs 'error' handler closes the upstream without
checking its WebSocket state, which can attempt to close an already-closed
connection; update the clientWs.on('error', ...) handler (the same handler that
sets clientClosed) to first check upstream.readyState (or upstream.readyState
=== WebSocket.OPEN/CONNECTING depending on your desired behavior) before calling
upstream.close(1000, 'Client error'), mirroring the readiness check used in the
clientWs.on('close') handler so upstream.close is only invoked when appropriate.
---
Nitpick comments:
In `@frontend/src/providers/implementations/SixtydbProvider.ts`:
- Around line 199-202: In SixtydbProvider (the method sending audio blobs),
avoid the race by awaiting the Blob.arrayBuffer() call instead of using .then,
then re-check that this.ws exists and is open (WebSocket.readyState ===
WebSocket.OPEN) before calling this.ws.send; also wrap the await/send in
try/catch to handle errors and avoid silently dropping audio when the socket
closes during conversion.
🪄 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: 4c0f63f0-1419-4e8a-8aad-049a0b347ee7
📒 Files selected for processing (9)
frontend/src/components/icons/ProviderLogos.tsxfrontend/src/providers/implementations/SixtydbProvider.tsfrontend/src/providers/index.tsfrontend/src/providers/registry.tsfrontend/src/types/asr/common.tsfrontend/src/types/asr/vendors/sixtydb.tsserver/src/index.tsserver/src/sixtydbProxy.tsshared/sixtydbProxyCore.ts
| async connect(config: ProviderConfig): Promise<void> { | ||
| const apiKey = config.apiKey as string | ||
|
|
||
| if (!apiKey) { | ||
| this.emitError(this.createError('MISSING_API_KEY', '60db API key is required')) | ||
| return | ||
| } |
There was a problem hiding this comment.
Reject promise when API key is missing.
When apiKey is missing, emitError is called but the Promise returned by connect() is never resolved or rejected. This leaves the caller hanging indefinitely.
Proposed fix
async connect(config: ProviderConfig): Promise<void> {
const apiKey = config.apiKey as string
if (!apiKey) {
- this.emitError(this.createError('MISSING_API_KEY', '60db API key is required'))
- return
+ const error = this.createError('MISSING_API_KEY', '60db API key is required')
+ this.emitError(error)
+ throw new Error(error.message)
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| async connect(config: ProviderConfig): Promise<void> { | |
| const apiKey = config.apiKey as string | |
| if (!apiKey) { | |
| this.emitError(this.createError('MISSING_API_KEY', '60db API key is required')) | |
| return | |
| } | |
| async connect(config: ProviderConfig): Promise<void> { | |
| const apiKey = config.apiKey as string | |
| if (!apiKey) { | |
| const error = this.createError('MISSING_API_KEY', '60db API key is required') | |
| this.emitError(error) | |
| throw new Error(error.message) | |
| } |
🤖 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 `@frontend/src/providers/implementations/SixtydbProvider.ts` around lines 92 -
98, The connect method in SixtydbProvider (connect(config: ProviderConfig))
calls this.emitError(this.createError(...)) when apiKey is missing but never
rejects or throws, leaving the caller hanging; after emitting the error you
should reject the Promise by throwing the created error (or returning
Promise.reject) so callers receive the failure. Update the connect
implementation to create the error via this.createError('MISSING_API_KEY', '60db
API key is required'), call this.emitError(error), then throw that error (or
return Promise.reject(error)) so the Promise is settled.
| case 'final': | ||
| console.log('[SixtydbProvider] final:', msg.text) | ||
| this.emitFinal(msg.text || '') | ||
| this.emitFinished() | ||
| break |
There was a problem hiding this comment.
Remove emitFinished() call on each final transcript.
Calling emitFinished() after every final message signals end of session repeatedly. In continuous transcription, multiple finals occur during a single session. emitFinished() should only be called when the session truly ends (e.g., on intentional disconnect or session_stopped).
Based on the downstream behavior in providerSession.ts, this could trigger unintended session-end callbacks multiple times.
Proposed fix
case 'final':
console.log('[SixtydbProvider] final:', msg.text)
this.emitFinal(msg.text || '')
- this.emitFinished()
break📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| case 'final': | |
| console.log('[SixtydbProvider] final:', msg.text) | |
| this.emitFinal(msg.text || '') | |
| this.emitFinished() | |
| break | |
| case 'final': | |
| console.log('[SixtydbProvider] final:', msg.text) | |
| this.emitFinal(msg.text || '') | |
| break |
🤖 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 `@frontend/src/providers/implementations/SixtydbProvider.ts` around lines 138 -
142, In the 'final' switch branch inside SixtydbProvider (the case handling
final messages) remove the call to this.emitFinished() so that
emitFinal(msg.text || '') is emitted without signaling session end; instead
ensure emitFinished() is only invoked from real session-termination paths (e.g.,
the session_stopped/disconnect handler or wherever session lifecycle is
explicitly closed) so multiple 'final' transcripts during continuous
transcription don't trigger end-of-session callbacks.
| clientWs.on('error', (error) => { | ||
| console.error('[SixtydbProxy] client WebSocket error:', error) | ||
| clientClosed = true | ||
| upstream.close(1000, 'Client error') | ||
| }) |
There was a problem hiding this comment.
Add readyState check before closing upstream on client error.
The clientWs.on('error') handler closes upstream without checking readyState, unlike the clientWs.on('close') handler on line 191. This could cause issues if the upstream is already closed.
Proposed fix
clientWs.on('error', (error) => {
console.error('[SixtydbProxy] client WebSocket error:', error)
clientClosed = true
- upstream.close(1000, 'Client error')
+ if (upstream.readyState === NodeWebSocket.OPEN) {
+ try { upstream.send(JSON.stringify({ type: 'stop' })) } catch { /* ignore */ }
+ upstream.close(1000, 'Client error')
+ }
})🤖 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 `@shared/sixtydbProxyCore.ts` around lines 197 - 201, The clientWs 'error'
handler closes the upstream without checking its WebSocket state, which can
attempt to close an already-closed connection; update the clientWs.on('error',
...) handler (the same handler that sets clientClosed) to first check
upstream.readyState (or upstream.readyState === WebSocket.OPEN/CONNECTING
depending on your desired behavior) before calling upstream.close(1000, 'Client
error'), mirroring the readiness check used in the clientWs.on('close') handler
so upstream.close is only invoked when appropriate.
Summary by CodeRabbit