jibo photo skills voice activation

This commit is contained in:
Jacob Dubin
2026-04-20 22:55:42 -05:00
parent ab47ad7a2d
commit e1dca81519
8 changed files with 266 additions and 1 deletions

View File

@@ -79,6 +79,9 @@ public sealed class DemoConversationBroker(JiboInteractionService interactionSer
"alarm_menu" => false,
"timer_value" => false,
"alarm_value" => false,
"photo_gallery" => false,
"snapshot" => false,
"photobooth" => false,
"news" => false,
_ => true
};

View File

@@ -39,6 +39,9 @@ public sealed class JiboInteractionService(
"alarm_menu" => BuildClockLaunchDecision("alarm", "Opening the alarm."),
"timer_value" => BuildTimerValueDecision(lowered),
"alarm_value" => BuildAlarmValueDecision(lowered),
"photo_gallery" => BuildPhotoGalleryLaunchDecision(),
"snapshot" => BuildPhotoCreateDecision("snapshot", "Taking a picture.", "createOnePhoto"),
"photobooth" => BuildPhotoCreateDecision("photobooth", "Starting photobooth.", "createSomePhotos"),
"hello" => new JiboInteractionDecision("hello", randomizer.Choose(catalog.GreetingReplies)),
"how_are_you" => new JiboInteractionDecision("how_are_you", randomizer.Choose(catalog.HowAreYouReplies)),
"yes" => new JiboInteractionDecision("yes", "Yes."),
@@ -146,6 +149,18 @@ public sealed class JiboInteractionService(
return "word_of_the_day";
}
if (string.Equals(clientIntent, "loadMenu", StringComparison.OrdinalIgnoreCase) &&
clientEntities.TryGetValue("destination", out var photoDestination))
{
return photoDestination.ToLowerInvariant() switch
{
"snapshot" => "snapshot",
"photobooth" => "photobooth",
"gallery" or "photo-gallery" or "photos" => "photo_gallery",
_ => "chat"
};
}
if (string.Equals(clientIntent, "askForTime", StringComparison.OrdinalIgnoreCase))
{
return "time";
@@ -251,6 +266,38 @@ public sealed class JiboInteractionService(
return "radio";
}
if (MatchesAny(
loweredTranscript,
"snap a picture",
"take a picture",
"take a photo",
"snap a photo"))
{
return "snapshot";
}
if (MatchesAny(
loweredTranscript,
"photo booth",
"photobooth",
"open photobooth",
"start photobooth"))
{
return "photobooth";
}
if (MatchesAny(
loweredTranscript,
"photo gallery",
"open the gallery",
"open photo gallery",
"show my photos",
"open my photos",
"gallery"))
{
return "photo_gallery";
}
if (MatchesAny(loweredTranscript, "dance", "boogie"))
{
return "dance";
@@ -352,6 +399,32 @@ public sealed class JiboInteractionService(
});
}
private static JiboInteractionDecision BuildPhotoGalleryLaunchDecision()
{
return new JiboInteractionDecision(
"photo_gallery",
"Opening the photo gallery.",
"@be/gallery",
new Dictionary<string, object?>(StringComparer.OrdinalIgnoreCase)
{
["skillId"] = "@be/gallery",
["localIntent"] = "menu"
});
}
private static JiboInteractionDecision BuildPhotoCreateDecision(string intentName, string replyText, string localIntent)
{
return new JiboInteractionDecision(
intentName,
replyText,
"@be/create",
new Dictionary<string, object?>(StringComparer.OrdinalIgnoreCase)
{
["skillId"] = "@be/create",
["localIntent"] = localIntent
});
}
private static JiboInteractionDecision BuildClockLaunchDecision(string domain, string replyText)
{
return new JiboInteractionDecision(

View File

@@ -25,7 +25,11 @@ public sealed class ResponsePlanToSocketMessagesMapper
var isWordOfDayGuess = string.Equals(plan.IntentName, "word_of_the_day_guess", StringComparison.OrdinalIgnoreCase);
var isRadioLaunch = string.Equals(plan.IntentName, "radio", StringComparison.OrdinalIgnoreCase) ||
string.Equals(plan.IntentName, "radio_genre", StringComparison.OrdinalIgnoreCase);
var isPhotoGalleryLaunch = string.Equals(plan.IntentName, "photo_gallery", StringComparison.OrdinalIgnoreCase);
var isPhotoCreateLaunch = string.Equals(plan.IntentName, "snapshot", StringComparison.OrdinalIgnoreCase) ||
string.Equals(plan.IntentName, "photobooth", StringComparison.OrdinalIgnoreCase);
var isClockSkillLaunch = string.Equals(skill?.SkillName, "@be/clock", StringComparison.OrdinalIgnoreCase);
var localIntent = ReadSkillPayloadString(skill, "localIntent");
var clockIntent = ReadSkillPayloadString(skill, "clockIntent");
var clockDomain = ReadSkillPayloadString(skill, "domain");
var timerHours = ReadSkillPayloadString(skill, "hours");
@@ -41,6 +45,8 @@ public sealed class ResponsePlanToSocketMessagesMapper
? "menu"
: isRadioLaunch
? "menu"
: (isPhotoGalleryLaunch || isPhotoCreateLaunch) && !string.IsNullOrWhiteSpace(localIntent)
? localIntent
: isClockSkillLaunch && !string.IsNullOrWhiteSpace(clockIntent)
? clockIntent
: isWordOfDayGuess
@@ -54,6 +60,8 @@ public sealed class ResponsePlanToSocketMessagesMapper
? string.Empty
: isRadioLaunch
? transcript
: isPhotoGalleryLaunch || isPhotoCreateLaunch
? transcript
: isClockSkillLaunch
? transcript
: string.Equals(clientIntent, "guess", StringComparison.OrdinalIgnoreCase) && !string.IsNullOrWhiteSpace(nluGuess)
@@ -67,6 +75,8 @@ public sealed class ResponsePlanToSocketMessagesMapper
? ["word-of-the-day/menu"]
: isRadioLaunch
? Array.Empty<string>()
: isPhotoGalleryLaunch || isPhotoCreateLaunch
? string.Equals(messageType, "CLIENT_NLU", StringComparison.OrdinalIgnoreCase) ? rules : Array.Empty<string>()
: isClockSkillLaunch
? string.Equals(messageType, "CLIENT_NLU", StringComparison.OrdinalIgnoreCase) ? rules : Array.Empty<string>()
: isWordOfDayGuess
@@ -108,6 +118,8 @@ public sealed class ResponsePlanToSocketMessagesMapper
entities,
isWordOfDayLaunch ? "@be/word-of-the-day" :
isRadioLaunch ? "@be/radio" :
isPhotoGalleryLaunch ? "@be/gallery" :
isPhotoCreateLaunch ? "@be/create" :
isClockSkillLaunch ? "@be/clock" :
null),
match = new
@@ -182,6 +194,24 @@ public sealed class ResponsePlanToSocketMessagesMapper
DelayMs: 125));
}
if ((isPhotoGalleryLaunch || isPhotoCreateLaunch) &&
!string.Equals(messageType, "CLIENT_NLU", StringComparison.OrdinalIgnoreCase))
{
var skillId = isPhotoGalleryLaunch ? "@be/gallery" : "@be/create";
messages.Add(new SocketReplyPlan(
JsonSerializer.Serialize(BuildSkillRedirectPayload(
transId,
skillId,
outboundIntent,
outboundAsrText,
outboundRules,
entities)),
DelayMs: 75));
messages.Add(new SocketReplyPlan(
JsonSerializer.Serialize(BuildCompletionOnlySkillPayload(transId, skillId)),
DelayMs: 125));
}
if (emitSkillActions && speak is not null)
{
messages.Add(new SocketReplyPlan(

View File

@@ -569,6 +569,9 @@ public sealed class WebSocketTurnFinalizationService(
!string.Equals(plan.IntentName, "alarm_menu", StringComparison.OrdinalIgnoreCase) &&
!string.Equals(plan.IntentName, "timer_value", StringComparison.OrdinalIgnoreCase) &&
!string.Equals(plan.IntentName, "alarm_value", StringComparison.OrdinalIgnoreCase) &&
!string.Equals(plan.IntentName, "photo_gallery", StringComparison.OrdinalIgnoreCase) &&
!string.Equals(plan.IntentName, "snapshot", StringComparison.OrdinalIgnoreCase) &&
!string.Equals(plan.IntentName, "photobooth", StringComparison.OrdinalIgnoreCase) &&
(messageType != "CLIENT_NLU" ||
string.Equals(plan.IntentName, "word_of_the_day_guess", StringComparison.OrdinalIgnoreCase));
var replies = ResponsePlanToSocketMessagesMapper.Map(plan, finalizedTurn, session, emitSkillActions).Select(map => new WebSocketReply