Skip to content

Unify local and remote transcription runs - #37

Merged
makoMakoGo merged 1 commit into
mainfrom
codex/remote-transcription-run
Jun 10, 2026
Merged

makoMakoGo merged 1 commit into
mainfrom
codex/remote-transcription-run

Conversation

@makoMakoGo

@makoMakoGo makoMakoGo commented Jun 9, 2026 •

Copy link
Copy Markdown
Owner

对应 Issue

Closes #26

变更

  • 新增统一的转录运行入口 runTranscription,本地文件和远程链接都产出同一套 status/progress/result/log/abort 语义。
  • 远程路径先通过浏览器远程音频获取 Module 得到 ASR 可消费的 File,再进入同一套 ASR 上传、等待识别、完成和错误处理。
  • useTranscriptionFlow 改为只把 local/remote input 交给 run module,不再维护两套转录运行逻辑。
  • 远程获取阶段传递同一个 AbortSignal,取消运行时可以取消当前下载或后续 ASR 请求。
  • 补充 run-level 测试覆盖本地路径、远程路径、下载失败、ASR 失败和取消路径。

验证

  • npm test -- --run tests/unit/transcription-run.test.ts tests/unit/browser-remote-audio.test.ts tests/unit/asr-transcription.test.ts
  • npm test
  • npm run build

依赖

Summary by Sourcery

Unify local file and remote URL transcription flows into a single run pipeline and decouple ASR and LLM runtime settings normalization.

New Features:

  • Add a shared transcription run module that drives status, progress, logging, and abort semantics for both local and remote audio sources.

Enhancements:

  • Refactor useTranscriptionFlow to delegate execution to the unified transcription runner and reuse common state handling.
  • Split app settings normalization into separate ASR and LLM runtime slices and reuse these in ASR and polish flows.

Tests:

  • Add comprehensive unit tests covering transcription run behavior for local and remote inputs, including success, empty results, API errors, download failures, and aborts.
  • Extend settings utilities tests to cover independent normalization of ASR and LLM runtime settings.

@coderabbitai

coderabbitai Bot commented Jun 9, 2026 •

Copy link
Copy Markdown

Warning

Review limit reached

@makoMakoGo, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 42 minutes and 30 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 7d1d1093-783d-4175-a572-e82043ac797c

📥 Commits

Reviewing files that changed from the base of the PR and between d83bcb0 and a951161.

📒 Files selected for processing (5)
  • hooks/use-transcription-flow.ts
  • lib/local-transcription-run.ts
  • lib/transcription-run.ts
  • tests/unit/local-transcription-run.test.ts
  • tests/unit/transcription-run.test.ts
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/remote-transcription-run

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.

@sourcery-ai

sourcery-ai Bot commented Jun 9, 2026 •

Copy link
Copy Markdown

Reviewer's Guide

Introduce a unified transcription run module that drives both local-file and remote-URL ASR transcriptions with shared state/log semantics, split ASR/LLM runtime settings normalization, and refactor hooks and tests to use the new abstractions.

Sequence diagram for unified local/remote transcription run

sequenceDiagram
  actor User
  participant useTranscriptionFlow
  participant runTranscription
  participant fetchRemoteAudioForAsr
  participant runAsrTranscription

  User->>useTranscriptionFlow: transcribe(file) / importFromUrl(url)
  useTranscriptionFlow->>runTranscription: runTranscription(input, settings, callbacks, options)
  alt input.source == remote
    runTranscription->>fetchRemoteAudioForAsr: fetchRemoteAudioForAsr(url, { maxAudioBytes, onProgress, signal })
    fetchRemoteAudioForAsr-->>runTranscription: File
    runTranscription->>runAsrTranscription: runAsrTranscription(file, normalizeAsrRunSettings(settings), { onUploadProgress, onUploadComplete, onWaitHeartbeat }, { signal })
  else input.source == local
    runTranscription->>runAsrTranscription: runAsrTranscription(file, normalizeAsrRunSettings(settings), { onUploadProgress, onUploadComplete, onWaitHeartbeat }, { signal })
  end
  runAsrTranscription-->>runTranscription: AsrTranscriptionResponse
  runTranscription-->>useTranscriptionFlow: TranscriptionRunResult
  Note over useTranscriptionFlow,runTranscription: shared onState/onLog updates status, progress, result, logs

  User-->>useTranscriptionFlow: abort()
  useTranscriptionFlow-->>runTranscription: AbortSignal
  runTranscription-->>fetchRemoteAudioForAsr: [signal aborts download]
  runTranscription-->>runAsrTranscription: [signal aborts ASR request]
Loading

File-Level Changes

Change Details Files
Refactor useTranscriptionFlow to delegate both local and remote transcriptions to a shared runTranscription module with unified state and logging.
  • Introduce prepareRun to handle common run-start side effects without directly mutating transcription status/result.
  • Add applyTranscriptionRunState to apply partial run state patches from the runner to hook state.
  • Replace inline ASR upload/heartbeat logic with runSelectedTranscription that calls runTranscription with callbacks and a shared AbortController.
  • Update local file transcribe and remote importFromUrl flows to construct TranscriptionRunInput objects and rely on runTranscription for upload, ASR, error, and abort handling.
hooks/use-transcription-flow.ts
Split app settings into ASR and LLM runtime slices with dedicated normalizers, and update consumers accordingly.
  • Introduce AsrRunSettings and LlmRunSettings types representing ASR and LLM runtime configuration subsets.
  • Add normalizeAsrRunSettings and normalizeLlmRunSettings to independently normalize ASR and LLM runtime settings, and redefine normalizeSettingsForStorage in terms of them.
  • Change hasAsrApiKey to operate on AsrRunSettings and export normalizeAsrRunSettings/AsrRunSettings from asr-transcription.
  • Update usePolishFlow to accept LlmRunSettings and use normalizeLlmRunSettings instead of normalizeSettingsForStorage.
lib/app-settings.ts
lib/asr-transcription.ts
hooks/use-polish-flow.ts
tests/unit/utils.test.ts
Introduce a new transcription-run module that encapsulates local/remote transcription orchestration, including remote audio fetch, ASR upload, state, logs, and abort handling.
  • Define TranscriptionRunInput/State/Result types plus callback interfaces and a shared TranscriptionRunContext that accumulates state patches and logs.
  • Implement runTranscription to handle both local and remote flows, including remote audio fetching via fetchRemoteAudioForAsr, upload status, and unified error/abort mapping.
  • Extract runAsrFileTranscription to wrap runAsrTranscription, emitting upload progress, heartbeats, completion states (success/empty), and formatted API errors.
  • Provide convenience aliases for local-only usage (runLocalTranscription) and re-exported types for local runs.
lib/transcription-run.ts
Add unit tests for the unified transcription-run module and for independent ASR/LLM settings normalization.
  • Create transcription-run tests that simulate XMLHttpRequest-based ASR calls, covering success, empty-text, non-2xx responses, request errors, aborts, remote fetch success/failure, and abort propagation into remote fetch.
  • Use a FakeXMLHttpRequest test double and custom timing hooks to validate heartbeat and logging behavior.
  • Add a utils test that asserts normalizeAsrRunSettings and normalizeLlmRunSettings normalize their respective slices independently without interfering with each other.
tests/unit/transcription-run.test.ts
tests/unit/utils.test.ts

Possibly linked issues


Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@gemini-code-assist gemini-code-assist 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.

Code Review

This pull request refactors the transcription flow by introducing a unified runTranscription runner in lib/transcription-run.ts to handle both local and remote audio sources, streamlining state updates and logging. It also splits settings normalization into independent ASR and LLM slices, and adds abort signal support to remote audio fetching. The review feedback highlights several improvement opportunities: adding defensive checks in settings normalization to handle potential nullish values for backward compatibility, using a more robust check for abort errors to ensure compatibility across different environments, avoiding the Uint8Array<ArrayBuffer> generic type argument to maintain compatibility with older TypeScript versions, and wrapping the runTranscription call in useTranscriptionFlow with a try-catch block to prevent unhandled promise rejections.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

I am having trouble creating individual review comments. Click here to see my feedback.

lib/app-settings.ts (49-60)

high

为了防止在应用升级时发生运行时崩溃(例如,当从 localStorage 加载旧版本存储的设置时,这些设置可能缺少新引入的 LLM 或 ASR 字段),我们应该在规范化(normalization)过程中防御性地处理可能为 undefined 或 null 的属性。使用可选链(optional chaining)和空值合并运算符(nullish coalescing)可以确保向后兼容性和系统的健壮性。

export const normalizeAsrRunSettings = (input: AsrRunSettings): AsrRunSettings => ({
  apiKey: (input?.apiKey ?? '').trim(),
  apiUrl: (input?.apiUrl ?? '').trim() || DEFAULT_SETTINGS.apiUrl,
  model: (input?.model ?? '').trim() || DEFAULT_SETTINGS.model,
})

export const normalizeLlmRunSettings = (input: LlmRunSettings): LlmRunSettings => ({
  llmApiUrl: (input?.llmApiUrl ?? '').trim() || DEFAULT_SETTINGS.llmApiUrl,
  llmModel: (input?.llmModel ?? '').trim() || DEFAULT_SETTINGS.llmModel,
  llmApiKey: (input?.llmApiKey ?? '').trim(),
  customInstructions: (input?.customInstructions ?? '').trim() || DEFAULT_SETTINGS.customInstructions,
})

lib/transcription-run.ts (67-70)

medium

在跨域环境(如 iframe)或某些 Node.js 测试环境中,检查 error instanceof DOMException 可能会失效,因为 DOMException 在这些环境中可能不是全局可用,或者其行为有所不同。此外,一些 fetch polyfill 或第三方库会抛出标准的 Error 对象(其 name 为 'AbortError')。使用 error instanceof Error && error.name === 'AbortError' 进行检查会更加健壮和通用。

const isAbortError = (error: unknown): boolean =>
  error instanceof Error && error.name === 'AbortError'

lib/browser-remote-audio.ts (109)

medium

使用 Uint8Array<ArrayBuffer> 指定了 Uint8Array 的泛型参数,这是 TypeScript 5.0 引入的特性。如果项目或任何开发/CI环境使用的是较旧的 TypeScript 版本,或者配置了较严格的类型检查,这可能会导致编译错误。直接使用标准的 Uint8Array 类型会更安全,且具有更好的兼容性。

  const chunks: Array<Uint8Array> = []

lib/browser-remote-audio.ts (119)

medium

同样地,避免在此处使用泛型 Uint8Array<ArrayBuffer> 类型断言,以保持与旧版本 TypeScript 的兼容性。

    chunks.push(value as Uint8Array)

hooks/use-transcription-flow.ts (134-149)

medium

虽然 runTranscription 内部捕获了错误并返回了已解析的状态,但在 runTranscription 执行(或其同步初始化)期间,任何意外的同步错误或异常仍可能被抛出。由于 transcribe 是作为一个“触发即忘”(fire-and-forget)的 Promise 被调用的(即 void transcribe(...)),任何未捕获的异常都会导致浏览器中出现未处理的 Promise 拒绝(unhandled promise rejection)。在 runSelectedTranscription 内部将调用包裹在 try-catch 块中,可以恢复健壮的错误处理并防止潜在的页面崩溃。

    try {
      await runTranscription(input, settings, { 
        onState: (patch) => {
          if (!isCurrentRun()) return
          applyTranscriptionRunState(patch)
        },
        onLog: ({ message, type }) => {
          if (!isCurrentRun()) return
          addLog(message, type)
        },
      }, { signal: controller.signal })
    } catch (e) {
      if (!isCurrentRun()) return
      const errorMsg = e instanceof Error ? e.message : String(e)
      setFlowError(`请求失败: ${errorMsg}`, '请求失败')
      addLog(`请求失败: ${errorMsg}`, 'error')
    } finally {
      if (!isCurrentRun()) return
      transcribeAbortRef.current = null
      setLoading(false)
    }

@sourcery-ai sourcery-ai 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.

Hey - I've left some high level feedback:

  • The initialization of TranscriptionRunState (result/uploadProgress/status/statusMessage) is duplicated between createRunContext, the remote branch in runTranscription, and runAsrFileTranscription; consider centralizing the initial state setup in a single helper to avoid subtle divergences in default values over time.
  • The mutable requestFailureStatusMessage / requestFailureLogPrefix pair in runTranscription couples error handling flow in a way that’s a bit hard to follow; you could make the remote and local paths each return a more explicit error-context object (or wrap the ASR call in a higher-order helper) to keep the error mapping logic more declarative.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The initialization of `TranscriptionRunState` (result/uploadProgress/status/statusMessage) is duplicated between `createRunContext`, the remote branch in `runTranscription`, and `runAsrFileTranscription`; consider centralizing the initial state setup in a single helper to avoid subtle divergences in default values over time.
- The mutable `requestFailureStatusMessage` / `requestFailureLogPrefix` pair in `runTranscription` couples error handling flow in a way that’s a bit hard to follow; you could make the remote and local paths each return a more explicit error-context object (or wrap the ASR call in a higher-order helper) to keep the error mapping logic more declarative.

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@makoMakoGo

Copy link
Copy Markdown
Owner Author

Bot review 处理进度:

  • Gemini:采纳 AbortError 判定建议,isAbortError 现在同时识别 Error-shaped AbortError 和 DOMException AbortError,并补了 run-level 测试。
  • Gemini:未采纳 settings normalizer 的 nullish fallback 建议。原因:不完整输入的边界已经由 applySettingsDefaults(Partial<Settings>) / env 加载路径处理;run normalizer 的输入类型是明确的 AsrRunSettings / LlmRunSettings,这里再加 nullish fallback 会把错误调用静默吞掉。
  • Gemini:未采纳移除 Uint8Array<ArrayBuffer> 的建议。当前 Next/TypeScript 构建要求 File 的 BlobPart 为 ArrayBuffer-backed view;改成裸 Uint8Array 会触发类型错误,已用 npm run build 验证。
  • Gemini:未采纳在 runSelectedTranscription 外再包 catch 的建议。runTranscription 是 run-level 错误边界;额外 catch 会隐藏意外同步 bug,不符合当前显式暴露失败的调试策略。
  • CodeRabbit:目标分支非默认分支,自动 review skipped。

本地验证:npm test 通过;npm run build 通过。

@makoMakoGo

Copy link
Copy Markdown
Owner Author

Bot review 处理完毕:

  • Gemini:采纳 AbortError 判定建议并补测试;其余 nullish fallback、移除 Uint8Array、外层 catch-all 建议已 rebuttal,原因见上一条评论。
  • Sourcery:无行级反馈,仅 review guide。
  • CodeRabbit:目标分支非默认分支,自动 review skipped。

本地验证:npm test 通过;npm run build 通过。

@makoMakoGo
makoMakoGo force-pushed the codex/browser-remote-audio-fetch branch from 8c68010 to 1a9f961 Compare June 10, 2026 09:35
@makoMakoGo
makoMakoGo force-pushed the codex/remote-transcription-run branch from 3255cd0 to a951161 Compare June 10, 2026 09:40
@makoMakoGo
makoMakoGo changed the base branch from codex/browser-remote-audio-fetch to main June 10, 2026 09:40
@makoMakoGo
makoMakoGo merged commit f135840 into main Jun 10, 2026
2 checks passed
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.

让远程音频转录复用同一个转录运行 Module

1 participant