Skip to content

Added 60dB integration#218

Open
manishEMS47 wants to merge 1 commit into
vargHQ:mainfrom
manishEMS47:main
Open

Added 60dB integration#218
manishEMS47 wants to merge 1 commit into
vargHQ:mainfrom
manishEMS47:main

Conversation

@manishEMS47

Copy link
Copy Markdown

What

Brief description of what this PR does.

Why

Why is this change needed?

How to test

Steps to verify the changes work correctly.

Checklist

  • Code follows existing style
  • Tested locally with bun run check
  • No breaking changes (or documented below)

@coderabbitai

coderabbitai Bot commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

this pr adds 60db as a speech provider across the sdk. you get tts/stt REST calls wrapped in a provider class, ai sdk v3 integration with optional alignment recovery, model definitions for billing, provider selection in voice actions, and full test/doc coverage.

Changes

60db Text-to-Speech Provider Integration

Layer / File(s) Summary
Type and schema contracts
src/core/schema/shared.ts
Added speechProviderSchema and SpeechProvider type for "elevenlabs" and "60db". Extended providerNameSchema enum to include "60db".
Core 60db provider implementation
src/providers/60db.ts
Implemented SixtyDBProvider class with textToSpeech (calls /tts-synthesize, decodes base64 to buffer), speechToText (handles multipart form uploads and timestamp extraction), resolveVoiceId (maps names to UUIDs via /myvoices), listVoices with in-memory caching, and error handling for HTTP/JSON responses.
Vercel AI SDK v3 speech model
src/ai-sdk/providers/60db.ts
Implemented SixtyDBSpeechModel for SpeechModelV3, with TTS endpoint calls, base64 audio decoding, optional STT round-trip to recover word timings and convert to character-level providerMetadata.sixtydb.alignment, and timeout/abort control via AbortController.
Model definition and pricing
src/definitions/models/60db.ts, src/definitions/models/index.ts
Created 60db-tts model definition with character-count-based pricing (min/max constraints) and registered it in allModels for pricing lookups.
Voice action provider selection
src/definitions/actions/voice.ts
Extended voice action input schema with provider field (default "elevenlabs"), wired execution to destructure and pass provider to generateVoice, and added provider-conditional branching: "60db" calls sixtyDbProvider.textToSpeech with voice ID resolution, "elevenlabs" uses existing VOICE_MAP + elevenlabsProvider path.
Public API wiring and registration
src/ai-sdk/index.ts, src/providers/index.ts
Re-exported 60db factory, defaults, instance, and types through AI SDK index; added barrel exports with *60db aliases; auto-registered sixtyDbProvider in central providers registry.
React speech element resolution
src/react/resolve.ts
Updated resolveSpeechElement to extract alignment from either providerMeta.elevenlabs or providerMeta.sixtydb metadata.
Documentation, environment config, and test coverage
.env.example, README.md, src/tests/all.test.ts, src/tests/unit.test.ts
Added SIXTYDB_API_KEY environment variable; documented 60db TTS/STT with JSX and CLI examples; added provider registration test, live API tests (TTS and list voices, gated by env var), and extended unit test expectations.

Possibly related PRs

  • vargHQ/sdk#201: overlaps on speech resolution metadata handling in src/react/resolve.ts, where both prs modify alignment extraction from provider metadata.
  • vargHQ/sdk#159: directly overlaps on same alignment-to-segmentation path in resolveSpeechElement, consuming provider-supplied metadata.
  • vargHQ/sdk#157: both touch voice action schema and VOICE_MAP handling in src/definitions/actions/voice.ts, with voice ID passthrough and provider routing.

Poem

📢 sixty's here to talk out loud
tts and stt, no elevenlabs crowd
align those words, pick your provider
speech flows smooth, a varg insider
meow 🐱


🎯 4 (Complex) | ⏱️ ~45 minutes

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning the description is a placeholder template with no actual details about what was changed, why, or how to test the 60db integration. fill in the template with actual implementation details: what the 60db integration does, why it's needed, and specific test steps beyond the checklist.
Docstring Coverage ⚠️ Warning Docstring coverage is 60.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed the title accurately describes the main change - adding a 60db provider integration across multiple files and systems.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

❤️ Share

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (2)
src/ai-sdk/providers/60db.ts (1)

174-176: ⚡ Quick win

unnecessary json round-trip

you're doing JSON.parse(JSON.stringify({ sixtydb: { alignment } })) but can just assign the object directly. the alignment is already a plain object with plain arrays, so it's already json-serializable.

skip the round-trip meow
         if (words.length > 0) {
           const alignment = wordsToCharacterAlignment(words);
-          providerMetadata = JSON.parse(
-            JSON.stringify({ sixtydb: { alignment } }),
-          );
+          providerMetadata = { sixtydb: { alignment } };
         }
🤖 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 `@src/ai-sdk/providers/60db.ts` around lines 174 - 176, The assignment to
providerMetadata uses an unnecessary JSON round-trip; replace providerMetadata =
JSON.parse(JSON.stringify({ sixtydb: { alignment } })) with a direct object
assignment like providerMetadata = { sixtydb: { alignment } } so the plain JS
object is used without serializing/deserializing; update the code path that sets
providerMetadata (the variable named providerMetadata in the sixtydb provider
code where alignment is referenced) accordingly.
src/providers/60db.ts (1)

100-100: 💤 Low value

job id prefix doesn't match provider name

you're using db60_ but the provider name is 60db. might wanna keep them consistent for easier debugging later.

tiny consistency fix
-    const jobId = `db60_${Date.now()}_${Math.random().toString(36).slice(2)}`;
+    const jobId = `60db_${Date.now()}_${Math.random().toString(36).slice(2)}`;
🤖 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 `@src/providers/60db.ts` at line 100, The jobId prefix is inconsistent with the
provider name: update the job id construction (the const jobId assignment that
currently uses `db60_`) to use the provider name prefix `60db_` instead so
identifiers match the provider; locate the `jobId` variable in this file (the
template string using Date.now() and Math.random()) and replace the `db60_`
prefix with `60db_` (and update any related places that rely on that exact
prefix if present).
🤖 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 `@src/definitions/models/60db.ts`:
- Line 14: The schema's text field documents a 5000-char cap but doesn't enforce
it—update the zod schema for the text property (the line defining text:
z.string().describe(...)) to include .max(5000) (and an appropriate error
message); separately, clamp the computed pricing to the declared bounds by
ensuring any function that returns minUsd/maxUsd (the pricing computation in
this module) enforces minUsd <= price <= maxUsd before returning (or returns the
clamped minUsd/maxUsd values), and apply the same fix to the related pricing
code referenced around the minUsd/maxUsd fields.

In `@src/providers/60db.ts`:
- Line 13: The code imports writeFileSync from "node:fs" and uses it to write
the TTS output; replace that synchronous node fs usage with Bun.file to make
textToSpeech fully async: remove the writeFileSync import, find the textToSpeech
function (and the line where writeFileSync is called) and use
Bun.file(...).write or .writeSync as appropriate (prefer .write which returns a
Promise) to write the speech buffer/bytes, ensuring you await the write so
textToSpeech remains async and returns only after the file is persisted.

---

Nitpick comments:
In `@src/ai-sdk/providers/60db.ts`:
- Around line 174-176: The assignment to providerMetadata uses an unnecessary
JSON round-trip; replace providerMetadata = JSON.parse(JSON.stringify({ sixtydb:
{ alignment } })) with a direct object assignment like providerMetadata = {
sixtydb: { alignment } } so the plain JS object is used without
serializing/deserializing; update the code path that sets providerMetadata (the
variable named providerMetadata in the sixtydb provider code where alignment is
referenced) accordingly.

In `@src/providers/60db.ts`:
- Line 100: The jobId prefix is inconsistent with the provider name: update the
job id construction (the const jobId assignment that currently uses `db60_`) to
use the provider name prefix `60db_` instead so identifiers match the provider;
locate the `jobId` variable in this file (the template string using Date.now()
and Math.random()) and replace the `db60_` prefix with `60db_` (and update any
related places that rely on that exact prefix if present).
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 9b84d15f-373c-425b-9c9f-7117730e79e5

📥 Commits

Reviewing files that changed from the base of the PR and between 5939942 and 6e4265d.

📒 Files selected for processing (18)
  • .claude/rules/video-generation.md
  • .claude/settings.local.json
  • .claude/skills/ffmpeg-mixing/SKILL.md
  • .claude/skills/varg-video-generation/GUIDE.md
  • .claude/skills/varg-video-generation/SKILL.md
  • .env.example
  • README.md
  • src/ai-sdk/index.ts
  • src/ai-sdk/providers/60db.ts
  • src/core/schema/shared.ts
  • src/definitions/actions/voice.ts
  • src/definitions/models/60db.ts
  • src/definitions/models/index.ts
  • src/providers/60db.ts
  • src/providers/index.ts
  • src/react/resolve.ts
  • src/tests/all.test.ts
  • src/tests/unit.test.ts
💤 Files with no reviewable changes (5)
  • .claude/skills/varg-video-generation/SKILL.md
  • .claude/rules/video-generation.md
  • .claude/settings.local.json
  • .claude/skills/varg-video-generation/GUIDE.md
  • .claude/skills/ffmpeg-mixing/SKILL.md


// Input schema with Zod
const sixtydbInputSchema = z.object({
text: z.string().describe("Text to convert to speech (max 5000 characters)"),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

enforce the declared 5k text cap and pricing bounds in code

line 14 documents a 5000-char cap but doesn’t validate it, and line 64 can return values outside the declared minUsd/maxUsd range. that makes pricing/bounds inconsistent for billing consumers.

suggested patch
 const sixtydbInputSchema = z.object({
-  text: z.string().describe("Text to convert to speech (max 5000 characters)"),
+  text: z
+    .string()
+    .min(1)
+    .max(5000)
+    .describe("Text to convert to speech (max 5000 characters)"),
   voice_id: z.string().optional().describe("60db voice_id (UUID) to use"),
@@
   pricing: {
     "60db": {
       description: "$0.00002 per character via 60db ($0.01 minimum).",
-      calculate: ({ characters = 500 }) => 0.00002 * characters,
+      calculate: ({ characters = 500 }) => {
+        const boundedCharacters = Math.min(Math.max(characters, 0), 5000);
+        const raw = 0.00002 * boundedCharacters;
+        return Math.min(0.1, Math.max(0.01, raw));
+      },
       minUsd: 0.01,
       maxUsd: 0.1, // ~5,000 char max input
     },
   },

Also applies to: 63-66

🤖 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 `@src/definitions/models/60db.ts` at line 14, The schema's text field documents
a 5000-char cap but doesn't enforce it—update the zod schema for the text
property (the line defining text: z.string().describe(...)) to include
.max(5000) (and an appropriate error message); separately, clamp the computed
pricing to the declared bounds by ensuring any function that returns
minUsd/maxUsd (the pricing computation in this module) enforces minUsd <= price
<= maxUsd before returning (or returns the clamped minUsd/maxUsd values), and
apply the same fix to the related pricing code referenced around the
minUsd/maxUsd fields.

Comment thread src/providers/60db.ts
* speech providers are interchangeable behind the `voice` action.
*/

import { writeFileSync } from "node:fs";

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

use bun.file instead of node:fs

the coding guidelines say to prefer Bun.file over node:fs readFile/writeFile. you're using this on line 200 for writing the output file.

✨ switch to bun.file api
-import { writeFileSync } from "node:fs";

then on line 200:

     if (outputPath) {
-      writeFileSync(outputPath, buffer);
+      await Bun.write(outputPath, buffer);
       console.log(`[60db] saved to ${outputPath}`);
     }

note: this makes textToSpeech fully async, which it already is meow

🤖 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 `@src/providers/60db.ts` at line 13, The code imports writeFileSync from
"node:fs" and uses it to write the TTS output; replace that synchronous node fs
usage with Bun.file to make textToSpeech fully async: remove the writeFileSync
import, find the textToSpeech function (and the line where writeFileSync is
called) and use Bun.file(...).write or .writeSync as appropriate (prefer .write
which returns a Promise) to write the speech buffer/bytes, ensuring you await
the write so textToSpeech remains async and returns only after the file is
persisted.

Source: Coding guidelines

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