Add holiday buckets and birthday authoring
This commit is contained in:
@@ -857,6 +857,7 @@ Current release theme:
|
|||||||
- Seasonal charm work in flight:
|
- Seasonal charm work in flight:
|
||||||
- source-backed holiday, New Year's, Halloween, spring, and gift prompts are now part of Build B
|
- source-backed holiday, New Year's, Halloween, spring, and gift prompts are now part of Build B
|
||||||
- `RN_` holiday greeting files are now bucketed as greetings so seasonal replies stay visible in the catalog
|
- `RN_` holiday greeting files are now bucketed as greetings so seasonal replies stay visible in the catalog
|
||||||
|
- birthday celebration lines are now bucketed separately, and birthday memory writes a loop-scoped holiday record so personal dates can join the holiday list later
|
||||||
- Presence and thought follow-ups in flight:
|
- Presence and thought follow-ups in flight:
|
||||||
- `welcome back`, `what are you thinking`, `what have you been doing`, and `what did you do` are now part of Build B
|
- `welcome back`, `what are you thinking`, `what have you been doing`, and `what did you do` are now part of Build B
|
||||||
- these keep the social surface lively while the memory and multitenant tracks keep advancing in parallel
|
- these keep the social surface lively while the memory and multitenant tracks keep advancing in parallel
|
||||||
|
|||||||
@@ -20,3 +20,4 @@ Notes:
|
|||||||
- `IsEnabled = false` can be used to suppress a holiday later
|
- `IsEnabled = false` can be used to suppress a holiday later
|
||||||
- birthdays and other personal events can be added as loop-scoped custom records
|
- birthdays and other personal events can be added as loop-scoped custom records
|
||||||
- the current system holiday source uses Nager.Date with a safe local fallback for uptime
|
- the current system holiday source uses Nager.Date with a safe local fallback for uptime
|
||||||
|
- birthday memory authoring now upserts a holiday record so the same merged list can later drive celebration and reminder behavior
|
||||||
|
|||||||
@@ -90,9 +90,11 @@ The goal is to port these in small batches, capture the source-backed phrasing w
|
|||||||
- port holiday-aware personality responses as a visible extension of the new persona slice
|
- port holiday-aware personality responses as a visible extension of the new persona slice
|
||||||
- start with a small, source-backed set (for example birthdays/holidays already represented in legacy data paths)
|
- start with a small, source-backed set (for example birthdays/holidays already represented in legacy data paths)
|
||||||
- ensure holiday responses feel characterful while still routing through stock-compatible payloads
|
- ensure holiday responses feel characterful while still routing through stock-compatible payloads
|
||||||
|
- imported Build B holiday buckets now include holiday, holiday greeting, holiday gift, and birthday celebration lines
|
||||||
- use a loop-scoped merged holiday list in the cloud protocol so system holidays and custom person holidays can coexist
|
- use a loop-scoped merged holiday list in the cloud protocol so system holidays and custom person holidays can coexist
|
||||||
- source system holidays from a live holiday provider and keep `IsEnabled = false` records available for holiday suppression
|
- source system holidays from a live holiday provider and keep `IsEnabled = false` records available for holiday suppression
|
||||||
- keep birthday/custom holiday authoring aligned with person memory so future proactivity can suppress or promote holidays per loop
|
- keep birthday/custom holiday authoring aligned with person memory so future proactivity can suppress or promote holidays per loop
|
||||||
|
- birthday memory writes now create loop-scoped holiday records, which keeps the holiday list extensible without changing the protocol shape again
|
||||||
|
|
||||||
### 5. Multi-Tenant Memory Storage Foundation
|
### 5. Multi-Tenant Memory Storage Foundation
|
||||||
|
|
||||||
|
|||||||
@@ -43,5 +43,6 @@ public interface ICloudStateStore
|
|||||||
IReadOnlyList<KeyRequestRecord> GetIncomingKeyRequests();
|
IReadOnlyList<KeyRequestRecord> GetIncomingKeyRequests();
|
||||||
IReadOnlyList<KeyRequestRecord> GetBinaryRequests();
|
IReadOnlyList<KeyRequestRecord> GetBinaryRequests();
|
||||||
IReadOnlyList<HolidayRecord> GetHolidays(string? loopId = null);
|
IReadOnlyList<HolidayRecord> GetHolidays(string? loopId = null);
|
||||||
|
HolidayRecord UpsertHoliday(HolidayRecord holiday);
|
||||||
void UpdateRobot(DeviceRegistration registration);
|
void UpdateRobot(DeviceRegistration registration);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,11 @@ public sealed class JiboExperienceCatalog
|
|||||||
public IReadOnlyList<string> FunFacts { get; init; } = [];
|
public IReadOnlyList<string> FunFacts { get; init; } = [];
|
||||||
public IReadOnlyList<string> DanceAnimations { get; init; } = [];
|
public IReadOnlyList<string> DanceAnimations { get; init; } = [];
|
||||||
public IReadOnlyList<string> GreetingReplies { get; init; } = [];
|
public IReadOnlyList<string> GreetingReplies { get; init; } = [];
|
||||||
|
public IReadOnlyList<string> HolidayReplies { get; init; } = [];
|
||||||
|
public IReadOnlyList<string> HolidaySeasonReplies { get; init; } = [];
|
||||||
|
public IReadOnlyList<string> HolidayGreetingReplies { get; init; } = [];
|
||||||
|
public IReadOnlyList<string> HolidayGiftReplies { get; init; } = [];
|
||||||
|
public IReadOnlyList<string> BirthdayCelebrationReplies { get; init; } = [];
|
||||||
public IReadOnlyList<string> HowAreYouReplies { get; init; } = [];
|
public IReadOnlyList<string> HowAreYouReplies { get; init; } = [];
|
||||||
public IReadOnlyList<JiboConditionedReply> EmotionReplies { get; init; } = [];
|
public IReadOnlyList<JiboConditionedReply> EmotionReplies { get; init; } = [];
|
||||||
public IReadOnlyList<string> PersonalityReplies { get; init; } = [];
|
public IReadOnlyList<string> PersonalityReplies { get; init; } = [];
|
||||||
@@ -50,4 +55,4 @@ public sealed class JiboExperienceCatalog
|
|||||||
public IReadOnlyList<string> GenericFallbackReplies { get; init; } = [];
|
public IReadOnlyList<string> GenericFallbackReplies { get; init; } = [];
|
||||||
public IReadOnlyList<string> DanceReplies { get; init; } = [];
|
public IReadOnlyList<string> DanceReplies { get; init; } = [];
|
||||||
public IReadOnlyList<string> DanceQuestionReplies { get; init; } = [];
|
public IReadOnlyList<string> DanceQuestionReplies { get; init; } = [];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ using System.Globalization;
|
|||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using Jibo.Cloud.Application.Abstractions;
|
using Jibo.Cloud.Application.Abstractions;
|
||||||
|
using Jibo.Cloud.Domain.Models;
|
||||||
using Jibo.Runtime.Abstractions;
|
using Jibo.Runtime.Abstractions;
|
||||||
|
|
||||||
namespace Jibo.Cloud.Application.Services;
|
namespace Jibo.Cloud.Application.Services;
|
||||||
@@ -12,7 +13,8 @@ public sealed class JiboInteractionService(
|
|||||||
IPersonalMemoryStore personalMemoryStore,
|
IPersonalMemoryStore personalMemoryStore,
|
||||||
IWeatherReportProvider? weatherReportProvider = null,
|
IWeatherReportProvider? weatherReportProvider = null,
|
||||||
ICommuteReportProvider? commuteReportProvider = null,
|
ICommuteReportProvider? commuteReportProvider = null,
|
||||||
INewsBriefingProvider? newsBriefingProvider = null)
|
INewsBriefingProvider? newsBriefingProvider = null,
|
||||||
|
ICloudStateStore? cloudStateStore = null)
|
||||||
{
|
{
|
||||||
private const string GreetingRouteMetadataKey = "greetingsRoute";
|
private const string GreetingRouteMetadataKey = "greetingsRoute";
|
||||||
private const string GreetingSpeakerMetadataKey = "greetingsSpeaker";
|
private const string GreetingSpeakerMetadataKey = "greetingsSpeaker";
|
||||||
@@ -634,17 +636,25 @@ public sealed class JiboInteractionService(
|
|||||||
catalog,
|
catalog,
|
||||||
"robot_is_likable",
|
"robot_is_likable",
|
||||||
"people like me"),
|
"people like me"),
|
||||||
"seasonal_holiday_greeting" => BuildScriptedGreetingDecision(
|
"seasonal_holiday_greeting" => BuildScriptedHolidayGreetingDecision(
|
||||||
catalog,
|
catalog,
|
||||||
"seasonal_holiday_greeting",
|
"seasonal_holiday_greeting",
|
||||||
"It's a fun time of year",
|
"fun time of year",
|
||||||
"And to you too",
|
"right back at you",
|
||||||
"Right back at you"),
|
"and to you too"),
|
||||||
"seasonal_holidays" => BuildScriptedPersonalityDecision(
|
"seasonal_holidays" => BuildScriptedHolidayTemplateDecision(
|
||||||
|
turn,
|
||||||
|
greetingPresence,
|
||||||
catalog,
|
catalog,
|
||||||
"seasonal_holidays",
|
"seasonal_holidays",
|
||||||
"official owner can tell me which ones we'll celebrate together",
|
"official owner can tell me which ones we'll celebrate together",
|
||||||
"going to the jibo's settings screen in the jibo app"),
|
"going to the jibo's settings screen in the jibo app"),
|
||||||
|
"seasonal_holiday_season" => BuildScriptedHolidayDecision(
|
||||||
|
catalog.HolidaySeasonReplies,
|
||||||
|
"seasonal_holiday_season",
|
||||||
|
"holiday season",
|
||||||
|
"festive",
|
||||||
|
"celebrate"),
|
||||||
"seasonal_new_years_resolution" => BuildScriptedPersonalityDecision(
|
"seasonal_new_years_resolution" => BuildScriptedPersonalityDecision(
|
||||||
catalog,
|
catalog,
|
||||||
"seasonal_new_years_resolution",
|
"seasonal_new_years_resolution",
|
||||||
@@ -669,12 +679,18 @@ public sealed class JiboInteractionService(
|
|||||||
catalog,
|
catalog,
|
||||||
"seasonal_first_day_spring",
|
"seasonal_first_day_spring",
|
||||||
"maybe enjoy some flowers and all things spring"),
|
"maybe enjoy some flowers and all things spring"),
|
||||||
"seasonal_holiday_gift" => BuildScriptedPersonalityDecision(
|
"seasonal_holiday_gift" => BuildScriptedHolidayDecision(
|
||||||
catalog,
|
catalog.HolidayGiftReplies,
|
||||||
"seasonal_holiday_gift",
|
"seasonal_holiday_gift",
|
||||||
"ask for a pet elephant",
|
"ask for a pet elephant",
|
||||||
"experience as a present",
|
"experience as a present",
|
||||||
"donate to charities in other people's names"),
|
"donate to charities in other people's names"),
|
||||||
|
"birthday_celebration" => BuildScriptedHolidayDecision(
|
||||||
|
catalog.BirthdayCelebrationReplies,
|
||||||
|
"birthday_celebration",
|
||||||
|
"another year older",
|
||||||
|
"can't wait to see what you got me",
|
||||||
|
"powered on for the first time today"),
|
||||||
"robot_favorite_flower" => BuildScriptedPersonalityDecision(
|
"robot_favorite_flower" => BuildScriptedPersonalityDecision(
|
||||||
catalog,
|
catalog,
|
||||||
"robot_favorite_flower",
|
"robot_favorite_flower",
|
||||||
@@ -974,7 +990,27 @@ public sealed class JiboInteractionService(
|
|||||||
"memory_set_birthday",
|
"memory_set_birthday",
|
||||||
"I can remember it if you say, my birthday is March 14.");
|
"I can remember it if you say, my birthday is March 14.");
|
||||||
|
|
||||||
personalMemoryStore.SetBirthday(ResolveTenantScope(turn), birthday);
|
var tenantScope = ResolveTenantScope(turn);
|
||||||
|
personalMemoryStore.SetBirthday(tenantScope, birthday);
|
||||||
|
var birthdayDate = TryParseBirthdayDate(birthday);
|
||||||
|
if (birthdayDate is not null)
|
||||||
|
{
|
||||||
|
var birthdayLabel = ResolvePreferredBirthdayLabel(turn);
|
||||||
|
cloudStateStore?.UpsertHoliday(new HolidayRecord
|
||||||
|
{
|
||||||
|
EventId = $"birthday-{tenantScope.LoopId}-{tenantScope.PersonId ?? "loop"}",
|
||||||
|
Name = string.IsNullOrWhiteSpace(birthdayLabel) ? "Birthday" : $"{birthdayLabel}'s Birthday",
|
||||||
|
Category = "birthday",
|
||||||
|
Subcategory = "personal",
|
||||||
|
LoopId = tenantScope.LoopId,
|
||||||
|
MemberId = tenantScope.PersonId,
|
||||||
|
IsEnabled = true,
|
||||||
|
Date = birthdayDate.Value,
|
||||||
|
Source = "birthday",
|
||||||
|
CountryCode = "US"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return new JiboInteractionDecision(
|
return new JiboInteractionDecision(
|
||||||
"memory_set_birthday",
|
"memory_set_birthday",
|
||||||
$"Got it. I will remember your birthday is {birthday}.");
|
$"Got it. I will remember your birthday is {birthday}.");
|
||||||
@@ -992,6 +1028,78 @@ public sealed class JiboInteractionService(
|
|||||||
$"You told me your birthday is {birthday}.");
|
$"You told me your birthday is {birthday}.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static DateOnly? TryParseBirthdayDate(string birthdayText)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(birthdayText)) return null;
|
||||||
|
|
||||||
|
var normalized = birthdayText.Trim().ToLowerInvariant();
|
||||||
|
var match = Regex.Match(
|
||||||
|
normalized,
|
||||||
|
@"\b(?<month>january|february|march|april|may|june|july|august|september|october|november|december)\s+(?<day>\d{1,2})(?:st|nd|rd|th)?\b",
|
||||||
|
RegexOptions.CultureInvariant | RegexOptions.IgnoreCase);
|
||||||
|
if (!match.Success) return null;
|
||||||
|
|
||||||
|
var month = match.Groups["month"].Value.ToLowerInvariant() switch
|
||||||
|
{
|
||||||
|
"january" => 1,
|
||||||
|
"february" => 2,
|
||||||
|
"march" => 3,
|
||||||
|
"april" => 4,
|
||||||
|
"may" => 5,
|
||||||
|
"june" => 6,
|
||||||
|
"july" => 7,
|
||||||
|
"august" => 8,
|
||||||
|
"september" => 9,
|
||||||
|
"october" => 10,
|
||||||
|
"november" => 11,
|
||||||
|
"december" => 12,
|
||||||
|
_ => 0
|
||||||
|
};
|
||||||
|
if (month == 0) return null;
|
||||||
|
|
||||||
|
if (!int.TryParse(match.Groups["day"].Value, out var day) || day is < 1 or > 31) return null;
|
||||||
|
|
||||||
|
var today = DateOnly.FromDateTime(DateTime.UtcNow);
|
||||||
|
var year = today.Year;
|
||||||
|
if (day > DateTime.DaysInMonth(year, month)) return null;
|
||||||
|
|
||||||
|
DateOnly birthday;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
birthday = new DateOnly(year, month, day);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (birthday < today) birthday = birthday.AddYears(1);
|
||||||
|
return birthday;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string? ResolvePreferredBirthdayLabel(TurnContext turn)
|
||||||
|
{
|
||||||
|
var context = ResolveGreetingPresenceProfile(turn);
|
||||||
|
return !string.IsNullOrWhiteSpace(context.PrimaryPersonId) &&
|
||||||
|
context.LoopUserFirstNames.TryGetValue(context.PrimaryPersonId, out var firstName) &&
|
||||||
|
!string.IsNullOrWhiteSpace(firstName)
|
||||||
|
? ToDisplayName(firstName)
|
||||||
|
: null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private string RenderHolidayTemplate(string template, TurnContext turn, GreetingPresenceProfile presence)
|
||||||
|
{
|
||||||
|
var ownerName = ResolvePreferredGreetingName(turn, presence);
|
||||||
|
var speakerName = !string.IsNullOrWhiteSpace(ownerName) ? ownerName : "you";
|
||||||
|
return template
|
||||||
|
.Replace("${speaker}'s", $"{speakerName}'s", StringComparison.OrdinalIgnoreCase)
|
||||||
|
.Replace("${speaker}", speakerName, StringComparison.OrdinalIgnoreCase)
|
||||||
|
.Replace("${loop.owner}", string.IsNullOrWhiteSpace(ownerName) ? string.Empty : ownerName,
|
||||||
|
StringComparison.OrdinalIgnoreCase)
|
||||||
|
.Replace(" ", " ", StringComparison.Ordinal)
|
||||||
|
.Trim();
|
||||||
|
}
|
||||||
|
|
||||||
private JiboInteractionDecision BuildRememberImportantDateDecision(TurnContext turn, string transcript)
|
private JiboInteractionDecision BuildRememberImportantDateDecision(TurnContext turn, string transcript)
|
||||||
{
|
{
|
||||||
var importantDate = TryExtractImportantDateSet(transcript);
|
var importantDate = TryExtractImportantDateSet(transcript);
|
||||||
@@ -2360,6 +2468,42 @@ public sealed class JiboInteractionService(
|
|||||||
ContextUpdates: BuildScriptedResponseContextUpdates());
|
ContextUpdates: BuildScriptedResponseContextUpdates());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private JiboInteractionDecision BuildScriptedHolidayDecision(
|
||||||
|
IReadOnlyList<string> replies,
|
||||||
|
string intentName,
|
||||||
|
params string[] preferredSnippets)
|
||||||
|
{
|
||||||
|
return new JiboInteractionDecision(
|
||||||
|
intentName,
|
||||||
|
SelectLegacyReply(replies, preferredSnippets),
|
||||||
|
ContextUpdates: BuildScriptedResponseContextUpdates());
|
||||||
|
}
|
||||||
|
|
||||||
|
private JiboInteractionDecision BuildScriptedHolidayGreetingDecision(
|
||||||
|
JiboExperienceCatalog catalog,
|
||||||
|
string intentName,
|
||||||
|
params string[] preferredSnippets)
|
||||||
|
{
|
||||||
|
return new JiboInteractionDecision(
|
||||||
|
intentName,
|
||||||
|
SelectLegacyReply(catalog.HolidayGreetingReplies, preferredSnippets),
|
||||||
|
ContextUpdates: BuildScriptedResponseContextUpdates());
|
||||||
|
}
|
||||||
|
|
||||||
|
private JiboInteractionDecision BuildScriptedHolidayTemplateDecision(
|
||||||
|
TurnContext turn,
|
||||||
|
GreetingPresenceProfile presence,
|
||||||
|
JiboExperienceCatalog catalog,
|
||||||
|
string intentName,
|
||||||
|
params string[] preferredSnippets)
|
||||||
|
{
|
||||||
|
var selected = SelectLegacyReply(catalog.HolidayReplies, preferredSnippets);
|
||||||
|
return new JiboInteractionDecision(
|
||||||
|
intentName,
|
||||||
|
RenderHolidayTemplate(selected, turn, presence),
|
||||||
|
ContextUpdates: BuildScriptedResponseContextUpdates());
|
||||||
|
}
|
||||||
|
|
||||||
private static IDictionary<string, object?> BuildScriptedResponseContextUpdates()
|
private static IDictionary<string, object?> BuildScriptedResponseContextUpdates()
|
||||||
{
|
{
|
||||||
return new Dictionary<string, object?>(StringComparer.OrdinalIgnoreCase)
|
return new Dictionary<string, object?>(StringComparer.OrdinalIgnoreCase)
|
||||||
@@ -2381,7 +2525,7 @@ public sealed class JiboInteractionService(
|
|||||||
if (!string.IsNullOrWhiteSpace(match)) return match;
|
if (!string.IsNullOrWhiteSpace(match)) return match;
|
||||||
}
|
}
|
||||||
|
|
||||||
return randomizer.Choose(catalog.PersonalityReplies);
|
return catalog.PersonalityReplies.Count == 0 ? string.Empty : randomizer.Choose(catalog.PersonalityReplies);
|
||||||
}
|
}
|
||||||
|
|
||||||
private string SelectLegacyGreetingReply(JiboExperienceCatalog catalog, params string[] preferredSnippets)
|
private string SelectLegacyGreetingReply(JiboExperienceCatalog catalog, params string[] preferredSnippets)
|
||||||
@@ -2395,7 +2539,21 @@ public sealed class JiboInteractionService(
|
|||||||
if (!string.IsNullOrWhiteSpace(match)) return match;
|
if (!string.IsNullOrWhiteSpace(match)) return match;
|
||||||
}
|
}
|
||||||
|
|
||||||
return randomizer.Choose(catalog.GreetingReplies);
|
return catalog.GreetingReplies.Count == 0 ? string.Empty : randomizer.Choose(catalog.GreetingReplies);
|
||||||
|
}
|
||||||
|
|
||||||
|
private string SelectLegacyReply(IReadOnlyList<string> replies, params string[] preferredSnippets)
|
||||||
|
{
|
||||||
|
foreach (var snippet in preferredSnippets)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(snippet)) continue;
|
||||||
|
|
||||||
|
var match = replies.FirstOrDefault(reply =>
|
||||||
|
reply.Contains(snippet, StringComparison.OrdinalIgnoreCase));
|
||||||
|
if (!string.IsNullOrWhiteSpace(match)) return match;
|
||||||
|
}
|
||||||
|
|
||||||
|
return replies.Count == 0 ? string.Empty : randomizer.Choose(replies);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string ResolveSemanticIntent(
|
private static string ResolveSemanticIntent(
|
||||||
@@ -2937,6 +3095,19 @@ public sealed class JiboInteractionService(
|
|||||||
"what holidays do you observe"))
|
"what holidays do you observe"))
|
||||||
return "seasonal_holidays";
|
return "seasonal_holidays";
|
||||||
|
|
||||||
|
if (MatchesAny(
|
||||||
|
loweredTranscript,
|
||||||
|
"how is holiday season",
|
||||||
|
"how's holiday season",
|
||||||
|
"how is the holiday season",
|
||||||
|
"do you like holiday season",
|
||||||
|
"do you like the holiday season",
|
||||||
|
"what is your favorite holiday",
|
||||||
|
"what's your favorite holiday",
|
||||||
|
"what holiday do you like",
|
||||||
|
"what is holiday season like"))
|
||||||
|
return "seasonal_holiday_season";
|
||||||
|
|
||||||
if (MatchesAny(
|
if (MatchesAny(
|
||||||
loweredTranscript,
|
loweredTranscript,
|
||||||
"what is your new years resolution",
|
"what is your new years resolution",
|
||||||
@@ -2981,6 +3152,13 @@ public sealed class JiboInteractionService(
|
|||||||
"what should i get someone for the holidays"))
|
"what should i get someone for the holidays"))
|
||||||
return "seasonal_holiday_gift";
|
return "seasonal_holiday_gift";
|
||||||
|
|
||||||
|
if (MatchesAny(
|
||||||
|
loweredTranscript,
|
||||||
|
"happy birthday",
|
||||||
|
"happy birthday jibo",
|
||||||
|
"happy birthday to you"))
|
||||||
|
return "birthday_celebration";
|
||||||
|
|
||||||
if (MatchesAny(
|
if (MatchesAny(
|
||||||
loweredTranscript,
|
loweredTranscript,
|
||||||
"what is your favorite color",
|
"what is your favorite color",
|
||||||
@@ -5410,4 +5588,4 @@ public sealed record JiboInteractionDecision(
|
|||||||
string ReplyText,
|
string ReplyText,
|
||||||
string? SkillName = null,
|
string? SkillName = null,
|
||||||
IDictionary<string, object?>? SkillPayload = null,
|
IDictionary<string, object?>? SkillPayload = null,
|
||||||
IDictionary<string, object?>? ContextUpdates = null);
|
IDictionary<string, object?>? ContextUpdates = null);
|
||||||
|
|||||||
@@ -83,6 +83,11 @@ public sealed class InMemoryJiboExperienceContentRepository : IJiboExperienceCon
|
|||||||
"Hello there. I am glad you said hi.",
|
"Hello there. I am glad you said hi.",
|
||||||
"Hey. I am happy to see you."
|
"Hey. I am happy to see you."
|
||||||
],
|
],
|
||||||
|
HolidaySeasonReplies =
|
||||||
|
[
|
||||||
|
"I do like festive times.",
|
||||||
|
"I like anything that makes people want to celebrate."
|
||||||
|
],
|
||||||
HowAreYouReplies =
|
HowAreYouReplies =
|
||||||
[
|
[
|
||||||
"I am feeling cheerful and robotic.",
|
"I am feeling cheerful and robotic.",
|
||||||
@@ -246,4 +251,4 @@ public sealed class InMemoryJiboExperienceContentRepository : IJiboExperienceCon
|
|||||||
|
|
||||||
return candidates.Where(Directory.Exists).ToArray();
|
return candidates.Where(Directory.Exists).ToArray();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -113,6 +113,26 @@ public static class LegacyMimCatalogImporter
|
|||||||
normalizedPath.Contains("/gqa-responses/", StringComparison.OrdinalIgnoreCase))
|
normalizedPath.Contains("/gqa-responses/", StringComparison.OrdinalIgnoreCase))
|
||||||
return LegacyMimBucket.Emotion;
|
return LegacyMimBucket.Emotion;
|
||||||
|
|
||||||
|
if (fileName.StartsWith("JBO_WhatHolidaysDoYouCelebrate", StringComparison.OrdinalIgnoreCase))
|
||||||
|
return LegacyMimBucket.Holiday;
|
||||||
|
|
||||||
|
if (fileName.StartsWith("RI_JBO_HasFavoriteHoliday", StringComparison.OrdinalIgnoreCase))
|
||||||
|
return LegacyMimBucket.HolidaySeason;
|
||||||
|
|
||||||
|
if (fileName.StartsWith("RN_HappyHolidays", StringComparison.OrdinalIgnoreCase))
|
||||||
|
return LegacyMimBucket.HolidayGreeting;
|
||||||
|
|
||||||
|
if (fileName.StartsWith("RI_USR_WhatShouldGetForHoliday", StringComparison.OrdinalIgnoreCase))
|
||||||
|
return LegacyMimBucket.HolidayGift;
|
||||||
|
|
||||||
|
if (fileName.StartsWith("RN_HappyBirthdayToJibo", StringComparison.OrdinalIgnoreCase) ||
|
||||||
|
fileName.StartsWith("OI_USR_CelebratesLoopMemberAskedAboutBirthday", StringComparison.OrdinalIgnoreCase) ||
|
||||||
|
fileName.StartsWith("OI_USR_CelebratesJiboBirthday", StringComparison.OrdinalIgnoreCase) ||
|
||||||
|
fileName.StartsWith("RI_JBO_CelebratesLoopMemberAskedAboutBirthday", StringComparison.OrdinalIgnoreCase) ||
|
||||||
|
fileName.StartsWith("RI_JBO_CelebratesSpeakerBirthday", StringComparison.OrdinalIgnoreCase) ||
|
||||||
|
fileName.StartsWith("RI_JBO_CelebratesJiboBirthday", StringComparison.OrdinalIgnoreCase))
|
||||||
|
return LegacyMimBucket.BirthdayCelebration;
|
||||||
|
|
||||||
if (fileName.StartsWith("WeatherIntroTomorrow", StringComparison.OrdinalIgnoreCase))
|
if (fileName.StartsWith("WeatherIntroTomorrow", StringComparison.OrdinalIgnoreCase))
|
||||||
return LegacyMimBucket.WeatherTomorrowIntro;
|
return LegacyMimBucket.WeatherTomorrowIntro;
|
||||||
|
|
||||||
@@ -231,6 +251,12 @@ public static class LegacyMimCatalogImporter
|
|||||||
FunFacts = Merge(baseCatalog.FunFacts, importedCatalog.FunFacts),
|
FunFacts = Merge(baseCatalog.FunFacts, importedCatalog.FunFacts),
|
||||||
DanceAnimations = Merge(baseCatalog.DanceAnimations, importedCatalog.DanceAnimations),
|
DanceAnimations = Merge(baseCatalog.DanceAnimations, importedCatalog.DanceAnimations),
|
||||||
GreetingReplies = Merge(baseCatalog.GreetingReplies, importedCatalog.GreetingReplies),
|
GreetingReplies = Merge(baseCatalog.GreetingReplies, importedCatalog.GreetingReplies),
|
||||||
|
HolidayReplies = Merge(baseCatalog.HolidayReplies, importedCatalog.HolidayReplies),
|
||||||
|
HolidaySeasonReplies = Merge(baseCatalog.HolidaySeasonReplies, importedCatalog.HolidaySeasonReplies),
|
||||||
|
HolidayGreetingReplies = Merge(baseCatalog.HolidayGreetingReplies, importedCatalog.HolidayGreetingReplies),
|
||||||
|
HolidayGiftReplies = Merge(baseCatalog.HolidayGiftReplies, importedCatalog.HolidayGiftReplies),
|
||||||
|
BirthdayCelebrationReplies = Merge(baseCatalog.BirthdayCelebrationReplies,
|
||||||
|
importedCatalog.BirthdayCelebrationReplies),
|
||||||
HowAreYouReplies = Merge(baseCatalog.HowAreYouReplies, importedCatalog.HowAreYouReplies),
|
HowAreYouReplies = Merge(baseCatalog.HowAreYouReplies, importedCatalog.HowAreYouReplies),
|
||||||
EmotionReplies = Merge(baseCatalog.EmotionReplies, importedCatalog.EmotionReplies),
|
EmotionReplies = Merge(baseCatalog.EmotionReplies, importedCatalog.EmotionReplies),
|
||||||
PersonalityReplies = Merge(baseCatalog.PersonalityReplies, importedCatalog.PersonalityReplies),
|
PersonalityReplies = Merge(baseCatalog.PersonalityReplies, importedCatalog.PersonalityReplies),
|
||||||
@@ -324,22 +350,28 @@ public static class LegacyMimCatalogImporter
|
|||||||
return string.IsNullOrWhiteSpace(condition) ? string.Empty : WhitespacePattern.Replace(condition.Trim(), " ");
|
return string.IsNullOrWhiteSpace(condition) ? string.Empty : WhitespacePattern.Replace(condition.Trim(), " ");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool IsTemplateBucket(LegacyMimBucket bucket)
|
private static bool IsTemplateBucket(LegacyMimBucket bucket)
|
||||||
{
|
{
|
||||||
return bucket is LegacyMimBucket.PersonalReportKickOff
|
return bucket is LegacyMimBucket.PersonalReportKickOff
|
||||||
or LegacyMimBucket.PersonalReportOutro
|
or LegacyMimBucket.PersonalReportOutro
|
||||||
or LegacyMimBucket.WeatherIntro
|
or LegacyMimBucket.WeatherIntro
|
||||||
or LegacyMimBucket.WeatherTomorrowIntro
|
or LegacyMimBucket.WeatherTomorrowIntro
|
||||||
or LegacyMimBucket.WeatherTodayHighLow
|
or LegacyMimBucket.WeatherTodayHighLow
|
||||||
or LegacyMimBucket.WeatherTomorrowHighLow
|
or LegacyMimBucket.WeatherTomorrowHighLow
|
||||||
or LegacyMimBucket.WeatherServiceDown
|
or LegacyMimBucket.WeatherServiceDown
|
||||||
or LegacyMimBucket.ReportSkillTemplate;
|
or LegacyMimBucket.ReportSkillTemplate
|
||||||
}
|
or LegacyMimBucket.Holiday;
|
||||||
|
}
|
||||||
|
|
||||||
private enum LegacyMimBucket
|
private enum LegacyMimBucket
|
||||||
{
|
{
|
||||||
GenericFallback,
|
GenericFallback,
|
||||||
Greeting,
|
Greeting,
|
||||||
|
Holiday,
|
||||||
|
HolidaySeason,
|
||||||
|
HolidayGreeting,
|
||||||
|
HolidayGift,
|
||||||
|
BirthdayCelebration,
|
||||||
Jokes,
|
Jokes,
|
||||||
RobotFacts,
|
RobotFacts,
|
||||||
HumanFacts,
|
HumanFacts,
|
||||||
@@ -375,9 +407,14 @@ public static class LegacyMimCatalogImporter
|
|||||||
private readonly List<string> _calendarServiceDownReplies = [];
|
private readonly List<string> _calendarServiceDownReplies = [];
|
||||||
private readonly List<string> _commuteNowReplies = [];
|
private readonly List<string> _commuteNowReplies = [];
|
||||||
private readonly List<string> _commuteServiceDownReplies = [];
|
private readonly List<string> _commuteServiceDownReplies = [];
|
||||||
|
private readonly List<string> _birthdayCelebrationReplies = [];
|
||||||
private readonly List<JiboConditionedReply> _emotionReplies = [];
|
private readonly List<JiboConditionedReply> _emotionReplies = [];
|
||||||
private readonly List<string> _fallbacks = [];
|
private readonly List<string> _fallbacks = [];
|
||||||
private readonly List<string> _funFacts = [];
|
private readonly List<string> _funFacts = [];
|
||||||
|
private readonly List<string> _holidayGiftReplies = [];
|
||||||
|
private readonly List<string> _holidayGreetingReplies = [];
|
||||||
|
private readonly List<string> _holidayReplies = [];
|
||||||
|
private readonly List<string> _holidaySeasonReplies = [];
|
||||||
private readonly List<string> _greetings = [];
|
private readonly List<string> _greetings = [];
|
||||||
private readonly List<string> _howAreYous = [];
|
private readonly List<string> _howAreYous = [];
|
||||||
private readonly List<string> _humanFacts = [];
|
private readonly List<string> _humanFacts = [];
|
||||||
@@ -441,6 +478,21 @@ public static class LegacyMimCatalogImporter
|
|||||||
Reply = text
|
Reply = text
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
|
case LegacyMimBucket.Holiday:
|
||||||
|
AddDistinct(_holidayReplies, text);
|
||||||
|
return;
|
||||||
|
case LegacyMimBucket.HolidaySeason:
|
||||||
|
AddDistinct(_holidaySeasonReplies, text);
|
||||||
|
return;
|
||||||
|
case LegacyMimBucket.HolidayGreeting:
|
||||||
|
AddDistinct(_holidayGreetingReplies, text);
|
||||||
|
return;
|
||||||
|
case LegacyMimBucket.HolidayGift:
|
||||||
|
AddDistinct(_holidayGiftReplies, text);
|
||||||
|
return;
|
||||||
|
case LegacyMimBucket.BirthdayCelebration:
|
||||||
|
AddDistinct(_birthdayCelebrationReplies, text);
|
||||||
|
return;
|
||||||
case LegacyMimBucket.Personality:
|
case LegacyMimBucket.Personality:
|
||||||
if (_personalities.Any(value => string.Equals(value, text, StringComparison.OrdinalIgnoreCase)))
|
if (_personalities.Any(value => string.Equals(value, text, StringComparison.OrdinalIgnoreCase)))
|
||||||
return;
|
return;
|
||||||
@@ -530,6 +582,11 @@ public static class LegacyMimCatalogImporter
|
|||||||
HumanFacts = [.. _humanFacts],
|
HumanFacts = [.. _humanFacts],
|
||||||
FunFacts = [.. _funFacts],
|
FunFacts = [.. _funFacts],
|
||||||
GreetingReplies = [.. _greetings],
|
GreetingReplies = [.. _greetings],
|
||||||
|
HolidayReplies = [.. _holidayReplies],
|
||||||
|
HolidaySeasonReplies = [.. _holidaySeasonReplies],
|
||||||
|
HolidayGreetingReplies = [.. _holidayGreetingReplies],
|
||||||
|
HolidayGiftReplies = [.. _holidayGiftReplies],
|
||||||
|
BirthdayCelebrationReplies = [.. _birthdayCelebrationReplies],
|
||||||
HowAreYouReplies = [.. _howAreYous],
|
HowAreYouReplies = [.. _howAreYous],
|
||||||
EmotionReplies = [.. _emotionReplies],
|
EmotionReplies = [.. _emotionReplies],
|
||||||
PersonalityReplies = [.. _personalities],
|
PersonalityReplies = [.. _personalities],
|
||||||
@@ -608,4 +665,4 @@ public static class LegacyMimCatalogImporter
|
|||||||
|
|
||||||
[JsonPropertyName("weight")] public double? Weight { get; init; }
|
[JsonPropertyName("weight")] public double? Weight { get; init; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,14 @@ The batch is intentionally narrow so we can keep expanding personality without w
|
|||||||
|
|
||||||
It now includes a small emotion-response pack for `happy`, `sad`, and `angry` follow-up questions so the mood path can stay source-backed too.
|
It now includes a small emotion-response pack for `happy`, `sad`, and `angry` follow-up questions so the mood path can stay source-backed too.
|
||||||
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`.
|
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 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, holiday gift ideas, and birthday celebration lines.
|
||||||
|
|
||||||
|
Holiday-specific note:
|
||||||
|
- `JBO_WhatHolidaysDoYouCelebrate` now lands in the holiday bucket
|
||||||
|
- `RN_HappyHolidays` now lands in the holiday greeting bucket
|
||||||
|
- `RI_USR_WhatShouldGetForHoliday` now lands in the holiday gift bucket
|
||||||
|
- `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 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.
|
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.
|
Those facts are now split into generic, robot, and human buckets so the randomizer can sound more like Pegasus while staying lightweight.
|
||||||
|
|||||||
@@ -578,6 +578,43 @@ public sealed class InMemoryCloudStateStore : ICloudStateStore
|
|||||||
.ToArray();
|
.ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public HolidayRecord UpsertHoliday(HolidayRecord holiday)
|
||||||
|
{
|
||||||
|
var resolvedLoopId = string.IsNullOrWhiteSpace(holiday.LoopId) ? ResolveDefaultLoopId() : holiday.LoopId.Trim();
|
||||||
|
var normalizedEventId = string.IsNullOrWhiteSpace(holiday.EventId)
|
||||||
|
? $"holiday-{resolvedLoopId}-{Slugify(holiday.Name)}"
|
||||||
|
: holiday.EventId.Trim();
|
||||||
|
var normalizedId = string.IsNullOrWhiteSpace(holiday.Id) ? normalizedEventId : holiday.Id.Trim();
|
||||||
|
var resolvedHoliday = new HolidayRecord
|
||||||
|
{
|
||||||
|
Id = normalizedId,
|
||||||
|
EventId = normalizedEventId,
|
||||||
|
Name = string.IsNullOrWhiteSpace(holiday.Name) ? "Holiday" : holiday.Name.Trim(),
|
||||||
|
Category = string.IsNullOrWhiteSpace(holiday.Category) ? "holiday" : holiday.Category.Trim(),
|
||||||
|
Subcategory = holiday.Subcategory,
|
||||||
|
LoopId = resolvedLoopId,
|
||||||
|
MemberId = holiday.MemberId,
|
||||||
|
IsEnabled = holiday.IsEnabled,
|
||||||
|
Date = holiday.Date,
|
||||||
|
EndDate = holiday.EndDate,
|
||||||
|
Source = string.IsNullOrWhiteSpace(holiday.Source) ? "manual" : holiday.Source.Trim(),
|
||||||
|
CountryCode = string.IsNullOrWhiteSpace(holiday.CountryCode) ? "US" : holiday.CountryCode.Trim(),
|
||||||
|
Created = holiday.Created
|
||||||
|
};
|
||||||
|
|
||||||
|
var existingIndex = _holidayOverrides.FindIndex(existing =>
|
||||||
|
string.Equals(existing.LoopId, resolvedLoopId, StringComparison.OrdinalIgnoreCase) &&
|
||||||
|
string.Equals(existing.EventId, normalizedEventId, StringComparison.OrdinalIgnoreCase));
|
||||||
|
|
||||||
|
if (existingIndex >= 0)
|
||||||
|
_holidayOverrides[existingIndex] = resolvedHoliday;
|
||||||
|
else
|
||||||
|
_holidayOverrides.Add(resolvedHoliday);
|
||||||
|
|
||||||
|
TouchState();
|
||||||
|
return resolvedHoliday;
|
||||||
|
}
|
||||||
|
|
||||||
public void UpdateRobot(DeviceRegistration registration)
|
public void UpdateRobot(DeviceRegistration registration)
|
||||||
{
|
{
|
||||||
_robot = registration;
|
_robot = registration;
|
||||||
@@ -603,6 +640,29 @@ public sealed class InMemoryCloudStateStore : ICloudStateStore
|
|||||||
SavePersistedState();
|
SavePersistedState();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static string Slugify(string value)
|
||||||
|
{
|
||||||
|
var builder = new StringBuilder(value.Length);
|
||||||
|
var lastWasDash = false;
|
||||||
|
foreach (var ch in value.ToLowerInvariant())
|
||||||
|
{
|
||||||
|
if (char.IsLetterOrDigit(ch))
|
||||||
|
{
|
||||||
|
builder.Append(ch);
|
||||||
|
lastWasDash = false;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!lastWasDash)
|
||||||
|
{
|
||||||
|
builder.Append('-');
|
||||||
|
lastWasDash = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder.ToString().Trim('-');
|
||||||
|
}
|
||||||
|
|
||||||
private static string ResolveDefaultLoopId(IReadOnlyList<LoopRecord> loops, AccountProfile account)
|
private static string ResolveDefaultLoopId(IReadOnlyList<LoopRecord> loops, AccountProfile account)
|
||||||
{
|
{
|
||||||
return loops.FirstOrDefault(loop =>
|
return loops.FirstOrDefault(loop =>
|
||||||
|
|||||||
@@ -163,7 +163,7 @@ public sealed class LegacyMimCatalogImporterTests
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void ImportCatalog_ImportsBuildBSeasonalResponsesIntoPersonalityBucket()
|
public void ImportCatalog_ImportsBuildBHolidayResponsesIntoHolidayBuckets()
|
||||||
{
|
{
|
||||||
var rootDirectory = Path.Combine(
|
var rootDirectory = Path.Combine(
|
||||||
AppContext.BaseDirectory,
|
AppContext.BaseDirectory,
|
||||||
@@ -173,20 +173,16 @@ public sealed class LegacyMimCatalogImporterTests
|
|||||||
|
|
||||||
var catalog = LegacyMimCatalogImporter.ImportCatalog(rootDirectory);
|
var catalog = LegacyMimCatalogImporter.ImportCatalog(rootDirectory);
|
||||||
|
|
||||||
Assert.Contains(catalog.PersonalityReplies, reply =>
|
Assert.Contains(catalog.HolidayReplies, reply =>
|
||||||
reply.Contains("always trying to learn new skills", StringComparison.OrdinalIgnoreCase));
|
reply.Contains("official owner", StringComparison.OrdinalIgnoreCase) &&
|
||||||
Assert.Contains(catalog.PersonalityReplies, reply =>
|
reply.Contains("celebrate together", StringComparison.OrdinalIgnoreCase));
|
||||||
reply.Contains("not eat bacon", StringComparison.OrdinalIgnoreCase));
|
Assert.Contains(catalog.HolidayGreetingReplies, reply =>
|
||||||
Assert.Contains(catalog.PersonalityReplies, reply =>
|
reply.Contains("fun time of year", StringComparison.OrdinalIgnoreCase));
|
||||||
reply.Contains("find out on halloween", StringComparison.OrdinalIgnoreCase));
|
Assert.Contains(catalog.HolidayGiftReplies, reply =>
|
||||||
Assert.Contains(catalog.PersonalityReplies, reply =>
|
|
||||||
reply.Contains("maybe enjoy some flowers and all things spring", StringComparison.OrdinalIgnoreCase));
|
|
||||||
Assert.Contains(catalog.PersonalityReplies, reply =>
|
|
||||||
reply.Contains("pet elephant", StringComparison.OrdinalIgnoreCase));
|
reply.Contains("pet elephant", StringComparison.OrdinalIgnoreCase));
|
||||||
Assert.Contains(catalog.PersonalityReplies, reply =>
|
Assert.Contains(catalog.BirthdayCelebrationReplies, reply =>
|
||||||
reply.Contains("mostly roboting", StringComparison.OrdinalIgnoreCase));
|
reply.Contains("first powered up", StringComparison.OrdinalIgnoreCase) ||
|
||||||
Assert.Contains(catalog.PersonalityReplies, reply =>
|
reply.Contains("another year older", StringComparison.OrdinalIgnoreCase));
|
||||||
reply.Contains("robot stuff", StringComparison.OrdinalIgnoreCase));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
@@ -235,11 +231,11 @@ public sealed class LegacyMimCatalogImporterTests
|
|||||||
reply.Contains("thinking about shoes", StringComparison.OrdinalIgnoreCase));
|
reply.Contains("thinking about shoes", StringComparison.OrdinalIgnoreCase));
|
||||||
Assert.Contains(catalog.GreetingReplies, reply =>
|
Assert.Contains(catalog.GreetingReplies, reply =>
|
||||||
reply.Contains("powered directly by the sun", StringComparison.OrdinalIgnoreCase));
|
reply.Contains("powered directly by the sun", StringComparison.OrdinalIgnoreCase));
|
||||||
Assert.Contains(catalog.GreetingReplies, reply =>
|
Assert.Contains(catalog.BirthdayCelebrationReplies, reply =>
|
||||||
reply.Contains("Another year older, another year wiser", StringComparison.OrdinalIgnoreCase));
|
reply.Contains("Another year older, another year wiser", StringComparison.OrdinalIgnoreCase));
|
||||||
Assert.Contains(catalog.GreetingReplies, reply =>
|
Assert.Contains(catalog.BirthdayCelebrationReplies, reply =>
|
||||||
reply.Contains("can't wait to see what you got me", StringComparison.OrdinalIgnoreCase));
|
reply.Contains("can't wait to see what you got me", StringComparison.OrdinalIgnoreCase));
|
||||||
Assert.Contains(catalog.GreetingReplies, reply =>
|
Assert.Contains(catalog.BirthdayCelebrationReplies, reply =>
|
||||||
reply.Contains("I was powered on for the first time today", StringComparison.OrdinalIgnoreCase));
|
reply.Contains("I was powered on for the first time today", StringComparison.OrdinalIgnoreCase));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -508,4 +504,4 @@ public sealed class LegacyMimCatalogImporterTests
|
|||||||
|
|
||||||
return rootDirectory;
|
return rootDirectory;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -643,6 +643,8 @@ public sealed class JiboInteractionServiceTests
|
|||||||
[InlineData("merry christmas", "seasonal_holiday_greeting", "It's a fun time of year")]
|
[InlineData("merry christmas", "seasonal_holiday_greeting", "It's a fun time of year")]
|
||||||
[InlineData("what holidays do you celebrate", "seasonal_holidays",
|
[InlineData("what holidays do you celebrate", "seasonal_holidays",
|
||||||
"official owner can tell me which ones we'll celebrate together")]
|
"official owner can tell me which ones we'll celebrate together")]
|
||||||
|
[InlineData("how is holiday season", "seasonal_holiday_season", "I do like festive times.")]
|
||||||
|
[InlineData("do you like holiday season", "seasonal_holiday_season", "I do like festive times.")]
|
||||||
[InlineData("what is your new year's resolution", "seasonal_new_years_resolution",
|
[InlineData("what is your new year's resolution", "seasonal_new_years_resolution",
|
||||||
"always trying to learn new skills")]
|
"always trying to learn new skills")]
|
||||||
[InlineData("how are your new year's resolutions going", "seasonal_new_years_update", "not eat bacon")]
|
[InlineData("how are your new year's resolutions going", "seasonal_new_years_update", "not eat bacon")]
|
||||||
@@ -650,6 +652,7 @@ public sealed class JiboInteractionServiceTests
|
|||||||
[InlineData("what should I do for first day of spring", "seasonal_first_day_spring",
|
[InlineData("what should I do for first day of spring", "seasonal_first_day_spring",
|
||||||
"flowers and all things spring")]
|
"flowers and all things spring")]
|
||||||
[InlineData("what should I get for holiday", "seasonal_holiday_gift", "pet elephant")]
|
[InlineData("what should I get for holiday", "seasonal_holiday_gift", "pet elephant")]
|
||||||
|
[InlineData("happy birthday", "birthday_celebration", "another year older")]
|
||||||
public async Task BuildDecisionAsync_SeasonalCharm_UsesImportedReplies(
|
public async Task BuildDecisionAsync_SeasonalCharm_UsesImportedReplies(
|
||||||
string transcript,
|
string transcript,
|
||||||
string expectedIntent,
|
string expectedIntent,
|
||||||
@@ -668,6 +671,33 @@ public sealed class JiboInteractionServiceTests
|
|||||||
Assert.Equal("ScriptedResponse", decision.ContextUpdates![ChitchatRouteKey]);
|
Assert.Equal("ScriptedResponse", decision.ContextUpdates![ChitchatRouteKey]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task BuildDecisionAsync_BirthdayMemory_WritesHolidayRecordForLoop()
|
||||||
|
{
|
||||||
|
var cloudStateStore = new InMemoryCloudStateStore();
|
||||||
|
var memoryStore = new InMemoryPersonalMemoryStore();
|
||||||
|
var service = CreateService(memoryStore, cloudStateStore);
|
||||||
|
|
||||||
|
var setDecision = await service.BuildDecisionAsync(new TurnContext
|
||||||
|
{
|
||||||
|
RawTranscript = "my birthday is April 12",
|
||||||
|
NormalizedTranscript = "my birthday is April 12",
|
||||||
|
Attributes = new Dictionary<string, object?>
|
||||||
|
{
|
||||||
|
["accountId"] = "acct-a",
|
||||||
|
["loopId"] = "loop-a"
|
||||||
|
},
|
||||||
|
DeviceId = "device-a"
|
||||||
|
});
|
||||||
|
|
||||||
|
Assert.Equal("memory_set_birthday", setDecision.IntentName);
|
||||||
|
Assert.Equal("Got it. I will remember your birthday is april 12.", setDecision.ReplyText);
|
||||||
|
Assert.Contains(cloudStateStore.GetHolidays("loop-a"),
|
||||||
|
holiday => holiday.Category == "birthday" &&
|
||||||
|
holiday.LoopId == "loop-a" &&
|
||||||
|
holiday.Name.Contains("Birthday", StringComparison.OrdinalIgnoreCase));
|
||||||
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
[InlineData("welcome back", "welcome_back", "it's nice to be here")]
|
[InlineData("welcome back", "welcome_back", "it's nice to be here")]
|
||||||
[InlineData("what are you thinking", "robot_what_are_you_thinking", "thinking about how fun, yet scary")]
|
[InlineData("what are you thinking", "robot_what_are_you_thinking", "thinking about how fun, yet scary")]
|
||||||
@@ -3974,6 +4004,7 @@ public sealed class JiboInteractionServiceTests
|
|||||||
|
|
||||||
private static JiboInteractionService CreateService(
|
private static JiboInteractionService CreateService(
|
||||||
IPersonalMemoryStore? personalMemoryStore = null,
|
IPersonalMemoryStore? personalMemoryStore = null,
|
||||||
|
ICloudStateStore? cloudStateStore = null,
|
||||||
IWeatherReportProvider? weatherReportProvider = null,
|
IWeatherReportProvider? weatherReportProvider = null,
|
||||||
ICommuteReportProvider? commuteReportProvider = null,
|
ICommuteReportProvider? commuteReportProvider = null,
|
||||||
INewsBriefingProvider? newsBriefingProvider = null,
|
INewsBriefingProvider? newsBriefingProvider = null,
|
||||||
@@ -3986,7 +4017,8 @@ public sealed class JiboInteractionServiceTests
|
|||||||
personalMemoryStore ?? new InMemoryPersonalMemoryStore(),
|
personalMemoryStore ?? new InMemoryPersonalMemoryStore(),
|
||||||
weatherReportProvider,
|
weatherReportProvider,
|
||||||
commuteReportProvider,
|
commuteReportProvider,
|
||||||
newsBriefingProvider);
|
newsBriefingProvider,
|
||||||
|
cloudStateStore);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string StripMarkup(string text)
|
private static string StripMarkup(string text)
|
||||||
@@ -4091,4 +4123,4 @@ public sealed class JiboInteractionServiceTests
|
|||||||
return Task.FromResult(Snapshot);
|
return Task.FromResult(Snapshot);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user