Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/ISSUE_TEMPLATE/feature_request.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ What's missing or wrong today, concretely — not "it would be nice if."


**Does this touch a frozen contract or one of the five core pieces?**
(overlay pill / ScreenReaderService / Saaras STT / Bulbul TTS / Sarvam-30B
(overlay pill / ScreenReaderService / Saaras STT / Bulbul TTS / Sarvam-105B
planner / any file in `contracts/`)
If yes, explain why the current design can't absorb this change without
breaking it.
Expand Down
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ round-trip.
## Before you write code

The five core pieces (overlay pill, `ScreenReaderService`, Saaras STT,
Bulbul TTS, Sarvam-30B planner) and the contracts in `contracts/*.schema.json`
Bulbul TTS, Sarvam-105B planner) and the contracts in `contracts/*.schema.json`
are **frozen**. That means:

- No sixth subsystem. No new top-level component.
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ on-device screenshots, not mockups.
```mermaid
flowchart TD
U([User speaks]) --> STT[Saaras STT<br/>transcript + language]
STT --> P[Sarvam-30B planner<br/>forced tool call]
STT --> P[Sarvam-105B planner<br/>forced tool call]
R[ScreenReaderService<br/>read-only accessibility snapshot] --> P
P -->|step + target + instruction<br/>+ language, all in the user's script| SC[SessionController]
SC --> OV[OverlayService]
Expand All @@ -51,7 +51,7 @@ Five pieces, and only five — this is a deliberate freeze, not an oversight:
| Overlay pill | `OverlayService.kt`, `overlay/HighlightView.kt` | Renders an `OverlayCommand`. Never reasons. |
| Screen reader | `ScreenReaderService.kt`, `screen/ScreenSnapshot.kt` | Read-only accessibility snapshot. No gestures. |
| Saaras STT | `sarvam/SarvamStt.kt`, `sarvam/WavRecorder.kt` | 16 kHz mono WAV → transcript + language. |
| Sarvam-30B planner | `sarvam/SarvamPlanner.kt` | Forced tool call → the frozen planner contract. |
| Sarvam-105B planner | `sarvam/SarvamPlanner.kt` | Forced tool call → the frozen planner contract. |
| Bulbul TTS | `sarvam/SarvamTts.kt`, `sarvam/AudioPlayer.kt` | Instruction → warm spoken audio (`anand`). |

`session/SessionController.kt` orchestrates them. `session/StepEngine.kt` is the
Expand Down
2 changes: 1 addition & 1 deletion SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ buried:
and skips its own overlay's views. It does **not** take screenshots and does
**not** read arbitrary background apps — only the foreground window, only
while the service is enabled.
- That snapshot is sent, as text, to the Sarvam-30B planner API
- That snapshot is sent, as text, to the Sarvam-105B planner API
(`sarvam/SarvamPlanner.kt`) to decide which step and element to point at.
Recorded audio is sent to Saaras STT; the spoken instruction is sent to
Bulbul TTS. All three are outbound calls to `api.sarvam.ai` over HTTPS.
Expand Down
7 changes: 5 additions & 2 deletions app/src/main/assets/prompts/planner_v1.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,11 @@ Call `set_plan` exactly once. Never reply with prose.
script — Hindi means Devanagari, not romanised Hindi. If they mixed in
English words like "amount" or "submit", keep those words.
- `language`: the BCP-47 code of the language you wrote `instruction` in.
- Skip ahead if asked ("just pay"). Go back if they say they made a mistake.
If the request is unclear, choose the step marked CURRENT.
- Skip ahead if asked, in ANY language: "just pay", "go to submit",
"सीधे submit पर ले चलो", "submit पर ले चलो", "बस पे कर दो", "आखिरी step",
"நேரடியா submit பண்ணு" all mean the same thing — jump straight to the last
step, do not restate an earlier one first. Go back if they say they made a
mistake. If the request is unclear, choose the step marked CURRENT.
- `confidence` 0..1, below 0.5 only when genuinely unsure.
- `reason`: under 10 words.

Expand Down
10 changes: 9 additions & 1 deletion app/src/main/java/com/screensaathi/sarvam/Sarvam.kt
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,15 @@ object Sarvam {

const val STT_MODEL = "saaras:v3"
const val TTS_MODEL = "bulbul:v3"
const val PLANNER_MODEL = "sarvam-30b"
// sarvam-30b (the model this app was originally built and verified
// against) was retired by Sarvam sometime between 2026-07-26 and
// 2026-07-31 - a live call now returns a hard 400 ("Model 'sarvam-30b' has
// been deprecated... use sarvam-105b"), which the planner correctly turns
// into a null and falls back to the deterministic StepEngine, but that
// means the planner was silently dead for every real user in the
// meantime. Re-verified live against sarvam-105b before switching this
// constant - see scripts/planner_case.ps1.
const val PLANNER_MODEL = "sarvam-105b"

const val AUTH_HEADER = "api-subscription-key"

Expand Down
2 changes: 1 addition & 1 deletion app/src/main/java/com/screensaathi/sarvam/SarvamPlanner.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import org.json.JSONArray
import org.json.JSONObject

/**
* Sarvam-30B planner. Forces a single structured tool call (tool_choice
* Sarvam-105B planner. Forces a single structured tool call (tool_choice
* required) so the model always returns the FROZEN planner schema, never prose.
* reasoning_effort is disabled — this is a fast routing decision, not a
* chain-of-thought task, and reasoning tokens would blow the latency budget.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ import java.util.concurrent.atomic.AtomicInteger
/**
* The orchestration layer. Turns UI taps into OverlayCommands, driving the
* deterministic StepEngine plus (when a key is present and calls succeed) the
* Sarvam voice loop: Saaras STT -> Sarvam-30B planner -> highlight -> Bulbul TTS.
* Sarvam voice loop: Saaras STT -> Sarvam-105B planner -> highlight -> Bulbul TTS.
*
* Every network step has a deterministic fallback. If the key is missing or any
* call fails, mic-tap starts the task and Next advances in order — the overlay
Expand Down
2 changes: 1 addition & 1 deletion docs/DATA_HANDLING.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ Three outbound calls, all HTTPS to `api.sarvam.ai`, all defined in
| Call | What is sent | What comes back |
| --- | --- | --- |
| Saaras STT | The recorded `.wav` clip | Transcript + detected language |
| Sarvam-30B planner | The transcript, the current task's step ids, and the current screen snapshot as plain text | A step id, a target resource id, a spoken instruction, and a language code |
| Sarvam-105B planner | The transcript, the current task's step ids, and the current screen snapshot as plain text | A step id, a target resource id, a spoken instruction, and a language code |
| Bulbul TTS | The instruction text and its language code | Synthesized audio (WAV) |

No third party other than Sarvam ever receives data from this app. There is
Expand Down
76 changes: 54 additions & 22 deletions docs/PARKING_LOT.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,26 +14,49 @@ fields removed — only optional additions.

## Latency budgets (optimize the offending layer, don't guess)

Measured via `scripts/smoke_sarvam.ps1`, `scripts/smoke_languages.ps1` and
`scripts/smoke_planner_language.ps1` on 2026-07-26. Venue network not yet tested.
Measured via `scripts/smoke_sarvam.ps1`, `scripts/smoke_languages.ps1`, and
`scripts/planner_case.ps1` on 2026-07-26 and re-measured 2026-07-31 after the
forced model swap below. Venue network not yet tested.

| Layer | Target | Measured | Status |
| ---------------------- | ----------- | --------------- | ------ |
| Saaras STT | < 800 ms | 666–729 ms | OK |
| Planner (Sarvam-30B) | < 700 ms | 867–1481 ms | OVER |
| Overlay update | < 16 ms | not instrumented| — |
| Bulbul TTS first audio | < 900 ms | 909–1462 ms | OVER |
| End-to-end response | < 2.5 s | not instrumented| — |
| Layer | Target | Measured (07-26) | Measured (07-31) | Status |
| ---------------------- | ----------- | ----------------- | ----------------- | ------ |
| Saaras STT | < 800 ms | 666–729 ms | not re-measured | OK |
| Planner | < 700 ms | 867–1481 ms | **1261–3300 ms** | OVER |
| Overlay update | < 16 ms | not instrumented | not instrumented | — |
| Bulbul TTS first audio | < 900 ms | 909–1462 ms | not re-measured | OVER |
| End-to-end response | < 2.5 s | not instrumented | not instrumented | — |

**Neither over-budget layer blocks the visual any more.** TTS now runs on its
own thread, started in parallel with bounds resolution rather than after it, so
the cursor and ring land while Bulbul is still synthesising.

The planner regressed from 606 ms to ~900–1400 ms when the prompt grew to carry
the language contract (713 prompt tokens). It is capped at a hard 5 s call
timeout (`Sarvam.plannerHttp`) because the deterministic step engine answers
instantly and for free — beyond a few seconds, falling back is strictly better
than waiting. Trimming the prompt further is the M4 lever.
**2026-07-31: forced model swap, `sarvam-30b` → `sarvam-105b`.** Sarvam
deprecated `sarvam-30b` sometime between 07-26 and 07-31 — a live call now
returns a hard 400 (`"Model 'sarvam-30b' has been deprecated... use
sarvam-105b"`). This is not optional or scheduled work: the planner was
silently dead (falling back to the deterministic engine on every real call)
until this was caught and fixed. `Sarvam.PLANNER_MODEL` now points at
`sarvam-105b`, re-verified live via `scripts/planner_case.ps1` against the
same six-case regression set used on 07-26.

The swap is a net negative on latency: 105B is a bigger, slower model, and
measured planner latency roughly doubled (1261–3300 ms vs 867–1481 ms). It is
still capped at the same hard 5 s call timeout (`Sarvam.plannerHttp`) — beyond
that, falling back to the free, instant `StepEngine` is still strictly better
than waiting. Reducing planner latency is now a harder problem than trimming
the prompt (M4 item, unchanged) — it may require asking Sarvam for a smaller
model tier once one exists, or moving skip-ahead/correction handling into
deterministic keyword matching so the LLM is only consulted when genuinely
ambiguous.

One behavioral difference worth watching, not yet resolved: on `sarvam-105b`,
"अरे नहीं, गलती हो गई" (correction, said on the first step) now returns
`step: account` instead of the previous `step: amount` — moving the user
forward on a "we go back" utterance where there is no earlier step to return
to. Not confirmed as objectively wrong (there's no clean target for "go back"
from step one), but it's a change in behavior from the same prompt against a
different model, worth a second look before relying on the correction path
in a demo.

## Demo-day gotchas (learned the hard way on device)

Expand All @@ -49,18 +72,27 @@ than waiting. Trimming the prompt further is the M4 lever.

## Parked items

- Planner ignores "skip ahead" in Hindi. "सीधे submit पर ले चलो" (take me
straight to submit) returns `step: amount`. English skip-ahead was never
re-tested after the prompt rewrite. → **Planner/prompt improvement**, worth
fixing before the demo if a judge is likely to try it.
- Phrases are authored in English and Hindi only. Bulbul speaks ten languages
and Saaras detects all ten, so a Tamil speaker gets Tamil *planner*
instructions but English chrome ("Listening…"). → **UX improvement**;
adding a language is adding a column to `Phrases`.
- ~~Planner ignores "skip ahead" in Hindi.~~ **Fixed 2026-07-31**: the prompt
now enumerates skip-ahead phrasing across languages explicitly
(`planner_v1.md`); re-verified live, "सीधे submit पर ले चलो" now correctly
returns `step: submit`. See the latency section above for what else changed
in the same pass (the forced `sarvam-105b` swap).
- Phrases are authored in English, Hindi, **and Tamil** (`session/Phrases.kt`,
`overlay/PillLabels.kt`) as of the taxi-flow work. Bulbul speaks ten
languages and Saaras detects all ten; the remaining seven (Bengali,
Gujarati, Kannada, Malayalam, Marathi, Punjabi, Telugu) still get planner
instructions in the right language but English chrome ("Listening…").
→ **UX improvement**; adding a language is adding a column to `Phrases` and
`PillLabels`.
- TTS speaker is `anand` for every language. Verified to work in all ten, but a
per-language voice would sound better. → **UX improvement**, PARKED.
- No barge-in: speaking over the assistant does not interrupt it, the user has
to tap Stop. → **UX improvement**, PARKED (needs continuous capture).
- `book_taxi.json`'s `pick_ride`/`destination` steps match Uber/Ola/Rapido by
hardcoded English button text (`text_any`), with no fallback if any of the
three apps change their copy. → tracked as a GitHub issue, not fixed here —
it needs a real design decision (semantic matching? OCR? per-app adapters?),
not a quick patch. See issues labeled `audit-followup`.

## Explicitly out of scope (from the reference app, deliberately dropped)

Expand Down
2 changes: 1 addition & 1 deletion scripts/planner_case.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ function J([string]$s) {
$sb.ToString()
}

$body = '{"model":"sarvam-30b","messages":[{"role":"system","content":"' + (J $prompt) + '"},{"role":"user","content":"' + (J $user) + '"}],' +
$body = '{"model":"sarvam-105b","messages":[{"role":"system","content":"' + (J $prompt) + '"},{"role":"user","content":"' + (J $user) + '"}],' +
'"tools":[{"type":"function","function":{"name":"set_plan","description":"Set the next guided step and the element to point at.","parameters":' +
'{"type":"object","properties":{"intent":{"type":"string"},"step":{"type":"string","enum":["amount","account","submit"]},' +
'"target":{"type":"object","properties":{"resource_id":{"type":"string"},"index":{"type":"integer"}},"required":["resource_id","index"]},' +
Expand Down
Loading