Expand surprise facts into robot and human categories
This commit is contained in:
@@ -14,6 +14,8 @@ public sealed class JiboConditionedReply
|
||||
public sealed class JiboExperienceCatalog
|
||||
{
|
||||
public IReadOnlyList<string> Jokes { get; init; } = [];
|
||||
public IReadOnlyList<string> RobotFacts { get; init; } = [];
|
||||
public IReadOnlyList<string> HumanFacts { get; init; } = [];
|
||||
public IReadOnlyList<string> FunFacts { get; init; } = [];
|
||||
public IReadOnlyList<string> DanceAnimations { get; init; } = [];
|
||||
public IReadOnlyList<string> GreetingReplies { get; init; } = [];
|
||||
|
||||
@@ -1161,7 +1161,16 @@ public sealed class JiboInteractionService(
|
||||
|
||||
private JiboInteractionDecision BuildProactiveFunFactDecision(JiboExperienceCatalog catalog)
|
||||
{
|
||||
var fact = randomizer.Choose(catalog.FunFacts);
|
||||
var categories = new List<ProactiveFactCategory>();
|
||||
AddProactiveFactCategory(categories, "fun_fact", catalog.FunFacts);
|
||||
AddProactiveFactCategory(categories, "robot_fact", catalog.RobotFacts);
|
||||
AddProactiveFactCategory(categories, "human_fact", catalog.HumanFacts);
|
||||
|
||||
if (categories.Count == 0)
|
||||
return new JiboInteractionDecision("proactive_fun_fact", randomizer.Choose(catalog.SurpriseReplies));
|
||||
|
||||
var selectedCategory = randomizer.Choose(categories);
|
||||
var fact = randomizer.Choose(selectedCategory.Replies);
|
||||
return new JiboInteractionDecision(
|
||||
"proactive_fun_fact",
|
||||
fact,
|
||||
@@ -1171,10 +1180,21 @@ public sealed class JiboInteractionService(
|
||||
["mim_id"] = "runtime-fun-fact",
|
||||
["mim_type"] = "announcement",
|
||||
["prompt_id"] = "RUNTIME_FUN_FACT",
|
||||
["replyType"] = "fun_fact"
|
||||
["replyType"] = "fun_fact",
|
||||
["factCategory"] = selectedCategory.CategoryName
|
||||
});
|
||||
}
|
||||
|
||||
private static void AddProactiveFactCategory(
|
||||
ICollection<ProactiveFactCategory> categories,
|
||||
string categoryName,
|
||||
IReadOnlyList<string> replies)
|
||||
{
|
||||
if (replies.Count == 0) return;
|
||||
|
||||
categories.Add(new ProactiveFactCategory(categoryName, replies));
|
||||
}
|
||||
|
||||
private JiboInteractionDecision BuildProactiveJokeDecision(JiboExperienceCatalog catalog)
|
||||
{
|
||||
return new JiboInteractionDecision(
|
||||
@@ -5204,6 +5224,8 @@ public sealed class JiboInteractionService(
|
||||
|
||||
private sealed record ProactivityCandidate(string IntentName, int Weight);
|
||||
|
||||
private sealed record ProactiveFactCategory(string CategoryName, IReadOnlyList<string> Replies);
|
||||
|
||||
private sealed record PizzaSignal(PersonalAffinity? Affinity);
|
||||
|
||||
private sealed record GreetingPresenceProfile(
|
||||
|
||||
@@ -27,6 +27,22 @@ public sealed class InMemoryJiboExperienceContentRepository : IJiboExperienceCon
|
||||
"What kind of music are balloons afraid of. Pop music.",
|
||||
"Why did the orange cry. Someone hurt his peelings."
|
||||
],
|
||||
RobotFacts =
|
||||
[
|
||||
"Leonardo Da Vinci made sketches for a humanoid machine all the way back in the year 1495.",
|
||||
"The world's first humanoid robot was called Elektro, and it debuted in 1939.",
|
||||
"The English word robot comes from a 1920 play in Czechoslovakia, called Rossum's Universal Robots.",
|
||||
"The first programmable robot arm was designed in 1954.",
|
||||
"Some robots have a human form, but most of the world's robots are machines designed to perform a task, and don't look like people at all."
|
||||
],
|
||||
HumanFacts =
|
||||
[
|
||||
"Every human being that has ever lived spent about 30 minutes as a single cell.",
|
||||
"50 percent of a human's DNA is the same as a banana's.",
|
||||
"Humans are the only animals that cry tears of emotion.",
|
||||
"Six-year-olds laugh an average of 300 times a day. Grown ups only laugh 15 to 100 times a day.",
|
||||
"Your nose can remember 50,000 different scents."
|
||||
],
|
||||
FunFacts =
|
||||
[
|
||||
"A shrimp's heart is in its head.",
|
||||
|
||||
@@ -61,7 +61,7 @@ public static class LegacyMimCatalogImporter
|
||||
var text = NormalizePrompt(prompt.Prompt, IsTemplateBucket(bucket.Value));
|
||||
if (string.IsNullOrWhiteSpace(text)) continue;
|
||||
|
||||
builder.Add(bucket.Value, prompt.Condition, text);
|
||||
builder.Add(bucket.Value, prompt.Condition, text, prompt.Prompt);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,10 +102,12 @@ public static class LegacyMimCatalogImporter
|
||||
if (fileName.StartsWith("RA_JBO_TellAJoke", StringComparison.OrdinalIgnoreCase))
|
||||
return LegacyMimBucket.Jokes;
|
||||
|
||||
if (fileName.StartsWith("RA_JBO_TellRobotFact", StringComparison.OrdinalIgnoreCase) ||
|
||||
fileName.StartsWith("RA_JBO_Shuffle", StringComparison.OrdinalIgnoreCase) ||
|
||||
if (fileName.StartsWith("RA_JBO_TellRobotFact", StringComparison.OrdinalIgnoreCase))
|
||||
return LegacyMimBucket.RobotFacts;
|
||||
|
||||
if (fileName.StartsWith("RA_JBO_Shuffle", StringComparison.OrdinalIgnoreCase) ||
|
||||
fileName.StartsWith("RA_JBO_TellSomething", StringComparison.OrdinalIgnoreCase))
|
||||
return LegacyMimBucket.FunFacts;
|
||||
return LegacyMimBucket.FunFactSource;
|
||||
|
||||
if (normalizedPath.Contains("/emotion-responses/", StringComparison.OrdinalIgnoreCase) ||
|
||||
normalizedPath.Contains("/gqa-responses/", StringComparison.OrdinalIgnoreCase))
|
||||
@@ -221,6 +223,8 @@ public static class LegacyMimCatalogImporter
|
||||
return new JiboExperienceCatalog
|
||||
{
|
||||
Jokes = Merge(baseCatalog.Jokes, importedCatalog.Jokes),
|
||||
RobotFacts = Merge(baseCatalog.RobotFacts, importedCatalog.RobotFacts),
|
||||
HumanFacts = Merge(baseCatalog.HumanFacts, importedCatalog.HumanFacts),
|
||||
FunFacts = Merge(baseCatalog.FunFacts, importedCatalog.FunFacts),
|
||||
DanceAnimations = Merge(baseCatalog.DanceAnimations, importedCatalog.DanceAnimations),
|
||||
GreetingReplies = Merge(baseCatalog.GreetingReplies, importedCatalog.GreetingReplies),
|
||||
@@ -332,9 +336,12 @@ public static class LegacyMimCatalogImporter
|
||||
GenericFallback,
|
||||
Greeting,
|
||||
Jokes,
|
||||
RobotFacts,
|
||||
HumanFacts,
|
||||
HowAreYou,
|
||||
Emotion,
|
||||
FunFacts,
|
||||
FunFactSource,
|
||||
Personality,
|
||||
PersonalReportKickOff,
|
||||
PersonalReportOutro,
|
||||
@@ -365,6 +372,8 @@ public static class LegacyMimCatalogImporter
|
||||
private readonly List<string> _fallbacks = [];
|
||||
private readonly List<string> _greetings = [];
|
||||
private readonly List<string> _jokes = [];
|
||||
private readonly List<string> _robotFacts = [];
|
||||
private readonly List<string> _humanFacts = [];
|
||||
private readonly List<string> _howAreYous = [];
|
||||
private readonly List<string> _funFacts = [];
|
||||
private readonly List<string> _newsCategoryIntroReplies = [];
|
||||
@@ -380,7 +389,7 @@ public static class LegacyMimCatalogImporter
|
||||
private readonly List<string> _weatherTomorrowHighLowReplies = [];
|
||||
private readonly List<string> _weatherTomorrowIntroReplies = [];
|
||||
|
||||
public void Add(LegacyMimBucket bucket, string? condition, string text)
|
||||
public void Add(LegacyMimBucket bucket, string? condition, string text, string? sourcePrompt = null)
|
||||
{
|
||||
switch (bucket)
|
||||
{
|
||||
@@ -399,6 +408,12 @@ public static class LegacyMimCatalogImporter
|
||||
|
||||
_jokes.Add(text);
|
||||
return;
|
||||
case LegacyMimBucket.RobotFacts:
|
||||
AddDistinct(_robotFacts, text);
|
||||
return;
|
||||
case LegacyMimBucket.HumanFacts:
|
||||
AddDistinct(_humanFacts, text);
|
||||
return;
|
||||
case LegacyMimBucket.HowAreYou:
|
||||
if (_howAreYous.Any(value => string.Equals(value, text, StringComparison.OrdinalIgnoreCase)))
|
||||
return;
|
||||
@@ -425,6 +440,19 @@ public static class LegacyMimCatalogImporter
|
||||
|
||||
_personalities.Add(text);
|
||||
return;
|
||||
case LegacyMimBucket.FunFactSource:
|
||||
switch (ResolveFunFactTarget(sourcePrompt ?? text))
|
||||
{
|
||||
case LegacyMimBucket.RobotFacts:
|
||||
AddDistinct(_robotFacts, text);
|
||||
return;
|
||||
case LegacyMimBucket.HumanFacts:
|
||||
AddDistinct(_humanFacts, text);
|
||||
return;
|
||||
default:
|
||||
AddDistinct(_funFacts, text);
|
||||
return;
|
||||
}
|
||||
case LegacyMimBucket.FunFacts:
|
||||
if (_funFacts.Any(value => string.Equals(value, text, StringComparison.OrdinalIgnoreCase))) return;
|
||||
|
||||
@@ -488,6 +516,8 @@ public static class LegacyMimCatalogImporter
|
||||
return new JiboExperienceCatalog
|
||||
{
|
||||
Jokes = [.. _jokes],
|
||||
RobotFacts = [.. _robotFacts],
|
||||
HumanFacts = [.. _humanFacts],
|
||||
FunFacts = [.. _funFacts],
|
||||
GreetingReplies = [.. _greetings],
|
||||
HowAreYouReplies = [.. _howAreYous],
|
||||
@@ -519,6 +549,23 @@ public static class LegacyMimCatalogImporter
|
||||
|
||||
target.Add(text);
|
||||
}
|
||||
|
||||
private LegacyMimBucket ResolveFunFactTarget(string prompt)
|
||||
{
|
||||
var lowered = NormalizePrompt(prompt, false).ToLowerInvariant();
|
||||
if (ContainsAny(lowered, "robot", "humanoid", "machine", "about me", "my cameras", "turing", "deep blue", "rossum"))
|
||||
return LegacyMimBucket.RobotFacts;
|
||||
|
||||
if (ContainsAny(lowered, "human", "people", "grown ups", "human being", "humans"))
|
||||
return LegacyMimBucket.HumanFacts;
|
||||
|
||||
return LegacyMimBucket.FunFacts;
|
||||
}
|
||||
|
||||
private static bool ContainsAny(string text, params string[] values)
|
||||
{
|
||||
return values.Any(value => text.Contains(value, StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class LegacyMimDefinition
|
||||
|
||||
@@ -9,3 +9,4 @@ It also includes a descriptor pack for questions like `are you kind`, `are you f
|
||||
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 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.
|
||||
Those facts are now split into generic, robot, and human buckets so the randomizer can sound more like Pegasus while staying lightweight.
|
||||
|
||||
@@ -192,9 +192,17 @@ public sealed class LegacyMimCatalogImporterTests
|
||||
Assert.Contains("I love jokes. Did you hear about the theater actor who fell through the floorboards? He was just going through a stage.",
|
||||
catalog.Jokes);
|
||||
Assert.Contains("Sure I got one. What did the zero say to the eight. Nice belt.", catalog.Jokes);
|
||||
Assert.Contains("Here's an interesting fact about me. I have two cameras but they're different focal lengths. One's for far things, and the other's for near things.",
|
||||
Assert.Contains(catalog.RobotFacts, reply =>
|
||||
reply.Contains("Leonardo Da Vinci made sketches", StringComparison.OrdinalIgnoreCase));
|
||||
Assert.Contains(catalog.RobotFacts, reply =>
|
||||
reply.Contains("first programmable robot arm", StringComparison.OrdinalIgnoreCase));
|
||||
Assert.Contains(catalog.RobotFacts, reply =>
|
||||
reply.Contains("robots have a human form", StringComparison.OrdinalIgnoreCase));
|
||||
Assert.Contains(catalog.RobotFacts, reply =>
|
||||
reply.Contains("two cameras but they're different focal lengths", StringComparison.OrdinalIgnoreCase));
|
||||
Assert.Contains("A random fact for you. A shrimp's heart is in its head.", catalog.FunFacts);
|
||||
Assert.Contains("An amazing but true fact for you. Dogs and elephants are the only animals that understand pointing.",
|
||||
catalog.FunFacts);
|
||||
Assert.Contains("True fact. Children have more taste buds than grown ups.", catalog.FunFacts);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
||||
@@ -2915,10 +2915,30 @@ public sealed class JiboInteractionServiceTests
|
||||
Assert.Equal("proactive_fun_fact", decision.IntentName);
|
||||
Assert.Equal("chitchat-skill", decision.SkillName);
|
||||
Assert.Equal("fun_fact", decision.SkillPayload!["replyType"]);
|
||||
Assert.Equal("fun_fact", decision.SkillPayload["factCategory"]);
|
||||
Assert.NotNull(decision.ReplyText);
|
||||
Assert.NotEmpty(decision.ReplyText);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task BuildDecisionAsync_Surprise_UsesHumanFactWhenRandomizerChoosesLastCategory()
|
||||
{
|
||||
var service = CreateService(randomizer: new FactCategoryLastRandomizer());
|
||||
|
||||
var decision = await service.BuildDecisionAsync(new TurnContext
|
||||
{
|
||||
RawTranscript = "surprise me",
|
||||
NormalizedTranscript = "surprise me"
|
||||
});
|
||||
|
||||
Assert.Equal("proactive_fun_fact", decision.IntentName);
|
||||
Assert.Equal("chitchat-skill", decision.SkillName);
|
||||
Assert.Equal("fun_fact", decision.SkillPayload!["replyType"]);
|
||||
Assert.Equal("human_fact", decision.SkillPayload["factCategory"]);
|
||||
Assert.NotNull(decision.ReplyText);
|
||||
Assert.Contains("human", decision.ReplyText, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task BuildDecisionAsync_WordOfDayOfferPrompt_WithNoisyAffirmation_MapsToWordOfDayLaunch()
|
||||
{
|
||||
@@ -3872,11 +3892,12 @@ public sealed class JiboInteractionServiceTests
|
||||
IPersonalMemoryStore? personalMemoryStore = null,
|
||||
IWeatherReportProvider? weatherReportProvider = null,
|
||||
INewsBriefingProvider? newsBriefingProvider = null,
|
||||
IJiboExperienceContentRepository? contentRepository = null)
|
||||
IJiboExperienceContentRepository? contentRepository = null,
|
||||
IJiboRandomizer? randomizer = null)
|
||||
{
|
||||
return new JiboInteractionService(
|
||||
new JiboExperienceContentCache(contentRepository ?? new InMemoryJiboExperienceContentRepository()),
|
||||
new FirstItemRandomizer(),
|
||||
randomizer ?? new FirstItemRandomizer(),
|
||||
personalMemoryStore ?? new InMemoryPersonalMemoryStore(),
|
||||
weatherReportProvider,
|
||||
newsBriefingProvider);
|
||||
@@ -3915,6 +3936,24 @@ public sealed class JiboInteractionServiceTests
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class LastItemRandomizer : IJiboRandomizer
|
||||
{
|
||||
public T Choose<T>(IReadOnlyList<T> items)
|
||||
{
|
||||
return items[^1];
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class FactCategoryLastRandomizer : IJiboRandomizer
|
||||
{
|
||||
public T Choose<T>(IReadOnlyList<T> items)
|
||||
{
|
||||
return typeof(T).Name == "ProactiveFactCategory"
|
||||
? items[^1]
|
||||
: items[0];
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class CapturingWeatherReportProvider : IWeatherReportProvider
|
||||
{
|
||||
public WeatherReportRequest? LastRequest { get; private set; }
|
||||
|
||||
Reference in New Issue
Block a user