Conversation Flow
Standard companion chat prepares a child-specific turn, sends it through the Gemini conversation generator, and returns a parsed reply with emotion and a robot suggestion.
[!NOTE] The source defines this flow, but does not verify that a deployed chat request reached Gemini or received a successful response.
flowchart TD
post["Chat POST handler"] --> checks["Authenticate; validate child and message; look up owned child"]
checks --> access["Check voice-AI consent, conditional age policy, and API key"]
access --> gate{"Access allowed?"}
gate -- "No" --> denied["Return access response; stop before session processing"]
gate -- "Yes" --> session["Validate or create session"]
session --> context["Assemble instructions and history"]
context --> ready{"Gemini ready?"}
ready -- "No" --> failure["Return failed turn"]
ready -- "Yes" --> models["Request with selected model"]
models --> turn["Gemini turn result"]
turn -- "Failed" --> failure
turn -- "Successful" --> persist["Batch transcript and session update"]
persist --> response["Return reply and suggestions"]
Gemini provider selection
The handler calls generateGeminiConversationTurn in app/api/_lib/gemini-conversation.ts with assembled instructions, history, child input, maxReply set to 1600, and maxOutputTokens set to 4096. The generator first checks readiness, then builds a deduplicated model list from GEMINI_CHAT_MODEL, gemini-3.6-flash, and gemini-flash-latest. It tries another model only when the result reason is model_not_found; other failures return immediately, and exhausting the list returns the last failure.
[!IMPORTANT] The access gate requires voice-AI consent, distinct from the long-term-memory consent used for the memory input below. It requires child-data-policy confirmation only when the child’s age is unknown or under 13. The generator’s readiness check always requires both
GEMINI_API_KEYandGEMINI_CHILD_DATA_POLICY_CONFIRMED. So a child aged 13 or older can pass the access gate while the generator returnsnot_configured; check the server-side child-data-policy setting if that occurs.
Context assembled for the turn
The handler combines profile, memory, history, locale, and personality inputs into the instructions and conversation context passed to Gemini.
[
{
"title": "Profile",
"body": "When a profile exists, its context includes preferred name (falling back to the child's name), age, dialect, communication style, attention span, interests, favorites, learning goals, and routines. Without a profile, the handler uses age and preferred language."
},
{
"title": "Approved memories",
"body": "The handler queries memories only when long-term-memory consent is present. It limits them to approved, non-sensitive, unexpired records, ordered by most recently updated, with a maximum of 20. If none are supplied, the instructions say there are no approved long-term memories."
},
{
"title": "Conversation history",
"body": "The handler retrieves up to 12 messages for the session in descending creation order, reverses them, and maps Mogo messages to assistant turns and other messages to user turns. The Gemini generator takes only the last 10 history entries, skips entries whose trimmed content is empty, then appends the current input."
},
{
"title": "Locale and personality",
"body": "The parser accepts mischievous and maps every other personality value to nice. The handler passes the parsed personality and locale to personalityInstructions, which selects localized safety, tone, language, and robot instructions."
}
]
Gemini outcomes
A failed generator result takes the handler’s error branch, which returns a 502 using the provider reason or assistant_failed.
| Generator outcome | Handler result |
|---|---|
| Readiness check fails | not_configured; no model request |
| Provider request throws | network_error |
| Provider returns a non-success response | A reason mapped from the response status and diagnostic |
| Safety block is reported | safety_blocked |
| Parsed reply is empty | empty_response |
| Parsed turn succeeds | Return a successful response |
parseConversationTurn in app/api/_lib/mogo-conversation.ts treats non-JSON text as a reply truncated to the configured maximum. For JSON, it trims a string reply or falls back to the raw text when reply is not a string; an empty reply is rejected.
[!WARNING] Treat the returned
robotvalue as a suggestion, not a completed robot action. The chat response does not establish that any downstream consumer acted on it or that a physical effect occurred.