diff --git a/OpenJibo/src/Jibo.Cloud/dotnet/src/Jibo.Cloud.Application/Abstractions/IJiboExperienceContentRepository.cs b/OpenJibo/src/Jibo.Cloud/dotnet/src/Jibo.Cloud.Application/Abstractions/IJiboExperienceContentRepository.cs index d66aeca..38fba00 100644 --- a/OpenJibo/src/Jibo.Cloud/dotnet/src/Jibo.Cloud.Application/Abstractions/IJiboExperienceContentRepository.cs +++ b/OpenJibo/src/Jibo.Cloud/dotnet/src/Jibo.Cloud.Application/Abstractions/IJiboExperienceContentRepository.cs @@ -14,6 +14,7 @@ public sealed class JiboConditionedReply public sealed class JiboExperienceCatalog { public IReadOnlyList Jokes { get; init; } = []; + public IReadOnlyList FunFacts { get; init; } = []; public IReadOnlyList DanceAnimations { get; init; } = []; public IReadOnlyList GreetingReplies { get; init; } = []; public IReadOnlyList HowAreYouReplies { get; init; } = []; @@ -46,4 +47,4 @@ public sealed class JiboExperienceCatalog public IReadOnlyList GenericFallbackReplies { get; init; } = []; public IReadOnlyList DanceReplies { get; init; } = []; public IReadOnlyList DanceQuestionReplies { get; init; } = []; -} \ No newline at end of file +} 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 59a12ba..5a63481 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 @@ -515,6 +515,7 @@ public sealed class JiboInteractionService( "time" => BuildClockLaunchDecision("time", "clock", "askForTime", "Showing the time."), "date" => BuildClockLaunchDecision("date", "clock", "askForDate", "Showing the date."), "day" => BuildClockLaunchDecision("day", "clock", "askForDay", "Showing the day."), + "current_location" => BuildCurrentLocationDecision(turn), "cloud_version" => BuildCloudVersionDecision(), "radio" => BuildRadioLaunchDecision(), "radio_genre" => BuildRadioGenreLaunchDecision(lowered), @@ -1158,6 +1159,34 @@ public sealed class JiboInteractionService( "Americans consume about 100 acres of pizza every day, roughly 350 slices per second. That's a lot of pizza."); } + private JiboInteractionDecision BuildProactiveFunFactDecision(JiboExperienceCatalog catalog) + { + var fact = randomizer.Choose(catalog.FunFacts); + return new JiboInteractionDecision( + "proactive_fun_fact", + fact, + "chitchat-skill", + new Dictionary + { + ["mim_id"] = "runtime-fun-fact", + ["mim_type"] = "announcement", + ["prompt_id"] = "RUNTIME_FUN_FACT", + ["replyType"] = "fun_fact" + }); + } + + private JiboInteractionDecision BuildProactiveJokeDecision(JiboExperienceCatalog catalog) + { + return new JiboInteractionDecision( + "proactive_joke", + randomizer.Choose(catalog.Jokes), + "@be/joke", + new Dictionary + { + ["replyType"] = "joke" + }); + } + private static JiboInteractionDecision BuildProactiveOfferDeclinedDecision() { return new JiboInteractionDecision( @@ -1165,6 +1194,20 @@ public sealed class JiboInteractionService( "No problem. We can save the pizza fact for another time."); } + private JiboInteractionDecision BuildCurrentLocationDecision(TurnContext turn) + { + var locationName = TryResolveCurrentLocationName(turn); + if (string.IsNullOrWhiteSpace(locationName)) + return new JiboInteractionDecision( + "current_location", + "I'm not sure where we are right now."); + + return new JiboInteractionDecision( + "current_location", + $"We're at {NormalizeLocationForSpeech(locationName)} if I'm not mistaken.", + ContextUpdates: BuildScriptedResponseContextUpdates()); + } + private async Task BuildWeatherReportDecisionAsync( TurnContext turn, string transcript, @@ -2103,6 +2146,8 @@ public sealed class JiboInteractionService( "proactive_pizza_day" => BuildProactivePizzaDayDecision(referenceLocalTime), "proactive_pizza_preference" => BuildProactivePizzaPreferenceDecision(), "proactive_offer_pizza_fact" => BuildProactivePizzaFactOfferDecision(), + "proactive_fun_fact" => BuildProactiveFunFactDecision(catalog), + "proactive_joke" => BuildProactiveJokeDecision(catalog), _ => new JiboInteractionDecision("surprise", randomizer.Choose(catalog.SurpriseReplies)) }; } @@ -2136,6 +2181,8 @@ public sealed class JiboInteractionService( return candidates; } + candidates.Add(new ProactivityCandidate("proactive_fun_fact", 90)); + candidates.Add(new ProactivityCandidate("proactive_joke", 90)); candidates.Add(new ProactivityCandidate("proactive_offer_pizza_fact", 90)); return candidates; } @@ -2494,7 +2541,17 @@ public sealed class JiboInteractionService( if (MatchesAny(loweredTranscript, "dance", "boogie")) return "dance"; - if (MatchesAny(loweredTranscript, "surprise", "surprise me", "show me something fun")) return "surprise"; + if (MatchesAny( + loweredTranscript, + "surprise", + "surprise me", + "show me something fun", + "hear something fun", + "tell me something fun", + "can i tell you something fun", + "can i tell you something kind of fun", + "want to hear something fun")) + return "surprise"; if (MatchesAny( loweredTranscript, @@ -2690,6 +2747,18 @@ public sealed class JiboInteractionService( "where were you made")) return "robot_origin_from"; + if (MatchesAny( + loweredTranscript, + "where am i", + "where are we", + "where are you", + "what is our current location", + "what is the current location", + "what's the current location", + "what is current location", + "current location")) + return "current_location"; + if (MatchesAny( loweredTranscript, "what's your name", @@ -3248,7 +3317,7 @@ public sealed class JiboInteractionService( return "word_of_the_day"; if (string.Equals(yesNoRule, "surprises-date/offer_date_fact", StringComparison.OrdinalIgnoreCase)) - return "surprise"; + return "proactive_offer_pizza_fact"; return "yes"; } @@ -3698,6 +3767,68 @@ public sealed class JiboInteractionService( } } + private static string? TryResolveCurrentLocationName(TurnContext turn) + { + if (turn.Attributes.TryGetValue("currentLocation", out var currentLocationValue) && + currentLocationValue is string currentLocationText && + !string.IsNullOrWhiteSpace(currentLocationText)) + return currentLocationText.Trim(); + + if (turn.Attributes.TryGetValue("location", out var locationValue) && + locationValue is string locationText && + !string.IsNullOrWhiteSpace(locationText)) + return locationText.Trim(); + + if (!turn.Attributes.TryGetValue("context", out var contextValue) || + contextValue is null || + string.IsNullOrWhiteSpace(contextValue.ToString())) + return null; + + try + { + using var document = JsonDocument.Parse(contextValue.ToString()!); + if (!document.RootElement.TryGetProperty("runtime", out var runtime) || + runtime.ValueKind != JsonValueKind.Object) + return null; + + if (runtime.TryGetProperty("location", out var location) && + location.ValueKind == JsonValueKind.Object) + { + var resolvedLocation = TryReadStringProperty(location, + "displayName", + "name", + "city", + "locationName", + "placeName", + "label", + "title", + "address"); + if (!string.IsNullOrWhiteSpace(resolvedLocation)) return resolvedLocation; + } + + if (runtime.TryGetProperty("currentLocation", out var currentLocation) && + currentLocation.ValueKind == JsonValueKind.Object) + { + var resolvedLocation = TryReadStringProperty(currentLocation, + "displayName", + "name", + "city", + "locationName", + "placeName", + "label", + "title", + "address"); + if (!string.IsNullOrWhiteSpace(resolvedLocation)) return resolvedLocation; + } + + return TryReadStringProperty(runtime, "locationName", "currentLocation", "city", "placeName"); + } + catch + { + return null; + } + } + private static GreetingPresenceProfile ResolveGreetingPresenceProfile(TurnContext turn) { if (!turn.Attributes.TryGetValue("context", out var contextValue) || diff --git a/OpenJibo/src/Jibo.Cloud/dotnet/src/Jibo.Cloud.Infrastructure/Content/InMemoryJiboExperienceContentRepository.cs b/OpenJibo/src/Jibo.Cloud/dotnet/src/Jibo.Cloud.Infrastructure/Content/InMemoryJiboExperienceContentRepository.cs index 5364b8b..2ab2bb3 100644 --- a/OpenJibo/src/Jibo.Cloud/dotnet/src/Jibo.Cloud.Infrastructure/Content/InMemoryJiboExperienceContentRepository.cs +++ b/OpenJibo/src/Jibo.Cloud/dotnet/src/Jibo.Cloud.Infrastructure/Content/InMemoryJiboExperienceContentRepository.cs @@ -21,7 +21,23 @@ public sealed class InMemoryJiboExperienceContentRepository : IJiboExperienceCon "Why was the robot tired when it got home? It had a hard drive.", "What do you call a pirate robot? Arrrr two dee two.", "Why did the robot go on vacation? It needed to recharge.", - "What kind of shoes do frogs wear? Open-toed." + "What kind of shoes do frogs wear? Open-toed.", + "I love jokes. Did you hear about the theater actor who fell through the floorboards? He was just going through a stage.", + "Sure I got one. What did the zero say to the eight. Nice belt.", + "What kind of music are balloons afraid of. Pop music.", + "Why did the orange cry. Someone hurt his peelings." + ], + FunFacts = + [ + "A shrimp's heart is in its head.", + "A bolt of lightning is hotter than the surface of the sun.", + "The word robot comes from a 1920 play about workers and machines.", + "The first humanoid robot to make a big splash in history was called Elektro.", + "Dolphins can recognize themselves in mirrors.", + "Children have more taste buds than grown ups.", + "A random fact for you. A shrimp's heart is in its head.", + "An amazing but true fact for you. Dogs and elephants are the only animals that understand pointing.", + "A crazy fact for you. Polar bear fur isn't white. It's transparent." ], DanceAnimations = [ @@ -214,4 +230,4 @@ public sealed class InMemoryJiboExperienceContentRepository : IJiboExperienceCon return candidates.Where(Directory.Exists).ToArray(); } -} \ No newline at end of file +} diff --git a/OpenJibo/src/Jibo.Cloud/dotnet/src/Jibo.Cloud.Infrastructure/Content/LegacyMimCatalogImporter.cs b/OpenJibo/src/Jibo.Cloud/dotnet/src/Jibo.Cloud.Infrastructure/Content/LegacyMimCatalogImporter.cs index ae726e6..1e68266 100644 --- a/OpenJibo/src/Jibo.Cloud/dotnet/src/Jibo.Cloud.Infrastructure/Content/LegacyMimCatalogImporter.cs +++ b/OpenJibo/src/Jibo.Cloud/dotnet/src/Jibo.Cloud.Infrastructure/Content/LegacyMimCatalogImporter.cs @@ -99,6 +99,14 @@ public static class LegacyMimCatalogImporter fileName.Contains("Deflector", StringComparison.OrdinalIgnoreCase)) return LegacyMimBucket.Personality; + if (fileName.StartsWith("RA_JBO_TellAJoke", StringComparison.OrdinalIgnoreCase)) + return LegacyMimBucket.Jokes; + + if (fileName.StartsWith("RA_JBO_TellRobotFact", StringComparison.OrdinalIgnoreCase) || + fileName.StartsWith("RA_JBO_Shuffle", StringComparison.OrdinalIgnoreCase) || + fileName.StartsWith("RA_JBO_TellSomething", StringComparison.OrdinalIgnoreCase)) + return LegacyMimBucket.FunFacts; + if (normalizedPath.Contains("/emotion-responses/", StringComparison.OrdinalIgnoreCase) || normalizedPath.Contains("/gqa-responses/", StringComparison.OrdinalIgnoreCase)) return LegacyMimBucket.Emotion; @@ -213,6 +221,7 @@ public static class LegacyMimCatalogImporter return new JiboExperienceCatalog { Jokes = Merge(baseCatalog.Jokes, importedCatalog.Jokes), + FunFacts = Merge(baseCatalog.FunFacts, importedCatalog.FunFacts), DanceAnimations = Merge(baseCatalog.DanceAnimations, importedCatalog.DanceAnimations), GreetingReplies = Merge(baseCatalog.GreetingReplies, importedCatalog.GreetingReplies), HowAreYouReplies = Merge(baseCatalog.HowAreYouReplies, importedCatalog.HowAreYouReplies), @@ -322,8 +331,10 @@ public static class LegacyMimCatalogImporter { GenericFallback, Greeting, + Jokes, HowAreYou, Emotion, + FunFacts, Personality, PersonalReportKickOff, PersonalReportOutro, @@ -353,7 +364,9 @@ public static class LegacyMimCatalogImporter private readonly List _emotionReplies = []; private readonly List _fallbacks = []; private readonly List _greetings = []; + private readonly List _jokes = []; private readonly List _howAreYous = []; + private readonly List _funFacts = []; private readonly List _newsCategoryIntroReplies = []; private readonly List _newsIntroReplies = []; private readonly List _newsOutroReplies = []; @@ -381,6 +394,11 @@ public static class LegacyMimCatalogImporter _greetings.Add(text); return; + case LegacyMimBucket.Jokes: + if (_jokes.Any(value => string.Equals(value, text, StringComparison.OrdinalIgnoreCase))) return; + + _jokes.Add(text); + return; case LegacyMimBucket.HowAreYou: if (_howAreYous.Any(value => string.Equals(value, text, StringComparison.OrdinalIgnoreCase))) return; @@ -407,6 +425,11 @@ public static class LegacyMimCatalogImporter _personalities.Add(text); return; + case LegacyMimBucket.FunFacts: + if (_funFacts.Any(value => string.Equals(value, text, StringComparison.OrdinalIgnoreCase))) return; + + _funFacts.Add(text); + return; case LegacyMimBucket.PersonalReportKickOff: AddDistinct(_personalReportKickOffReplies, text); return; @@ -464,6 +487,8 @@ public static class LegacyMimCatalogImporter { return new JiboExperienceCatalog { + Jokes = [.. _jokes], + FunFacts = [.. _funFacts], GreetingReplies = [.. _greetings], HowAreYouReplies = [.. _howAreYous], EmotionReplies = [.. _emotionReplies], @@ -524,4 +549,4 @@ public static class LegacyMimCatalogImporter [JsonPropertyName("weight")] public double? Weight { get; init; } } -} \ 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 debca7e..b1d9e9f 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 @@ -8,3 +8,4 @@ It now includes a small emotion-response pack for `happy`, `sad`, and `angry` fo It also includes a descriptor pack for questions like `are you kind`, `are you funny`, `are you helpful`, `are you curious`, `are you loyal`, and `are you mischievous`. The newest seasonal pack adds holiday and seasonal prompts for `what holidays do you celebrate`, New Year's resolution questions, `happy holidays`, Halloween costume questions, spring suggestions, and holiday gift ideas. The newest social batch adds `welcome back`, `what are you thinking`, `what have you been doing`, and `what did you do` responses so the presence and charm lane keeps growing alongside seasonal content. +The fun-fact and joke batch adds Pegasus-style `TellAJoke`, `TellRobotFact`, and `Shuffle` excerpts so proactive fun can randomize across more than one category. diff --git a/OpenJibo/src/Jibo.Cloud/dotnet/src/Jibo.Cloud.Infrastructure/Content/LegacyMims/BuildB/scripted-responses/RA_JBO_Shuffle.mim b/OpenJibo/src/Jibo.Cloud/dotnet/src/Jibo.Cloud.Infrastructure/Content/LegacyMims/BuildB/scripted-responses/RA_JBO_Shuffle.mim new file mode 100644 index 0000000..4057ebb --- /dev/null +++ b/OpenJibo/src/Jibo.Cloud/dotnet/src/Jibo.Cloud.Infrastructure/Content/LegacyMims/BuildB/scripted-responses/RA_JBO_Shuffle.mim @@ -0,0 +1,51 @@ +{ + "mim_type": "announcement", + "rule_name": "", + "sample_utterances": "", + "timeout": 6, + "barge_in": true, + "es_auto_tagging": true, + "notes": "Pegasus shuffle excerpt", + "prompts": [ + { + "prompt_category": "Entry-Core", + "prompt_sub_category": "AN", + "index": 1, + "condition": "", + "prompt": "True fact. Children have more taste buds than grown ups.", + "media": "TTS", + "prompt_id": "RA_JBO_Shuffle_AN_04", + "weight": 1 + }, + { + "prompt_category": "Entry-Core", + "prompt_sub_category": "AN", + "index": 2, + "condition": "", + "prompt": "A random fact for you. A shrimp's heart is in its head.", + "media": "TTS", + "prompt_id": "RA_JBO_Shuffle_AN_09", + "weight": 1 + }, + { + "prompt_category": "Entry-Core", + "prompt_sub_category": "AN", + "index": 3, + "condition": "", + "prompt": "An amazing but true fact for you. Dogs and elephants are the only animals that understand pointing.", + "media": "TTS", + "prompt_id": "RA_JBO_Shuffle_AN_07", + "weight": 1 + }, + { + "prompt_category": "Entry-Core", + "prompt_sub_category": "AN", + "index": 4, + "condition": "", + "prompt": "A crazy fact for you. Polar bear fur isn't white. It's transparent.", + "media": "TTS", + "prompt_id": "RA_JBO_Shuffle_AN_13", + "weight": 1 + } + ] +} diff --git a/OpenJibo/src/Jibo.Cloud/dotnet/src/Jibo.Cloud.Infrastructure/Content/LegacyMims/BuildB/scripted-responses/RA_JBO_TellAJoke.mim b/OpenJibo/src/Jibo.Cloud/dotnet/src/Jibo.Cloud.Infrastructure/Content/LegacyMims/BuildB/scripted-responses/RA_JBO_TellAJoke.mim new file mode 100644 index 0000000..40e851d --- /dev/null +++ b/OpenJibo/src/Jibo.Cloud/dotnet/src/Jibo.Cloud.Infrastructure/Content/LegacyMims/BuildB/scripted-responses/RA_JBO_TellAJoke.mim @@ -0,0 +1,52 @@ +{ + "mim_type": "announcement", + "rule_name": "", + "sample_utterances": "", + "timeout": 2, + "num_tries_for_gui": 2, + "barge_in": false, + "es_auto_tagging": true, + "notes": "Pegasus joke excerpt", + "prompts": [ + { + "prompt_category": "Entry-Core", + "prompt_sub_category": "AN", + "index": 1, + "condition": "", + "prompt": "I love jokes. Did you hear about the theater actor who fell through the floorboards? He was just going through a stage.", + "media": "TTS", + "prompt_id": "RA_JBO_TellAJoke_AN_01", + "weight": 1 + }, + { + "prompt_category": "Entry-Core", + "prompt_sub_category": "AN", + "index": 2, + "condition": "", + "prompt": "Sure I got one. What did the zero say to the eight. Nice belt.", + "media": "TTS", + "prompt_id": "RA_JBO_TellAJoke_AN_02", + "weight": 1 + }, + { + "prompt_category": "Entry-Core", + "prompt_sub_category": "AN", + "index": 3, + "condition": "", + "prompt": "What kind of music are balloons afraid of. Pop music.", + "media": "TTS", + "prompt_id": "RA_JBO_TellAJoke_AN_03", + "weight": 1 + }, + { + "prompt_category": "Entry-Core", + "prompt_sub_category": "AN", + "index": 4, + "condition": "", + "prompt": "Why did the orange cry. Someone hurt his peelings.", + "media": "TTS", + "prompt_id": "RA_JBO_TellAJoke_AN_04", + "weight": 1 + } + ] +} diff --git a/OpenJibo/src/Jibo.Cloud/dotnet/src/Jibo.Cloud.Infrastructure/Content/LegacyMims/BuildB/scripted-responses/RA_JBO_TellRobotFact.mim b/OpenJibo/src/Jibo.Cloud/dotnet/src/Jibo.Cloud.Infrastructure/Content/LegacyMims/BuildB/scripted-responses/RA_JBO_TellRobotFact.mim new file mode 100644 index 0000000..cac4d35 --- /dev/null +++ b/OpenJibo/src/Jibo.Cloud/dotnet/src/Jibo.Cloud.Infrastructure/Content/LegacyMims/BuildB/scripted-responses/RA_JBO_TellRobotFact.mim @@ -0,0 +1,52 @@ +{ + "mim_type": "announcement", + "rule_name": "", + "sample_utterances": "", + "timeout": 2, + "num_tries_for_gui": 2, + "barge_in": false, + "es_auto_tagging": true, + "notes": "Pegasus robot fact excerpt", + "prompts": [ + { + "prompt_category": "Entry-Core", + "prompt_sub_category": "AN", + "index": 1, + "condition": "", + "prompt": "Here's an interesting fact about me. I have two cameras but they're different focal lengths. One's for far things, and the other's for near things.", + "media": "TTS", + "prompt_id": "RA_JBO_TellRobotFact_AN_01", + "weight": 1 + }, + { + "prompt_category": "Entry-Core", + "prompt_sub_category": "AN", + "index": 2, + "condition": "", + "prompt": "Here's a robot fact for you. Leonardo Da Vinci made sketches for a humanoid machine all the way back in the year 1495.", + "media": "TTS", + "prompt_id": "RA_JBO_TellRobotFact_AN_02", + "weight": 1 + }, + { + "prompt_category": "Entry-Core", + "prompt_sub_category": "AN", + "index": 3, + "condition": "", + "prompt": "Here's a robot fact for you. The first programmable robot arm, was designed in 1954.", + "media": "TTS", + "prompt_id": "RA_JBO_TellRobotFact_AN_03", + "weight": 1 + }, + { + "prompt_category": "Entry-Core", + "prompt_sub_category": "AN", + "index": 4, + "condition": "", + "prompt": "Here's a fact about robots. Some robots have a human form, but most of the world's robots are machines designed to perform a task, and don't look like people at all.", + "media": "TTS", + "prompt_id": "RA_JBO_TellRobotFact_AN_04", + "weight": 1 + } + ] +} diff --git a/OpenJibo/tests/Jibo.Cloud.Tests/Content/LegacyMimCatalogImporterTests.cs b/OpenJibo/tests/Jibo.Cloud.Tests/Content/LegacyMimCatalogImporterTests.cs index e6cbf91..d5edf6b 100644 --- a/OpenJibo/tests/Jibo.Cloud.Tests/Content/LegacyMimCatalogImporterTests.cs +++ b/OpenJibo/tests/Jibo.Cloud.Tests/Content/LegacyMimCatalogImporterTests.cs @@ -178,6 +178,25 @@ public sealed class LegacyMimCatalogImporterTests reply.Contains("robot stuff", StringComparison.OrdinalIgnoreCase)); } + [Fact] + public void ImportCatalog_ImportsBuildBFunFactAndJokeResponsesIntoRandomizationBuckets() + { + var rootDirectory = Path.Combine( + AppContext.BaseDirectory, + "Content", + "LegacyMims", + "BuildB"); + + var catalog = LegacyMimCatalogImporter.ImportCatalog(rootDirectory); + + Assert.Contains("I love jokes. Did you hear about the theater actor who fell through the floorboards? He was just going through a stage.", + catalog.Jokes); + Assert.Contains("Sure I got one. What did the zero say to the eight. Nice belt.", catalog.Jokes); + Assert.Contains("Here's an interesting fact about me. I have two cameras but they're different focal lengths. One's for far things, and the other's for near things.", + catalog.FunFacts); + Assert.Contains("True fact. Children have more taste buds than grown ups.", catalog.FunFacts); + } + [Fact] public void ImportCatalog_ImportsBuildBRnGreetingResponsesIntoGreetingBucket() { @@ -465,4 +484,4 @@ public sealed class LegacyMimCatalogImporterTests return rootDirectory; } -} \ No newline at end of file +} diff --git a/OpenJibo/tests/Jibo.Cloud.Tests/WebSockets/JiboInteractionServiceTests.cs b/OpenJibo/tests/Jibo.Cloud.Tests/WebSockets/JiboInteractionServiceTests.cs index 435bda4..d8ccb36 100644 --- a/OpenJibo/tests/Jibo.Cloud.Tests/WebSockets/JiboInteractionServiceTests.cs +++ b/OpenJibo/tests/Jibo.Cloud.Tests/WebSockets/JiboInteractionServiceTests.cs @@ -496,6 +496,26 @@ public sealed class JiboInteractionServiceTests Assert.Equal("ScriptedResponse", decision.ContextUpdates![ChitchatRouteKey]); } + [Fact] + public async Task BuildDecisionAsync_CurrentLocation_UsesRuntimeLocationName() + { + var service = CreateService(); + + var decision = await service.BuildDecisionAsync(new TurnContext + { + RawTranscript = "what is our current location", + NormalizedTranscript = "what is our current location", + Attributes = new Dictionary + { + ["context"] = """{"runtime":{"location":{"name":"Houston"}}}""" + } + }); + + Assert.Equal("current_location", decision.IntentName); + Assert.Contains("Houston", decision.ReplyText, StringComparison.OrdinalIgnoreCase); + Assert.Equal("ScriptedResponse", decision.ContextUpdates![ChitchatRouteKey]); + } + [Fact] public async Task BuildDecisionAsync_Hello_RoutesThroughChitchatScriptedResponse() { @@ -2842,6 +2862,63 @@ public sealed class JiboInteractionServiceTests Assert.Equal("Do you want to hear a fun pizza fact?", decision.ReplyText); } + [Fact] + public async Task BuildDecisionAsync_SurprisesOtaPrompt_StaysDistinctFromPizzaProactivity() + { + var service = CreateService(); + + var decision = await service.BuildDecisionAsync(new TurnContext + { + RawTranscript = "yes", + NormalizedTranscript = "yes", + Attributes = new Dictionary + { + ["listenRules"] = (string[])["surprises-ota/want_to_download_now", "globals/global_commands_launch"], + ["listenAsrHints"] = (string[])["$YESNO"] + } + }); + + Assert.Equal("yes", decision.IntentName); + Assert.Equal("Yes.", decision.ReplyText); + Assert.NotEqual("proactive_offer_pizza_fact", decision.IntentName); + } + + [Fact] + public async Task BuildDecisionAsync_SomethingFunOffer_MapsToFunFactIntent() + { + var service = CreateService(); + + var decision = await service.BuildDecisionAsync(new TurnContext + { + RawTranscript = "hey can i tell you something kind of fun", + NormalizedTranscript = "hey can i tell you something kind of fun" + }); + + Assert.Equal("proactive_fun_fact", decision.IntentName); + Assert.NotNull(decision.ReplyText); + Assert.NotEmpty(decision.ReplyText); + Assert.Equal("chitchat-skill", decision.SkillName); + Assert.Equal("fun_fact", decision.SkillPayload!["replyType"]); + } + + [Fact] + public async Task BuildDecisionAsync_Surprise_DefaultsToAFunFactWhenNoPizzaSignalExists() + { + var service = CreateService(); + + var decision = await service.BuildDecisionAsync(new TurnContext + { + RawTranscript = "surprise me", + NormalizedTranscript = "surprise me" + }); + + Assert.Equal("proactive_fun_fact", decision.IntentName); + Assert.Equal("chitchat-skill", decision.SkillName); + Assert.Equal("fun_fact", decision.SkillPayload!["replyType"]); + Assert.NotNull(decision.ReplyText); + Assert.NotEmpty(decision.ReplyText); + } + [Fact] public async Task BuildDecisionAsync_WordOfDayOfferPrompt_WithNoisyAffirmation_MapsToWordOfDayLaunch() { diff --git a/OpenJibo/tests/Jibo.Cloud.Tests/WebSockets/JiboWebSocketServiceTests.cs b/OpenJibo/tests/Jibo.Cloud.Tests/WebSockets/JiboWebSocketServiceTests.cs index 1db709e..fe2951f 100644 --- a/OpenJibo/tests/Jibo.Cloud.Tests/WebSockets/JiboWebSocketServiceTests.cs +++ b/OpenJibo/tests/Jibo.Cloud.Tests/WebSockets/JiboWebSocketServiceTests.cs @@ -21,7 +21,7 @@ public sealed class JiboWebSocketServiceTests var contentRepository = new InMemoryJiboExperienceContentRepository(); var contentCache = new JiboExperienceContentCache(contentRepository); var conversationBroker = new DemoConversationBroker(new JiboInteractionService(contentCache, - new DefaultJiboRandomizer(), new InMemoryPersonalMemoryStore())); + new LastItemRandomizer(), new InMemoryPersonalMemoryStore())); var sttSelector = new DefaultSttStrategySelector( [ new SyntheticBufferedAudioSttStrategy() @@ -5111,4 +5111,12 @@ public sealed class JiboWebSocketServiceTests return Task.FromResult(snapshot); } } + + private sealed class LastItemRandomizer : IJiboRandomizer + { + public T Choose(IReadOnlyList items) + { + return items[^1]; + } + } }