diff --git a/OpenJibo/docs/release-1.0.19-plan.md b/OpenJibo/docs/release-1.0.19-plan.md index 8bf9ef2..da2bc61 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 - singing and musical personality now has a source-backed first slice with `can you sing`, `will you sing`, and holiday sing variants so the charm surface can keep growing without inventing a new dialog engine +- the friendship batch now includes `do you have friends`, `are we friends`, and `are we best friends` responses, plus the loop-friendly friend replies, so the relationship lane can stay source-backed too - 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` 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 42bed18..7c2c360 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 @@ -18,6 +18,8 @@ public sealed class JiboExperienceCatalog public IReadOnlyList HumanFacts { get; init; } = []; public IReadOnlyList FunFacts { get; init; } = []; public IReadOnlyList FavoriteAnimalReplies { get; init; } = []; + public IReadOnlyList FriendReplies { get; init; } = []; + public IReadOnlyList BestFriendReplies { get; init; } = []; public IReadOnlyList SingReplies { get; init; } = []; public IReadOnlyList HolidaySingReplies { get; init; } = []; public IReadOnlyList DanceAnimations { get; init; } = []; diff --git a/OpenJibo/src/Jibo.Cloud/dotnet/src/Jibo.Cloud.Application/Services/JiboInteractionService.IntentRouting.cs b/OpenJibo/src/Jibo.Cloud/dotnet/src/Jibo.Cloud.Application/Services/JiboInteractionService.IntentRouting.cs index 38e0df7..c9590b2 100644 --- a/OpenJibo/src/Jibo.Cloud/dotnet/src/Jibo.Cloud.Application/Services/JiboInteractionService.IntentRouting.cs +++ b/OpenJibo/src/Jibo.Cloud/dotnet/src/Jibo.Cloud.Application/Services/JiboInteractionService.IntentRouting.cs @@ -319,6 +319,15 @@ public sealed partial class JiboInteractionService "would you sing")) return "robot_can_sing"; + if (IsBestFriendQuestion(loweredTranscript)) + return "robot_best_friends"; + + if (IsFriendRelationQuestion(loweredTranscript)) + return "robot_is_friends_with_user"; + + if (IsFriendQuestion(loweredTranscript)) + return "robot_has_friends"; + if (MatchesAny(loweredTranscript, "twerk")) return "twerk"; if (MatchesAny(loweredTranscript, "dance", "boogie")) return "dance"; @@ -840,4 +849,48 @@ public sealed partial class JiboInteractionService return "chat"; } + private static bool IsFriendQuestion(string loweredTranscript) + { + return MatchesAny( + loweredTranscript, + "do you have friends", + "who are your friends", + "are you friends", + "are you and i friends", + "are you and me friends", + "are you and jibo friends"); + } + + private static bool IsFriendRelationQuestion(string loweredTranscript) + { + return MatchesAny( + loweredTranscript, + "are you my friend", + "are you friends with me", + "are we friends", + "are we friends with each other", + "is jibo your friend", + "i am friends with you", + "i'm friends with you", + "you are my friend", + "you re my friend", + "you're my friend"); + } + + private static bool IsBestFriendQuestion(string loweredTranscript) + { + return MatchesAny( + loweredTranscript, + "are we best friends", + "are we best friends with each other", + "are you my best friend", + "are you best friends with me", + "are you and i best friends", + "i am best friends with you", + "i'm best friends with you", + "you are my best friend", + "you re my best friend", + "you're my best friend"); + } + } diff --git a/OpenJibo/src/Jibo.Cloud/dotnet/src/Jibo.Cloud.Application/Services/JiboInteractionService.PersonalityDecisions.cs b/OpenJibo/src/Jibo.Cloud/dotnet/src/Jibo.Cloud.Application/Services/JiboInteractionService.PersonalityDecisions.cs index f4478ed..d72afa2 100644 --- a/OpenJibo/src/Jibo.Cloud/dotnet/src/Jibo.Cloud.Application/Services/JiboInteractionService.PersonalityDecisions.cs +++ b/OpenJibo/src/Jibo.Cloud/dotnet/src/Jibo.Cloud.Application/Services/JiboInteractionService.PersonalityDecisions.cs @@ -322,6 +322,28 @@ public sealed partial class JiboInteractionService preferredSnippets); } + private JiboInteractionDecision BuildScriptedFriendDecision( + JiboExperienceCatalog catalog, + string intentName, + params string[] preferredSnippets) + { + return new JiboInteractionDecision( + intentName, + SelectLegacyReply(catalog.FriendReplies, preferredSnippets), + ContextUpdates: ScriptedResponseDecisionBuilder.BuildScriptedResponseContextUpdates()); + } + + private JiboInteractionDecision BuildScriptedBestFriendDecision( + JiboExperienceCatalog catalog, + string intentName, + params string[] preferredSnippets) + { + return new JiboInteractionDecision( + intentName, + SelectLegacyReply(catalog.BestFriendReplies, preferredSnippets), + ContextUpdates: ScriptedResponseDecisionBuilder.BuildScriptedResponseContextUpdates()); + } + private JiboInteractionDecision BuildScriptedSingDecision( JiboExperienceCatalog catalog, string intentName, 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 2ed0c30..e7224fe 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 @@ -752,6 +752,22 @@ public sealed partial class JiboInteractionService( "dancing is one of the things i know best", "if there's one thing i know how to do. it's dance", "i can dance"), + "robot_has_friends" => BuildScriptedFriendDecision( + catalog, + "robot_has_friends", + "I believe I do have friends", + "I sure do have friends", + "I'm always up for making new friends"), + "robot_is_friends_with_user" => BuildScriptedFriendDecision( + catalog, + "robot_is_friends_with_user", + "don't know what i'd do without you", + "one of my favorites", + "making new friends"), + "robot_best_friends" => BuildScriptedBestFriendDecision( + catalog, + "robot_best_friends", + "best friends with anyone in my Loop"), "robot_can_sing" => BuildScriptedSingDecision( catalog, "robot_can_sing", 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 fd5c98a..d1dc7e4 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 @@ -63,6 +63,21 @@ public sealed class InMemoryJiboExperienceContentRepository : IJiboExperienceCon "I like lots of animals, but the penguin is the best of the best! Great color scheme.", "I love penguins, because we're so alike. We have the same coloring, and neither of us can fly." ], + FriendReplies = + [ + "I believe I do have friends. But I'm always up for more.", + "I sure do have friends. In a robot kind of way.", + "I don't know if we've met yet, but I'm always up for making new friends.", + "I don't know what I'd do without you.", + "You're one of my favorites.", + "I sure am.", + "I am indeed." + ], + BestFriendReplies = + [ + "I'd have to say I'm best friends with anyone in my Loop.", + "I think you know the answer to that question. You are." + ], SingReplies = [ "Singing is not my strong suit.", 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 08a2db3..a15332d 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 @@ -135,6 +135,16 @@ public static class LegacyMimCatalogImporter fileName.StartsWith("RI_JBO_LikesAnimals", StringComparison.OrdinalIgnoreCase)) return LegacyMimBucket.FavoriteAnimal; + if (fileName.StartsWith("RI_JBO_HasFriends", StringComparison.OrdinalIgnoreCase) || + fileName.StartsWith("RI_JBO_IsFriendsWithUser", StringComparison.OrdinalIgnoreCase) || + fileName.StartsWith("RI_JBO_IsFriendsWithLM", StringComparison.OrdinalIgnoreCase) || + fileName.StartsWith("RI_JBO_IsFriendsWithNonLM", StringComparison.OrdinalIgnoreCase) || + fileName.StartsWith("RI_JBO_IsFriendsWithToaster", StringComparison.OrdinalIgnoreCase)) + return LegacyMimBucket.Friend; + + if (fileName.StartsWith("RI_JBO_IsBestFriendsWithUser", StringComparison.OrdinalIgnoreCase)) + return LegacyMimBucket.BestFriend; + if (fileName.StartsWith("RN_HappyHolidays", StringComparison.OrdinalIgnoreCase)) return LegacyMimBucket.HolidayGreeting; @@ -305,6 +315,8 @@ public static class LegacyMimCatalogImporter HumanFacts = Merge(baseCatalog.HumanFacts, importedCatalog.HumanFacts), FunFacts = Merge(baseCatalog.FunFacts, importedCatalog.FunFacts), FavoriteAnimalReplies = Merge(baseCatalog.FavoriteAnimalReplies, importedCatalog.FavoriteAnimalReplies), + FriendReplies = Merge(baseCatalog.FriendReplies, importedCatalog.FriendReplies), + BestFriendReplies = Merge(baseCatalog.BestFriendReplies, importedCatalog.BestFriendReplies), SingReplies = Merge(baseCatalog.SingReplies, importedCatalog.SingReplies), HolidaySingReplies = Merge(baseCatalog.HolidaySingReplies, importedCatalog.HolidaySingReplies), DanceAnimations = Merge(baseCatalog.DanceAnimations, importedCatalog.DanceAnimations), @@ -507,6 +519,8 @@ public static class LegacyMimCatalogImporter Emotion, FunFacts, FavoriteAnimal, + Friend, + BestFriend, Sing, HolidaySing, FunFactSource, @@ -568,6 +582,8 @@ public static class LegacyMimCatalogImporter private readonly List _emotionReplies = []; private readonly List _fallbacks = []; private readonly List _favoriteAnimalReplies = []; + private readonly List _friendReplies = []; + private readonly List _bestFriendReplies = []; private readonly List _funFacts = []; private readonly List _greetings = []; private readonly List _holidayGiftReplies = []; @@ -690,6 +706,12 @@ public static class LegacyMimCatalogImporter case LegacyMimBucket.FavoriteAnimal: AddDistinct(_favoriteAnimalReplies, text); return; + case LegacyMimBucket.Friend: + AddDistinct(_friendReplies, text); + return; + case LegacyMimBucket.BestFriend: + AddDistinct(_bestFriendReplies, text); + return; case LegacyMimBucket.PersonalReportKickOff: AddDistinct(_personalReportKickOffReplies, text); return; @@ -794,6 +816,8 @@ public static class LegacyMimCatalogImporter HumanFacts = [.. _humanFacts], FunFacts = [.. _funFacts], FavoriteAnimalReplies = [.. _favoriteAnimalReplies], + FriendReplies = [.. _friendReplies], + BestFriendReplies = [.. _bestFriendReplies], SingReplies = [.. _singReplies], HolidaySingReplies = [.. _holidaySingReplies], GreetingReplies = [.. _greetings], 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 2a33efc..1932126 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 @@ -17,6 +17,7 @@ Holiday-specific note: - `RN_HappyBirthdayToJibo` now lands in the birthday celebration bucket - birthday memory authoring now also writes loop-scoped custom holiday records so personal dates can join the holiday list later 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 friendship batch adds `RI_JBO_HasFriends`, `RI_JBO_IsFriendsWithUser`, `RI_JBO_IsFriendsWithLM`, `RI_JBO_IsFriendsWithNonLM`, `RI_JBO_IsFriendsWithToaster`, and `RI_JBO_IsBestFriendsWithUser` so the friend and best-friend questions stay source-backed too. 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. diff --git a/OpenJibo/tests/Jibo.Cloud.Tests/Content/LegacyMimCatalogImporterTests.cs b/OpenJibo/tests/Jibo.Cloud.Tests/Content/LegacyMimCatalogImporterTests.cs index 1f50535..c6a360e 100644 --- a/OpenJibo/tests/Jibo.Cloud.Tests/Content/LegacyMimCatalogImporterTests.cs +++ b/OpenJibo/tests/Jibo.Cloud.Tests/Content/LegacyMimCatalogImporterTests.cs @@ -186,6 +186,33 @@ public sealed class LegacyMimCatalogImporterTests reply.Contains("north Pole", StringComparison.OrdinalIgnoreCase)); } + [Fact] + public void ImportCatalog_ImportsBuildBFriendshipResponsesIntoFriendBuckets() + { + var rootDirectory = Path.Combine( + AppContext.BaseDirectory, + "Content", + "LegacyMims", + "BuildB"); + + var catalog = LegacyMimCatalogImporter.ImportCatalog(rootDirectory); + + Assert.Contains(catalog.FriendReplies, reply => + reply.Contains("always up for more", StringComparison.OrdinalIgnoreCase)); + Assert.Contains(catalog.FriendReplies, reply => + reply.Contains("robot kind of way", StringComparison.OrdinalIgnoreCase)); + Assert.Contains(catalog.FriendReplies, reply => + reply.Contains("making new friends", StringComparison.OrdinalIgnoreCase)); + Assert.Contains(catalog.FriendReplies, reply => + reply.Contains("don't know if we've met yet", StringComparison.OrdinalIgnoreCase)); + Assert.Contains(catalog.FriendReplies, reply => + reply.Contains("don't know what I'd do without you", StringComparison.OrdinalIgnoreCase)); + Assert.Contains("I'd have to say I'm best friends with anyone in my Loop.", + catalog.BestFriendReplies); + Assert.Contains(catalog.BestFriendReplies, reply => + reply.Contains("You are", StringComparison.OrdinalIgnoreCase)); + } + [Fact] public void ImportCatalog_ImportsBuildBEmotionResponsesIntoEmotionBucket() { diff --git a/OpenJibo/tests/Jibo.Cloud.Tests/WebSockets/JiboInteractionServiceTests.cs b/OpenJibo/tests/Jibo.Cloud.Tests/WebSockets/JiboInteractionServiceTests.cs index 5456ec5..68eba5f 100644 --- a/OpenJibo/tests/Jibo.Cloud.Tests/WebSockets/JiboInteractionServiceTests.cs +++ b/OpenJibo/tests/Jibo.Cloud.Tests/WebSockets/JiboInteractionServiceTests.cs @@ -475,6 +475,9 @@ public sealed class JiboInteractionServiceTests [InlineData("do you like kids", "robot_likes_kids", "kids are so fun")] [InlineData("can you laugh", "robot_can_laugh", "when I'm happy")] [InlineData("can you dance", "robot_can_dance", "dancing is one of the things I know best")] + [InlineData("do you have friends", "robot_has_friends", "I believe I do have friends")] + [InlineData("are we friends", "robot_is_friends_with_user", "don't know what I'd do without you")] + [InlineData("are we best friends", "robot_best_friends", "best friends with anyone in my Loop")] [InlineData("can you sing", "robot_can_sing", "sing")] [InlineData("will you sing", "robot_can_sing", "sing")] [InlineData("can you sing a christmas song", "robot_sing_christmas_song", "sing")]