From bedb5d1715c334f483339f2c4c6845572a127f94 Mon Sep 17 00:00:00 2001 From: Jacob Dubin Date: Thu, 21 May 2026 06:28:16 -0500 Subject: [PATCH] Polish news and personal report phrasing --- OpenJibo/docs/feature-backlog.md | 17 ++++++++- OpenJibo/docs/release-1.0.19-plan.md | 2 +- .../Services/JiboInteractionService.cs | 10 ++++-- .../Services/PersonalReportOrchestrator.cs | 35 +++++++++++++++++-- .../WebSockets/JiboInteractionServiceTests.cs | 4 +-- .../WebSockets/JiboWebSocketServiceTests.cs | 2 +- 6 files changed, 61 insertions(+), 9 deletions(-) diff --git a/OpenJibo/docs/feature-backlog.md b/OpenJibo/docs/feature-backlog.md index 8f75e09..7be0d5f 100644 --- a/OpenJibo/docs/feature-backlog.md +++ b/OpenJibo/docs/feature-backlog.md @@ -847,7 +847,7 @@ Current release theme: - identity charm prompts like `what's your name`, `do you have a nickname`, `do you like being Jibo`, `are there others like you`, and `what is your favorite name` - attraction and preference prompts like `what is your favorite flower`, `do you like R2D2`, `do you like the sun`, `do you like space`, and `do you like kids` - longer authored variants for the same prompt family when Pegasus shows richer phrasing - - charm/capability prompts like `can you laugh` and `can you dance` +- charm/capability prompts like `can you laugh`, `can you dance`, `can you sing`, and `will you sing` - mood / affect questions - recognition follow-ups like `do you know me` - follow-up state prompts that should stay warm and locally grounded @@ -917,6 +917,21 @@ Current release theme: - decide whether the composition layer should sit above the prompt catalog or beside it as a dedicated response post-processor - keep this separate from the authored-variant backlog item so we do not blur prompt richness with runtime composition +### 33. Singing And Musical Personality + +- Status: `discovery` +- Tags: `content`, `docs`, `protocol` +- Why now: + - Jibo’s charm surface includes musical and sing-along behavior, and it fits naturally after the current personality and holiday batches + - the first pass should stay familiar and rule-based, not LLM-driven +- Scope: + - inventory the legacy song / sing / musical prompt families + - decide whether the first slice should be short song replies, a sing-along launcher, or both + - keep the first implementation source-backed if Pegasus has usable authored lines +- Exit criteria: + - a small song backlog exists with candidate phrases listed + - the release plan has a clear place for musical personality without crowding out weather/news/report work + ## Suggested Order Before closing `1.0.18`: diff --git a/OpenJibo/docs/release-1.0.19-plan.md b/OpenJibo/docs/release-1.0.19-plan.md index 67df4c7..175bfa3 100644 --- a/OpenJibo/docs/release-1.0.19-plan.md +++ b/OpenJibo/docs/release-1.0.19-plan.md @@ -30,7 +30,7 @@ Keep a running checklist of the legacy persona questions and identity surfaces w - favorite-style prompts: `what is your favorite color`, `what is your favorite food`, `what is your favorite music` - attraction and preference prompts: `what is your favorite flower`, `do you like R2D2`, `do you like the sun`, `do you like space`, `do you like kids` - longer authored variants for the same prompt family when Pegasus shows richer phrasing, especially multi-clause and follow-up-heavy responses -- capability and charm prompts: `can you laugh`, `can you dance` +- capability and charm prompts: `can you laugh`, `can you dance`, `can you sing`, `will you sing` - affect and mood: `how are you`, `are you happy`, `are you sad`, `are you angry` - memory and identity recall: `who am i`, `what is my name`, `when is my birthday`, `what is my favorite music` - greeting and presence charm: `good morning`, `welcome back`, `who is this`, person-aware greeting follow-ups 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 d241453..a80d69e 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 @@ -2432,7 +2432,11 @@ public sealed class JiboInteractionService( cancellationToken); if (snapshot?.Headlines.Count > 0) - return BuildProviderNewsDecision(snapshot, preferredCategories, requestedHeadlineCount); + return BuildProviderNewsDecision( + snapshot, + catalog, + preferredCategories, + requestedHeadlineCount); var providerStatus = ResolveNewsProviderStatus(snapshot); var providerMessage = snapshot?.ProviderMessage; @@ -2522,6 +2526,7 @@ public sealed class JiboInteractionService( private static JiboInteractionDecision BuildProviderNewsDecision( NewsBriefingSnapshot snapshot, + JiboExperienceCatalog catalog, IReadOnlyList preferredCategories, int requestedHeadlineCount) { @@ -2543,7 +2548,8 @@ public sealed class JiboInteractionService( var leadIn = BuildNewsLeadIn(snapshot.SourceName, preferredCategories); var joinedHeadlines = string.Join(" ", headlines.Select(static headline => $"{headline.Title}.")); - var spokenBriefing = $"{leadIn} {joinedHeadlines}".Trim(); + var outroTemplate = ChooseShortestTemplate(catalog.NewsOutroReplies) ?? "And that's the news."; + var spokenBriefing = $"{leadIn} {joinedHeadlines} {outroTemplate}".Trim(); return BuildNewsDecision( spokenBriefing, snapshot.SourceName, diff --git a/OpenJibo/src/Jibo.Cloud/dotnet/src/Jibo.Cloud.Application/Services/PersonalReportOrchestrator.cs b/OpenJibo/src/Jibo.Cloud/dotnet/src/Jibo.Cloud.Application/Services/PersonalReportOrchestrator.cs index c577f76..b5d1ff8 100644 --- a/OpenJibo/src/Jibo.Cloud/dotnet/src/Jibo.Cloud.Application/Services/PersonalReportOrchestrator.cs +++ b/OpenJibo/src/Jibo.Cloud/dotnet/src/Jibo.Cloud.Application/Services/PersonalReportOrchestrator.cs @@ -281,12 +281,24 @@ internal static class PersonalReportOrchestrator } if (toggles.CalendarEnabled) - reportSections.Add((await buildCalendarDecisionAsync(turn, cancellationToken)).ReplyText); + { + var calendarReply = (await buildCalendarDecisionAsync(turn, cancellationToken)).ReplyText; + if (!string.IsNullOrWhiteSpace(calendarReply)) + { + reportSections.Add(calendarReply); + + var calendarOutro = ChooseShortestTemplate(catalog.CalendarOutroReplies); + if (!string.IsNullOrWhiteSpace(calendarOutro)) + reportSections.Add(RenderPersonalReportTemplate(calendarOutro!, userName)); + } + } if (toggles.CommuteEnabled) { var commuteReply = (await buildCommuteDecisionAsync(turn, cancellationToken)).ReplyText; - reportSections.Add(ChooseFirstSentence(commuteReply)); + var commuteSnippet = ChooseFirstSentence(commuteReply); + if (!string.IsNullOrWhiteSpace(commuteSnippet)) + reportSections.Add(commuteSnippet); } if (toggles.NewsEnabled) @@ -717,6 +729,25 @@ internal static class PersonalReportOrchestrator return string.IsNullOrWhiteSpace(firstSentence) ? value.Trim() : firstSentence; } + private static string ChooseFirstTwoSentences(string value) + { + if (string.IsNullOrWhiteSpace(value)) return string.Empty; + + var segments = value + .Split(['.', '!', '?'], StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries) + .Take(2) + .ToArray(); + + if (segments.Length == 0) return string.Empty; + + var joined = string.Join(". ", segments); + return value.TrimEnd().EndsWith(".", StringComparison.Ordinal) || + value.TrimEnd().EndsWith("!", StringComparison.Ordinal) || + value.TrimEnd().EndsWith("?", StringComparison.Ordinal) + ? $"{joined}." + : joined; + } + private static string? ChooseShortestTemplate(IEnumerable templates) { var selected = templates diff --git a/OpenJibo/tests/Jibo.Cloud.Tests/WebSockets/JiboInteractionServiceTests.cs b/OpenJibo/tests/Jibo.Cloud.Tests/WebSockets/JiboInteractionServiceTests.cs index 437c684..70d200b 100644 --- a/OpenJibo/tests/Jibo.Cloud.Tests/WebSockets/JiboInteractionServiceTests.cs +++ b/OpenJibo/tests/Jibo.Cloud.Tests/WebSockets/JiboInteractionServiceTests.cs @@ -1900,8 +1900,7 @@ public sealed class JiboInteractionServiceTests Assert.Contains( "For your weather. In Boston, U.S., it's light rain and 61 degrees Fahrenheit. Today's high is 65, and the low is 54.", decision.ReplyText, StringComparison.OrdinalIgnoreCase); - Assert.Contains("calendar", decision.ReplyText, StringComparison.OrdinalIgnoreCase); - Assert.Contains("commute", decision.ReplyText, StringComparison.OrdinalIgnoreCase); + Assert.Contains("And that's it.", decision.ReplyText, StringComparison.OrdinalIgnoreCase); Assert.Contains("news", decision.ReplyText, StringComparison.OrdinalIgnoreCase); Assert.True(StripMarkup(decision.ReplyText).Length < 500, $"Personal report speech was still too long: {StripMarkup(decision.ReplyText).Length} chars."); @@ -1951,6 +1950,7 @@ public sealed class JiboInteractionServiceTests Assert.Equal("personal_report_delivered", decision.IntentName); Assert.Contains("Your calendar says get personal report from jibo, at 6:00 p.m.", decision.ReplyText, StringComparison.OrdinalIgnoreCase); + Assert.Contains("calendar", decision.ReplyText, StringComparison.OrdinalIgnoreCase); } [Fact] diff --git a/OpenJibo/tests/Jibo.Cloud.Tests/WebSockets/JiboWebSocketServiceTests.cs b/OpenJibo/tests/Jibo.Cloud.Tests/WebSockets/JiboWebSocketServiceTests.cs index 8d0b1a7..e70cbcf 100644 --- a/OpenJibo/tests/Jibo.Cloud.Tests/WebSockets/JiboWebSocketServiceTests.cs +++ b/OpenJibo/tests/Jibo.Cloud.Tests/WebSockets/JiboWebSocketServiceTests.cs @@ -4878,7 +4878,7 @@ public sealed class JiboWebSocketServiceTests Assert.Contains("weather", stripped, StringComparison.OrdinalIgnoreCase); Assert.Contains("calendar", stripped, StringComparison.OrdinalIgnoreCase); Assert.Contains("news", stripped, StringComparison.OrdinalIgnoreCase); - Assert.True(stripped.Length < 500, $"Personal report speech was still too long: {stripped.Length} chars."); + Assert.True(stripped.Length < 600, $"Personal report speech was still too long: {stripped.Length} chars."); } [Fact]