Improve Japanese STT accuracy and strip Whisper outros - #1
yosinn1-blip wants to merge 7 commits into
Conversation
Repeat trailing hallucination removal (including ご視聴ありがとうございましたです / ごちしょう) and send temperature 0 plus a conversational ja prompt while keeping whisper-large-v3 for accuracy.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 8bbdc7f8c4
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
|
||
| private static func removingAppendedPhrase(from text: String, phrases: [String]) -> String { | ||
| let withoutTerminalPunctuation = text.trimmingCharacters(in: terminalPunctuation) | ||
| for phrase in phrases where withoutTerminalPunctuation.hasSuffix(phrase) { |
There was a problem hiding this comment.
Gate outro stripping on actual hallucination evidence
For ordinary active speech ending with one of these phrases, the suffix match silently deletes legitimate dictated content. For example, 昨日はありがとうございました。 becomes 昨日は, and 使っているのはAPI音声ソフト becomes 使っているのは; this happens to both the raw transcript and cleaned output regardless of audio activity. Standalone-phrase protection does not cover these common sentences, so removal should require stronger hallucination evidence rather than merely a matching suffix.
Useful? React with 👍 / 👎.
| append("Content-Disposition: form-data; name=\"prompt\"\r\n\r\n") | ||
| append("\(Self.transcriptionPrompt)\r\n") |
There was a problem hiding this comment.
Avoid sending a Japanese prompt for non-Japanese profiles
When languageHint is a supported non-Japanese value such as en, the multipart request correctly sends that language but still always includes a prompt declaring that the audio is Japanese conversation. Since this prompt is explicitly intended to guide Whisper's style and the repository supports an English profile, the contradictory prompt can bias English transcription toward Japanese; select a language-appropriate prompt or omit this one unless the resolved language is Japanese.
Useful? React with 👍 / 👎.
User never intends ご視聴ありがとうございました (and punctuation/です/ます variants) even as the entire utterance; keep a genuine standalone ありがとうございました.
9/13以降の未コミット分と今日の修正が AppDelegate 上で不可分だったため1コミットにまとめた。 ## 消えないオーバーレイ(1日1回の再起動の原因) process() の失敗経路5箇所で overlay.show(.failed) の後に hide() を呼んでおらず、 オーバーレイは ignoresMouseEvents = true なのでクリックでも消せなかった。 つまり消す手段がアプリの再起動しか無かった。debug.log 上、restart requested 14回のうち 複数が "音声認識結果が空でした" の直後に集中していた。 表示の期限を「表示する側」ではなく「状態そのもの」に持たせた(OverlayAutoHidePolicy)。 失敗・音声なし4秒、貼り付け完了2秒、処理中は保険として90秒(通信タイムアウト60秒より長く)、 録音中と会話待機はユーザーが終わらせるまで。状態を足したときに期限を忘れないよう 総当たりテストを置いた。 ## 背面タブのYouTubeが止まらない AppleScript には「どのタブが鳴っているか」を知る手段がない。全タブへ JS を撃つと Chrome が破棄済みタブを1枚ずつ復帰させ実測842秒まで膨らみ、アクティブタブのみに絞ると 背面タブを取りこぼす。実機は Chrome 3ウィンドウ139タブで、9/16〜9/18 は35件の巡回で 停止0件だった。 YouTube ページに常駐する content script と、アプリ側のローカル SSE サーバ (127.0.0.1 のみ、ポート47623) に変更した。content script は生きているタブだけで動くため、 鳴っているタブは必ず含まれ、破棄済みタブを起こすこともない。巡回コストが構造的に発生しない。 - Origin を https の YouTube 系に限定(平文HTTPは中間者が名乗れるので除外) - Chrome の Private Network Access 用に Access-Control-Allow-Private-Network を返す - 停止本数の報告は GET。Chrome が POST の fetch をローカル宛に送らずハングする場面を実機で踏んだ - 拡張は onInstalled で既存タブへ注入し直す。discarded: false で絞り、破棄済みタブは起こさない - AppleScript のアクティブタブ巡回は拡張が無いときの保険として残した ## make restart が無言で空振りしていた SwiftPM の成果物が .build/arm64-apple-macosx/release から .build/out/Products/Release へ 移り、cp が失敗してアプリが古いバイナリのまま動き続けていた。ビルドシステムに依存しない .build/release を使う。過去の「直したのに直っていない」の一因だった可能性がある。 テスト: XCTest 98件 + Swift Testing 19件 すべて合格 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
There was a problem hiding this comment.
Devin Review found 2 potential issues.
1 flag not posted on this PR by your GitHub settings — view it in Devin Review. (Configure)
| for phrase in phrases where withoutTerminalPunctuation.hasSuffix(phrase) { | ||
| let contentEnd = withoutTerminalPunctuation.index(withoutTerminalPunctuation.endIndex, offsetBy: -phrase.count) | ||
| let precedingContent = String(withoutTerminalPunctuation[..<contentEnd]) | ||
| .trimmingCharacters(in: .whitespacesAndNewlines) | ||
| // Standalone utterance of this phrase (including nested shorter names like | ||
| // "API音声ソフト"): keep the original text and do not fall through to a | ||
| // shorter suffix that would wrongly strip part of the phrase. | ||
| if precedingContent.isEmpty { | ||
| return text | ||
| } | ||
| if delimiterOnlyPhrases.contains(phrase) && !endsWithDelimiter(precedingContent) { | ||
| continue | ||
| } | ||
| return precedingContent |
There was a problem hiding this comment.
🔴 Real Japanese endings are deleted
Genuine dictation ending with ありがとうございます or ごちそうさまでした makes removingAppendedPhrase delete the spoken ending. Most phrases require neither a delimiter nor quiet audio, so valid content reaches cleanup already truncated.
Learn more
The filter runs on every transcript before cleanup and uses only textual suffix matching. delimiterOnlyPhrases protects just ご視聴 and ごちそう; longer closings, thanks phrases, and app names are removed even when directly attached to meaningful speech. The later silence guard cannot recover text already deleted by the pipeline.
Example: Speaking ご対応ありがとうございます produces ご対応. Speaking 夕食はごちそうさまでした produces 夕食は, although both endings are genuine content.
Recommended fix: Do not remove ambiguous phrases from active transcripts based only on suffixes. Require a clear phrase boundary and use AudioActivitySummary or provider metadata to distinguish hallucinations, while preserving exact spoken content when that evidence is unavailable.
Was this helpful? React with 👍 or 👎 to provide feedback.
| static let transcriptionPrompt = | ||
| "これは日本語の日常会話の書き起こしです。話した内容だけを正確に書き取り、字幕や動画エンディングの定型文は付けない。Codex, Claude, ChatGPT, Gemini, Groq, Whisper, OpenAI, Anthropic, YouTube, GitHub, Git, Swift, Xcode, API" |
There was a problem hiding this comment.
🟡 English transcription receives Japanese context
A non-Japanese languageHint still sends transcriptionPrompt, which declares the recording Japanese. The conflicting context can degrade or misidentify speech for English and other profiles.
Learn more
VoiceProfile permits arbitrary language hints, and stored profiles already include an English example at profile persistence tests. resolvedLanguage preserves en, but the multipart request always adds a prompt saying the recording is Japanese daily conversation. Whisper treats this prompt as transcription context, so it conflicts with the explicit language field.
Example: A profile with sttLanguageHint: "en" sends language=en together with これは日本語の日常会話の書き起こしです, giving the model contradictory context for English audio.
Recommended fix: Select a language-appropriate prompt from the resolved language, or omit this Japanese prompt whenever the resolved language is not ja. Add a multipart-body test for en that rejects Japanese-only context.
Was this helpful? React with 👍 or 👎 to provide feedback.
言語ヒントがja以外のとき、日本語宣言を含むpromptがそのまま送られ 認識精度や言語判定に悪影響が出ていたため、multipart bodyのprompt フィールドをja限定にした。 Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
「ありがとうございました」「ごちそうさまでした」「API音声ソフト」など、 日常会話でも普通に出る語尾が、区切り文字なしで文末に続いている場合でも 剥がされていた。その結果「いつもありがとうございます」が「いつも」になる等、 本人が発話した内容が失われていた。 これらを delimiterOnlyPhrases に移し、区切り文字(。!?など)の直後に 現れたときだけ剥がすようにした。「ご視聴ありがとうございました」「ごちしょう」 系は口述筆記で実発話になり得ないため、従来どおり無条件で剥がす。 再現テストを RealEndingRegressionTests として追加。既存テストの期待値は変更なし。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…cy' into tmp/merge-stt # Conflicts: # Sources/APIVoiceInputCore/GroqTranscriptionProvider.swift
操作の速さを優先し、Enter後の録音テールは0秒。以前の0.35秒方式はRecordingTailPolicyに残し、回帰テストで固定する。READMEとリリース手順をnotarize済み配布に合わせる。
Summary
ご視聴ありがとうございましたです/ごちしょうare removed when appended to real speech; keep standalone greetings.EmptyUtteranceGuardnow matches those variants plus concatenated known closings.whisper-large-v3(lower WER than turbo). Pinlanguagetojawhen the hint is blank, sendtemperature=0, and add a conversational Japanese prompt that avoids seeding YouTube outros / the app name.transcriptionPromptは日本語宣言を含むため、resolvedLanguageがjaのときだけ multipart body に載せる。それ以外(例:en)ではpromptフィールド自体を送らない。Test plan
swift test(63 XCTest + 19 Swift Testing)swift test(jaのとき prompt あり /enのとき prompt なしを検証する追加テストを含む再実行。Linux 環境のため未実施)scripts/install-app.sh→~/Applications/API音声ソフト.appLink to Devin session: https://app.devin.ai/sessions/ed894bdc9bc84fa695fedd50daf06ccd
Open in Devin Desktop: https://app.devin.ai/desktop/session/ed894bdc9bc84fa695fedd50daf06ccd?variant=devin
Requested by: @yosinn1-blip