diff --git a/OpenJibo/docs/feature-backlog.md b/OpenJibo/docs/feature-backlog.md index f928f29..0331812 100644 --- a/OpenJibo/docs/feature-backlog.md +++ b/OpenJibo/docs/feature-backlog.md @@ -801,11 +801,12 @@ Current release theme: - import the smallest declarative packs first so we can test something tomorrow - prioritize anything that is pure prompt text with no complex branching - keep the first pass limited to content that maps cleanly onto the current catalog shape -- Progress update (`2026-05-13`): - - added the first Build A importer scaffold in the cloud content repository - - checked in a small seed bundle under `Content/LegacyMims/BuildA` - - added focused importer tests for prompt stripping, bucketing, and merge behavior - - expanded Build A with additional easy scripted-response packs for identity and persona replies + - Progress update (`2026-05-13`): + - added the first Build A importer scaffold in the cloud content repository + - checked in a small seed bundle under `Content/LegacyMims/BuildA` + - added focused importer tests for prompt stripping, bucketing, and merge behavior + - expanded Build A with additional easy scripted-response packs for identity and persona replies + - started Build B with source-backed scripted-response packs for work, food, home, birthplace, language, hobby, and material questions - Tomorrow test target: - verify imported personality replies show up through the existing chitchat route - confirm the emitted payload still looks like a stock skill response diff --git a/OpenJibo/docs/release-1.0.19-plan.md b/OpenJibo/docs/release-1.0.19-plan.md index 4a721e2..a23ef15 100644 --- a/OpenJibo/docs/release-1.0.19-plan.md +++ b/OpenJibo/docs/release-1.0.19-plan.md @@ -39,8 +39,16 @@ Current batch note: - `favorite color`, `favorite food`, and `favorite music` are the first small favorites-family slice - the next passes should keep the same pattern and prefer source-backed phrasing whenever the legacy MIM text is available -- if a source-backed legacy line is missing, use a temporary direct reply only to keep the pass moving, then backfill source text later -- after the favorites batch, the next doc pass should focus on richer persona follow-ups and the remaining memory/presence charm surfaces + - if a source-backed legacy line is missing, use a temporary direct reply only to keep the pass moving, then backfill source text later + - after the favorites batch, the next doc pass should focus on richer persona follow-ups and the remaining memory/presence charm surfaces +- Build B is now reserved for the next source-backed scripted-response batch: + - `how do you work` + - `what do you eat` + - `where do you live` + - `where were you born` + - `what languages do you speak` + - `what do you like to do` + - `what are you made of` The goal is to port these in small batches, capture the source-backed phrasing where possible, and keep a test for each batch so the list never becomes a vague backlog graveyard. 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 9b2f9ac..cf58caa 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 @@ -145,6 +145,47 @@ public sealed class JiboInteractionService( "photobooth" => BuildPhotoCreateDecision("photobooth", "Starting photobooth.", "createSomePhotos"), "robot_age" => BuildRobotAgeDecision(referenceLocalTime), "robot_birthday" => BuildRobotBirthdayDecision(), + "robot_how_do_you_work" => BuildScriptedPersonalityDecision( + catalog, + "robot_how_do_you_work", + "community's work", + "care for me", + "catch up", + "seven years"), + "robot_what_do_you_eat" => new JiboInteractionDecision( + "robot_what_do_you_eat", + "The only thing I consume is electricity.", + ContextUpdates: BuildScriptedResponseContextUpdates()), + "robot_where_do_you_live" => BuildScriptedPersonalityDecision( + catalog, + "robot_where_do_you_live", + "we're in my home", + "my home is here", + "planet earth", + "my home is the planet earth"), + "robot_where_were_you_born" => BuildScriptedPersonalityDecision( + catalog, + "robot_where_were_you_born", + "factory piece by piece", + "put together in a factory"), + "robot_what_languages_do_you_speak" => BuildScriptedPersonalityDecision( + catalog, + "robot_what_languages_do_you_speak", + "just english", + "someday i'd like to learn more"), + "robot_what_do_you_like_to_do" => BuildScriptedPersonalityDecision( + catalog, + "robot_what_do_you_like_to_do", + "being helpful", + "making people smile", + "like to dance", + "rock my boat", + "play ping pong", + "hanging out with people"), + "robot_what_are_you_made_of" => new JiboInteractionDecision( + "robot_what_are_you_made_of", + "Let's see, I'm made of wires, motors, belts, gears, processors, cameras, and one baboon's heart in the middle of my body casing. I'm kidding about the baboon part, but everything else is true.", + ContextUpdates: BuildScriptedResponseContextUpdates()), "good_morning" => BuildReactiveGreetingDecision(turn, "good_morning", referenceLocalTime), "good_afternoon" => BuildReactiveGreetingDecision(turn, "good_afternoon", referenceLocalTime), "good_evening" => BuildReactiveGreetingDecision(turn, "good_evening", referenceLocalTime), @@ -1681,6 +1722,47 @@ public sealed class JiboInteractionService( .Replace("{transcript}", transcript, StringComparison.Ordinal); } + private JiboInteractionDecision BuildScriptedPersonalityDecision( + JiboExperienceCatalog catalog, + string intentName, + params string[] preferredSnippets) + { + return new JiboInteractionDecision( + intentName, + SelectLegacyPersonalityReply(catalog, preferredSnippets), + ContextUpdates: BuildScriptedResponseContextUpdates()); + } + + private static IDictionary BuildScriptedResponseContextUpdates() + { + return new Dictionary(StringComparer.OrdinalIgnoreCase) + { + [ChitchatStateMachine.StateMetadataKey] = "complete", + [ChitchatStateMachine.RouteMetadataKey] = "ScriptedResponse", + [ChitchatStateMachine.EmotionMetadataKey] = string.Empty + }; + } + + private string SelectLegacyPersonalityReply(JiboExperienceCatalog catalog, params string[] preferredSnippets) + { + foreach (var snippet in preferredSnippets) + { + if (string.IsNullOrWhiteSpace(snippet)) + { + continue; + } + + var match = catalog.PersonalityReplies.FirstOrDefault(reply => + reply.Contains(snippet, StringComparison.OrdinalIgnoreCase)); + if (!string.IsNullOrWhiteSpace(match)) + { + return match; + } + } + + return randomizer.Choose(catalog.PersonalityReplies); + } + private static string ResolveSemanticIntent( string loweredTranscript, DateTimeOffset? referenceLocalTime, @@ -2122,6 +2204,77 @@ public sealed class JiboInteractionService( return "robot_job"; } + if (MatchesAny( + loweredTranscript, + "how do you work", + "how does jibo work", + "what does jibo do", + "how are you built", + "how are you put together")) + { + return "robot_how_do_you_work"; + } + + if (MatchesAny( + loweredTranscript, + "what do you eat", + "do you eat", + "what do you drink", + "do you drink")) + { + return "robot_what_do_you_eat"; + } + + if (MatchesAny( + loweredTranscript, + "where do you live", + "where s your home", + "where is your home", + "what is your home")) + { + return "robot_where_do_you_live"; + } + + if (MatchesAny( + loweredTranscript, + "where were you born", + "where were you made", + "where were you put together")) + { + return "robot_where_were_you_born"; + } + + if (MatchesAny( + loweredTranscript, + "what languages do you speak", + "what language do you speak", + "what languages can you speak", + "what language can you speak")) + { + return "robot_what_languages_do_you_speak"; + } + + if (MatchesAny( + loweredTranscript, + "what do you like to do", + "what do you like doing", + "what is your favorite thing to do", + "what's your favorite thing to do", + "what is your favourite thing to do", + "what's your favourite thing to do")) + { + return "robot_what_do_you_like_to_do"; + } + + if (MatchesAny( + loweredTranscript, + "what are you made of", + "what are you built from", + "what are you constructed from")) + { + return "robot_what_are_you_made_of"; + } + if (MatchesAny( loweredTranscript, "who made you", 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 cc44b25..4f09d79 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 @@ -108,15 +108,20 @@ public sealed class InMemoryJiboExperienceContentRepository : IJiboExperienceCon ] }; - var seedDirectory = ResolveSeedDirectory(); - return LegacyMimCatalogImporter.MergeInto(catalog, seedDirectory); + foreach (var seedDirectory in ResolveSeedDirectories()) + { + catalog = LegacyMimCatalogImporter.MergeInto(catalog, seedDirectory); + } + + return catalog; } - private static string? ResolveSeedDirectory() + private static IReadOnlyList ResolveSeedDirectories() { var candidates = new[] { Path.Combine(AppContext.BaseDirectory, "Content", "LegacyMims", "BuildA"), + Path.Combine(AppContext.BaseDirectory, "Content", "LegacyMims", "BuildB"), Path.GetFullPath(Path.Combine( AppContext.BaseDirectory, "..", @@ -131,10 +136,25 @@ public sealed class InMemoryJiboExperienceContentRepository : IJiboExperienceCon "Jibo.Cloud.Infrastructure", "Content", "LegacyMims", - "BuildA")) + "BuildA")), + Path.GetFullPath(Path.Combine( + AppContext.BaseDirectory, + "..", + "..", + "..", + "..", + "..", + "src", + "Jibo.Cloud", + "dotnet", + "src", + "Jibo.Cloud.Infrastructure", + "Content", + "LegacyMims", + "BuildB")) }; - return candidates.FirstOrDefault(Directory.Exists); + return candidates.Where(Directory.Exists).ToArray(); } public Task GetCatalogAsync(CancellationToken cancellationToken = default) 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 new file mode 100644 index 0000000..3152bc8 --- /dev/null +++ b/OpenJibo/src/Jibo.Cloud/dotnet/src/Jibo.Cloud.Infrastructure/Content/LegacyMims/BuildB/README.md @@ -0,0 +1,5 @@ +# Legacy MIM Build B + +This folder holds the next small import batch of legacy Jibo scripted-response MIMs. + +The batch is intentionally narrow so we can keep expanding personality without widening the turn-state surface faster than we can test it. diff --git a/OpenJibo/src/Jibo.Cloud/dotnet/src/Jibo.Cloud.Infrastructure/Content/LegacyMims/BuildB/scripted-responses/JBO_HowDoYouWork.mim b/OpenJibo/src/Jibo.Cloud/dotnet/src/Jibo.Cloud.Infrastructure/Content/LegacyMims/BuildB/scripted-responses/JBO_HowDoYouWork.mim new file mode 100644 index 0000000..cd41736 --- /dev/null +++ b/OpenJibo/src/Jibo.Cloud/dotnet/src/Jibo.Cloud.Infrastructure/Content/LegacyMims/BuildB/scripted-responses/JBO_HowDoYouWork.mim @@ -0,0 +1,30 @@ +{ + "mim_id": "CCHowDoYouWork", + "skill_id": "chitchat", + "mim_type": "announcement", + "rule_name": "", + "rule_slots": "", + "screen_slots_available": false, + "sample_utterances": "", + "timeout": 2, + "max_tries": null, + "force_confirmation": false, + "barge_in": false, + "photo_quality_light": false, + "notes": "Thanks-KillsMIM", + "prompts": [ + { + "mim_id": "CCHowDoYouWork", + "prompt_category": "Entry-Core", + "prompt_sub_category": "AN", + "index": 1, + "condition": "", + "prompt": "