Interaction Layer – Runtime & MQTT¶
This page explains how the interaction layer runs at runtime: from microphone input, through the dialogue manager and multi-agent graph, to MQTT messages that drive speech and animations.
Runtime Entrypoint (nadine/__main__.py)¶
python -m nadine launches the interaction stack.
Initialization¶
Nadine.__init__(nomqtt: bool):
- Loads environment variables from
interaction/.env. - Reads the current language from
language_config(default: French). - Creates:
loggerviaLoggersFactory.getLogger().Translationfor multi-language translations.UIwindow (status, user text, agent reply).STTManagerfor microphone + Google STT.MQTTCommunicationfor all interaction-layer MQTT I/O.DialogueManagerfor multi-agent conversation logic.
Modes¶
__main__.py supports:
- Default mode: voice + UI + MQTT.
nadine = Nadine(nomqtt=False)nadine.start_all()--nomqtt:- Runs without MQTT (e.g. for offline experimentation).
--chatmode:- Text-only mode:
- Creates
DialogueManager()directly. - Simple REPL in the console:
- User types a message, DM returns text.
- Creates
Voice Interaction Flow¶
In normal mode each user utterance passes through three phases: listen, process, respond.
Listen¶
STT runs continuously and hands finished utterances to Nadine.
Nadine.start_all():- Starts STT listening.
- Sets initial UI status (microphone, user availability, etc.).
-
Starts the UI event loop.
-
User speaks:
STTManagerconverts audio to text and callsNadine.user_speech_detected(text).
Process¶
user_speech_detected gates the microphone and hands the text to the dialogue manager.
- user_speech_detected:
- Suspends STT while Nadine is speaking (when MQTT is enabled).
- Translates user input to English if current
languageis not English. - Calls
DialogueManager.processInput(text_en).
Respond¶
The reply is shown, spoken, and the microphone is re-armed.
- Updates the UI (user input + agent output).
- Calls
mqtt_comm.speak(reply_en, self.language)to trigger speech and animation. - After response:
- STT is re-activated.
- UI status is updated again to reflect current listening/speaking state.
This loop repeats for each user utterance.
DialogueManager Runtime (dm.py)¶
DialogueManager.processInput(user_input: str) orchestrates the main interaction logic.
Initialization (__init__)¶
- Creates:
mqtt_comm– sharedMQTTCommunicationinstance.logger– shared logger.chat_history– list of LangChainHumanMessage/AIMessage.user_id– unique ID viagenerate_unique_user_id().user_info– default profile dict viauser_info_init(user_id).c_state– current graph state (custom state dict).multi_agent_graph– compiled LangGraph frombuild_agent_graph().conversation_limit– how many recent messages to keep inchat_history(4, i.e. two user–robot exchanges).- Does not warm up models itself: the Ollama models are pre-warmed by
start_nadine.shbefore the interaction process starts (warmup_llmsinutils.pyis an unused helper).
Prepare the turn¶
Before the graph runs, DM syncs the user, answers trivial inputs directly, and resolves any pending name confirmation.
State refresh & name confirmation¶
Before invoking the graph, DM:
- Checks face-recognition info via
mqtt_comm.get_detected_user_info(). _refresh_state(detected_user_id):- If a different user was detected:
- Saves an episodic memory for the outgoing user (
_save_episodic_on_switch, skipped for unknown users). - Loads the new user's
user_info.jsonfrom the interaction DB. - Clears
chat_historyand resetsc_state, so greeting flags, affect, and memories are per user. - Resets the language to English through
set_language_callback.
- Saves an episodic memory for the outgoing user (
- Updates
c_stateviadefault_custom_state(c_state, chat_history, user_info).
Pre-graph short-circuits¶
Before the graph runs, _short_circuit_reply answers two kinds of input directly and appends them to the history:
- A request for a joke returns a random entry from
french_kids_jokes.json. - "stop talking" and similar phrases return "Okay. I will stop talking."
Independently, should_trigger_handshake matches phrases such as "shake hands" and calls mqtt_comm.give_handshake() before the graph runs.
If the memory agents previously requested name confirmation, name_confirmation(user_input):
- Handles:
- Confirming a suggested existing user.
- Creating a new user from a name explicitly mentioned by the user.
- Rotating through remaining candidate names if needed.
- Synchronizes user info back to face recognition via MQTT.
Run the graph¶
One invoke call runs the whole multi-agent graph on the prepared state.
Graph invocation¶
After state prep:
results = multi_agent_graph.invoke(self.c_state)
The result includes:
- Updated
user_info - Updated
affectstate - Updated memories
final_message– raw response text/JSON- Optional
name_confirmationpayload intent– classified intent (e.g.,first_greeting,end_conversation)
Respond¶
DM adopts the new state, extracts the reply, and triggers any motion tied to the intent.
Result handling¶
The DM:
- Optionally calls
set_language_callbackifresults["language"]changed. - Adopts
self.c_state = results. - Ensures
user_infoanduser_idin DM align with graph output. - Extracts:
- Final text + emotion via
_extract_robot_response(results). - Updates
chat_historywith the new AI message. - Resets chat history on
end_conversationand trims toconversation_limitmessages. After the reply is spoken,__main__.pyalso resets the language to French onend_conversation.
Motion side-effects¶
For certain intents (first_greeting, end_conversation), DM asks the control layer to wave:
self.mqtt_comm.give_wave()→ publishes anadine/agent/control/animationcommand.
MQTT Topics (Interaction Perspective)¶
The interaction layer uses MQTTCommunication as its main MQTT client.
Subscribed
nadine/graph/user_detected- From perception layer.
- Payload:
{"user_name": str, "user_id": str, "confidence": float}. -
Used to track which user is currently in front of Nadine.
-
nadine/graph/face_stored - From perception layer.
- Payload:
{"user_name": str, "user_id": str, "status": "face_stored" | "face_stored_new_user"}. -
Used to update
interaction/db/user_ids.jsonwhen new users are added. -
nadine/agent/feedback/start_speak,nadine/agent/feedback/end_speak - From control layer.
- Indicate when Nadine starts/finishes speaking.
Published
nadine/face_recognition/user_info- To perception layer.
- Payload:
{"user_name": ..., "user_id": ...}. -
Used when memory agents or DM finalize a user’s name → triggers face storage.
-
nadine/perception/capture_current_view - To perception layer.
- Payload: image path string.
-
Used to request a snapshot for vision/visual memory.
-
nadine/agent/control/speak - To control layer.
- Payload: final response text (in the appropriate language).
-
Triggers TTS + lip-sync + associated animations.
-
nadine/agent/control/animation - To control layer.
- Payload: animation name.
-
Used by helper methods like
give_wave,give_greeting,give_smile,give_handshake, etc. -
nadine/agent/control/look_at_target - To control layer.
- Payload: posture name (
Posture_LookAtInterviewer,Posture_LookAtZoom, orLOOKUPPostureDefaultto clear the target). -
Sent by the UI's "Gaze Direction" radio buttons via
mqtt_comm.look_at_target. -
nadine/affect/state - To perception layer.
- Payload:
{"label": str, "arousal": float, "intensity": float}. - Published by the affective appraisal node after every appraisal; perception's selective memory uses it to decide whether the current scene is memorable.
Quick Dev Tips¶
- To debug the LangGraph flow in isolation, run
graph.pydirectly; itsasync main()REPL (started withasyncio.run) drives the graph with its own simplified name-confirmation handling. - To test the dialogue manager without STT/UI, you can either:
-
Run the built‑in chat mode:
cd interaction conda activate nadine python -m nadine --chatmode -
Or call the
DialogueManagerdirectly from a small script:from nadine.agents.dm import DialogueManager dm = DialogueManager() while True: text = input("You: ") if text.lower() == "exit": break reply = dm.processInput(text) print("Nadine:", reply) -
Use the Agents & Graph and Memory & RAG pages to understand how state fields like
user_info,conversation_memory,episode_memory, andvisual_memoryare set and used.