Skip to content

Improve Japanese STT accuracy and strip Whisper outros - #1

Open
yosinn1-blip wants to merge 7 commits into
mainfrom
fix/stt-hallucination-and-accuracy
Open

yosinn1-blip wants to merge 7 commits into
mainfrom
fix/stt-hallucination-and-accuracy

Conversation

@yosinn1-blip

@yosinn1-blip yosinn1-blip commented Sep 1, 2026

Copy link
Copy Markdown
Owner

Summary

  • Repeat trailing Whisper hallucination stripping (longest-first) so stacked closings like ご視聴ありがとうございましたです / ごちしょう are removed when appended to real speech; keep standalone greetings.
  • Silence-only EmptyUtteranceGuard now matches those variants plus concatenated known closings.
  • Groq stays on whisper-large-v3 (lower WER than turbo). Pin language to ja when the hint is blank, send temperature=0, and add a conversational Japanese prompt that avoids seeding YouTube outros / the app name.
  • transcriptionPrompt は日本語宣言を含むため、resolvedLanguageja のときだけ multipart body に載せる。それ以外(例: en)では prompt フィールド自体を送らない。
let language = resolvedLanguage(from: languageHint)
...
if language == "ja" { append(prompt field) }   // 以前は常に送信

Test plan

  • swift test (63 XCTest + 19 Swift Testing)
  • swift testja のとき prompt あり / en のとき prompt なしを検証する追加テストを含む再実行。Linux 環境のため未実施)
  • scripts/install-app.sh~/Applications/API音声ソフト.app
  • Speak a Japanese sentence once and confirm the pasted text (no trailing ご視聴 / ごちしょう)
  • Confirm a real standalone 「ありがとうございました」 is still kept
  • 英語プロファイルで話し、日本語に誤判定されないことを確認

Link 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

Repeat trailing hallucination removal (including ご視聴ありがとうございましたです / ごちしょう) and send temperature 0 plus a conversational ja prompt while keeping whisper-large-v3 for accuracy.

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 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) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge 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 👍 / 👎.

Comment on lines +101 to +102
append("Content-Disposition: form-data; name=\"prompt\"\r\n\r\n")
append("\(Self.transcriptionPrompt)\r\n")

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge 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 👍 / 👎.

yosinn1-blip and others added 2 commits September 1, 2026 14:57
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>

@devin-ai-integration devin-ai-integration 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.

Devin Review found 2 potential issues.

1 flag not posted on this PR by your GitHub settings — view it in Devin Review. (Configure)

Devin Review

Comment on lines +111 to +124
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

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔴 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.

Devin Review


Was this helpful? React with 👍 or 👎 to provide feedback.

Comment on lines +43 to +44
static let transcriptionPrompt =
"これは日本語の日常会話の書き起こしです。話した内容だけを正確に書き取り、字幕や動画エンディングの定型文は付けない。Codex, Claude, ChatGPT, Gemini, Groq, Whisper, OpenAI, Anthropic, YouTube, GitHub, Git, Swift, Xcode, API"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 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.

Devin Review


Was this helpful? React with 👍 or 👎 to provide feedback.

yosinn1-blip and others added 4 commits September 21, 2026 09:12
言語ヒントが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済み配布に合わせる。

This branch has not been deployed

No deployments
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