patches for jibo version 18 testing
This commit is contained in:
@@ -21,16 +21,15 @@ Release `1.0.18` is now in feature-hardening. Its main bug-fix theme is alarm an
|
||||
|
||||
## Latest Live Evidence
|
||||
|
||||
`jibo test 22` was captured against a robot that spoke `Open Jibo Cloud version 1 dot 0 dot 18.`
|
||||
`jibo test 23` was captured after the `jibo test 22` alarm/photo hardening pass.
|
||||
|
||||
- Radio live validation passed.
|
||||
- News routing was observed in websocket telemetry from the phrase `So, play the news.`, but the user did not get enough live confidence to call news complete because a backup notification/slowness path was active during the session.
|
||||
- The backup notification came from stock `@be/surprises-ota` checking `jibo.scheduler.backupStatus`; no `Backup_*` HTTP operation appeared in the captured cloud traffic. The update-menu block therefore looks more like a robot-local scheduler/backup load issue than a cloud `Backup.List` response issue.
|
||||
- The robot log showed high load, a `jibo-server-service` broken pipe, a settings error path for `Q4-Server_connection_lost`, and the stock backup prompt: `hey i'm sorry if I seem a little slow, I can be that way while i'm doing a backup.`
|
||||
- Photo/gallery reached the local gallery/create path, but a missed short reply left repeated `create/is_it_a_keeper` listens and the visible blue-ring/listening state.
|
||||
- Alarm attempts were dominated by collapsed transcripts such as `set and alarm`, `Set and Alonzo`, and `Set an alarm for...`; one path reached local alarm clarification but did not get a complete value-setting pass.
|
||||
- The turn telemetry contained `ffmpeg` failures where local whisper tried to decode buffered Ogg/Opus turns that were not usable by `ffmpeg`.
|
||||
- The websocket capture still contained `OPENJIBO_TURN_PENDING`, `OPENJIBO_CONTEXT_ACK`, and proactive `OPENJIBO_ACK` output in the deployed run. The current source has no references to those synthetic OpenJibo events, so the next deployment needs an artifact/build verification pass.
|
||||
- Radio remained live-valid.
|
||||
- News live validation passed. The robot spoke the current synthetic quick brief: `technology companies are still racing on AI...`. This proves the Nimbus-shaped path, not provider-backed headline expansion.
|
||||
- Backup/update remains unresolved. The user observed backup-in-progress sluggishness and the update menu could not proceed while backups were active; the spoken "start backups" style command is not currently a wired OpenJibo voice path.
|
||||
- The first alarm path succeeded: `set an alarm for 743` scheduled a local `7:43 AM` alarm and the alarm fired. A later clarification answer was logged by the robot as `- Time. - 7, 14.` and local NLU interpreted it as `7:00 PM`, causing the confusing replacement prompt.
|
||||
- Current source now accepts short clock value answers during `clock/alarm_set_value` / `clock/timer_set_value`, parses comma-separated alarm digits such as `7, 44`, and maps stock alarm yes/no prompt rules `clock/alarm_timer_change` and `clock/alarm_timer_none_set`.
|
||||
- Photo/gallery still needs a clean live pass. The gallery path opened, but several `shared/yes_no` turns had empty ASR text, so there was no positive `yes` transcript for the cloud to map.
|
||||
- No `ffmpeg` / `whisper.cpp` error was evident in the `jibo test 23` turn timeline, unlike `jibo test 22`. Treat any future decode errors separately from the gallery yes/no payload path.
|
||||
|
||||
## Release Rhythm
|
||||
|
||||
@@ -94,14 +93,15 @@ The following behavior is present in source and covered by focused tests:
|
||||
- radio voice launch supports `open the radio` and genre launch such as `play country music`, using local `@be/radio` `menu` payloads, `SKILL_REDIRECT`, and silent completion
|
||||
- news has a first Nimbus-shaped cloud path using `match.cloudSkill = news` and a `news` `SKILL_ACTION` with synthetic briefing content
|
||||
- stock-shaped clock handoffs cover time, date, day, clock open, timer/alarm menu, timer/alarm value, timer/alarm clarification, and timer/alarm delete
|
||||
- alarm parsing covers forms such as `7:30 am`, `830`, `8 30`, `10-25`, `10:25 pm`, and `10 25 p m`
|
||||
- alarm parsing covers forms such as `7:30 am`, `830`, `8 30`, `7, 44`, `10-25`, `10:25 pm`, and `10 25 p m`
|
||||
- ambiguous alarm times can prefer the next local occurrence when the robot context includes `runtime.location.iso`
|
||||
- short clock value follow-up transcripts are accepted under `clock/alarm_set_value` and `clock/timer_set_value` instead of being dropped before parsing
|
||||
- `CLIENT_NLU intent=set` with only `domain=alarm` stays on the local clock clarification path instead of defaulting to a fabricated time
|
||||
- `CLIENT_NLU intent=cancel` on `clock/alarm_timer_query_menu` can reuse the last active clock domain
|
||||
- photo flows route `open photo gallery` to `@be/gallery`, `snap a picture` to `@be/create/createOnePhoto`, and `open photobooth` to `@be/create/createSomePhotos`
|
||||
- passive gallery/create context does not reopen a stale cloud turn
|
||||
- media metadata persists across store recreation and `/media/{path}` can serve the current text-body placeholder payload
|
||||
- constrained yes/no handling covers `create/is_it_a_keeper`, `shared/yes_no`, `settings/download_now_later`, `surprises-date/offer_date_fact`, `surprises-ota/want_to_download_now`, and `$YESNO` hints
|
||||
- constrained yes/no handling covers `clock/alarm_timer_change`, `clock/alarm_timer_none_set`, `create/is_it_a_keeper`, `shared/yes_no`, `settings/download_now_later`, `surprises-date/offer_date_fact`, `surprises-ota/want_to_download_now`, and `$YESNO` hints
|
||||
- outbound constrained yes/no responses strip unrelated `globals/*` rules so stock OS stays local
|
||||
- no-input fallback for constrained yes/no prompts emits local `LISTEN`/`EOS` instead of relaunching generic Nimbus speech, including `shared/yes_no` after STT failure
|
||||
- repeated empty `create/is_it_a_keeper` replies redirect to `@be/idle` after the second miss so the photo/create flow can settle instead of leaving a stale listening state
|
||||
@@ -115,8 +115,9 @@ Use these sources as evidence, not as code to copy blindly:
|
||||
- OpenJibo Node oracle: [open-jibo-link.js](../src/Jibo.Cloud/node/open-jibo-link.js)
|
||||
- Current hosted `.NET` cloud: [src/Jibo.Cloud/dotnet](../src/Jibo.Cloud/dotnet)
|
||||
- Live captures and robot logs: `.\artifact-output`
|
||||
- Original Pegasus cloud source: `..\jibo\pegasus`
|
||||
- Original SDK and skill source snapshot: `..\jibo\sdk`
|
||||
- User-provided original source snapshot: `..\jibo` when extracted locally
|
||||
- Original Pegasus cloud source inside that snapshot: `pegasus`
|
||||
- Original SDK and skill source inside that snapshot: `sdk`
|
||||
- JiboOS reference tree: `..\JiboOS`
|
||||
- JiboOS skill snapshot: `..\JiboOS\opt\jibo\Jibo\Skills\@be`
|
||||
|
||||
@@ -132,14 +133,14 @@ Before calling `1.0.18` complete, prove or explicitly defer these:
|
||||
|
||||
- Run the focused `.NET` cloud test suite after the last feature slice.
|
||||
- Confirm the running robot build reports cloud version `1.0.18`.
|
||||
- Regression test alarm flows again after the `jibo test 22` fixes: set with explicit time, set with compact/spoken time, clarify missing time, cancel alarm, and local cleanup prompts.
|
||||
- Regression test photo/gallery flows again after the `jibo test 22` fixes: open gallery, answer the stock `shared/yes_no` prompt, hand into create, take one photo, and avoid blue-ring stale turns.
|
||||
- Regression test alarm flows again after the `jibo test 23` fixes: set with explicit time, set with compact/spoken/comma-separated time, clarify missing time, replace an existing alarm, cancel/delete by voice, and verify the menu agrees.
|
||||
- Regression test photo/gallery flows again after the `jibo test 23` fixes: open gallery, answer the stock `shared/yes_no` prompt with a transcript-bearing `yes`, hand into create, take one photo, and avoid blue-ring stale turns.
|
||||
- Live-test radio launch: `open the radio` passed in `jibo test 22`; re-run `play country music` if that exact phrase was not captured.
|
||||
- Live-test first news path again: `jibo test 22` reached the news intent, but the live behavior still needs a clean non-backup session.
|
||||
- Recheck constrained yes/no prompts for update/backup/share/gallery without leaking global rules.
|
||||
- Treat basic news as live-proven by `jibo test 23`; defer provider-backed or category-expanded news unless it is chosen as an optional feature slice.
|
||||
- Recheck constrained yes/no prompts for update/backup/share/gallery/alarm replacement without leaking global rules.
|
||||
- Recheck that stock OS no longer logs OpenJibo-only websocket events such as synthetic pending/context/ack packets from the current build.
|
||||
- Recheck backup/update behavior with explicit attention to robot-local `jibo.scheduler.backupStatus`, CPU/load, and whether the deployed cloud is involved at all.
|
||||
- Treat remaining `ffmpeg` / `whisper.cpp` transcript failures as STT work unless the capture proves a separate turn-routing regression.
|
||||
- Treat remaining empty-ASR, `ffmpeg`, or `whisper.cpp` transcript failures as STT work unless the capture proves a separate turn-routing regression.
|
||||
|
||||
## Known Gaps
|
||||
|
||||
@@ -150,7 +151,8 @@ These are not blockers for calling `1.0.18` complete unless the live test shows
|
||||
- state persistence is local JSON, not Azure SQL / Blob Storage
|
||||
- update, backup, and restore are not end-to-end proven, and the `jibo test 22` sluggishness appears tied to robot-local backup status/load
|
||||
- deployed-build verification needs to prove that synthetic OpenJibo websocket events are gone from the hosted artifact, not just from source
|
||||
- news content is synthetic
|
||||
- news content is synthetic; `jibo test 23` proved the path but not live provider-backed headlines
|
||||
- gallery `shared/yes_no` still needs a successful transcript-bearing live `yes` pass
|
||||
- weather, calendar, commute, personal report, identity, memory, and proactivity are still mostly discovery or placeholder content paths
|
||||
- volume, stop, robot age, and command-versus-question personality routing are not implemented yet
|
||||
|
||||
|
||||
@@ -36,8 +36,9 @@ Current release theme:
|
||||
|
||||
- alarm and photo/gallery quirks have received the main bug-fix attention
|
||||
- Word of the Day cleanup, constrained yes/no routing, unknown websocket event suppression, and local state persistence are already in the current code
|
||||
- radio, ESML apostrophe cleanup, and first news are implemented in source/tests and need live confidence before the version is called complete
|
||||
- radio, ESML apostrophe cleanup, and first news are implemented in source/tests; radio and basic news are live-proven as of `jibo test 23`
|
||||
- `jibo test 22` validated radio, exposed backup/load interference, exposed a shared yes/no no-input gap, exposed repeated create keeper prompts after photo handoff, and showed local whisper `ffmpeg` failures on unusable buffered audio
|
||||
- `jibo test 23` validated basic news, proved one alarm set/fire path at `7:43 AM`, exposed comma-separated/short alarm follow-up parsing risk, showed stock alarm replacement yes/no rules that needed cloud handling, and showed photo gallery still failing when `shared/yes_no` ASR came back empty
|
||||
|
||||
## Immediate `1.0.18` Queue
|
||||
|
||||
@@ -63,9 +64,9 @@ Current release theme:
|
||||
|
||||
### 2. News Through Nimbus
|
||||
|
||||
- Status: `polish`
|
||||
- Status: `implemented`
|
||||
- Tags: `protocol`, `content`
|
||||
- Why now: the first Nimbus-compatible cloud path is implemented and test-backed; content can stay synthetic for `1.0.18`.
|
||||
- Why now: the first Nimbus-compatible cloud path is implemented, test-backed, and live-proven; content can stay synthetic for `1.0.18`.
|
||||
- Current code:
|
||||
- `tell me the news` maps to `IntentName = news`
|
||||
- outbound listen match includes `cloudSkill = news`
|
||||
@@ -73,11 +74,12 @@ Current release theme:
|
||||
- Evidence:
|
||||
- JiboOS Nimbus checks `match.cloudSkill === "news"` and waits for a cloud response
|
||||
- `jibo test 22` captured the phrase `So, play the news.` reaching the `news` intent, but live behavior was not cleanly confirmed
|
||||
- `jibo test 23` successfully played the synthetic quick brief
|
||||
- Exit criteria:
|
||||
- live `tell me the news` reaches a non-placeholder Nimbus path
|
||||
- live `tell me the news` reaches the Nimbus-shaped path
|
||||
- the robot behavior feels like a cloud skill response, not generic chat playback
|
||||
- Next action:
|
||||
- live-test the first pass; provider-backed headlines can wait for `1.0.19`
|
||||
- keep the basic path in regression; provider-backed or category-expanded headlines can wait for `1.0.19` unless chosen as the optional feature slice
|
||||
|
||||
### 3. Backup / OTA / Share Yes-No Reliability
|
||||
|
||||
@@ -86,7 +88,7 @@ Current release theme:
|
||||
- Why now: constrained yes/no behavior affects daily-use prompts and was tangled with the alarm/photo/gallery work.
|
||||
- Current code:
|
||||
- yes/no detection reads `listenRules`, `clientRules`, and `$YESNO` hints
|
||||
- covered prompt families include `settings/download_now_later`, `surprises-ota/want_to_download_now`, `surprises-date/offer_date_fact`, `shared/yes_no`, and `create/is_it_a_keeper`
|
||||
- covered prompt families include `settings/download_now_later`, `surprises-ota/want_to_download_now`, `surprises-date/offer_date_fact`, `shared/yes_no`, `create/is_it_a_keeper`, `clock/alarm_timer_change`, and `clock/alarm_timer_none_set`
|
||||
- outbound replies strip global rules and keep the local rule
|
||||
- no-input fallback for constrained prompts emits local `LISTEN`/`EOS`
|
||||
- `shared/yes_no` now participates in the STT-failure no-input path instead of staying pending behind `$YESNO` hints
|
||||
@@ -95,11 +97,12 @@ Current release theme:
|
||||
- `jibo test 22` did not show `Backup_*` HTTP traffic during the backup complaint
|
||||
- stock `@be/surprises-ota` drives the backup notification from robot-local `jibo.scheduler.backupStatus`
|
||||
- a spoken `take a backup` command currently routes as generic chat and is not the same as proving the local backup scheduler path
|
||||
- `jibo test 23` again showed backup-in-progress sluggishness and update-menu blockage while backups were active; explicit backup voice launch remains unwired
|
||||
- Exit criteria:
|
||||
- spoken `yes` and `no` work on update, backup, share/offer, and gallery/create prompts
|
||||
- empty or missed short replies retry locally instead of relaunching Nimbus or generic chat
|
||||
- Next action:
|
||||
- re-run these prompt families in the `1.0.18` live regression pass after the shared yes/no and create no-input fixes
|
||||
- re-run these prompt families in the `1.0.18` live regression pass after the shared yes/no, alarm yes/no, and create no-input fixes
|
||||
- keep explicit backup creation as part of the update/backup/restore proof slice, not as an assumed yes/no prompt test
|
||||
|
||||
### 4. Alarm And Photo Gallery Release Regression
|
||||
@@ -108,9 +111,11 @@ Current release theme:
|
||||
- Tags: `protocol`, `stt`
|
||||
- Why now: this is the main bug-fix theme for `1.0.18`.
|
||||
- Current code:
|
||||
- alarm values parse explicit, compact, spaced, hyphenated, and local-context ambiguous times
|
||||
- alarm values parse explicit, compact, spaced, comma-separated, hyphenated, and local-context ambiguous times
|
||||
- short alarm/timer value replies are accepted during clock value follow-up rules instead of being filtered out before parsing
|
||||
- missing alarm times stay in local `@be/clock` clarification
|
||||
- alarm cancel can reuse the last active clock domain
|
||||
- stock alarm replacement/no-alarm prompts use the constrained yes/no path
|
||||
- gallery opens as `@be/gallery`; snapshot and photobooth open through `@be/create`
|
||||
- passive gallery/create context no longer reopens stale cloud turns
|
||||
- `shared/yes_no` no-input fallback and repeated create keeper cleanup were added after `jibo test 22`
|
||||
@@ -118,9 +123,11 @@ Current release theme:
|
||||
- gallery opened and handed into create, but repeated `create/is_it_a_keeper` prompts could leave the blue ring/listening state
|
||||
- alarm recognition collapsed several attempts before a complete alarm value could be set
|
||||
- `ffmpeg` failures were present during the same test window, so alarm/gallery retest should separate transcript quality from payload shape
|
||||
- `jibo test 23` set and fired a `7:43 AM` alarm, then failed a later clarify/replacement path when the robot heard `- Time. - 7, 14.` and stock NLU converted that to `7:00 PM`
|
||||
- `jibo test 23` photo gallery got stuck on `shared/yes_no` turns with empty ASR, not on a transcript-bearing `yes` that the cloud mapped incorrectly
|
||||
- Exit criteria:
|
||||
- gallery opens, offers to take a picture if empty, accepts `yes`, and hands into create
|
||||
- alarm set, clarify, and cancel flows behave locally without blue-ring stale turns
|
||||
- alarm set, clarify, replacement yes/no, and cancel/delete flows behave locally and agree with the menu state
|
||||
- failures caused by collapsed STT transcripts are logged as STT issues rather than misdiagnosed as payload bugs
|
||||
- Next action:
|
||||
- re-run a stock OS `1.9` regression bundle before declaring `1.0.18` complete
|
||||
@@ -168,7 +175,7 @@ Current release theme:
|
||||
- Result:
|
||||
- Nimbus-shaped `news` cloud-skill lane is implemented with synthetic briefing content
|
||||
- Follow-up:
|
||||
- live validation remains in the immediate queue
|
||||
- basic live validation passed in `jibo test 23`
|
||||
- provider-backed headlines belong in `1.0.19` or later
|
||||
|
||||
### Clock / Alarm Family
|
||||
@@ -178,7 +185,9 @@ Current release theme:
|
||||
- Result:
|
||||
- time/date/day and clock open route through local `@be/clock`
|
||||
- timer/alarm menu, value, clarify, and delete are implemented
|
||||
- compact and spoken alarm parsing has focused tests
|
||||
- compact, spoken, comma-separated, and local-context alarm parsing has focused tests
|
||||
- short clock value replies under `clock/alarm_set_value` and `clock/timer_set_value` are not filtered out by websocket finalization
|
||||
- alarm replacement/no-alarm yes/no prompts are mapped as constrained local prompts
|
||||
- client NLU alarm clarify/cancel cases from `jibo test 20` and `jibo test 21` are reflected in source
|
||||
- Follow-up:
|
||||
- live regression remains in the immediate queue
|
||||
@@ -203,9 +212,9 @@ Current release theme:
|
||||
- Result:
|
||||
- `shared/yes_no` is included in yes/no STT-failure detection
|
||||
- local no-input replies strip global rules and keep the active constrained rule
|
||||
- update, OTA, share/date-offer, gallery shared yes/no, and create keeper rules share the same no-input fallback machinery
|
||||
- update, OTA, share/date-offer, gallery shared yes/no, alarm replacement/no-alarm, and create keeper rules share the same no-input fallback machinery
|
||||
- Follow-up:
|
||||
- live update/backup/share/gallery prompts still need another clean pass
|
||||
- live update/backup/share/gallery/alarm replacement prompts still need another clean pass
|
||||
|
||||
### Word Of The Day Cleanup
|
||||
|
||||
@@ -299,7 +308,8 @@ Current release theme:
|
||||
- Why next:
|
||||
- feature paths are now often correct when a transcript exists, but short replies and low-quality audio still block otherwise-correct flows
|
||||
- Current evidence:
|
||||
- live captures still show `ffmpeg` and `whisper.cpp` failures
|
||||
- `jibo test 22` showed `ffmpeg` and `whisper.cpp` failures
|
||||
- `jibo test 23` did not show the same decode failure pattern, but gallery yes/no turns still produced empty ASR
|
||||
- current source now skips local whisper when buffered audio does not contain an Opus identification header
|
||||
- yes/no and alarm flows are especially sensitive to short or collapsed transcripts
|
||||
- Implementation notes:
|
||||
@@ -467,7 +477,7 @@ Current release theme:
|
||||
Before closing `1.0.18`:
|
||||
|
||||
1. Radio live validation
|
||||
2. News live validation
|
||||
2. Basic news regression, with provider-backed expansion deferred
|
||||
3. Backup / OTA / share yes-no regression
|
||||
4. Alarm and photo/gallery regression
|
||||
5. Optional small feature only if the regression pass stays calm
|
||||
|
||||
@@ -680,6 +680,8 @@ public sealed class JiboInteractionService(
|
||||
.Concat(ReadRules(turn, "listenAsrHints"))
|
||||
.Any(static rule =>
|
||||
string.Equals(rule, "$YESNO", StringComparison.OrdinalIgnoreCase) ||
|
||||
string.Equals(rule, "clock/alarm_timer_change", StringComparison.OrdinalIgnoreCase) ||
|
||||
string.Equals(rule, "clock/alarm_timer_none_set", StringComparison.OrdinalIgnoreCase) ||
|
||||
string.Equals(rule, "create/is_it_a_keeper", StringComparison.OrdinalIgnoreCase) ||
|
||||
string.Equals(rule, "shared/yes_no", StringComparison.OrdinalIgnoreCase) ||
|
||||
string.Equals(rule, "settings/download_now_later", StringComparison.OrdinalIgnoreCase) ||
|
||||
@@ -1204,7 +1206,7 @@ public sealed class JiboInteractionService(
|
||||
private sealed record ClockAlarmValue(string Time, string AmPm);
|
||||
|
||||
private static readonly Regex SplitAlarmPattern = new(
|
||||
@"\b(?<hour>\d{1,2}|one|two|three|four|five|six|seven|eight|nine|ten|eleven|twelve)(?:[:\s-](?<minute>\d{2}|[a-z\-]+(?:\s+[a-z\-]+)?))?\s*(?<ampm>a[\s\.]*m\.?|p[\s\.]*m\.?)?\b",
|
||||
@"\b(?<hour>\d{1,2}|one|two|three|four|five|six|seven|eight|nine|ten|eleven|twelve)(?:[:\s,-]+(?<minute>\d{2}|[a-z\-]+(?:\s+[a-z\-]+)?))?\s*(?<ampm>a[\s\.]*m\.?|p[\s\.]*m\.?)?\b",
|
||||
RegexOptions.IgnoreCase | RegexOptions.CultureInvariant | RegexOptions.Compiled);
|
||||
|
||||
private static readonly Regex CompactAlarmPattern = new(
|
||||
|
||||
@@ -456,6 +456,8 @@ public sealed class ResponsePlanToSocketMessagesMapper
|
||||
{
|
||||
return ReadRuleValues(turn)
|
||||
.FirstOrDefault(static rule =>
|
||||
string.Equals(rule, "clock/alarm_timer_change", StringComparison.OrdinalIgnoreCase) ||
|
||||
string.Equals(rule, "clock/alarm_timer_none_set", StringComparison.OrdinalIgnoreCase) ||
|
||||
string.Equals(rule, "create/is_it_a_keeper", StringComparison.OrdinalIgnoreCase) ||
|
||||
string.Equals(rule, "shared/yes_no", StringComparison.OrdinalIgnoreCase) ||
|
||||
string.Equals(rule, "settings/download_now_later", StringComparison.OrdinalIgnoreCase) ||
|
||||
|
||||
@@ -691,6 +691,11 @@ public sealed class WebSocketTurnFinalizationService(
|
||||
return false;
|
||||
}
|
||||
|
||||
if (listenRules.Any(IsClockValueRule))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (transcript.Length >= 6)
|
||||
{
|
||||
return true;
|
||||
@@ -733,12 +738,7 @@ public sealed class WebSocketTurnFinalizationService(
|
||||
{
|
||||
return ReadRules(turn, "listenRules")
|
||||
.Concat(ReadRules(turn, "clientRules"))
|
||||
.FirstOrDefault(static rule =>
|
||||
string.Equals(rule, "clock/alarm_timer_okay", StringComparison.OrdinalIgnoreCase) ||
|
||||
string.Equals(rule, "create/is_it_a_keeper", StringComparison.OrdinalIgnoreCase) ||
|
||||
string.Equals(rule, "settings/download_now_later", StringComparison.OrdinalIgnoreCase) ||
|
||||
string.Equals(rule, "surprises-date/offer_date_fact", StringComparison.OrdinalIgnoreCase) ||
|
||||
string.Equals(rule, "surprises-ota/want_to_download_now", StringComparison.OrdinalIgnoreCase));
|
||||
.FirstOrDefault(IsLocalNoInputRule);
|
||||
}
|
||||
|
||||
private static string? ReadPrimaryYesNoRule(TurnContext turn)
|
||||
@@ -797,9 +797,17 @@ public sealed class WebSocketTurnFinalizationService(
|
||||
IsConstrainedYesNoRule(rule);
|
||||
}
|
||||
|
||||
private static bool IsClockValueRule(string rule)
|
||||
{
|
||||
return string.Equals(rule, "clock/alarm_set_value", StringComparison.OrdinalIgnoreCase) ||
|
||||
string.Equals(rule, "clock/timer_set_value", StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
private static bool IsConstrainedYesNoRule(string rule)
|
||||
{
|
||||
return string.Equals(rule, "create/is_it_a_keeper", StringComparison.OrdinalIgnoreCase) ||
|
||||
return string.Equals(rule, "clock/alarm_timer_change", StringComparison.OrdinalIgnoreCase) ||
|
||||
string.Equals(rule, "clock/alarm_timer_none_set", StringComparison.OrdinalIgnoreCase) ||
|
||||
string.Equals(rule, "create/is_it_a_keeper", StringComparison.OrdinalIgnoreCase) ||
|
||||
string.Equals(rule, "shared/yes_no", StringComparison.OrdinalIgnoreCase) ||
|
||||
string.Equals(rule, "settings/download_now_later", StringComparison.OrdinalIgnoreCase) ||
|
||||
string.Equals(rule, "surprises-date/offer_date_fact", StringComparison.OrdinalIgnoreCase) ||
|
||||
|
||||
@@ -133,6 +133,46 @@ public sealed class JiboInteractionServiceTests
|
||||
Assert.Equal("Yes.", decision.ReplyText);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task BuildDecisionAsync_AlarmTimerChangePrompt_MapsShortAffirmationToYesIntent()
|
||||
{
|
||||
var service = CreateService();
|
||||
|
||||
var decision = await service.BuildDecisionAsync(new TurnContext
|
||||
{
|
||||
RawTranscript = "yes",
|
||||
NormalizedTranscript = "yes",
|
||||
Attributes = new Dictionary<string, object?>
|
||||
{
|
||||
["listenRules"] = new[] { "clock/alarm_timer_change", "globals/gui_nav" },
|
||||
["listenAsrHints"] = new[] { "$YESNO" }
|
||||
}
|
||||
});
|
||||
|
||||
Assert.Equal("yes", decision.IntentName);
|
||||
Assert.Equal("Yes.", decision.ReplyText);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task BuildDecisionAsync_AlarmTimerNoneSetPrompt_MapsShortDenialToNoIntent()
|
||||
{
|
||||
var service = CreateService();
|
||||
|
||||
var decision = await service.BuildDecisionAsync(new TurnContext
|
||||
{
|
||||
RawTranscript = "no",
|
||||
NormalizedTranscript = "no",
|
||||
Attributes = new Dictionary<string, object?>
|
||||
{
|
||||
["listenRules"] = new[] { "clock/alarm_timer_none_set", "globals/global_commands_launch" },
|
||||
["listenAsrHints"] = new[] { "$YESNO" }
|
||||
}
|
||||
});
|
||||
|
||||
Assert.Equal("no", decision.IntentName);
|
||||
Assert.Equal("No.", decision.ReplyText);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task BuildDecisionAsync_SettingsDownloadPrompt_MapsShortDenialToNoIntent()
|
||||
{
|
||||
@@ -486,6 +526,29 @@ public sealed class JiboInteractionServiceTests
|
||||
Assert.Equal("am", decision.SkillPayload["ampm"]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task BuildDecisionAsync_AlarmValueFollowUp_ParsesCommaSeparatedSpokenDigits()
|
||||
{
|
||||
var service = CreateService();
|
||||
|
||||
var decision = await service.BuildDecisionAsync(new TurnContext
|
||||
{
|
||||
RawTranscript = "7, 44",
|
||||
NormalizedTranscript = "7, 44",
|
||||
Attributes = new Dictionary<string, object?>
|
||||
{
|
||||
["listenRules"] = new[] { "clock/alarm_set_value" },
|
||||
["context"] = """{"runtime":{"location":{"iso":"2026-04-26T07:43:00-05:00"}}}"""
|
||||
}
|
||||
});
|
||||
|
||||
Assert.Equal("alarm_value", decision.IntentName);
|
||||
Assert.Equal("@be/clock", decision.SkillName);
|
||||
Assert.Equal("start", decision.SkillPayload!["clockIntent"]);
|
||||
Assert.Equal("7:44", decision.SkillPayload["time"]);
|
||||
Assert.Equal("am", decision.SkillPayload["ampm"]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task BuildDecisionAsync_SetAlarmWithoutTime_AsksForClarification()
|
||||
{
|
||||
|
||||
@@ -648,6 +648,35 @@ public sealed class JiboWebSocketServiceTests
|
||||
Assert.Equal("am", listenPayload.RootElement.GetProperty("data").GetProperty("nlu").GetProperty("entities").GetProperty("ampm").GetString());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ClientAsr_AlarmValueFollowUp_ParsesCommaSeparatedSpokenDigitsIntoClockStartIntent()
|
||||
{
|
||||
await _service.HandleMessageAsync(new WebSocketMessageEnvelope
|
||||
{
|
||||
HostName = "neo-hub.jibo.com",
|
||||
Path = "/listen",
|
||||
Kind = "neo-hub-listen",
|
||||
Token = "hub-clock-alarm-comma-followup-token",
|
||||
Text = """{"type":"LISTEN","transID":"trans-clock-alarm-comma-followup","data":{"rules":["clock/alarm_set_value"]}}"""
|
||||
});
|
||||
|
||||
var replies = await _service.HandleMessageAsync(new WebSocketMessageEnvelope
|
||||
{
|
||||
HostName = "neo-hub.jibo.com",
|
||||
Path = "/listen",
|
||||
Kind = "neo-hub-listen",
|
||||
Token = "hub-clock-alarm-comma-followup-token",
|
||||
Text = """{"type":"CLIENT_ASR","transID":"trans-clock-alarm-comma-followup","data":{"text":"7, 44"}}"""
|
||||
});
|
||||
|
||||
Assert.Equal(4, replies.Count);
|
||||
|
||||
using var listenPayload = JsonDocument.Parse(replies[0].Text!);
|
||||
Assert.Equal("start", listenPayload.RootElement.GetProperty("data").GetProperty("nlu").GetProperty("intent").GetString());
|
||||
Assert.Equal("alarm", listenPayload.RootElement.GetProperty("data").GetProperty("nlu").GetProperty("entities").GetProperty("domain").GetString());
|
||||
Assert.Equal("7:44", listenPayload.RootElement.GetProperty("data").GetProperty("nlu").GetProperty("entities").GetProperty("time").GetString());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ClientAsr_SetAlarmWithoutTime_RedirectsIntoClockSkillWithoutDefaultingTime()
|
||||
{
|
||||
@@ -1063,6 +1092,37 @@ public sealed class JiboWebSocketServiceTests
|
||||
Assert.Equal("shared/yes_no", listenPayload.RootElement.GetProperty("data").GetProperty("match").GetProperty("rule").GetString());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ClientAsr_AlarmTimerChangeYesNoPrompt_StripsGlobalRulesAndStaysLocal()
|
||||
{
|
||||
await _service.HandleMessageAsync(new WebSocketMessageEnvelope
|
||||
{
|
||||
HostName = "neo-hub.jibo.com",
|
||||
Path = "/listen",
|
||||
Kind = "neo-hub-listen",
|
||||
Token = "hub-alarm-change-yesno-token",
|
||||
Text = """{"type":"LISTEN","transID":"trans-alarm-change-yesno","data":{"rules":["clock/alarm_timer_change","globals/gui_nav","globals/mim_repeat","globals/global_commands_launch"],"asr":{"hints":["$YESNO"]}}}"""
|
||||
});
|
||||
|
||||
var replies = await _service.HandleMessageAsync(new WebSocketMessageEnvelope
|
||||
{
|
||||
HostName = "neo-hub.jibo.com",
|
||||
Path = "/listen",
|
||||
Kind = "neo-hub-listen",
|
||||
Token = "hub-alarm-change-yesno-token",
|
||||
Text = """{"type":"CLIENT_ASR","transID":"trans-alarm-change-yesno","data":{"text":"yes"}}"""
|
||||
});
|
||||
|
||||
Assert.Equal(3, replies.Count);
|
||||
|
||||
using var listenPayload = JsonDocument.Parse(replies[0].Text!);
|
||||
Assert.Equal("yes", listenPayload.RootElement.GetProperty("data").GetProperty("nlu").GetProperty("intent").GetString());
|
||||
var rules = listenPayload.RootElement.GetProperty("data").GetProperty("nlu").GetProperty("rules");
|
||||
Assert.Single(rules.EnumerateArray());
|
||||
Assert.Equal("clock/alarm_timer_change", rules[0].GetString());
|
||||
Assert.Equal("clock/alarm_timer_change", listenPayload.RootElement.GetProperty("data").GetProperty("match").GetProperty("rule").GetString());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task BufferedAudio_YesNoPromptWithSttFailure_AutoFinalizesAsLocalNoInput()
|
||||
{
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
4014
artifact-output/jibo-test-23/jibo test 23.txt
Normal file
4014
artifact-output/jibo-test-23/jibo test 23.txt
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user