Personalize how_are_you replies with remembered names

This commit is contained in:
Jacob Dubin
2026-05-21 14:31:59 -05:00
parent b75d9f7941
commit 764a2b2d4f
3 changed files with 54 additions and 8 deletions

View File

@@ -167,6 +167,7 @@ internal static class ChitchatStateMachine
JiboExperienceCatalog catalog,
IJiboRandomizer randomizer,
string? currentEmotion,
string? preferredName,
Func<string> buildErrorResponse)
{
var normalizedLoweredTranscript = NormalizeForPhraseMatching(loweredTranscript);
@@ -187,7 +188,7 @@ internal static class ChitchatStateMachine
case "how_are_you":
return BuildEmotionQueryDecision(
"how_are_you",
SelectEmotionQueryReply(catalog, randomizer, currentEmotion));
SelectEmotionQueryReply(catalog, randomizer, currentEmotion, preferredName));
case "robot_desire":
return BuildScriptedResponseDecision(
"robot_desire",
@@ -287,7 +288,7 @@ internal static class ChitchatStateMachine
if (IsEmotionQuery(normalizedLoweredTranscript))
return BuildEmotionQueryDecision(
"emotion_query",
SelectEmotionQueryReply(catalog, randomizer, currentEmotion));
SelectEmotionQueryReply(catalog, randomizer, currentEmotion, preferredName));
if (TryResolveEmotionCommand(normalizedLoweredTranscript, out var emotion))
return BuildEmotionCommandDecision(randomizer, emotion!);
@@ -391,16 +392,36 @@ internal static class ChitchatStateMachine
private static string SelectEmotionQueryReply(
JiboExperienceCatalog catalog,
IJiboRandomizer randomizer,
string? currentEmotion)
string? currentEmotion,
string? preferredName)
{
if (catalog.EmotionReplies.Count == 0) return randomizer.Choose(catalog.HowAreYouReplies);
if (catalog.EmotionReplies.Count == 0)
return PersonalizeHowAreYouReply(randomizer.Choose(catalog.HowAreYouReplies), preferredName);
var emotionVariants = ResolveEmotionVariants(currentEmotion);
foreach (var reply in catalog.EmotionReplies)
if (ConditionMatches(reply.Condition, emotionVariants))
return reply.Reply;
return PersonalizeHowAreYouReply(reply.Reply, preferredName);
return randomizer.Choose(catalog.HowAreYouReplies);
return PersonalizeHowAreYouReply(randomizer.Choose(catalog.HowAreYouReplies), preferredName);
}
private static string PersonalizeHowAreYouReply(string replyText, string? preferredName)
{
if (string.IsNullOrWhiteSpace(replyText) || string.IsNullOrWhiteSpace(preferredName)) return replyText;
var trimmedName = preferredName.Trim();
if (replyText.Contains(trimmedName, StringComparison.OrdinalIgnoreCase)) return replyText;
var trimmedReply = replyText.Trim();
var firstSentenceEnd = trimmedReply.IndexOfAny(['.', '!', '?']);
if (firstSentenceEnd <= 0)
return $"{trimmedReply}, {trimmedName}.";
if (firstSentenceEnd == trimmedReply.Length - 1)
return $"{trimmedReply[..firstSentenceEnd]}, {trimmedName}.";
return $"{trimmedReply[..firstSentenceEnd]}, {trimmedName}{trimmedReply[firstSentenceEnd..]}";
}
private static bool ConditionMatches(string? condition, IReadOnlyList<string> emotionVariants)

View File

@@ -502,6 +502,7 @@ public sealed partial class JiboInteractionService(
turnContext => ResolveTenantScope(turnContext));
if (householdListDecision is not null) return householdListDecision;
var preferredName = ResolvePreferredGreetingName(turn, greetingPresence);
var chitchatDecision = ChitchatStateMachine.TryBuildDecision(
semanticIntent,
transcript,
@@ -509,6 +510,7 @@ public sealed partial class JiboInteractionService(
catalog,
randomizer,
chitchatEmotion,
preferredName,
() => BuildGenericReply(catalog, transcript, lowered));
if (chitchatDecision is not null) return chitchatDecision;

View File

@@ -861,6 +861,29 @@ public sealed class JiboInteractionServiceTests
Assert.Equal("EmotionQuery", decision.ContextUpdates![ChitchatRouteKey]);
}
[Fact]
public async Task BuildDecisionAsync_HowAreYou_UsesRememberedNameForStateDrivenReply()
{
var memoryStore = new InMemoryPersonalMemoryStore();
memoryStore.SetName(new PersonalMemoryTenantScope("acct-how", "loop-how", "device-how"), "jake");
var service = CreateService(memoryStore);
var decision = await service.BuildDecisionAsync(new TurnContext
{
RawTranscript = "how are you",
NormalizedTranscript = "how are you",
Attributes = new Dictionary<string, object?>
{
["accountId"] = "acct-how",
["loopId"] = "loop-how"
},
DeviceId = "device-how"
});
Assert.Equal("how_are_you", decision.IntentName);
Assert.Equal("All systems are go, Jake.", decision.ReplyText);
}
[Theory]
[InlineData("what are you up to", "being helpful")]
[InlineData("what are you doing", "making people smile")]