fix(quick-router): compose a digit with a following magnitude word - #825
fix(quick-router): compose a digit with a following magnitude word#825jaytbarimbao-collab wants to merge 1 commit into
Conversation
`parse_spoken_number`'s digit fast-path returned on the first integer token and never consumed a following "hundred"/"thousand", so a mixed digit + magnitude utterance — how STT commonly renders round numbers — was emitted as two tokens: - "100 thousand divided by 4" -> "100 1000 / 4" (should be "100000 / 4") - "20 thousand times 2" -> "20 1000 * 2" (should be "20000 * 2") - "5 hundred plus 10" -> "5 100 + 10" (should be "500 + 10") This is the same garble the all-word path already handles (`routes_compound_spoken_cardinals_to_calculate`: "one hundred / five" was once "1 100 / 5"); the digit + magnitude form still slipped through. - `parse_spoken_number` (`crates/genie-core/src/tools/number_words.rs`): in the digit fast-path, scale the value by an immediately following "hundred"/ "thousand" and consume that word, instead of returning the bare digit. A digit with no magnitude word after it still returns as-is, so "100 divided by 5" and "a 15 minute timer" (article "a" + digit) are unchanged. - Add `routes_digit_with_magnitude_word_to_calculate`.
📝 WalkthroughWalkthrough
ChangesDigit magnitude parsing
Estimated code review effort: 2 (Simple) | ~10 minutes Suggested labels: Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@crates/genie-core/src/tools/number_words.rs`:
- Around line 16-20: Update the number parsing logic around the match on
tokens.get(start + 1) so hundred and thousand values initialize the existing
parser state with the scaled value, then continue parsing from start + 2 to
consume suffixes such as twenty, 300, or and 5. Retain the immediate return path
only for bare digits.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: b1982df1-d4ed-41d2-bbdf-6ffdff30cb88
📒 Files selected for processing (2)
crates/genie-core/src/tools/number_words.rscrates/genie-core/src/tools/quick.rs
| return Some(match tokens.get(start + 1) { | ||
| Some(&"hundred") => (value.saturating_mul(100), start + 2), | ||
| Some(&"thousand") => (value.saturating_mul(1000), start + 2), | ||
| _ => (value, start + 1), | ||
| }); |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
The early return truncates mixed cardinal numbers.
Returning after digit + magnitude leaves suffixes such as twenty, 300, or and 5 unconsumed, producing malformed expressions like 500 20 and 2000 300. Initialize the existing parser state from the scaled value and continue parsing from start + 2; retain the immediate return only for bare digits.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@crates/genie-core/src/tools/number_words.rs` around lines 16 - 20, Update the
number parsing logic around the match on tokens.get(start + 1) so hundred and
thousand values initialize the existing parser state with the scaled value, then
continue parsing from start + 2 to consume suffixes such as twenty, 300, or and
5. Retain the immediate return path only for bare digits.
matedev01
left a comment
There was a problem hiding this comment.
Confirmed CodeRabbit's finding directly (added a throwaway test to check the actual return value, not just reading the code):
parse_spoken_number(&["100", "thousand", "300"], 0)returns(100000, 2), leaving"300"completely unconsumed.parse_spoken_number(&["5", "hundred", "and", "20"], 0)returns(500, 2), leaving"and 20"dangling.
The early return after composing digit+magnitude stops before the existing while-loop's suffix handling ever runs, so a triple STT rendering like "one hundred thousand three hundred" → "100 thousand 300" produces a malformed/un-evaluatable expression instead of 100300 — which is exactly the class of input this PR sets out to fix. CodeRabbit's suggested fix is right: initialize the existing parser state (total/group/matched) from the scaled value and continue from start + 2 instead of returning immediately, keeping the immediate-return only for a bare digit with no magnitude word.
Summary
parse_spoken_number's digit fast-path returned on the first integer token andnever consumed a following
hundred/thousand, so a mixed digit + magnitudeutterance — how STT commonly renders round numbers ("100 thousand") — was
emitted as two tokens and produced a garbled, un-evaluatable expression:
what is 100 thousand divided by 4100 1000 / 4100000 / 4what is 20 thousand times 220 1000 * 220000 * 2what is 5 hundred plus 105 100 + 10500 + 10This is the same garble the all-word path already handles
(
routes_compound_spoken_cardinals_to_calculatedocuments"one hundred / five"once becoming
"1 100 / 5"); the digit + magnitude form still slipped throughthe fast-path.
Changes
parse_spoken_number(crates/genie-core/src/tools/number_words.rs): in thedigit fast-path, scale the value by an immediately following
hundred/thousandand consume that word, instead of returning the bare digit. A digitwith no magnitude word after it still returns as-is.
routes_digit_with_magnitude_word_to_calculate.Real Behavior Proof
Verified locally against the real
route()(x86 dev host — pure input parsing,no hardware path):
A first attempt that parsed digit tokens inside the accumulation loop regressed
"a 15 minute timer"(articlea=1 absorbed the15→ 16 min); the final fixhandles the pair only in the fast-path, leaving that case correct. Full
cargo test -p genie-core— all 34 test binaries pass;cargo fmt --checkclean;
cargo clippy --all-targetsclean on default and--no-default-features.laptopprofile.Tested profile / hardware:
jetsonraspberry_piportable_sbclaptopSummary by CodeRabbit
Bug Fixes
Tests