Skip to content

Answer the user in the language they spoke, and point with a moving cursor - #3

Merged
NITISH-R-G merged 1 commit into
mainfrom
fix/devanagari-task-matching
Jul 26, 2026
Merged

NITISH-R-G merged 1 commit into
mainfrom
fix/devanagari-task-matching

Conversation

@NITISH-R-G

Copy link
Copy Markdown
Owner

Two things the demo needed: reply in the user's own language, and make the pill visibly guide rather than sit still.

The multilingual break, and where it was

Language detection already reached TTS — lastLanguage was set from Saaras and passed to Bulbul. But the text stayed English. A Hindi speaker heard English words requested as hi-IN, which Bulbul rejects outright:

400 "Text must contain at least one character from the allowed languages."

A rejected synthesis is indistinguishable from the assistant having nothing to say, so the app just went quiet. Fixed in the order the words travel:

Where Was Now
Spoken(text, language) text and code drifted apart every string carries the language it is written in — the mismatch is unrepresentable
Language.reconcile() trusts the script over the claim before anything reaches Bulbul
SarvamPlanner no language in, no language out detected language + CURRENT step in; instruction in that language + language code out
Task DSL English-only instruction optional per-language instructions, so the offline path is multilingual too
Phrases / PillLabels English literals in the controller and renderer keyed by language, and honest about falling back
TaskRepository.normalize() [^a-z0-9 ] strips by character category

Two of those deserve detail:

reconcile uses presence, not majority — matching Bulbul's own rule. If the claimed language's script appears at all, keep the claim. That is what makes code-switching work: "amount यहाँ भरिए" stays hi-IN and Bulbul reads the English word naturally. A majority count gets this wrong, because Devanagari vowel signs (, ि) are combining marks rather than letters — it scores 6 Latin to 5 Devanagari and calls it English. A unit test caught this.

The utterance matcher was dead in Hindi. [^a-z0-9 ] deletes every Devanagari character, and Saaras returns Hindi speech as Devanagari. So matching scored zero on the primary demo language and silently matched nothing — masked today only because there is one task and the fallback picks it anyway. It would have broken the moment task #2 landed in M3.

Verified live, not assumed

  • scripts/smoke_languages.ps1 — all ten languages synthesise with speaker anand, and Saaras detects each one back correctly.
  • scripts/planner_case.ps1 — the real prompt and tool spec, driven through curl:
said step language instruction
help me pay this bill 1240 ms amount en-IN Enter the amount you want to pay.
बिजली का बिल भरना है 867 ms amount hi-IN बिल की राशि दर्ज करें
मुझे amount यहाँ भरना है 1155 ms amount hi-IN अब यहाँ राशि दर्ज करें।
எனக்கு பில் கட்ட வேண்டும் 1103 ms amount ta-IN பில் தொகை எவ்வளவு?
আমি বিল দিতে চাই 1092 ms amount bn-IN আপনি কত টাকা দিতে চান?

Moving cursor

HighlightView now flies a cursor along an arc to the target, trailing a comet tail and a tether back to the pill, and the ring blooms only once it arrives so the two read as cause and effect. The ring alone teleported between fields, which reads as a highlight appearing rather than as something guiding you. Confirmed on device.

Stop

onStopTapped() silences speech, invalidates the in-flight turn, flies the cursor home and clears the ring — while keeping the step position, so the next mic tap resumes rather than restarts.

Also

Speech moved to its own thread and starts in parallel with bounds resolution, so the visual never queues behind Bulbul's ~1.2 s. The planner got a hard 5 s call timeout — the step engine answers instantly and for free, so waiting longer is strictly worse than falling back.

What was tested

  • assembleDebug + testDebugUnitTest — 56 tests green (39 new)
  • Installed on a real device: cursor flight, ring, tether, Stop button and the language chip all render; no crashes.

Still needs verification

  • A real spoken utterance. Every layer is verified live and the wiring is verified on device, but no human has yet spoken Hindi into the phone end to end. That is the one step I cannot run myself.
  • Planner latency regressed: 606 ms → 867–1481 ms, because the prompt now carries the language contract (713 prompt tokens). Over the 700 ms budget, capped at 5 s, and the highlight lands first — recorded in PARKING_LOT.md.
  • Hindi skip-ahead is wrong: "सीधे submit पर ले चलो" returns step: amount. Parked with the fix bucket named; worth doing before the demo if a judge is likely to try it.

…ursor

Two things the demo needed: it has to reply in the user's own language, and
the pill has to visibly guide rather than sit still.

## Multilingual

Language detection already reached TTS, but the TEXT stayed English. A Hindi
speaker heard English words requested as hi-IN, which Bulbul rejects outright:

    400 "Text must contain at least one character from the allowed languages."

A rejected synthesis is indistinguishable from having nothing to say, so the
app just went quiet. Fixes, in the order the words travel:

- Spoken(text, language) pairs every string with the language it is actually
  written in, so the mismatch is now unrepresentable.
- Language.reconcile() trusts the script over the claim before anything is
  sent to Bulbul. The rule is presence, not majority, matching Bulbul's own:
  if the claimed language's script appears at all, keep it. That is what makes
  code-switching work - "amount यहाँ भरिए" stays hi-IN and the English word is
  read naturally. A majority count gets this wrong, because Devanagari vowel
  signs are combining marks rather than letters, so it scores 6 Latin to 5
  Devanagari and calls it English.
- The planner now receives the detected language and the CURRENT step, and
  returns instruction in that language plus a `language` code. The prompt rule
  "if unclear, stay on the current step" was previously unfollowable - the
  model was never told which step that was.
- The task DSL carries per-language wording, so the offline fallback is
  multilingual too, and Phrases/PillLabels move the assistant's own words and
  the pill's labels off English literals.
- TaskRepository.normalize() stripped with [^a-z0-9 ], which deletes every
  Devanagari character. Saaras returns Hindi speech AS Devanagari, so utterance
  matching scored zero on the primary demo language and silently matched
  nothing. It now strips by character category.

Verified live against Sarvam, not assumed:
- scripts/smoke_languages.ps1 - all ten languages synthesise with speaker
  `anand` and Saaras detects each one back correctly.
- scripts/planner_case.ps1 - the real prompt and tool spec return Devanagari
  for Hindi, Tamil for Tamil, Bengali for Bengali, each correctly labelled.

## Moving cursor

HighlightView now flies a cursor along an arc to the target, trailing a comet
tail and a tether back to the pill, and the ring only blooms once it arrives so
the two read as cause and effect. The ring alone teleported between fields,
which read as a highlight appearing rather than as something guiding you.

## Stop

onStopTapped() silences speech, invalidates the in-flight turn, flies the
cursor home and clears the ring - while keeping the step position, so the next
mic tap resumes instead of restarting.

## Also

Speech moved to its own thread and now starts in parallel with bounds
resolution, so the visual never queues behind Bulbul's ~1.2s. The planner got a
hard 5s call timeout: the step engine answers instantly and for free, so
waiting longer than that is strictly worse than falling back.

Tested: assembleDebug and testDebugUnitTest green (56 tests, 39 new); installed
and exercised on a real device - cursor flight, ring, tether, Stop button and
the language chip all render, no crashes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

@greptile-apps greptile-apps 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.

Your trial has ended. Reactivate Greptile to resume code reviews.

@coderabbitai

coderabbitai Bot commented Jul 26, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@NITISH-R-G, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 7 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

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.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 3410ee33-1ec4-45f6-b57d-9a4590156af1

📥 Commits

Reviewing files that changed from the base of the PR and between b306118 and c7950d7.

📒 Files selected for processing (26)
  • PARKING_LOT.md
  • app/src/main/assets/prompts/planner_v1.md
  • app/src/main/assets/tasks/pay_bill.json
  • app/src/main/java/com/screensaathi/OverlayService.kt
  • app/src/main/java/com/screensaathi/overlay/HighlightView.kt
  • app/src/main/java/com/screensaathi/overlay/OverlayCommand.kt
  • app/src/main/java/com/screensaathi/overlay/PillLabels.kt
  • app/src/main/java/com/screensaathi/sarvam/Language.kt
  • app/src/main/java/com/screensaathi/sarvam/PlannerResult.kt
  • app/src/main/java/com/screensaathi/sarvam/Sarvam.kt
  • app/src/main/java/com/screensaathi/sarvam/SarvamPlanner.kt
  • app/src/main/java/com/screensaathi/sarvam/SarvamStt.kt
  • app/src/main/java/com/screensaathi/sarvam/SarvamTts.kt
  • app/src/main/java/com/screensaathi/session/Phrases.kt
  • app/src/main/java/com/screensaathi/session/SessionController.kt
  • app/src/main/java/com/screensaathi/session/VoiceDebug.kt
  • app/src/main/java/com/screensaathi/task/TaskModels.kt
  • app/src/main/java/com/screensaathi/task/TaskRepository.kt
  • app/src/main/res/layout/overlay_pill.xml
  • app/src/test/java/com/screensaathi/sarvam/LanguageTest.kt
  • app/src/test/java/com/screensaathi/sarvam/SarvamPlannerParseTest.kt
  • app/src/test/java/com/screensaathi/session/PhrasesTest.kt
  • app/src/test/java/com/screensaathi/task/MultilingualTaskTest.kt
  • contracts/planner.schema.json
  • scripts/planner_case.ps1
  • scripts/smoke_languages.ps1
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/devanagari-task-matching

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.

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

Sorry @NITISH-R-G, you have reached your weekly rate limit of 500000 diff characters.

Please try again later or upgrade to continue using Sourcery

@sonarqubecloud

Copy link
Copy Markdown

@NITISH-R-G
NITISH-R-G marked this pull request as ready for review July 26, 2026 09:22
@NITISH-R-G
NITISH-R-G merged commit 4d80be0 into main Jul 26, 2026
4 checks passed

@greptile-apps greptile-apps 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.

Your trial has ended. Reactivate Greptile to resume code reviews.

@NITISH-R-G
NITISH-R-G deleted the fix/devanagari-task-matching branch July 26, 2026 09:22
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