Skip to content

docs(stt): Add implementation plan of speech-to-text support in neuro… - #789

Closed
shambhavik-25 wants to merge 1 commit into
juspay:releasefrom
shambhavik-25:BZ-48102-add-speech-to-text-support-in-neuro-link
Closed

docs(stt): Add implementation plan of speech-to-text support in neuro…#789
shambhavik-25 wants to merge 1 commit into
juspay:releasefrom
shambhavik-25:BZ-48102-add-speech-to-text-support-in-neuro-link

Conversation

@shambhavik-25

@shambhavik-25 shambhavik-25 commented Jan 23, 2026

Copy link
Copy Markdown

docs(stt): Add implementation plan of speech-to-text support in neurolink

Pull Request

Description

What does this PR do?

Adds comprehensive documentation describing the Speech-to-Text (STT) implementation plan for NeuroLink.

The document outlines the architecture, provider abstractions, SDK and CLI integration, configuration, error handling, and extensibility roadmap for STT support, with initial focus on Google Cloud Speech-to-Text.

Related Issues

Does this PR close any issues?
N/A

Type of Change

Please select the type of change:

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Documentation update
  • Refactoring (no functional changes)
  • Performance improvement
  • Test coverage improvement
  • Build/CI configuration
  • Other (please describe):

Motivation and Context

Why is this change needed? What problem does it solve?

NeuroLink previously lacked formal documentation describing how Speech-to-Text support is designed and integrated. This made it harder for contributors and reviewers to:

  • Understand the planned STT architecture
  • Evaluate provider abstractions and constraints
  • Ensure consistency with the existing TTS design
  • Contribute new STT providers in the future

This documentation provides a clear, shared reference for the STT pipeline before and during implementation.

Changes Made

  • Added an STT implementation plan to /memory-bank

Breaking Changes

Does this PR introduce breaking changes?

  • No breaking changes
  • Yes, breaking changes (describe below)

Testing

How has this been tested?

Please describe the tests you ran and their results:

  • Unit tests added/updated
  • Integration tests added/updated
  • E2E tests pass
  • Manual testing completed for documentation
  • Tested with multiple providers: [list providers]
  • Tested on multiple platforms: [list platforms]

Code Quality

Have you followed code quality standards?

  • Code follows the project's style guidelines (ESLint passes)
  • Code is properly formatted (Prettier applied)
  • Self-review of code completed
  • No console.log statements (using logger instead)
  • No hardcoded API keys or secrets
  • TypeScript strict mode compliance
  • Proper error handling implemented
  • TODO/FIXME comments reference issues

Documentation

Have you updated documentation?

  • JSDoc comments added/updated for public APIs
  • README.md updated (if needed)
  • Documentation in /memory-bank updated (if needed)
  • Code examples added/updated (if needed)
  • CHANGELOG.md updated (if applicable)
  • Migration guide provided (if breaking changes)

Commit Message Format

Does your commit follow semantic commit conventions?

  • Commit message follows format: type(scope): description
  • Valid type used: feat, fix, docs, style, refactor, test, chore, build, ci, perf, revert
  • Scope specified (e.g., providers, cli, docs, middleware)

Example: feat(providers): add support for LiteLLM proxy

Dependencies

Does this PR add, update, or remove dependencies?

  • No dependency changes
  • Dependencies added (list below)
  • Dependencies updated (list below)
  • Dependencies removed (list below)

If yes, list dependencies and justification:

package-name@version - Reason for adding/updating

Performance Impact

Does this change affect performance?

  • No performance impact
  • Performance improved (provide metrics)
  • Performance degraded (justify why acceptable)

Security Considerations

Are there any security implications?

  • No security implications
  • Security review needed
  • Security vulnerability fixed

Deployment Notes

Special deployment instructions?

  • No special deployment steps
  • Requires environment variable changes (list below)
  • Requires database migration
  • Requires Redis schema update
  • Other (describe below)

Reviewer Checklist

For reviewers:

  • Code follows project style and conventions
  • Changes are well-documented
  • Tests provide adequate coverage
  • No obvious performance issues
  • No security vulnerabilities introduced
  • Breaking changes are properly documented
  • Documentation is clear and accurate

Thank you for contributing to NeuroLink!

Summary by CodeRabbit

  • Documentation
    • Added Speech-to-Text support documentation outlining a provider-agnostic architecture with Google Cloud Speech-to-Text as the initial provider. Includes planned SDK APIs, CLI integration, multi-language support, speaker identification, timestamps, and a roadmap for additional providers.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai

coderabbitai Bot commented Jan 23, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto incremental reviews are disabled on this repository.

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.

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

Walkthrough

A new planning document detailing a provider-agnostic STT (Speech-to-Text) architecture for NeuroLink with Google Cloud Speech-to-Text as the initial provider, including SDK APIs, CLI integration, normalized output models, and an extensibility roadmap for additional providers.

Changes

Cohort / File(s) Summary
Planning & Architecture Documentation
memory-bank/neurolink-stt-support-plan.md
New 294-line design document defining STT architecture including: core components (sttTypes, STTProcessor, provider handlers), SDK APIs (transcribe), CLI integration (neurolink transcribe), normalized STTResult model with transcript/confidence/timing/speaker data, Google Cloud configuration, error handling, and roadmap for streaming/async and additional providers.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~15 minutes

Suggested reviewers

  • murdore

Poem

🐰 A whisper becomes words, a plan takes its shape,
STT support blooms in architectural drape,
From Google's vast voice to providers untamed,
NeuroLink's new symphony eloquently framed! 🎙️✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically summarizes the main change: adding an implementation plan for speech-to-text support in NeuroLink.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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

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 `@memory-bank/neurolink-stt-support-plan.md`:
- Around line 15-47: Replace the redundant phrase "SDK + CLI interfaces" with
"SDK + CLI APIs" throughout the document; locate the exact string "SDK + CLI
interfaces" (e.g., the top header currently "Consistent SDK + CLI APIs" or any
occurrences in the body/Table of Contents) and change it to "SDK + CLI APIs" (or
"SDK + CLI commands" where more appropriate), ensuring the Table of Contents
entries and any section headings or descriptive sentences are updated to
maintain consistency.

Comment on lines +15 to +47
* Consistent SDK + CLI APIs

---

## Table of Contents

1. [Problem Statement & Solution](#problem-statement--solution)
2. [Architecture Overview](#architecture-overview)
3. [Core Components](#core-components)
4. [NeuroLink SDK Integration](#neurolink-sdk-integration)
5. [CLI Integration](#cli-integration)
6. [Output Model](#output-model-sttresult)
7. [Configuration](#configuration)
8. [Error Handling](#error-handling)
9. [Extensibility Roadmap](#extensibility-roadmap)
10. [Conclusion](#conclusion)

---

## Problem Statement & Solution

### Problem Statement

NeuroLink lacked a **unified Speech-to-Text (STT) layer**, forcing developers to deal with provider-specific APIs, inconsistent outputs, and duplicated logic across SDKs and CLI tools. This made audio workflows harder to build, extend, and maintain.

### Solution

NeuroLink adds a **provider-agnostic STT pipeline** that mirrors the TTS architecture:

* A single processor for validation, routing, and error handling
* Pluggable provider handlers for easy extensibility
* Normalized transcription results with optional metadata
* Consistent SDK and CLI interfaces

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Prefer “SDK + CLI APIs” over “SDK + CLI interfaces”

“CLI” already expands to “command-line interface,” so “CLI interfaces” is redundant. Consider “SDK + CLI APIs” or “SDK + CLI commands.”

🧰 Tools
🪛 LanguageTool

[style] ~47-~47: This phrase is redundant (‘I’ stands for ‘interfaces’). Use simply “CLIs”.
Context: ... optional metadata * Consistent SDK and CLI interfaces This makes Speech-to-Text a **first-cl...

(ACRONYM_TAUTOLOGY)

🤖 Prompt for AI Agents
In `@memory-bank/neurolink-stt-support-plan.md` around lines 15 - 47, Replace the
redundant phrase "SDK + CLI interfaces" with "SDK + CLI APIs" throughout the
document; locate the exact string "SDK + CLI interfaces" (e.g., the top header
currently "Consistent SDK + CLI APIs" or any occurrences in the body/Table of
Contents) and change it to "SDK + CLI APIs" (or "SDK + CLI commands" where more
appropriate), ensuring the Table of Contents entries and any section headings or
descriptive sentences are updated to maintain consistency.

@shambhavik-25
shambhavik-25 force-pushed the BZ-48102-add-speech-to-text-support-in-neuro-link branch from b5e220d to 2556695 Compare February 23, 2026 12:41
@vercel

vercel Bot commented Feb 23, 2026

Copy link
Copy Markdown

@shambhavik-25 is attempting to deploy a commit to the Sachin Sharma's projects Team on Vercel.

A member of the Team first needs to authorize it.

@shambhavik-25

Copy link
Copy Markdown
Author

@coderabbitai can you review my pr? compare it with other multimodality support implementation plan and find out if mine aligns with the established architecture.

@coderabbitai

coderabbitai Bot commented Feb 23, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

@shambhavik-25, I'll review your STT implementation plan and compare it with other multimodality support plans to verify architectural alignment.


🏁 Script executed:

#!/bin/bash
# Read the new STT implementation plan
echo "=== STT Implementation Plan ==="
cat memory-bank/neurolink-stt-support-plan.md

echo -e "\n\n=== Finding other implementation plans in memory-bank ==="
fd -e md . memory-bank/ --exec echo "File: {}"

Length of output: 30751


🏁 Script executed:

#!/bin/bash
# Read TTS implementation plan to compare with STT
echo "=== TTS Provider Implementation Plan ==="
cat memory-bank/tts-provider-implementation-plan.md

echo -e "\n\n=== Video Analysis Implementation Plan ==="
cat memory-bank/video-analysis-implementation-plan.md

echo -e "\n\n=== Multimodal Implementation Guide ==="
cat memory-bank/MULTIMODAL_IMPLEMENTATION_GUIDE.md

Length of output: 123359


@shambhavik-25, I'll provide a comprehensive architectural alignment analysis of your STT implementation plan compared to the established patterns.

🎯 Executive Summary

Overall Alignment: 85% ✅

Your STT plan follows the established architecture well, with some areas needing adjustments to match the proven patterns from TTS, Video Analysis, and the Multimodal Implementation Guide.

📊 Detailed Architectural Comparison

1. Core Architecture Pattern ✅ EXCELLENT

Aspect STT Plan TTS/Video Pattern Alignment
Modality Type Input modality TTS: Output, Video: Input ✅ Correct
Provider Reuse google-ai + vertex Same providers ✅ Correct
Authentication API key + service account Same approach ✅ Correct
Zero Breaking Changes Emphasized Same emphasis ✅ Correct

Finding: Your framing of STT as an "input modality" perfectly mirrors how TTS is an "output modality" and aligns with CSV/PDF processing patterns.


2. Implementation Strategy ⚠️ NEEDS ALIGNMENT

❌ CRITICAL ISSUE: Your plan deviates from the proven 7-step recipe

Your Plan (6 phases):

  1. Type System Updates
  2. STT Utilities
  3. Audio File Processing Integration
  4. Provider Integration
  5. CLI Integration
  6. Testing & Validation

Proven Pattern (7 steps from Multimodal Guide):

  1. Create Processor Class
  2. Add Type Definitions
  3. Integrate with FileDetector
  4. Update CLI Integration
  5. Update Message Builder
  6. Add Provider Support
  7. Testing & Documentation

Recommendation: Restructure your phases to match the 7-step recipe. Here's the corrected alignment:


🧠 Learnings used
Learnt from: vigneshJuspay
Repo: juspay/neurolink PR: 237
File: memory-bank/tts-provider-implementation-plan.md:92-106
Timestamp: 2025-11-17T13:53:20.209Z
Learning: In PR 237's TTS modality implementation approach, TTS functionality uses GOOGLE_AI_API_KEY (not GOOGLE_TTS_API_KEY) when using the google-ai provider. TTS is implemented as an output modality that leverages the existing google-ai provider authentication.

Learnt from: vigneshJuspay
Repo: juspay/neurolink PR: 0
File: :0-0
Timestamp: 2025-12-15T18:35:37.783Z
Learning: In juspay/neurolink TTS implementation (PR `#691`), the new StreamChunk discriminated union type introduced in TTS-019 will be integrated with StreamResult.stream during the actual TTS streaming implementation PR (TTS-020/TTS-021), not in the type-definition PR. This phased approach keeps type updates and implementation changes atomic.

@shambhavik-25

Copy link
Copy Markdown
Author

@coderabbitai based on the code base, check the implementation plan put forward by this pr. Identify gaps, if any. Suggest ways to fix the gaps.

@coderabbitai

coderabbitai Bot commented Feb 23, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

@shambhavik-25, I'll analyze your STT implementation plan against the actual codebase to identify gaps and provide concrete recommendations.


🏁 Script executed:

#!/bin/bash

echo "=== Reading STT Implementation Plan ==="
cat memory-bank/neurolink-stt-support-plan.md

echo -e "\n\n=== Examining existing processor implementations ==="
fd -t f "Processor\\.ts$" src/

echo -e "\n\n=== Checking TTS implementation pattern ==="
fd -t f -e ts . src/ | grep -i tts | head -20

echo -e "\n\n=== Examining FileDetector implementation ==="
cat src/utils/fileDetector.ts

echo -e "\n\n=== Examining MessageBuilder implementation ==="
cat src/utils/messageBuilder.ts

echo -e "\n\n=== Checking existing processor implementations ==="
ls -la src/processors/

echo -e "\n\n=== Reading VideoProcessor as reference ==="
cat src/processors/VideoProcessor.ts

echo -e "\n\n=== Reading CSVProcessor as reference ==="
cat src/processors/CSVProcessor.ts

echo -e "\n\n=== Checking provider structure ==="
ls -la src/providers/

echo -e "\n\n=== Examining google-ai provider ==="
cat src/providers/google-ai/index.ts

echo -e "\n\n=== Checking types structure ==="
ls -la src/types/

echo -e "\n\n=== Reading core types ==="
cat src/types/index.ts

echo -e "\n\n=== Checking CLI structure ==="
ls -la src/cli/

echo -e "\n\n=== Reading CLI entry point ==="
cat src/cli/index.ts

echo -e "\n\n=== Checking for audio-related utilities ==="
fd -t f -e ts . src/utils/ --exec echo "File: {}"

Length of output: 32342


🏁 Script executed:

#!/bin/bash

echo "=== Understanding actual directory structure ==="
tree -L 3 -d src/

echo -e "\n\n=== Finding actual file paths ==="
fd -t f -e ts . src/lib | head -50

echo -e "\n\n=== Finding actual FileDetector ==="
fd -t f "fileDetector" src/

echo -e "\n\n=== Finding actual MessageBuilder ==="
fd -t f "messageBuilder" src/

echo -e "\n\n=== Reading actual AudioProcessor ==="
cat src/lib/processors/media/AudioProcessor.ts

echo -e "\n\n=== Checking actual types directory ==="
ls -la src/lib/types/

echo -e "\n\n=== Reading generateTypes ==="
cat src/lib/types/generateTypes.ts

echo -e "\n\n=== Reading multimodal types ==="
cat src/lib/types/multimodal.ts

echo -e "\n\n=== Finding actual providers ==="
fd -t d provider src/lib

echo -e "\n\n=== Listing provider implementations ==="
ls -la src/lib/providers/

echo -e "\n\n=== Reading google-ai provider ==="
cat src/lib/providers/googleAiStudio.ts

echo -e "\n\n=== Reading TTS implementation for reference ==="
cat src/lib/utils/ttsProcessor.ts

echo -e "\n\n=== Reading TTS types ==="
cat src/lib/types/ttsTypes.ts

echo -e "\n\n=== Checking CLI commands ==="
ls -la src/cli/commands/

echo -e "\n\n=== Reading generate command ==="
cat src/cli/commands/generate.ts

Length of output: 143777


🏁 Script executed:

#!/bin/bash

echo "=== Finding CLI command files ==="
ls -la src/cli/commands/ | grep -E "\.ts$"

echo -e "\n\n=== Searching for generate/stream command implementations ==="
rg -n "command.*generate|command.*stream" src/cli/ -A 5

echo -e "\n\n=== Reading messageBuilder ==="
cat src/lib/utils/messageBuilder.ts | head -300

echo -e "\n\n=== Reading fileDetector ==="
cat src/lib/utils/fileDetector.ts | head -400

echo -e "\n\n=== Checking CSV processor implementation ==="
cat src/lib/utils/csvProcessor.ts | head -200

echo -e "\n\n=== Checking PDF processor implementation ==="
cat src/lib/utils/pdfProcessor.ts | head -200

echo -e "\n\n=== Checking how files are processed in messageBuilder ==="
rg -n "processExplicitCsvFiles|processExplicitPdfFiles|audioFiles|videoFiles" src/lib/utils/messageBuilder.ts -B 3 -A 10

echo -e "\n\n=== Finding STT-related code in codebase ==="
rg -n "sttOptions|STTOptions|speech.*text|transcribe" src/lib --type ts | head -30

echo -e "\n\n=== Checking BaseProvider for common patterns ==="
cat src/lib/core/baseProvider.ts | head -300

echo -e "\n\n=== Checking how TTS is integrated ==="
rg -n "ttsOptions|TTSOptions|tts\\.enabled" src/lib --type ts -B 2 -A 3 | head -50

Length of output: 48693


@shambhavik-25, I've completed a comprehensive codebase analysis. Here's my detailed gap analysis with concrete recommendations:

🔍 STT Implementation Plan - Gap Analysis & Recommendations

✅ Executive Summary

Overall Alignment: 65% (Down from initial assessment due to deeper codebase analysis)

Your STT plan has good conceptual alignment but suffers from implementation gaps due to missing codebase-specific details. Below are 10 critical gaps with concrete fixes.


🚨 Critical Gaps (Must Fix Before Implementation)

Gap 1: Wrong Directory Structure

Issue:

// ❌ Your Plan (doesn't exist in codebase)
src/lib/stt/stt-service.ts
src/lib/stt/language-registry.ts
src/lib/stt/audio-validator.ts
src/lib/types/sttTypes.ts

Fix:

// ✅ Follow Existing Patterns
src/lib/utils/sttProcessor.ts          // Like ttsProcessor.ts
src/lib/adapters/stt/googleSTTHandler.ts  // Like googleTTSHandler.ts
src/lib/types/sttTypes.ts               // ✅ Correct

Evidence from Codebase:

  • TTS: src/lib/utils/ttsProcessor.ts + src/lib/adapters/tts/googleTTSHandler.ts
  • Video: src/lib/utils/videoAnalysisProcessor.ts + src/lib/adapters/video/vertexVideoHandler.ts

Gap 2: Missing STT Type Definitions

Issue:
Plan mentions STTOptions in generateTypes.ts but doesn't show the actual type definition structure.

Fix:

📝 Add to src/lib/types/sttTypes.ts
/**
 * STT Type Definitions (following ttsTypes.ts pattern)
 */

export type STTOptions = {
  /** Language code (default: "en-US") */
  language?: string;
  /** STT model variant */
  model?: "chirp" | "chirp_2" | "long" | "short";
  /** Enable word-level timestamps */
  enableWordTimestamps?: boolean;
  /** Enable automatic punctuation (default: true) */
  enableAutomaticPunctuation?: boolean;
  /** Filter profanity */
  profanityFilter?: boolean;
  /** Max alternative transcriptions (default: 1) */
  maxAlternatives?: number;
  /** Audio sample rate override */
  sampleRateHertz?: number;
};

export type STTResult = {
  /** Transcribed text */
  transcript: string;
  /** Confidence score 0-1 */
  confidence: number;
  /** Alternative transcriptions */
  alternatives?: Array<{ transcript: string; confidence: number }>;
  /** Word-level timestamps */
  wordTimestamps?: Array<{
    word: string;
    startTime: number;
    endTime: number;
    confidence: number;
  }>;
  /** Audio duration in seconds */
  duration?: number;
  /** Provider metadata */
  metadata?: {
    provider: string;
    model: string;
    language: string;
    latency: number;
  };
};

// Type guards
export function isValidSTTOptions(options: unknown): options is STTOptions {
  // Similar to isValidTTSOptions in ttsTypes.ts
  if (!options || typeof options !== "object") return false;
  const opts = options as STTOptions;
  if (opts.sampleRateHertz && (opts.sampleRateHertz < 8000 || opts.sampleRateHertz > 48000)) {
    return false;
  }
  return true;
}

Add to src/lib/types/generateTypes.ts:

import type { STTOptions, STTResult } from "./sttTypes.js";

export type GenerateOptions = {
  input: {
    text: string;
    images?: Array<Buffer | string | ImageWithAltText>;
    csvFiles?: Array<Buffer | string>;
    pdfFiles?: Array<Buffer | string>;
    audioFiles?: Array<Buffer | string>;  // ✅ ADD THIS
    videoFiles?: Array<Buffer | string>;
    files?: Array<Buffer | string>;
  };
  
  // ✅ ADD STT OPTIONS
  sttOptions?: STTOptions;
  
  // ... rest of options
};

export type GenerateResult = {
  content: string;
  audio?: TTSResult;
  transcript?: STTResult;  // ✅ ADD THIS for STT output
  // ... rest of result
};

Gap 3: Wrong CLI Integration Approach

Issue:
Plan suggests creating src/cli/commands/stt.ts (doesn't exist - CLI uses factory pattern)

Fix:

📝 Add to src/cli/factories/commandFactory.ts
// Around line 1087 (after stream command)
{
  command: "transcribe <audio-file>",
  describe: "Transcribe audio to text using Speech-to-Text",
  builder: (yargs) => {
    return yargs
      .positional("audio-file", {
        describe: "Audio file path to transcribe",
        type: "string",
      })
      .option("stt-language", {
        describe: "Language code (e.g., en-US, hi-IN)",
        type: "string",
        default: "en-US",
      })
      .option("stt-model", {
        describe: "STT model: chirp, chirp_2, chirp_3, long, short",
        type: "string",
        default: "chirp_2",
        choices: ["chirp", "chirp_2", "chirp_3", "long", "short"],
      })
      .option("stt-timestamps", {
        describe: "Enable word-level timestamps",
        type: "boolean",
        default: false,
      })
      .option("output", {
        describe: "Output file for transcript",
        type: "string",
        alias: "o",
      })
      .option("provider", {
        describe: "Provider (google-ai or vertex)",
        type: "string",
        default: "google-ai",
        choices: ["google-ai", "vertex"],
      });
  },
  handler: async (argv) => {
    // Implementation similar to generate handler
    const neurolink = new NeuroLink();
    const result = await neurolink.generate({
      input: {
        text: "Transcribe this audio",
        audioFiles: [argv["audio-file"]],
      },
      provider: argv.provider,
      sttOptions: {
        language: argv["stt-language"],
        model: argv["stt-model"],
        enableWordTimestamps: argv["stt-timestamps"],
      },
    });
    
    if (argv.output) {
      fs.writeFileSync(argv.output, result.transcript?.transcript || result.content);
    } else {
      console.log(result.transcript?.transcript || result.content);
    }
  },
}

Gap 4: Missing audioFiles Processing in MessageBuilder

Issue:
Plan mentions processExplicitAudioFiles but messageBuilder only has CSV and PDF processing.

Fix:

📝 Add to src/lib/utils/messageBuilder.ts (after processExplicitPdfFiles)
/**
 * Process explicit audio files array and inject transcriptions into prompt
 * Follows the same pattern as processExplicitCsvFiles
 */
async function processExplicitAudioFiles(
  options: GenerateOptions,
  provider: string,
): Promise<void> {
  if (!options.input.audioFiles || options.input.audioFiles.length === 0) {
    return;
  }

  logger.info(`[STT] Processing ${options.input.audioFiles.length} audio file(s)`);

  options.input.text = options.input.text || "";

  for (let i = 0; i < options.input.audioFiles.length; i++) {
    const audioFile = options.input.audioFiles[i];

    try {
      // Use FileDetector to process audio file with STT
      const result = await FileDetector.detectAndProcess(audioFile, {
        allowedTypes: ["audio"],
        sttOptions: options.sttOptions,
        provider: provider,
      });

      if (result.type === "audio" && result.content) {
        const filename = extractFilename(audioFile, i);
        let audioSection = `\n\n## Transcription from "${filename}":\n`;
        
        if (result.metadata) {
          audioSection += formatAudioMetadata(result.metadata) + '\n\n';
        }
        
        audioSection += '```\n' + result.content + '\n```\n';
        options.input.text += audioSection;
        
        logger.info(`[STT] ✅ Transcribed: ${filename}`);
      }
    } catch (error) {
      logger.error(`[STT] ❌ Failed:`, error);
      const filename = extractFilename(audioFile, i);
      options.input.text += `\n\n## Audio Transcription Error: Failed to process "${filename}"`;
      options.input.text += `\nReason: ${error instanceof Error ? error.message : "Unknown error"}`;
    }
  }
}

// Helper function
function formatAudioMetadata(metadata: any): string {
  return `Duration: ${metadata.duration || 'Unknown'} | Language: ${metadata.language || 'Unknown'}`;
}

// ✅ Call this in buildMessages function (around line 1305)
// After: await processExplicitCsvFiles(options);
await processExplicitAudioFiles(options, provider);

Gap 5: Missing FileDetector Audio Routing

Issue:
Plan doesn't specify how FileDetector should route audio files to STT processing.

Fix:

📝 Update src/lib/utils/fileDetector.ts
// Add to processAudioFile method (around line 800)
private static async processAudioFile(
  content: Buffer,
  detection: FileDetectionResult,
  options?: { sttOptions?: STTOptions; provider?: string }
): Promise<FileProcessingResult> {
  const audioFilename = detection.metadata.filename || "audio";
  
  try {
    // Process with AudioProcessor (includes STT if configured)
    const audioResult = await audioProcessor.processFile(
      {
        id: audioFilename,
        name: audioFilename,
        mimetype: detection.mimeType || "audio/mpeg",
        size: content.length,
        buffer: content,
      },
      {
        sttOptions: options?.sttOptions,  // ✅ Pass STT options
        provider: options?.provider,       // ✅ Pass provider
      }
    );
    
    if (audioResult.success && audioResult.data) {
      // Return transcription as text content
      return {
        type: "audio",
        content: audioResult.data.transcript || audioResult.data.textContent,
        mimeType: detection.mimeType,
        metadata: {
          ...detection.metadata,
          hasTranscript: audioResult.data.hasTranscript,
          transcriptionProvider: audioResult.data.transcriptionProvider,
          duration: audioResult.data.metadata.duration,
        },
      };
    }
  } catch (error) {
    logger.warn(`[FileDetector] AudioProcessor failed, using fallback`);
  }
  
  // Fallback to metadata-only placeholder
  return {
    type: "audio",
    content: FileDetector.formatInformativePlaceholder("Audio", audioFilename, content, detection),
    mimeType: detection.mimeType,
    metadata: detection.metadata,
  };
}

Gap 6: AudioProcessor STT Integration Incomplete

Issue:
Plan shows adding Google Cloud STT but doesn't explain integration with existing Whisper transcription (lines 392-489).

Fix:

📝 Update src/lib/processors/media/AudioProcessor.ts
// Update attemptTranscription method (around line 399)
private async attemptTranscription(
  buffer: Buffer,
  filename: string,
  mimetype: string | undefined,
  sttOptions?: STTOptions,  // ✅ Add STT options parameter
  provider?: string         // ✅ Add provider parameter
): Promise<{
  transcript: string | undefined;
  hasTranscript: boolean;
  transcriptionProvider: string | undefined;
  wordTimestamps?: Array<{ word: string; startTime: number; endTime: number }>;
}> {
  // ✅ Priority 1: Try Google Cloud STT v2 if credentials + sttOptions present
  if (sttOptions && provider && this.hasGoogleCloudCredentials(provider)) {
    try {
      const STTProcessor = await import('../utils/sttProcessor.js');
      const result = await STTProcessor.STTProcessor.synthesize(
        buffer,
        provider,
        sttOptions
      );
      return {
        transcript: result.transcript,
        hasTranscript: true,
        transcriptionProvider: 'google-cloud-stt-v2',
        wordTimestamps: result.wordTimestamps,
      };
    } catch (error) {
      logger.warn('[AudioProcessor] Google Cloud STT failed, falling back to Whisper', error);
    }
  }

  // ✅ Priority 2: Fallback to OpenAI Whisper (existing implementation)
  return this.attemptWhisperTranscription(buffer, filename, mimetype);
}

// ✅ Add credential check method
private hasGoogleCloudCredentials(provider: string): boolean {
  if (provider === 'google-ai') {
    return !!process.env.GOOGLE_AI_API_KEY;
  }
  if (provider === 'vertex') {
    return !!process.env.GOOGLE_APPLICATION_CREDENTIALS;
  }
  return false;
}

// ✅ Update processFile to accept sttOptions (around line 237)
override async processFile(
  fileInfo: FileInfo,
  options?: ProcessOptions & { sttOptions?: STTOptions; provider?: string }
): Promise<FileProcessingResult<ProcessedAudio>> {
  // ... existing validation code ...

  // Step 7: Attempt transcription (pass STT options)
  const transcriptionResult = await this.attemptTranscription(
    buffer,
    filename,
    fileInfo.mimetype,
    options?.sttOptions,  // ✅ Pass through
    options?.provider      // ✅ Pass through
  );

  // ... rest of method ...
}

Gap 7: Missing STTProcessor + STTHandler Pattern

Issue:
Plan doesn't follow the proven TTSProcessor + TTSHandler architecture.

Fix:

📝 Create src/lib/utils/sttProcessor.ts (following ttsProcessor.ts)
/**
 * Speech-to-Text (STT) Processing Utility
 * Central orchestrator for all STT operations across providers
 * Follows the same pattern as TTSProcessor
 */

import { logger } from "./logger.js";
import type { STTOptions, STTResult } from "../types/sttTypes.js";
import { ErrorCategory, ErrorSeverity } from "../constants/enums.js";
import { NeuroLinkError } from "./errorHandling.js";

export const STT_ERROR_CODES = {
  EMPTY_AUDIO: "STT_EMPTY_AUDIO",
  AUDIO_TOO_LARGE: "STT_AUDIO_TOO_LARGE",
  PROVIDER_NOT_SUPPORTED: "STT_PROVIDER_NOT_SUPPORTED",
  PROVIDER_NOT_CONFIGURED: "STT_PROVIDER_NOT_CONFIGURED",
  TRANSCRIPTION_FAILED: "STT_TRANSCRIPTION_FAILED",
  INVALID_FORMAT: "STT_INVALID_FORMAT",
} as const;

export class STTError extends NeuroLinkError {
  constructor(options: {
    code: string;
    message: string;
    category?: ErrorCategory;
    severity?: ErrorSeverity;
    retriable?: boolean;
    context?: Record<string, unknown>;
    originalError?: Error;
  }) {
    super({
      code: options.code,
      message: options.message,
      category: options.category ?? ErrorCategory.VALIDATION,
      severity: options.severity ?? ErrorSeverity.MEDIUM,
      retriable: options.retriable ?? false,
      context: options.context,
      originalError: options.originalError,
    });
    this.name = "STTError";
  }
}

/**
 * STT Handler interface (mirrors TTSHandler)
 */
export type STTHandler = {
  transcribe(audioBuffer: Buffer, options: STTOptions): Promise<STTResult>;
  isConfigured(): boolean;
  maxAudioSizeMB?: number;
};

export class STTProcessor {
  private static readonly handlers = new Map<string, STTHandler>();
  private static readonly DEFAULT_MAX_AUDIO_SIZE_MB = 10;

  static registerHandler(providerName: string, handler: STTHandler): void {
    if (!providerName || !handler) {
      throw new Error("Provider name and handler are required");
    }
    const normalizedName = providerName.toLowerCase();
    this.handlers.set(normalizedName, handler);
    logger.debug(`[STTProcessor] Registered STT handler for provider: ${normalizedName}`);
  }

  static supports(providerName: string): boolean {
    return this.handlers.has(providerName.toLowerCase());
  }

  static async transcribe(
    audioBuffer: Buffer,
    provider: string,
    options: STTOptions,
  ): Promise<STTResult> {
    // Validation
    if (!audioBuffer || audioBuffer.length === 0) {
      throw new STTError({
        code: STT_ERROR_CODES.EMPTY_AUDIO,
        message: "Audio buffer is required for STT transcription",
        severity: ErrorSeverity.LOW,
      });
    }

    const handler = this.handlers.get(provider.toLowerCase());
    if (!handler) {
      throw new STTError({
        code: STT_ERROR_CODES.PROVIDER_NOT_SUPPORTED,
        message: `STT provider "${provider}" is not supported`,
        context: { availableProviders: Array.from(this.handlers.keys()) },
      });
    }

    const maxSizeMB = handler.maxAudioSizeMB ?? this.DEFAULT_MAX_AUDIO_SIZE_MB;
    const sizeMB = audioBuffer.length / (1024 * 1024);
    if (sizeMB > maxSizeMB) {
      throw new STTError({
        code: STT_ERROR_CODES.AUDIO_TOO_LARGE,
        message: `Audio size ${sizeMB.toFixed(2)}MB exceeds ${maxSizeMB}MB limit`,
        context: { sizeMB, maxSizeMB },
      });
    }

    if (!handler.isConfigured()) {
      throw new STTError({
        code: STT_ERROR_CODES.PROVIDER_NOT_CONFIGURED,
        message: `STT provider "${provider}" is not configured`,
        category: ErrorCategory.CONFIGURATION,
      });
    }

    try {
      logger.debug(`[STTProcessor] Starting transcription with provider: ${provider}`);
      const result = await handler.transcribe(audioBuffer, options);
      logger.info(`[STTProcessor] Transcription successful: ${result.transcript.length} chars`);
      return result;
    } catch (err) {
      if (err instanceof STTError) throw err;
      
      const errorMessage = err instanceof Error ? err.message : String(err);
      throw new STTError({
        code: STT_ERROR_CODES.TRANSCRIPTION_FAILED,
        message: `STT transcription failed: ${errorMessage}`,
        category: ErrorCategory.EXECUTION,
        retriable: true,
        originalError: err instanceof Error ? err : undefined,
      });
    }
  }
}
📝 Create src/lib/adapters/stt/googleSTTHandler.ts
/**
 * Google Cloud Speech-to-Text v2 Handler
 * Implements STTHandler interface for Google Cloud STT
 */

import type { STTHandler, STTOptions, STTResult } from "../../types/sttTypes.js";
import { logger } from "../../utils/logger.js";

export class GoogleSTTHandler implements STTHandler {
  maxAudioSizeMB = 10;

  async transcribe(audioBuffer: Buffer, options: STTOptions): Promise<STTResult> {
    const { SpeechClient } = await import("@google-cloud/speech");
    
    const client = new SpeechClient({
      apiKey: process.env.GOOGLE_AI_API_KEY,
    });

    const audio = { content: audioBuffer.toString("base64") };
    const config = {
      encoding: "LINEAR16" as const,
      sampleRateHertz: options.sampleRateHertz || 16000,
      languageCode: options.language || "en-US",
      model: options.model || "chirp_2",
      enableWordTimeOffsets: options.enableWordTimestamps || false,
      enableAutomaticPunctuation: options.enableAutomaticPunctuation !== false,
    };

    const request = { audio, config };
    
    logger.debug("[GoogleSTTHandler] Sending transcription request");
    const [response] = await client.recognize(request);
    
    const transcription = response.results
      ?.map((result) => result.alternatives?.[0]?.transcript)
      .join("\n") || "";

    const wordTimestamps = options.enableWordTimestamps
      ? response.results?.flatMap((result) =>
          result.alternatives?.[0]?.words?.map((word) => ({
            word: word.word || "",
            startTime: Number(word.startTime?.seconds || 0),
            endTime: Number(word.endTime?.seconds || 0),
            confidence: word.confidence || 0,
          })) || []
        )
      : undefined;

    return {
      transcript: transcription,
      confidence: response.results?.[0]?.alternatives?.[0]?.confidence || 0,
      wordTimestamps,
      metadata: {
        provider: "google-cloud-stt-v2",
        model: options.model || "chirp_2",
        language: options.language || "en-US",
        latency: 0, // Calculate if needed
      },
    };
  }

  isConfigured(): boolean {
    return !!(
      process.env.GOOGLE_AI_API_KEY || 
      process.env.GOOGLE_APPLICATION_CREDENTIALS
    );
  }
}

// Auto-register handler
import { STTProcessor } from "../../utils/sttProcessor.js";
STTProcessor.registerHandler("google-ai", new GoogleSTTHandler());
STTProcessor.registerHandler("vertex", new GoogleSTTHandler());

Gap 8: Missing Dependencies

Issue:
Plan lists @google-cloud/speech but doesn't show package.json updates.

Fix:

{
  "dependencies": {
    "@google-cloud/speech": "^7.0.0"
  }
}

Gap 9: Wrong Phase Ordering ⚠️

Issue:
Plan has 6 phases that don't match the proven 7-step multimodal recipe.

Fix:

Recommended Implementation Order (7 Steps):

  1. Create STTProcessor Class (src/lib/utils/sttProcessor.ts)
  2. Add Type Definitions (src/lib/types/sttTypes.ts)
  3. Integrate with FileDetector (audio routing to STT)
  4. Update CLI Integration (commandFactory.ts)
  5. Update MessageBuilder (processExplicitAudioFiles)
  6. Add Provider Support (GoogleSTTHandler)
  7. Testing & Documentation

Gap 10: Missing Error Handling & Validation ⚠️

Issue:
Plan doesn't specify validation for:

  • Audio format support (MP3, WAV, FLAC, etc.)
  • File size limits per provider
  • Language code validation
  • Sample rate validation

Fix:

📝 Add Audio Validator
// In sttProcessor.ts or separate validator file

const SUPPORTED_AUDIO_FORMATS = [
  "audio/mpeg", "audio/mp3", "audio/wav", "audio/x-wav",
  "audio/flac", "audio/ogg", "audio/webm", "audio/amr"
];

const SAMPLE_RATE_RANGE = { min: 8000, max: 48000 };

function validateAudioBuffer(buffer: Buffer, options: STTOptions): void {
  // Magic byte validation
  const isMP3 = buffer[0] === 0xFF && buffer[1] === 0xD8;
  const isWAV = buffer.toString('ascii', 0, 4) === 'RIFF';
  const isFLAC = buffer.toString('ascii', 0, 4) === 'fLaC';
  
  if (!isMP3 && !isWAV && !isFLAC) {
    throw new STTError({
      code: STT_ERROR_CODES.INVALID_FORMAT,
      message: "Unsupported audio format. Use MP3, WAV, or FLAC",
    });
  }

  if (options.sampleRateHertz) {
    if (options.sampleRateHertz < SAMPLE_RATE_RANGE.min || 
        options.sampleRateHertz > SAMPLE_RATE_RANGE.max) {
      throw new STTError({
        code: STT_ERROR_CODES.INVALID_INPUT,
        message: `Sample rate must be between ${SAMPLE_RATE_RANGE.min} and ${SAMPLE_RATE_RANGE.max} Hz`,
      });
    }
  }
}

📋 Implementation Checklist

Before starting implementation, complete these steps:

  • Read existing TTS implementation (src/lib/utils/ttsProcessor.ts)
  • Read existing CSV processing (src/lib/utils/csvProcessor.ts)
  • Study messageBuilder.ts for file processing patterns
  • Review AudioProcessor.ts Whisper integration (lines 392-489)
  • Create src/lib/types/sttTypes.ts (Gap 2)
  • Create src/lib/utils/sttProcessor.ts (Gap 7)
  • Create src/lib/adapters/stt/googleSTTHandler.ts (Gap 7)
  • Update generateTypes.ts with STTOptions (Gap 2)
  • Update messageBuilder.ts with processExplicitAudioFiles (Gap 4)
  • Update fileDetector.ts audio routing (Gap 5)
  • Update AudioProcessor.ts STT integration (Gap 6)
  • Update commandFactory.ts CLI command (Gap 3)
  • Add @google-cloud/speech dependency (Gap 8)
  • Write unit tests for STTProcessor
  • Write integration tests with Google Cloud STT

🎯 Key Recommendations

  1. Start with TTS Code Review: Study src/lib/utils/ttsProcessor.ts - it's your blueprint
  2. Follow Existing Patterns: Don't reinvent - reuse CSV/PDF/TTS patterns
  3. Use Factory Pattern: CLI uses commandFactory.ts, not separate command files
  4. Prioritize Fallback: Keep Whisper as fallback when Google Cloud STT fails
  5. Test Early: Start with unit tests before integration

Would you like me to generate starter code for any specific component?


🧠 Learnings used
Learnt from: vigneshJuspay
Repo: juspay/neurolink PR: 237
File: memory-bank/tts-provider-implementation-plan.md:92-106
Timestamp: 2025-11-17T13:53:20.209Z
Learning: In PR 237's TTS modality implementation approach, TTS functionality uses GOOGLE_AI_API_KEY (not GOOGLE_TTS_API_KEY) when using the google-ai provider. TTS is implemented as an output modality that leverages the existing google-ai provider authentication.

Learnt from: vigneshJuspay
Repo: juspay/neurolink PR: 0
File: :0-0
Timestamp: 2025-12-15T18:35:37.783Z
Learning: In juspay/neurolink TTS implementation (PR `#691`), the new StreamChunk discriminated union type introduced in TTS-019 will be integrated with StreamResult.stream during the actual TTS streaming implementation PR (TTS-020/TTS-021), not in the type-definition PR. This phased approach keeps type updates and implementation changes atomic.

@shambhavik-25
shambhavik-25 force-pushed the BZ-48102-add-speech-to-text-support-in-neuro-link branch 2 times, most recently from 0eafcc8 to e9e290a Compare February 23, 2026 14:35
@shambhavik-25

Copy link
Copy Markdown
Author
  1. STT is handled as an input file preprocessing step within the existing file pipeline, while TTS is an output synthesis step handled separately via provider-specific handlers after the LLM responds. Thus, it should not follow processor+handler architecture.
  2. The error handling and validation will be handled in AudioProcessor.ts.
  3. The code will be added in the implementation phase.

@shambhavik-25
shambhavik-25 force-pushed the BZ-48102-add-speech-to-text-support-in-neuro-link branch 2 times, most recently from 4b2e1bd to dec89f5 Compare February 23, 2026 18:23
@shambhavik-25
shambhavik-25 force-pushed the BZ-48102-add-speech-to-text-support-in-neuro-link branch from dec89f5 to 1fe5adc Compare February 25, 2026 11:52
@murdore
murdore force-pushed the release branch 2 times, most recently from 09ff4a5 to 405e3e5 Compare March 12, 2026 10:48
@murdore

murdore commented Mar 29, 2026

Copy link
Copy Markdown
Contributor

Closing — STT plan doc superseded by PR #846 (actual implementation attempt). Project audit (2026-03-29).

@murdore murdore closed this Mar 29, 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.

3 participants