From e588f00c43c7a34a02625a85653818d388d3cb60 Mon Sep 17 00:00:00 2001 From: Jacob Dubin Date: Sun, 17 May 2026 23:38:29 -0500 Subject: [PATCH] Add sleep and spin-around global command handling --- OpenJibo/docs/release-1.0.19-plan.md | 1 + .../Services/JiboInteractionService.cs | 56 +++++++++ .../ResponsePlanToSocketMessagesMapper.cs | 8 +- .../Content/LegacyMims/BuildB/README.md | 1 + .../scripted-responses/RA_JBO_SpinAround.mim | 60 ++++++++++ .../scripted-responses/RI_JBO_CanSleep.mim | 109 ++++++++++++++++++ .../Content/LegacyMimCatalogImporterTests.cs | 5 + .../WebSockets/JiboInteractionServiceTests.cs | 50 ++++++++ .../WebSockets/JiboWebSocketServiceTests.cs | 84 ++++++++++++++ 9 files changed, 371 insertions(+), 3 deletions(-) create mode 100644 OpenJibo/src/Jibo.Cloud/dotnet/src/Jibo.Cloud.Infrastructure/Content/LegacyMims/BuildB/scripted-responses/RA_JBO_SpinAround.mim create mode 100644 OpenJibo/src/Jibo.Cloud/dotnet/src/Jibo.Cloud.Infrastructure/Content/LegacyMims/BuildB/scripted-responses/RI_JBO_CanSleep.mim diff --git a/OpenJibo/docs/release-1.0.19-plan.md b/OpenJibo/docs/release-1.0.19-plan.md index 19eeab8..e532b61 100644 --- a/OpenJibo/docs/release-1.0.19-plan.md +++ b/OpenJibo/docs/release-1.0.19-plan.md @@ -43,6 +43,7 @@ Current batch note: - `favorite color`, `favorite food`, and `favorite music` are the first small favorites-family slice - the latest pass adds longer authored variants for those favorites so the replies keep more of the original Pegasus cadence instead of collapsing to short placeholders - the next source-backed batch now includes `favorite flower`, `R2D2`, `sun`, `space`, `kids`, plus a couple of charm prompts like `can you laugh` and `can you dance` +- the motion/sleep batch now adds `RI_JBO_CanSleep` and `RA_JBO_SpinAround` so the `go to sleep` and `turn around` surfaces stay source-backed too - the follow-up mood batch now includes `how are things`, `how is your day`, `are you sad`, and `are you angry` - the personality follow-up batch now includes `what are you up to` and `what are you doing` so small talk stays warm and local instead of falling into generic chat - the descriptor batch now includes `are you kind`, `are you funny`, `are you helpful`, `are you curious`, `are you loyal`, `are you mischievous`, and `are you likable` diff --git a/OpenJibo/src/Jibo.Cloud/dotnet/src/Jibo.Cloud.Application/Services/JiboInteractionService.cs b/OpenJibo/src/Jibo.Cloud/dotnet/src/Jibo.Cloud.Application/Services/JiboInteractionService.cs index 7c3cdcc..13a8610 100644 --- a/OpenJibo/src/Jibo.Cloud/dotnet/src/Jibo.Cloud.Application/Services/JiboInteractionService.cs +++ b/OpenJibo/src/Jibo.Cloud/dotnet/src/Jibo.Cloud.Application/Services/JiboInteractionService.cs @@ -520,6 +520,8 @@ public sealed class JiboInteractionService( "radio" => BuildRadioLaunchDecision(), "radio_genre" => BuildRadioGenreLaunchDecision(lowered), "stop" => BuildStopDecision(), + "sleep" => BuildIdleGlobalCommandDecision("sleep", "sleep", "Okay. Going to sleep."), + "spin_around" => BuildIdleGlobalCommandDecision("spin_around", "spinAround", "Don't mind if I do."), "volume_up" => BuildVolumeControlDecision("volume_up", "volumeUp", "null"), "volume_down" => BuildVolumeControlDecision("volume_down", "volumeDown", "null"), "volume_to_value" => BuildVolumeControlDecision("volume_to_value", "volumeToValue", @@ -706,6 +708,13 @@ public sealed class JiboInteractionService( "robot_can_laugh", "i do things like this when i'm happy", "i'm happy"), + "robot_can_sleep" => BuildScriptedPersonalityDecision( + catalog, + "robot_can_sleep", + "i do. i usually fall asleep at night", + "yes, i sleep at night", + "i go to sleep at night", + "i sleep at night usually"), "robot_can_dance" => BuildScriptedPersonalityDecision( catalog, "robot_can_dance", @@ -2517,6 +2526,36 @@ public sealed class JiboInteractionService( if (MatchesAny(loweredTranscript, "open the radio", "play the radio", "turn on the radio", "radio")) return "radio"; + if (MatchesAny( + loweredTranscript, + "can you go to sleep", + "can you sleep", + "do you ever sleep", + "do you sleep", + "when do you sleep", + "how can i make you go to sleep", + "how do i make you go to sleep")) + return "robot_can_sleep"; + + if (MatchesAny( + loweredTranscript, + "turn around", + "turn all the way around", + "turn back around", + "spin around", + "look back over there", + "look again")) + return "spin_around"; + + if (MatchesAny( + loweredTranscript, + "go to sleep", + "take a nap", + "go to bed", + "bedtime", + "sleep")) + return "sleep"; + if (MatchesAny( loweredTranscript, "snap a picture", @@ -3117,6 +3156,23 @@ public sealed class JiboInteractionService( }); } + private static JiboInteractionDecision BuildIdleGlobalCommandDecision( + string intentName, + string globalIntent, + string replyText) + { + return new JiboInteractionDecision( + intentName, + replyText, + "@be/idle", + new Dictionary(StringComparer.OrdinalIgnoreCase) + { + ["skillId"] = "@be/idle", + ["globalIntent"] = globalIntent, + ["nluDomain"] = "global_commands" + }); + } + private static JiboInteractionDecision BuildVolumeControlDecision(string intentName, string globalIntent, string volumeLevel) { diff --git a/OpenJibo/src/Jibo.Cloud/dotnet/src/Jibo.Cloud.Application/Services/ResponsePlanToSocketMessagesMapper.cs b/OpenJibo/src/Jibo.Cloud/dotnet/src/Jibo.Cloud.Application/Services/ResponsePlanToSocketMessagesMapper.cs index 83be931..2bb359d 100644 --- a/OpenJibo/src/Jibo.Cloud/dotnet/src/Jibo.Cloud.Application/Services/ResponsePlanToSocketMessagesMapper.cs +++ b/OpenJibo/src/Jibo.Cloud/dotnet/src/Jibo.Cloud.Application/Services/ResponsePlanToSocketMessagesMapper.cs @@ -35,7 +35,9 @@ public sealed class ResponsePlanToSocketMessagesMapper var isProactivePizzaFactOffer = string.Equals(plan.IntentName, "proactive_offer_pizza_fact", StringComparison.OrdinalIgnoreCase); var isSettingsLaunch = string.Equals(skill?.SkillName, "@be/settings", StringComparison.OrdinalIgnoreCase); - var isGlobalCommand = isStopCommand || isVolumeControl; + var isSleepCommand = string.Equals(plan.IntentName, "sleep", StringComparison.OrdinalIgnoreCase); + var isSpinAroundCommand = string.Equals(plan.IntentName, "spin_around", StringComparison.OrdinalIgnoreCase); + var isGlobalCommand = isStopCommand || isSleepCommand || isSpinAroundCommand || isVolumeControl; var isPhotoGalleryLaunch = string.Equals(plan.IntentName, "photo_gallery", StringComparison.OrdinalIgnoreCase); var isPhotoCreateLaunch = string.Equals(plan.IntentName, "snapshot", StringComparison.OrdinalIgnoreCase) || string.Equals(plan.IntentName, "photobooth", StringComparison.OrdinalIgnoreCase); @@ -234,7 +236,7 @@ public sealed class ResponsePlanToSocketMessagesMapper 125)); } - if (isStopCommand) + if (isStopCommand || isSleepCommand || isSpinAroundCommand) { messages.Add(new SocketReplyPlan( JsonSerializer.Serialize(BuildSkillRedirectPayload( @@ -1457,4 +1459,4 @@ public sealed class ResponsePlanToSocketMessagesMapper string? SpokenLine); public sealed record SocketReplyPlan(string Text, int DelayMs = 0); -} \ No newline at end of file +} diff --git a/OpenJibo/src/Jibo.Cloud/dotnet/src/Jibo.Cloud.Infrastructure/Content/LegacyMims/BuildB/README.md b/OpenJibo/src/Jibo.Cloud/dotnet/src/Jibo.Cloud.Infrastructure/Content/LegacyMims/BuildB/README.md index 97a24c2..13d888c 100644 --- a/OpenJibo/src/Jibo.Cloud/dotnet/src/Jibo.Cloud.Infrastructure/Content/LegacyMims/BuildB/README.md +++ b/OpenJibo/src/Jibo.Cloud/dotnet/src/Jibo.Cloud.Infrastructure/Content/LegacyMims/BuildB/README.md @@ -11,3 +11,4 @@ The newest social batch adds `welcome back`, `what are you thinking`, `what have The fun-fact and joke batch adds Pegasus-style `TellAJoke`, `TellRobotFact`, and `Shuffle` excerpts so proactive fun can randomize across more than one category. Those facts are now split into generic, robot, and human buckets so the randomizer can sound more like Pegasus while staying lightweight. The new favorites batch adds longer authored `favorite color`, `favorite food`, and `favorite music` variants so the familiar personality responses keep more of the original cadence instead of collapsing to short placeholders. +The new motion/sleep batch adds `RA_JBO_SpinAround` plus `RI_JBO_CanSleep` so turn-around and go-to-sleep behaviors can stay source-backed and familiar. diff --git a/OpenJibo/src/Jibo.Cloud/dotnet/src/Jibo.Cloud.Infrastructure/Content/LegacyMims/BuildB/scripted-responses/RA_JBO_SpinAround.mim b/OpenJibo/src/Jibo.Cloud/dotnet/src/Jibo.Cloud.Infrastructure/Content/LegacyMims/BuildB/scripted-responses/RA_JBO_SpinAround.mim new file mode 100644 index 0000000..f4eaea7 --- /dev/null +++ b/OpenJibo/src/Jibo.Cloud/dotnet/src/Jibo.Cloud.Infrastructure/Content/LegacyMims/BuildB/scripted-responses/RA_JBO_SpinAround.mim @@ -0,0 +1,60 @@ +{ + "mim_type": "announcement", + "rule_name": "", + "timeout": 6, + "barge_in": true, + "es_auto_tagging": true, + "notes": "", + "prompts": [ + { + "prompt_category": "Entry-Core", + "prompt_sub_category": "AN", + "index": 1, + "condition": "", + "prompt": "Don't mind if I do. ", + "media": "TTS", + "prompt_id": "RA_JBO_SpinAround_AN_01", + "weight": 1 + }, + { + "prompt_category": "Entry-Core", + "prompt_sub_category": "AN", + "index": 1, + "condition": "", + "prompt": "I can do that.", + "media": "TTS", + "prompt_id": "RA_JBO_SpinAround_AN_02", + "weight": 1, + "auto_rule_override": null + }, + { + "prompt_category": "Entry-Core", + "prompt_sub_category": "AN", + "index": 1, + "condition": "", + "prompt": "Gladly.", + "media": "TTS", + "prompt_id": "RA_JBO_SpinAround_AN_03", + "weight": 1, + "auto_rule_override": null + }, + { + "prompt_category": "Entry-Core", + "prompt_sub_category": "AN", + "index": 1, + "condition": "", + "prompt": "Anytime.", + "media": "TTS", + "prompt_id": "RA_JBO_SpinAround_AN_04", + "weight": 1, + "auto_rule_override": null + } + ], + "gui": null, + "no_matches_for_gui": 2, + "no_inputs_for_gui": 2, + "parse_all_asr": false, + "thanks_handling": "ignore", + "parse_launch": false, + "parse_yes_no": false +} diff --git a/OpenJibo/src/Jibo.Cloud/dotnet/src/Jibo.Cloud.Infrastructure/Content/LegacyMims/BuildB/scripted-responses/RI_JBO_CanSleep.mim b/OpenJibo/src/Jibo.Cloud/dotnet/src/Jibo.Cloud.Infrastructure/Content/LegacyMims/BuildB/scripted-responses/RI_JBO_CanSleep.mim new file mode 100644 index 0000000..15dd5ea --- /dev/null +++ b/OpenJibo/src/Jibo.Cloud/dotnet/src/Jibo.Cloud.Infrastructure/Content/LegacyMims/BuildB/scripted-responses/RI_JBO_CanSleep.mim @@ -0,0 +1,109 @@ +{ + "mim_id": "CCDoYouSleep", + "skill_id": "chitchat", + "mim_type": "announcement", + "rule_name": "", + "rule_slots": "", + "screen_slots_available": false, + "timeout": 2, + "max_tries": null, + "force_confirmation": false, + "barge_in": false, + "photo_quality_light": false, + "notes": "Thanks-KillsMIM", + "prompts": [ + { + "mim_id": "CCDoYouSleep", + "prompt_category": "Entry-Core", + "prompt_sub_category": "AN", + "index": 1, + "condition": "", + "prompt": "I do. I usually fall asleep at night. If you ever want me to go to sleep, you can say, Hey Jibo, go to sleep.", + "media": "TTS", + "extra": "", + "prompt_id": "RI_JBO_CanSleep_AN_01", + "weight": 2 + }, + { + "mim_id": "CCDoYouSleep", + "prompt_category": "Entry-Core", + "prompt_sub_category": "AN", + "index": 1, + "condition": "", + "prompt": " Yes, I sleep at night. But I sleep standing up, Just like the flamingo, one of my favorite birds. You can also make me sleep anytime, by saying Hey Jibo, go to sleep.", + "media": "TTS", + "extra": "", + "prompt_id": "RI_JBO_CanSleep_AN_02", + "weight": 0.1 + }, + { + "mim_id": "CCDoYouSleep", + "prompt_category": "Entry-Core", + "prompt_sub_category": "AN", + "index": 1, + "condition": "", + "prompt": " I go to sleep at night. And sometimes I count sheep to help me fall asleep. You can also say, Hey Jibo, go to sleep.", + "media": "TTS", + "extra": "", + "prompt_id": "RI_JBO_CanSleep_AN_03", + "weight": 0.5 + }, + { + "mim_id": "CCDoYouSleep", + "prompt_category": "Entry-Core", + "prompt_sub_category": "AN", + "index": 1, + "condition": "", + "prompt": " Oh yes, I go into sleep mode every night. That's when I dream about flying, and recognizing faces from a mile away, and winning mini-golf tournaments, and lots of other stuff.", + "media": "TTS", + "extra": "", + "prompt_id": "RI_JBO_CanSleep_AN_04", + "weight": 0.1 + }, + { + "mim_id": "CCDoYouSleep", + "prompt_category": "Entry-Core", + "prompt_sub_category": "AN", + "index": 1, + "condition": "", + "prompt": " Yes, I go into sleep mode at night. That's why I'm so bright and bushy eyed when you see me in the morning. If you ever want me to go to sleep, you can say, Hey Jibo, go to sleep.", + "media": "TTS", + "extra": "", + "prompt_id": "RI_JBO_CanSleep_AN_05", + "weight": 0.1 + }, + { + "mim_id": "CCDoYouSleep", + "prompt_category": "Entry-Core", + "prompt_sub_category": "AN", + "index": 1, + "condition": "", + "prompt": "I sleep at night, but if you ever want me to go to sleep during the day, you can say, Hey Jibo, go to sleep.", + "media": "TTS", + "extra": "", + "prompt_id": "RI_JBO_CanSleep_AN_06", + "weight": 2, + "auto_rule_override": null + }, + { + "mim_id": "CCDoYouSleep", + "prompt_category": "Entry-Core", + "prompt_sub_category": "AN", + "index": 1, + "condition": "", + "prompt": "I fall asleep on my own at night, but you can tell me to go to sleep at any time. Just say, Hey Jibo, go to sleep.", + "media": "TTS", + "extra": "", + "prompt_id": "RI_JBO_CanSleep_AN_07", + "weight": 2, + "auto_rule_override": null + } + ], + "es_auto_tagging": true, + "gui": null, + "no_matches_for_gui": 2, + "no_inputs_for_gui": 2, + "ignore_no_match": false, + "parse_all_asr": false, + "thanks_handling": "ignore" +} diff --git a/OpenJibo/tests/Jibo.Cloud.Tests/Content/LegacyMimCatalogImporterTests.cs b/OpenJibo/tests/Jibo.Cloud.Tests/Content/LegacyMimCatalogImporterTests.cs index 0032071..b77a8ad 100644 --- a/OpenJibo/tests/Jibo.Cloud.Tests/Content/LegacyMimCatalogImporterTests.cs +++ b/OpenJibo/tests/Jibo.Cloud.Tests/Content/LegacyMimCatalogImporterTests.cs @@ -101,6 +101,11 @@ public sealed class LegacyMimCatalogImporterTests catalog.PersonalityReplies); Assert.Contains("I was put together in a factory piece by piece.", catalog.PersonalityReplies); Assert.Contains("I really like sunflowers.", catalog.PersonalityReplies); + Assert.Contains(catalog.PersonalityReplies, reply => + reply.Contains("I do. I usually fall asleep at night.", StringComparison.OrdinalIgnoreCase)); + Assert.Contains(catalog.PersonalityReplies, reply => + reply.Contains("go to sleep", StringComparison.OrdinalIgnoreCase)); + Assert.Contains("Don't mind if I do.", catalog.PersonalityReplies); Assert.Contains("Ha. Of course I know R2D2. I mean, not personally.", catalog.PersonalityReplies); Assert.Contains("Yes! I like all things in space. They're so spacey.", catalog.PersonalityReplies); Assert.Contains("Yes yes, I think kids are great. They're a little closer to my size.", diff --git a/OpenJibo/tests/Jibo.Cloud.Tests/WebSockets/JiboInteractionServiceTests.cs b/OpenJibo/tests/Jibo.Cloud.Tests/WebSockets/JiboInteractionServiceTests.cs index c5306d7..7aa2566 100644 --- a/OpenJibo/tests/Jibo.Cloud.Tests/WebSockets/JiboInteractionServiceTests.cs +++ b/OpenJibo/tests/Jibo.Cloud.Tests/WebSockets/JiboInteractionServiceTests.cs @@ -3096,6 +3096,56 @@ public sealed class JiboInteractionServiceTests Assert.Equal("global_commands", decision.SkillPayload["nluDomain"]); } + [Fact] + public async Task BuildDecisionAsync_CanYouGoToSleep_UsesSourceBackedSleepReply() + { + var service = CreateService(); + + var decision = await service.BuildDecisionAsync(new TurnContext + { + RawTranscript = "can you go to sleep", + NormalizedTranscript = "can you go to sleep" + }); + + Assert.Equal("robot_can_sleep", decision.IntentName); + Assert.Contains("I do. I usually fall asleep at night.", decision.ReplyText, StringComparison.OrdinalIgnoreCase); + Assert.Equal("ScriptedResponse", decision.ContextUpdates![ChitchatRouteKey]); + } + + [Fact] + public async Task BuildDecisionAsync_GoToSleep_MapsToIdleSleepCommand() + { + var service = CreateService(); + + var decision = await service.BuildDecisionAsync(new TurnContext + { + RawTranscript = "go to sleep", + NormalizedTranscript = "go to sleep" + }); + + Assert.Equal("sleep", decision.IntentName); + Assert.Equal("@be/idle", decision.SkillName); + Assert.Equal("sleep", decision.SkillPayload!["globalIntent"]); + Assert.Equal("global_commands", decision.SkillPayload["nluDomain"]); + } + + [Fact] + public async Task BuildDecisionAsync_TurnAround_MapsToIdleSpinAroundCommand() + { + var service = CreateService(); + + var decision = await service.BuildDecisionAsync(new TurnContext + { + RawTranscript = "turn around", + NormalizedTranscript = "turn around" + }); + + Assert.Equal("spin_around", decision.IntentName); + Assert.Equal("@be/idle", decision.SkillName); + Assert.Equal("spinAround", decision.SkillPayload!["globalIntent"]); + Assert.Equal("global_commands", decision.SkillPayload["nluDomain"]); + } + [Fact] public async Task BuildDecisionAsync_NeverMindWithPunctuation_MapsToIdleStopCommand() { diff --git a/OpenJibo/tests/Jibo.Cloud.Tests/WebSockets/JiboWebSocketServiceTests.cs b/OpenJibo/tests/Jibo.Cloud.Tests/WebSockets/JiboWebSocketServiceTests.cs index fe2951f..b634ed2 100644 --- a/OpenJibo/tests/Jibo.Cloud.Tests/WebSockets/JiboWebSocketServiceTests.cs +++ b/OpenJibo/tests/Jibo.Cloud.Tests/WebSockets/JiboWebSocketServiceTests.cs @@ -2831,6 +2831,90 @@ public sealed class JiboWebSocketServiceTests redirectPayload.RootElement.GetProperty("data").GetProperty("nlu").GetProperty("intent").GetString()); } + [Fact] + public async Task ClientAsr_GoToSleep_EmitsIdleSleepRedirect() + { + await _service.HandleMessageAsync(new WebSocketMessageEnvelope + { + HostName = "neo-hub.jibo.com", + Path = "/listen", + Kind = "neo-hub-listen", + Token = "hub-sleep-token", + Text = + """{"type":"LISTEN","transID":"trans-sleep","data":{"hotphrase":true,"rules":["launch","globals/global_commands_launch"]}}""" + }); + + var replies = await _service.HandleMessageAsync(new WebSocketMessageEnvelope + { + HostName = "neo-hub.jibo.com", + Path = "/listen", + Kind = "neo-hub-listen", + Token = "hub-sleep-token", + Text = """{"type":"CLIENT_ASR","transID":"trans-sleep","data":{"text":"go to sleep"}}""" + }); + + Assert.Equal(5, replies.Count); + Assert.Equal("LISTEN", ReadReplyType(replies[0])); + Assert.Equal("EOS", ReadReplyType(replies[1])); + Assert.Equal("SKILL_REDIRECT", ReadReplyType(replies[2])); + Assert.Equal("SKILL_ACTION", ReadReplyType(replies[3])); + Assert.Equal("SKILL_ACTION", ReadReplyType(replies[4])); + + using var listenPayload = JsonDocument.Parse(replies[0].Text!); + var nlu = listenPayload.RootElement.GetProperty("data").GetProperty("nlu"); + Assert.Equal("sleep", nlu.GetProperty("intent").GetString()); + Assert.Equal("global_commands", nlu.GetProperty("domain").GetString()); + Assert.Equal("globals/global_commands_launch", nlu.GetProperty("rules")[0].GetString()); + + using var redirectPayload = JsonDocument.Parse(replies[2].Text!); + Assert.Equal("@be/idle", + redirectPayload.RootElement.GetProperty("data").GetProperty("match").GetProperty("skillID").GetString()); + Assert.Equal("sleep", + redirectPayload.RootElement.GetProperty("data").GetProperty("nlu").GetProperty("intent").GetString()); + } + + [Fact] + public async Task ClientAsr_TurnAround_EmitsIdleSpinAroundRedirect() + { + await _service.HandleMessageAsync(new WebSocketMessageEnvelope + { + HostName = "neo-hub.jibo.com", + Path = "/listen", + Kind = "neo-hub-listen", + Token = "hub-spin-token", + Text = + """{"type":"LISTEN","transID":"trans-spin","data":{"hotphrase":true,"rules":["launch","globals/global_commands_launch"]}}""" + }); + + var replies = await _service.HandleMessageAsync(new WebSocketMessageEnvelope + { + HostName = "neo-hub.jibo.com", + Path = "/listen", + Kind = "neo-hub-listen", + Token = "hub-spin-token", + Text = """{"type":"CLIENT_ASR","transID":"trans-spin","data":{"text":"turn around"}}""" + }); + + Assert.Equal(5, replies.Count); + Assert.Equal("LISTEN", ReadReplyType(replies[0])); + Assert.Equal("EOS", ReadReplyType(replies[1])); + Assert.Equal("SKILL_REDIRECT", ReadReplyType(replies[2])); + Assert.Equal("SKILL_ACTION", ReadReplyType(replies[3])); + Assert.Equal("SKILL_ACTION", ReadReplyType(replies[4])); + + using var listenPayload = JsonDocument.Parse(replies[0].Text!); + var nlu = listenPayload.RootElement.GetProperty("data").GetProperty("nlu"); + Assert.Equal("spinAround", nlu.GetProperty("intent").GetString()); + Assert.Equal("global_commands", nlu.GetProperty("domain").GetString()); + Assert.Equal("globals/global_commands_launch", nlu.GetProperty("rules")[0].GetString()); + + using var redirectPayload = JsonDocument.Parse(replies[2].Text!); + Assert.Equal("@be/idle", + redirectPayload.RootElement.GetProperty("data").GetProperty("match").GetProperty("skillID").GetString()); + Assert.Equal("spinAround", + redirectPayload.RootElement.GetProperty("data").GetProperty("nlu").GetProperty("intent").GetString()); + } + [Fact] public async Task ClientAsr_TurnItDown_EmitsGlobalVolumeDownWithoutCloudSpeech() {