Repository navigation
fix(refine): reject same-script translations in the cleanup language guard - #252
Merged
Merged
Conversation
…guard RefineOutputGuard only compared writing systems, so a cleanup that changed the LANGUAGE but kept the script passed: measured on llm-bench (2026-11-01), Qwen3.5-0.8B/2B rewrote a Ukrainian dictation in Russian and the shipped default Qwen2.5-1.5B rewrote German in English — both pasted as "cleaned". When the script survives, plain cleanups now also run a language-level check with the on-device NLLanguageRecognizer: reject only when the input's language is confidently identified (>= 0.80), the output is confidently a different one (>= 0.80), and the input's language is all but gone from it (<= 0.10). Needs >= 20 letters on both sides; easily-confused pairs (nb/da, hr/sr/bs, ms/id) count as one language, uk/ru deliberately do not. Intended translations (expectedOutputScript set) keep the old script-only contract. Applies to every caller: dictation cleanup, live chunks, meeting summaries, Scratchpad AI. No AppState changes. Tests use the real bench outputs; the Ukrainian->Russian and German->English cases fail on the old guard and pass now, and every legitimate cleanup from the same runs still passes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
4 of 6 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What & why
RefineOutputGuardonly compared writing systems, so a cleanup that changed the language but kept the alphabet was pasted as if it had been cleaned. In a local refine benchmark on 2026-11-01 (scripts/llm-bench.sh, the app's real cleanup prompt, temperature 0):Fix: when the script survives, plain cleanups now also get a language-level check using the on-device
NLLanguageRecognizer(NaturalLanguage). It rejects only when all three hold:Other rules:
expectedOutputScriptset, i.e. translate-to-English) keep the old script-only contract.It applies to every caller of
outputTranslatedAway: dictation cleanup, live chunks, meeting summaries and Scratchpad AI. All of them already promise "keep the original language". The agent-bridge, spoken-instruction and custom-Mode paths stay exempt throughshouldLanguageGuard, unchanged.The thresholds come from the bench corpus. Legitimate cleanups keep their language at 0.96–1.00, and real translations flip at 1.00. The decision itself is a pure function over hypothesis maps (
languageChanged(inputHypotheses:outputHypotheses:)), so thresholds are unit-tested independently of Apple's model.How it was tested
swift testpasses: 2,645 tests, 0 failures, including 13 new tests inRefineOutputGuardLanguageTestsscripts/dev-app.sh);check-appstate-ratchet.shOK, with noAppStatechangesOpenWhispCorebuilds for iOS (xcodebuild -scheme OpenWhispCore -destination 'generic/platform=iOS'): NaturalLanguage is available there, and the import is behind#if canImport(NaturalLanguage)./build.sh: not run on this branch, which was cut before chore(deps): update all native deps + CI, retire WhisperKit fork, ship SpeechAnalyzer again #250's Xcode 27 build fix. CI builds the merge withmain.Privacy checklist
NLLanguageRecognizerruns fully on device.Notes
outputTranslatedAwayispublicinOpenWhispCore, and its behavior gets stricter (it can now returntruefor same-script input/output).languageChanged(input:output:)and the pure overload are newpublicAPI.--reasoning offonllama-server, because Qwen3.5 thinks by default under llama.cpp's template and otherwise returns empty output. That's a separate change.🤖 Generated with Claude Code