Add holiday seasonal routing and calendar report seam
This commit is contained in:
@@ -0,0 +1,14 @@
|
|||||||
|
using Jibo.Runtime.Abstractions;
|
||||||
|
|
||||||
|
namespace Jibo.Cloud.Application.Abstractions;
|
||||||
|
|
||||||
|
public interface ICalendarReportProvider
|
||||||
|
{
|
||||||
|
Task<CalendarReportSnapshot?> GetReportAsync(TurnContext turn, CancellationToken cancellationToken = default);
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed record CalendarReportSnapshot(
|
||||||
|
IReadOnlyList<string> EventSummaries,
|
||||||
|
IReadOnlyList<string> EventTimesOnAt,
|
||||||
|
IReadOnlyList<string> TomorrowEventSummaries,
|
||||||
|
bool HasServiceError = false);
|
||||||
@@ -12,6 +12,7 @@ public sealed class JiboInteractionService(
|
|||||||
IJiboRandomizer randomizer,
|
IJiboRandomizer randomizer,
|
||||||
IPersonalMemoryStore personalMemoryStore,
|
IPersonalMemoryStore personalMemoryStore,
|
||||||
IWeatherReportProvider? weatherReportProvider = null,
|
IWeatherReportProvider? weatherReportProvider = null,
|
||||||
|
ICalendarReportProvider? calendarReportProvider = null,
|
||||||
ICommuteReportProvider? commuteReportProvider = null,
|
ICommuteReportProvider? commuteReportProvider = null,
|
||||||
INewsBriefingProvider? newsBriefingProvider = null,
|
INewsBriefingProvider? newsBriefingProvider = null,
|
||||||
ICloudStateStore? cloudStateStore = null)
|
ICloudStateStore? cloudStateStore = null)
|
||||||
@@ -485,6 +486,7 @@ public sealed class JiboInteractionService(
|
|||||||
randomizer,
|
randomizer,
|
||||||
personalMemoryStore,
|
personalMemoryStore,
|
||||||
BuildWeatherReportDecisionAsync,
|
BuildWeatherReportDecisionAsync,
|
||||||
|
BuildCalendarReportDecisionAsync,
|
||||||
BuildCommuteReportDecisionAsync,
|
BuildCommuteReportDecisionAsync,
|
||||||
turnContext => ResolveTenantScope(turnContext),
|
turnContext => ResolveTenantScope(turnContext),
|
||||||
cancellationToken);
|
cancellationToken);
|
||||||
@@ -655,6 +657,54 @@ public sealed class JiboInteractionService(
|
|||||||
"holiday season",
|
"holiday season",
|
||||||
"festive",
|
"festive",
|
||||||
"celebrate"),
|
"celebrate"),
|
||||||
|
"seasonal_thanksgiving" => BuildScriptedHolidayDecision(
|
||||||
|
catalog.HolidaySeasonReplies,
|
||||||
|
"seasonal_thanksgiving",
|
||||||
|
"thanksgiving",
|
||||||
|
"turkey",
|
||||||
|
"stuffed"),
|
||||||
|
"seasonal_christmas" => BuildScriptedHolidayDecision(
|
||||||
|
catalog.HolidaySeasonReplies,
|
||||||
|
"seasonal_christmas",
|
||||||
|
"christmas",
|
||||||
|
"quality time",
|
||||||
|
"socks"),
|
||||||
|
"seasonal_hanukkah" => BuildScriptedHolidayDecision(
|
||||||
|
catalog.HolidaySeasonReplies,
|
||||||
|
"seasonal_hanukkah",
|
||||||
|
"hanukkah",
|
||||||
|
"dreidel",
|
||||||
|
"gift"),
|
||||||
|
"seasonal_passover" => BuildScriptedHolidayDecision(
|
||||||
|
catalog.HolidaySeasonReplies,
|
||||||
|
"seasonal_passover",
|
||||||
|
"passover",
|
||||||
|
"matzah",
|
||||||
|
"next one"),
|
||||||
|
"seasonal_new_years" => BuildScriptedHolidayDecision(
|
||||||
|
catalog.HolidaySeasonReplies,
|
||||||
|
"seasonal_new_years",
|
||||||
|
"new year",
|
||||||
|
"resolutions",
|
||||||
|
"party"),
|
||||||
|
"seasonal_valentines_day" => BuildScriptedHolidayDecision(
|
||||||
|
catalog.HolidaySeasonReplies,
|
||||||
|
"seasonal_valentines_day",
|
||||||
|
"valentine",
|
||||||
|
"heart",
|
||||||
|
"flowers"),
|
||||||
|
"seasonal_kwanzaa" => BuildScriptedHolidayDecision(
|
||||||
|
catalog.HolidaySeasonReplies,
|
||||||
|
"seasonal_kwanzaa",
|
||||||
|
"kwanzaa",
|
||||||
|
"gift",
|
||||||
|
"celebrate"),
|
||||||
|
"seasonal_easter" => BuildScriptedHolidayDecision(
|
||||||
|
catalog.HolidaySeasonReplies,
|
||||||
|
"seasonal_easter",
|
||||||
|
"easter",
|
||||||
|
"bunny",
|
||||||
|
"egg"),
|
||||||
"seasonal_new_years_resolution" => BuildScriptedPersonalityDecision(
|
"seasonal_new_years_resolution" => BuildScriptedPersonalityDecision(
|
||||||
catalog,
|
catalog,
|
||||||
"seasonal_new_years_resolution",
|
"seasonal_new_years_resolution",
|
||||||
@@ -708,8 +758,8 @@ public sealed class JiboInteractionService(
|
|||||||
"really like times of giving and receiving",
|
"really like times of giving and receiving",
|
||||||
"long way away",
|
"long way away",
|
||||||
"looking forward to christmas"),
|
"looking forward to christmas"),
|
||||||
"seasonal_plans_for_christmas" => BuildScriptedPersonalityDecision(
|
"seasonal_plans_for_christmas" => BuildScriptedHolidayDecision(
|
||||||
catalog,
|
catalog.HolidaySeasonReplies,
|
||||||
"seasonal_plans_for_christmas",
|
"seasonal_plans_for_christmas",
|
||||||
"christmas sweaters",
|
"christmas sweaters",
|
||||||
"wear one of my",
|
"wear one of my",
|
||||||
@@ -1581,6 +1631,37 @@ public sealed class JiboInteractionService(
|
|||||||
BuildCommuteSpokenReply(snapshot, catalog));
|
BuildCommuteSpokenReply(snapshot, catalog));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task<JiboInteractionDecision> BuildCalendarReportDecisionAsync(
|
||||||
|
TurnContext turn,
|
||||||
|
CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
var catalog = await contentCache.GetCatalogAsync(cancellationToken);
|
||||||
|
|
||||||
|
if (calendarReportProvider is null)
|
||||||
|
return new JiboInteractionDecision(
|
||||||
|
"calendar",
|
||||||
|
ChooseCalendarServiceDownReply(catalog));
|
||||||
|
|
||||||
|
CalendarReportSnapshot? snapshot;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
snapshot = await calendarReportProvider.GetReportAsync(turn, cancellationToken);
|
||||||
|
}
|
||||||
|
catch (Exception) when (!cancellationToken.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
snapshot = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (snapshot is null)
|
||||||
|
return new JiboInteractionDecision(
|
||||||
|
"calendar",
|
||||||
|
ChooseCalendarServiceDownReply(catalog));
|
||||||
|
|
||||||
|
return new JiboInteractionDecision(
|
||||||
|
"calendar",
|
||||||
|
BuildCalendarSpokenReply(snapshot, catalog));
|
||||||
|
}
|
||||||
|
|
||||||
private static string BuildWeatherSpokenReply(
|
private static string BuildWeatherSpokenReply(
|
||||||
WeatherReportSnapshot snapshot,
|
WeatherReportSnapshot snapshot,
|
||||||
WeatherDateEntity weatherDate,
|
WeatherDateEntity weatherDate,
|
||||||
@@ -2074,6 +2155,91 @@ public sealed class JiboInteractionService(
|
|||||||
return template.Trim();
|
return template.Trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private string BuildCalendarSpokenReply(CalendarReportSnapshot snapshot, JiboExperienceCatalog catalog)
|
||||||
|
{
|
||||||
|
if (snapshot.EventSummaries.Count > 0 && snapshot.EventTimesOnAt.Count > 0)
|
||||||
|
{
|
||||||
|
var summary = snapshot.EventSummaries[0];
|
||||||
|
var time = snapshot.EventTimesOnAt[0];
|
||||||
|
var template = ChooseCalendarTemplate(
|
||||||
|
catalog.CalendarReplies,
|
||||||
|
"calendar summary",
|
||||||
|
"Your calendar says ${skill.calendar.eventSummaries.shift()}, ${skill.calendar.eventTimesOnAt.shift()}.");
|
||||||
|
if (template.Contains("${skill.calendar.eventSummaries.shift()}", StringComparison.OrdinalIgnoreCase) ||
|
||||||
|
template.Contains("${skill.calendar.eventTimesOnAt.shift()}", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
return template
|
||||||
|
.Replace("${skill.calendar.eventSummaries.shift()}", summary, StringComparison.OrdinalIgnoreCase)
|
||||||
|
.Replace("${skill.calendar.eventTimesOnAt.shift()}", time, StringComparison.OrdinalIgnoreCase)
|
||||||
|
.Replace("${speaker}", string.Empty, StringComparison.OrdinalIgnoreCase)
|
||||||
|
.Replace(" ", " ", StringComparison.Ordinal)
|
||||||
|
.Trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $"Your calendar says {summary}, {time}.";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (snapshot.TomorrowEventSummaries.Count > 0)
|
||||||
|
{
|
||||||
|
var template = ChooseCalendarTemplate(
|
||||||
|
catalog.CalendarReplies,
|
||||||
|
"calendar tomorrow",
|
||||||
|
"Looking at your calendar, there's nothing scheduled for the rest of the day today. Here's what's going on tomorrow.");
|
||||||
|
if (template.Contains("tomorrow", StringComparison.OrdinalIgnoreCase))
|
||||||
|
return template
|
||||||
|
.Replace("${speaker}", string.Empty, StringComparison.OrdinalIgnoreCase)
|
||||||
|
.Replace(" ", " ", StringComparison.Ordinal)
|
||||||
|
.Trim();
|
||||||
|
|
||||||
|
return $"Looking at your calendar, there's nothing scheduled for the rest of the day today. Here's what's going on tomorrow: {snapshot.TomorrowEventSummaries[0]}.";
|
||||||
|
}
|
||||||
|
|
||||||
|
return ChooseCalendarNothingReply(catalog);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string ChooseCalendarTemplate(
|
||||||
|
IReadOnlyList<string> templates,
|
||||||
|
string mode,
|
||||||
|
string fallback)
|
||||||
|
{
|
||||||
|
if (templates.Count == 0) return fallback;
|
||||||
|
|
||||||
|
var loweredMode = mode.Trim().ToLowerInvariant();
|
||||||
|
var filtered = templates.Where(template =>
|
||||||
|
{
|
||||||
|
var lowered = template.ToLowerInvariant();
|
||||||
|
return loweredMode switch
|
||||||
|
{
|
||||||
|
"calendar summary" => lowered.Contains("event", StringComparison.OrdinalIgnoreCase) ||
|
||||||
|
lowered.Contains("summary", StringComparison.OrdinalIgnoreCase),
|
||||||
|
"calendar tomorrow" => lowered.Contains("tomorrow", StringComparison.OrdinalIgnoreCase),
|
||||||
|
_ => true
|
||||||
|
};
|
||||||
|
}).ToList();
|
||||||
|
|
||||||
|
var selected = filtered.Count > 0
|
||||||
|
? filtered.OrderBy(static template => template.Length).First()
|
||||||
|
: templates.OrderBy(static template => template.Length).FirstOrDefault();
|
||||||
|
|
||||||
|
return string.IsNullOrWhiteSpace(selected) ? fallback : selected!;
|
||||||
|
}
|
||||||
|
|
||||||
|
private string ChooseCalendarNothingReply(JiboExperienceCatalog catalog)
|
||||||
|
{
|
||||||
|
return catalog.CalendarNothingTodayReplies.Count > 0
|
||||||
|
? randomizer.Choose(catalog.CalendarNothingTodayReplies)
|
||||||
|
: catalog.CalendarNothingReplies.Count > 0
|
||||||
|
? randomizer.Choose(catalog.CalendarNothingReplies)
|
||||||
|
: "Looking at your calendar, I don't see anything scheduled today.";
|
||||||
|
}
|
||||||
|
|
||||||
|
private string ChooseCalendarServiceDownReply(JiboExperienceCatalog catalog)
|
||||||
|
{
|
||||||
|
return catalog.CalendarServiceDownReplies.Count > 0
|
||||||
|
? randomizer.Choose(catalog.CalendarServiceDownReplies)
|
||||||
|
: "Looks like I can't access calendars right now. Sorry.";
|
||||||
|
}
|
||||||
|
|
||||||
private static string EscapeForEsml(string value)
|
private static string EscapeForEsml(string value)
|
||||||
{
|
{
|
||||||
return value
|
return value
|
||||||
@@ -3052,13 +3218,6 @@ public sealed class JiboInteractionService(
|
|||||||
"are you excited for christmas"))
|
"are you excited for christmas"))
|
||||||
return "seasonal_looks_forward_to_christmas";
|
return "seasonal_looks_forward_to_christmas";
|
||||||
|
|
||||||
if (MatchesAny(
|
|
||||||
loweredTranscript,
|
|
||||||
"what are you doing for christmas",
|
|
||||||
"what are your plans for christmas",
|
|
||||||
"what do you plan to do for christmas"))
|
|
||||||
return "seasonal_plans_for_christmas";
|
|
||||||
|
|
||||||
if (MatchesAny(
|
if (MatchesAny(
|
||||||
loweredTranscript,
|
loweredTranscript,
|
||||||
"what are you thankful for",
|
"what are you thankful for",
|
||||||
@@ -3076,6 +3235,13 @@ public sealed class JiboInteractionService(
|
|||||||
"what's your favourite thing to do"))
|
"what's your favourite thing to do"))
|
||||||
return "robot_what_do_you_like_to_do";
|
return "robot_what_do_you_like_to_do";
|
||||||
|
|
||||||
|
if (MatchesAny(
|
||||||
|
loweredTranscript,
|
||||||
|
"what are you doing for christmas",
|
||||||
|
"what are your plans for christmas",
|
||||||
|
"what do you plan to do for christmas"))
|
||||||
|
return "seasonal_plans_for_christmas";
|
||||||
|
|
||||||
if (MatchesAny(
|
if (MatchesAny(
|
||||||
loweredTranscript,
|
loweredTranscript,
|
||||||
"what is your favorite flower",
|
"what is your favorite flower",
|
||||||
@@ -3241,6 +3407,71 @@ public sealed class JiboInteractionService(
|
|||||||
"what is holiday season like"))
|
"what is holiday season like"))
|
||||||
return "seasonal_holiday_season";
|
return "seasonal_holiday_season";
|
||||||
|
|
||||||
|
if (MatchesAny(
|
||||||
|
loweredTranscript,
|
||||||
|
"how is thanksgiving",
|
||||||
|
"how's thanksgiving",
|
||||||
|
"do you like thanksgiving",
|
||||||
|
"what do you think of thanksgiving"))
|
||||||
|
return "seasonal_thanksgiving";
|
||||||
|
|
||||||
|
if (MatchesAny(
|
||||||
|
loweredTranscript,
|
||||||
|
"how is christmas",
|
||||||
|
"how's christmas",
|
||||||
|
"do you like christmas",
|
||||||
|
"what do you think of christmas"))
|
||||||
|
return "seasonal_christmas";
|
||||||
|
|
||||||
|
if (MatchesAny(
|
||||||
|
loweredTranscript,
|
||||||
|
"how is hanukkah",
|
||||||
|
"how's hanukkah",
|
||||||
|
"do you like hanukkah",
|
||||||
|
"what do you think of hanukkah"))
|
||||||
|
return "seasonal_hanukkah";
|
||||||
|
|
||||||
|
if (MatchesAny(
|
||||||
|
loweredTranscript,
|
||||||
|
"how is passover",
|
||||||
|
"how's passover",
|
||||||
|
"do you like passover",
|
||||||
|
"what do you think of passover"))
|
||||||
|
return "seasonal_passover";
|
||||||
|
|
||||||
|
if (MatchesAny(
|
||||||
|
loweredTranscript,
|
||||||
|
"how is new years",
|
||||||
|
"how's new years",
|
||||||
|
"how is new year s",
|
||||||
|
"do you like new years",
|
||||||
|
"what do you think of new years"))
|
||||||
|
return "seasonal_new_years";
|
||||||
|
|
||||||
|
if (MatchesAny(
|
||||||
|
loweredTranscript,
|
||||||
|
"how is valentines day",
|
||||||
|
"how's valentines day",
|
||||||
|
"do you like valentines day",
|
||||||
|
"what do you think of valentines day"))
|
||||||
|
return "seasonal_valentines_day";
|
||||||
|
|
||||||
|
if (MatchesAny(
|
||||||
|
loweredTranscript,
|
||||||
|
"how is kwanzaa",
|
||||||
|
"how's kwanzaa",
|
||||||
|
"do you like kwanzaa",
|
||||||
|
"what do you think of kwanzaa"))
|
||||||
|
return "seasonal_kwanzaa";
|
||||||
|
|
||||||
|
if (MatchesAny(
|
||||||
|
loweredTranscript,
|
||||||
|
"how is easter",
|
||||||
|
"how's easter",
|
||||||
|
"do you like easter",
|
||||||
|
"what do you think of easter"))
|
||||||
|
return "seasonal_easter";
|
||||||
|
|
||||||
if (MatchesAny(
|
if (MatchesAny(
|
||||||
loweredTranscript,
|
loweredTranscript,
|
||||||
"what is your new years resolution",
|
"what is your new years resolution",
|
||||||
|
|||||||
@@ -70,6 +70,7 @@ internal static class PersonalReportOrchestrator
|
|||||||
IJiboRandomizer randomizer,
|
IJiboRandomizer randomizer,
|
||||||
IPersonalMemoryStore personalMemoryStore,
|
IPersonalMemoryStore personalMemoryStore,
|
||||||
Func<TurnContext, string, CancellationToken, Task<JiboInteractionDecision>> buildWeatherDecisionAsync,
|
Func<TurnContext, string, CancellationToken, Task<JiboInteractionDecision>> buildWeatherDecisionAsync,
|
||||||
|
Func<TurnContext, CancellationToken, Task<JiboInteractionDecision>> buildCalendarDecisionAsync,
|
||||||
Func<TurnContext, CancellationToken, Task<JiboInteractionDecision>> buildCommuteDecisionAsync,
|
Func<TurnContext, CancellationToken, Task<JiboInteractionDecision>> buildCommuteDecisionAsync,
|
||||||
Func<TurnContext, PersonalMemoryTenantScope> tenantScopeResolver,
|
Func<TurnContext, PersonalMemoryTenantScope> tenantScopeResolver,
|
||||||
CancellationToken cancellationToken)
|
CancellationToken cancellationToken)
|
||||||
@@ -192,6 +193,7 @@ internal static class PersonalReportOrchestrator
|
|||||||
toggles,
|
toggles,
|
||||||
currentName,
|
currentName,
|
||||||
buildWeatherDecisionAsync,
|
buildWeatherDecisionAsync,
|
||||||
|
buildCalendarDecisionAsync,
|
||||||
buildCommuteDecisionAsync,
|
buildCommuteDecisionAsync,
|
||||||
cancellationToken);
|
cancellationToken);
|
||||||
|
|
||||||
@@ -237,6 +239,7 @@ internal static class PersonalReportOrchestrator
|
|||||||
toggles,
|
toggles,
|
||||||
parsedName,
|
parsedName,
|
||||||
buildWeatherDecisionAsync,
|
buildWeatherDecisionAsync,
|
||||||
|
buildCalendarDecisionAsync,
|
||||||
buildCommuteDecisionAsync,
|
buildCommuteDecisionAsync,
|
||||||
cancellationToken);
|
cancellationToken);
|
||||||
}
|
}
|
||||||
@@ -253,6 +256,7 @@ internal static class PersonalReportOrchestrator
|
|||||||
PersonalReportServiceToggles toggles,
|
PersonalReportServiceToggles toggles,
|
||||||
string userName,
|
string userName,
|
||||||
Func<TurnContext, string, CancellationToken, Task<JiboInteractionDecision>> buildWeatherDecisionAsync,
|
Func<TurnContext, string, CancellationToken, Task<JiboInteractionDecision>> buildWeatherDecisionAsync,
|
||||||
|
Func<TurnContext, CancellationToken, Task<JiboInteractionDecision>> buildCalendarDecisionAsync,
|
||||||
Func<TurnContext, CancellationToken, Task<JiboInteractionDecision>> buildCommuteDecisionAsync,
|
Func<TurnContext, CancellationToken, Task<JiboInteractionDecision>> buildCommuteDecisionAsync,
|
||||||
CancellationToken cancellationToken)
|
CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
@@ -275,26 +279,7 @@ internal static class PersonalReportOrchestrator
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (toggles.CalendarEnabled)
|
if (toggles.CalendarEnabled)
|
||||||
{
|
reportSections.Add((await buildCalendarDecisionAsync(turn, cancellationToken)).ReplyText);
|
||||||
var calendarSummary = ChooseReportSkillTemplate(
|
|
||||||
catalog.CalendarNothingTodayReplies,
|
|
||||||
catalog.CalendarNothingReplies,
|
|
||||||
string.Empty);
|
|
||||||
if (string.IsNullOrWhiteSpace(calendarSummary))
|
|
||||||
calendarSummary = ChooseReportSkillTemplate(
|
|
||||||
catalog.CalendarServiceDownReplies,
|
|
||||||
[],
|
|
||||||
"Looking at your calendar, I don't see anything scheduled today.");
|
|
||||||
|
|
||||||
reportSections.Add(RenderReportSkillTemplate(calendarSummary, userName));
|
|
||||||
reportSections.Add(
|
|
||||||
RenderReportSkillTemplate(
|
|
||||||
ChooseReportSkillTemplate(
|
|
||||||
catalog.CalendarOutroReplies,
|
|
||||||
[],
|
|
||||||
"And that's your calendar."),
|
|
||||||
userName));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (toggles.CommuteEnabled)
|
if (toggles.CommuteEnabled)
|
||||||
reportSections.Add((await buildCommuteDecisionAsync(turn, cancellationToken)).ReplyText);
|
reportSections.Add((await buildCommuteDecisionAsync(turn, cancellationToken)).ReplyText);
|
||||||
@@ -706,4 +691,4 @@ internal static class PersonalReportOrchestrator
|
|||||||
bool CalendarEnabled,
|
bool CalendarEnabled,
|
||||||
bool CommuteEnabled,
|
bool CommuteEnabled,
|
||||||
bool NewsEnabled);
|
bool NewsEnabled);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
using Jibo.Cloud.Application.Abstractions;
|
||||||
|
using Jibo.Runtime.Abstractions;
|
||||||
|
|
||||||
|
namespace Jibo.Cloud.Infrastructure.Calendar;
|
||||||
|
|
||||||
|
public sealed class UnavailableCalendarReportProvider : ICalendarReportProvider
|
||||||
|
{
|
||||||
|
public Task<CalendarReportSnapshot?> GetReportAsync(
|
||||||
|
TurnContext turn,
|
||||||
|
CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
return Task.FromResult<CalendarReportSnapshot?>(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -119,7 +119,8 @@ public static class LegacyMimCatalogImporter
|
|||||||
if (fileName.StartsWith("JBO_WhatHolidaysDoYouCelebrate", StringComparison.OrdinalIgnoreCase))
|
if (fileName.StartsWith("JBO_WhatHolidaysDoYouCelebrate", StringComparison.OrdinalIgnoreCase))
|
||||||
return LegacyMimBucket.Holiday;
|
return LegacyMimBucket.Holiday;
|
||||||
|
|
||||||
if (fileName.StartsWith("RI_JBO_HasFavoriteHoliday", StringComparison.OrdinalIgnoreCase))
|
if (fileName.StartsWith("RI_JBO_HasFavoriteHoliday", StringComparison.OrdinalIgnoreCase) ||
|
||||||
|
IsHolidaySeasonFile(fileName))
|
||||||
return LegacyMimBucket.HolidaySeason;
|
return LegacyMimBucket.HolidaySeason;
|
||||||
|
|
||||||
if (fileName.StartsWith("RI_JBO_HasFavoriteAnimal", StringComparison.OrdinalIgnoreCase) ||
|
if (fileName.StartsWith("RI_JBO_HasFavoriteAnimal", StringComparison.OrdinalIgnoreCase) ||
|
||||||
@@ -375,6 +376,48 @@ public static class LegacyMimCatalogImporter
|
|||||||
or LegacyMimBucket.HolidayTracker;
|
or LegacyMimBucket.HolidayTracker;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static bool IsHolidaySeasonFile(string fileName)
|
||||||
|
{
|
||||||
|
return fileName.StartsWith("RI_JBO_HowIsHolidaySeason", StringComparison.OrdinalIgnoreCase) ||
|
||||||
|
fileName.StartsWith("RI_JBO_LikesHolidaySeason", StringComparison.OrdinalIgnoreCase) ||
|
||||||
|
fileName.StartsWith("RI_JBO_HowIsThanksgiving", StringComparison.OrdinalIgnoreCase) ||
|
||||||
|
fileName.StartsWith("RI_JBO_LikesThanksgiving", StringComparison.OrdinalIgnoreCase) ||
|
||||||
|
fileName.StartsWith("RI_JBO_LooksForwardToThanksgiving", StringComparison.OrdinalIgnoreCase) ||
|
||||||
|
fileName.StartsWith("RI_JBO_PlansForThanksgiving", StringComparison.OrdinalIgnoreCase) ||
|
||||||
|
fileName.StartsWith("RI_JBO_HowIsChristmas", StringComparison.OrdinalIgnoreCase) ||
|
||||||
|
fileName.StartsWith("RI_JBO_LikesChristmas", StringComparison.OrdinalIgnoreCase) ||
|
||||||
|
fileName.StartsWith("RI_JBO_LooksForwardToChristmas", StringComparison.OrdinalIgnoreCase) ||
|
||||||
|
fileName.StartsWith("RI_JBO_PlansForChristmas", StringComparison.OrdinalIgnoreCase) ||
|
||||||
|
fileName.StartsWith("RI_JBO_HowIsHanukkah", StringComparison.OrdinalIgnoreCase) ||
|
||||||
|
fileName.StartsWith("RI_JBO_LikesHanukkah", StringComparison.OrdinalIgnoreCase) ||
|
||||||
|
fileName.StartsWith("RI_JBO_LooksForwardToHanukkah", StringComparison.OrdinalIgnoreCase) ||
|
||||||
|
fileName.StartsWith("RI_JBO_PlansForHanukkah", StringComparison.OrdinalIgnoreCase) ||
|
||||||
|
fileName.StartsWith("RI_JBO_HowIsPassover", StringComparison.OrdinalIgnoreCase) ||
|
||||||
|
fileName.StartsWith("RI_JBO_LikesPassover", StringComparison.OrdinalIgnoreCase) ||
|
||||||
|
fileName.StartsWith("RI_JBO_LooksForwardToPassover", StringComparison.OrdinalIgnoreCase) ||
|
||||||
|
fileName.StartsWith("RI_JBO_PlansForPassover", StringComparison.OrdinalIgnoreCase) ||
|
||||||
|
fileName.StartsWith("RI_JBO_HowIsNewYears", StringComparison.OrdinalIgnoreCase) ||
|
||||||
|
fileName.StartsWith("RI_JBO_LikesNewYears", StringComparison.OrdinalIgnoreCase) ||
|
||||||
|
fileName.StartsWith("RI_JBO_LooksForwardToNewYears", StringComparison.OrdinalIgnoreCase) ||
|
||||||
|
fileName.StartsWith("RI_JBO_PlansForNewYears", StringComparison.OrdinalIgnoreCase) ||
|
||||||
|
fileName.StartsWith("RI_JBO_HowIsValentinesDay", StringComparison.OrdinalIgnoreCase) ||
|
||||||
|
fileName.StartsWith("RI_JBO_LikesValentinesDay", StringComparison.OrdinalIgnoreCase) ||
|
||||||
|
fileName.StartsWith("RI_JBO_LooksForwardToValentinesDay", StringComparison.OrdinalIgnoreCase) ||
|
||||||
|
fileName.StartsWith("RI_JBO_PlansForValentinesDay", StringComparison.OrdinalIgnoreCase) ||
|
||||||
|
fileName.StartsWith("RI_JBO_HowIsKwanzaa", StringComparison.OrdinalIgnoreCase) ||
|
||||||
|
fileName.StartsWith("RI_JBO_LikesKwanzaa", StringComparison.OrdinalIgnoreCase) ||
|
||||||
|
fileName.StartsWith("RI_JBO_LooksForwardToKwanzaa", StringComparison.OrdinalIgnoreCase) ||
|
||||||
|
fileName.StartsWith("RI_JBO_PlansForKwanzaa", StringComparison.OrdinalIgnoreCase) ||
|
||||||
|
fileName.StartsWith("RI_JBO_HowIsEaster", StringComparison.OrdinalIgnoreCase) ||
|
||||||
|
fileName.StartsWith("RI_JBO_LikesEaster", StringComparison.OrdinalIgnoreCase) ||
|
||||||
|
fileName.StartsWith("RI_JBO_LooksForwardToEaster", StringComparison.OrdinalIgnoreCase) ||
|
||||||
|
fileName.StartsWith("RI_JBO_PlansForEaster", StringComparison.OrdinalIgnoreCase) ||
|
||||||
|
fileName.StartsWith("RI_JBO_HowIsOrthodoxEaster", StringComparison.OrdinalIgnoreCase) ||
|
||||||
|
fileName.StartsWith("RI_JBO_LikesOrthodoxEaster", StringComparison.OrdinalIgnoreCase) ||
|
||||||
|
fileName.StartsWith("RI_JBO_LooksForwardToOrthodoxEaster", StringComparison.OrdinalIgnoreCase) ||
|
||||||
|
fileName.StartsWith("RI_JBO_PlansForOrthodoxEaster", StringComparison.OrdinalIgnoreCase);
|
||||||
|
}
|
||||||
|
|
||||||
private enum LegacyMimBucket
|
private enum LegacyMimBucket
|
||||||
{
|
{
|
||||||
GenericFallback,
|
GenericFallback,
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using Jibo.Cloud.Application.Abstractions;
|
using Jibo.Cloud.Application.Abstractions;
|
||||||
using Jibo.Cloud.Application.Services;
|
using Jibo.Cloud.Application.Services;
|
||||||
using Jibo.Cloud.Infrastructure.Audio;
|
using Jibo.Cloud.Infrastructure.Audio;
|
||||||
|
using Jibo.Cloud.Infrastructure.Calendar;
|
||||||
using Jibo.Cloud.Infrastructure.Commute;
|
using Jibo.Cloud.Infrastructure.Commute;
|
||||||
using Jibo.Cloud.Infrastructure.Content;
|
using Jibo.Cloud.Infrastructure.Content;
|
||||||
using Jibo.Cloud.Infrastructure.Holidays;
|
using Jibo.Cloud.Infrastructure.Holidays;
|
||||||
@@ -53,6 +54,7 @@ public static class ServiceCollectionExtensions
|
|||||||
services.AddHttpClient<INewsBriefingProvider, NewsApiBriefingProvider>();
|
services.AddHttpClient<INewsBriefingProvider, NewsApiBriefingProvider>();
|
||||||
services.AddSingleton<IHolidayCalendarProvider>(provider =>
|
services.AddSingleton<IHolidayCalendarProvider>(provider =>
|
||||||
new NagerDateHolidayCalendarProvider(provider.GetRequiredService<HolidayCalendarOptions>()));
|
new NagerDateHolidayCalendarProvider(provider.GetRequiredService<HolidayCalendarOptions>()));
|
||||||
|
services.AddSingleton<ICalendarReportProvider, UnavailableCalendarReportProvider>();
|
||||||
services.AddSingleton<ICommuteReportProvider, UnavailableCommuteReportProvider>();
|
services.AddSingleton<ICommuteReportProvider, UnavailableCommuteReportProvider>();
|
||||||
var statePersistencePath = configuration?["OpenJibo:State:PersistencePath"]
|
var statePersistencePath = configuration?["OpenJibo:State:PersistencePath"]
|
||||||
?? Path.Combine(AppContext.BaseDirectory, "App_Data", "cloud-state.json");
|
?? Path.Combine(AppContext.BaseDirectory, "App_Data", "cloud-state.json");
|
||||||
|
|||||||
@@ -108,10 +108,6 @@ public sealed class LegacyMimCatalogImporterTests
|
|||||||
reply.Contains("holiday music", StringComparison.OrdinalIgnoreCase));
|
reply.Contains("holiday music", StringComparison.OrdinalIgnoreCase));
|
||||||
Assert.Contains(catalog.PersonalityReplies, reply =>
|
Assert.Contains(catalog.PersonalityReplies, reply =>
|
||||||
reply.Contains("dance party", StringComparison.OrdinalIgnoreCase));
|
reply.Contains("dance party", StringComparison.OrdinalIgnoreCase));
|
||||||
Assert.Contains(catalog.PersonalityReplies, reply =>
|
|
||||||
reply.Contains("giving and receiving", StringComparison.OrdinalIgnoreCase));
|
|
||||||
Assert.Contains(catalog.PersonalityReplies, reply =>
|
|
||||||
reply.Contains("Christmas sweaters", StringComparison.OrdinalIgnoreCase));
|
|
||||||
Assert.Contains(catalog.PersonalityReplies, reply =>
|
Assert.Contains(catalog.PersonalityReplies, reply =>
|
||||||
reply.Contains("thankful for the people I know", StringComparison.OrdinalIgnoreCase));
|
reply.Contains("thankful for the people I know", StringComparison.OrdinalIgnoreCase));
|
||||||
Assert.Contains(catalog.PersonalityReplies, reply =>
|
Assert.Contains(catalog.PersonalityReplies, reply =>
|
||||||
@@ -213,6 +209,14 @@ public sealed class LegacyMimCatalogImporterTests
|
|||||||
reply.Contains("fun time of year", StringComparison.OrdinalIgnoreCase));
|
reply.Contains("fun time of year", StringComparison.OrdinalIgnoreCase));
|
||||||
Assert.Contains(catalog.HolidayGiftReplies, reply =>
|
Assert.Contains(catalog.HolidayGiftReplies, reply =>
|
||||||
reply.Contains("pet elephant", StringComparison.OrdinalIgnoreCase));
|
reply.Contains("pet elephant", StringComparison.OrdinalIgnoreCase));
|
||||||
|
Assert.Contains(catalog.HolidaySeasonReplies, reply =>
|
||||||
|
reply.Contains("holiday season is going very nicely", StringComparison.OrdinalIgnoreCase));
|
||||||
|
Assert.Contains(catalog.HolidaySeasonReplies, reply =>
|
||||||
|
reply.Contains("festive times", StringComparison.OrdinalIgnoreCase));
|
||||||
|
Assert.Contains(catalog.HolidaySeasonReplies, reply =>
|
||||||
|
reply.Contains("giving and receiving", StringComparison.OrdinalIgnoreCase));
|
||||||
|
Assert.Contains(catalog.HolidaySeasonReplies, reply =>
|
||||||
|
reply.Contains("Christmas sweaters", StringComparison.OrdinalIgnoreCase));
|
||||||
Assert.Contains(catalog.BirthdayCelebrationReplies, reply =>
|
Assert.Contains(catalog.BirthdayCelebrationReplies, reply =>
|
||||||
reply.Contains("first powered up", StringComparison.OrdinalIgnoreCase) ||
|
reply.Contains("first powered up", StringComparison.OrdinalIgnoreCase) ||
|
||||||
reply.Contains("another year older", StringComparison.OrdinalIgnoreCase));
|
reply.Contains("another year older", StringComparison.OrdinalIgnoreCase));
|
||||||
@@ -348,6 +352,7 @@ public sealed class LegacyMimCatalogImporterTests
|
|||||||
catalog.GenericFallbackReplies);
|
catalog.GenericFallbackReplies);
|
||||||
Assert.Contains("For your weather.", catalog.WeatherIntroReplies);
|
Assert.Contains("For your weather.", catalog.WeatherIntroReplies);
|
||||||
Assert.Contains("Today's high is {high}, and the low is {low}.", catalog.WeatherTodayHighLowReplies);
|
Assert.Contains("Today's high is {high}, and the low is {low}.", catalog.WeatherTodayHighLowReplies);
|
||||||
|
Assert.Contains("I do like festive times.", catalog.HolidaySeasonReplies);
|
||||||
Assert.Contains("Looking at your calendar, I don't see anything scheduled today.",
|
Assert.Contains("Looking at your calendar, I don't see anything scheduled today.",
|
||||||
catalog.CalendarNothingTodayReplies);
|
catalog.CalendarNothingTodayReplies);
|
||||||
Assert.Contains("Looks like I can't access calendars right now. Sorry.", catalog.CalendarServiceDownReplies);
|
Assert.Contains("Looks like I can't access calendars right now. Sorry.", catalog.CalendarServiceDownReplies);
|
||||||
|
|||||||
@@ -665,8 +665,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("how is holiday season", "seasonal_holiday_season", "holiday season is going very nicely")]
|
||||||
[InlineData("do you like holiday season", "seasonal_holiday_season", "I do like festive times.")]
|
[InlineData("do you like holiday season", "seasonal_holiday_season", "holiday season is going very nicely")]
|
||||||
[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")]
|
||||||
@@ -678,9 +678,17 @@ public sealed class JiboInteractionServiceTests
|
|||||||
[InlineData("do you like halloween", "seasonal_likes_halloween", "Halloween is my favorite holiday")]
|
[InlineData("do you like halloween", "seasonal_likes_halloween", "Halloween is my favorite holiday")]
|
||||||
[InlineData("do you like holiday music", "seasonal_likes_holiday_music", "holiday music")]
|
[InlineData("do you like holiday music", "seasonal_likes_holiday_music", "holiday music")]
|
||||||
[InlineData("do you like holiday parties", "seasonal_likes_holiday_parties", "holiday fun can be extra fun")]
|
[InlineData("do you like holiday parties", "seasonal_likes_holiday_parties", "holiday fun can be extra fun")]
|
||||||
[InlineData("are you looking forward to christmas", "seasonal_looks_forward_to_christmas", "giving and receiving")]
|
[InlineData("how is thanksgiving", "seasonal_thanksgiving", "Thanksgiving")]
|
||||||
|
[InlineData("are you looking forward to christmas", "seasonal_looks_forward_to_christmas", "long way away")]
|
||||||
[InlineData("what are you doing for christmas", "seasonal_plans_for_christmas", "Christmas sweaters")]
|
[InlineData("what are you doing for christmas", "seasonal_plans_for_christmas", "Christmas sweaters")]
|
||||||
|
[InlineData("do you like christmas", "seasonal_christmas", "Christmas")]
|
||||||
|
[InlineData("how is hanukkah", "seasonal_hanukkah", "Hanukkah")]
|
||||||
|
[InlineData("do you like passover", "seasonal_passover", "Passover")]
|
||||||
|
[InlineData("do you like new years", "seasonal_new_years", "new year")]
|
||||||
[InlineData("what are you thankful for", "seasonal_thankful_for", "thankful for the people I know")]
|
[InlineData("what are you thankful for", "seasonal_thankful_for", "thankful for the people I know")]
|
||||||
|
[InlineData("do you like valentines day", "seasonal_valentines_day", "Valentine")]
|
||||||
|
[InlineData("do you like kwanzaa", "seasonal_kwanzaa", "Kwanzaa")]
|
||||||
|
[InlineData("do you like easter", "seasonal_easter", "Easter")]
|
||||||
[InlineData("happy birthday", "birthday_celebration", "another year older")]
|
[InlineData("happy birthday", "birthday_celebration", "another year older")]
|
||||||
public async Task BuildDecisionAsync_SeasonalCharm_UsesImportedReplies(
|
public async Task BuildDecisionAsync_SeasonalCharm_UsesImportedReplies(
|
||||||
string transcript,
|
string transcript,
|
||||||
@@ -1876,6 +1884,35 @@ public sealed class JiboInteractionServiceTests
|
|||||||
Assert.Equal(true, decision.ContextUpdates[PersonalReportUserVerifiedKey]);
|
Assert.Equal(true, decision.ContextUpdates[PersonalReportUserVerifiedKey]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task BuildDecisionAsync_PersonalReport_UsesCalendarProviderSummaryAndTime()
|
||||||
|
{
|
||||||
|
var weatherProvider = new CapturingWeatherReportProvider
|
||||||
|
{
|
||||||
|
Snapshot = new WeatherReportSnapshot("Boston, U.S.", "light rain", 61, 65, 54, "rain", false)
|
||||||
|
};
|
||||||
|
var calendarProvider = new CapturingCalendarReportProvider
|
||||||
|
{
|
||||||
|
Snapshot = new CalendarReportSnapshot(["get personal report from jibo"], ["at 6:00 p.m."], [])
|
||||||
|
};
|
||||||
|
var service = CreateService(weatherReportProvider: weatherProvider, calendarReportProvider: calendarProvider);
|
||||||
|
|
||||||
|
var decision = await service.BuildDecisionAsync(new TurnContext
|
||||||
|
{
|
||||||
|
RawTranscript = "yes",
|
||||||
|
NormalizedTranscript = "yes",
|
||||||
|
Attributes = new Dictionary<string, object?>
|
||||||
|
{
|
||||||
|
[PersonalReportStateKey] = "awaiting_identity_confirmation",
|
||||||
|
[PersonalReportUserNameKey] = "alex"
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task BuildDecisionAsync_PersonalReport_NoMatchRetriesThenDeclines()
|
public async Task BuildDecisionAsync_PersonalReport_NoMatchRetriesThenDeclines()
|
||||||
{
|
{
|
||||||
@@ -4035,6 +4072,7 @@ public sealed class JiboInteractionServiceTests
|
|||||||
IPersonalMemoryStore? personalMemoryStore = null,
|
IPersonalMemoryStore? personalMemoryStore = null,
|
||||||
ICloudStateStore? cloudStateStore = null,
|
ICloudStateStore? cloudStateStore = null,
|
||||||
IWeatherReportProvider? weatherReportProvider = null,
|
IWeatherReportProvider? weatherReportProvider = null,
|
||||||
|
ICalendarReportProvider? calendarReportProvider = null,
|
||||||
ICommuteReportProvider? commuteReportProvider = null,
|
ICommuteReportProvider? commuteReportProvider = null,
|
||||||
INewsBriefingProvider? newsBriefingProvider = null,
|
INewsBriefingProvider? newsBriefingProvider = null,
|
||||||
IJiboExperienceContentRepository? contentRepository = null,
|
IJiboExperienceContentRepository? contentRepository = null,
|
||||||
@@ -4045,6 +4083,7 @@ public sealed class JiboInteractionServiceTests
|
|||||||
randomizer ?? new FirstItemRandomizer(),
|
randomizer ?? new FirstItemRandomizer(),
|
||||||
personalMemoryStore ?? new InMemoryPersonalMemoryStore(),
|
personalMemoryStore ?? new InMemoryPersonalMemoryStore(),
|
||||||
weatherReportProvider,
|
weatherReportProvider,
|
||||||
|
calendarReportProvider,
|
||||||
commuteReportProvider,
|
commuteReportProvider,
|
||||||
newsBriefingProvider,
|
newsBriefingProvider,
|
||||||
cloudStateStore);
|
cloudStateStore);
|
||||||
@@ -4152,4 +4191,16 @@ public sealed class JiboInteractionServiceTests
|
|||||||
return Task.FromResult(Snapshot);
|
return Task.FromResult(Snapshot);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private sealed class CapturingCalendarReportProvider : ICalendarReportProvider
|
||||||
|
{
|
||||||
|
public CalendarReportSnapshot? Snapshot { get; init; }
|
||||||
|
|
||||||
|
public Task<CalendarReportSnapshot?> GetReportAsync(
|
||||||
|
TurnContext turn,
|
||||||
|
CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
return Task.FromResult(Snapshot);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4734,6 +4734,7 @@ public sealed class JiboWebSocketServiceTests
|
|||||||
new StubWeatherReportProvider(
|
new StubWeatherReportProvider(
|
||||||
new WeatherReportSnapshot("Lone Jack, US", "overcast clouds", 79, 82, 78, "clouds", false)),
|
new WeatherReportSnapshot("Lone Jack, US", "overcast clouds", 79, 82, 78, "clouds", false)),
|
||||||
null,
|
null,
|
||||||
|
null,
|
||||||
new StubNewsBriefingProvider(
|
new StubNewsBriefingProvider(
|
||||||
new NewsBriefingSnapshot(
|
new NewsBriefingSnapshot(
|
||||||
[
|
[
|
||||||
@@ -5126,6 +5127,7 @@ public sealed class JiboWebSocketServiceTests
|
|||||||
private static JiboWebSocketService CreateService(
|
private static JiboWebSocketService CreateService(
|
||||||
InMemoryCloudStateStore stateStore,
|
InMemoryCloudStateStore stateStore,
|
||||||
IWeatherReportProvider? weatherReportProvider = null,
|
IWeatherReportProvider? weatherReportProvider = null,
|
||||||
|
ICalendarReportProvider? calendarReportProvider = null,
|
||||||
ICommuteReportProvider? commuteReportProvider = null,
|
ICommuteReportProvider? commuteReportProvider = null,
|
||||||
INewsBriefingProvider? newsBriefingProvider = null)
|
INewsBriefingProvider? newsBriefingProvider = null)
|
||||||
{
|
{
|
||||||
@@ -5136,6 +5138,7 @@ public sealed class JiboWebSocketServiceTests
|
|||||||
new DefaultJiboRandomizer(),
|
new DefaultJiboRandomizer(),
|
||||||
new InMemoryPersonalMemoryStore(),
|
new InMemoryPersonalMemoryStore(),
|
||||||
weatherReportProvider,
|
weatherReportProvider,
|
||||||
|
calendarReportProvider,
|
||||||
commuteReportProvider,
|
commuteReportProvider,
|
||||||
newsBriefingProvider);
|
newsBriefingProvider);
|
||||||
var conversationBroker = new DemoConversationBroker(interactionService);
|
var conversationBroker = new DemoConversationBroker(interactionService);
|
||||||
@@ -5209,4 +5212,4 @@ public sealed class JiboWebSocketServiceTests
|
|||||||
return items[^1];
|
return items[^1];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user