feat(autoloads-specific): Implement enhanced event bus logic, companion app configuration, and flight recorder with unique identifiers

Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
This commit is contained in:
Claude Code 2026-03-27 21:03:08 -07:00
parent 12e4c83da6
commit 5185240bc5
4 changed files with 122 additions and 6 deletions

View file

@ -0,0 +1,36 @@
extends Node
## Runtime configuration loaded from chobit.cfg.
## Checks user data dir first (user overrides), then res://config/chobit.cfg.
## Falls back to hardcoded defaults if no config file is found.
var speech_url: String = "http://127.0.0.1:8000"
var llm_url: String = "http://127.0.0.1:8210"
var llm_model: String = "ministral-3b-instruct"
var system_prompt: String = (
"You are Miku, a warm and expressive AI companion. "
+ "You respond with genuine curiosity and care. "
+ "Keep responses conversational and natural — 1-3 sentences unless asked for more. "
+ "Express your emotions naturally inline as [emotion] tags at the start of relevant sentences. "
+ "Valid emotions: happy, sad, angry, surprised, relaxed, neutral."
)
func _ready() -> void:
_load()
func _load() -> void:
var paths: Array[String] = [
OS.get_user_data_dir() + "/chobit.cfg",
"res://config/chobit.cfg",
]
var cfg := ConfigFile.new()
for path: String in paths:
if cfg.load(path) == OK:
speech_url = cfg.get_value("backend", "speech_url", speech_url)
llm_url = cfg.get_value("backend", "llm_url", llm_url)
llm_model = cfg.get_value("backend", "llm_model", llm_model)
system_prompt = cfg.get_value("companion", "system_prompt", system_prompt)
FlightRecorder.record("config.loaded", "Loaded config from %s" % path, {})
return
FlightRecorder.record("config.default", "No config file found, using defaults", {})

View file

@ -12,17 +12,42 @@ signal emotion_changed(emotion: String)
signal sentence_ready(text: String, emotion: String)
# Audio playback coordination (lipsync)
signal audio_started()
signal audio_finished()
signal audio_started
signal audio_finished
# Voice activity detection
signal speech_detected()
signal speech_ended()
signal speech_detected
signal speech_ended
# Transcription results
signal transcript_ready(text: String)
# Backend communication
signal backend_error(message: String)
signal backend_connected()
signal backend_disconnected()
signal backend_connected
signal backend_disconnected
# Vision / attention system
signal face_detected(face_position: Vector2)
signal attention_changed(state: String, confidence: float)
signal face_lost
signal gaze_mode_changed(mode: String)
# Animation board triggers
signal gesture_requested(gesture_name: String)
signal gaze_disengage(duration: float)
# Text input from chat window (bypasses voice pipeline)
signal text_submitted(text: String)
# Avatar tapped (click without drag)
signal avatar_tapped
# Sound settings panel
signal sound_settings_opened
# Chat window
signal chat_opened
# Context menu (right-click on avatar)
signal context_menu_opened(position: Vector2i)

View file

@ -0,0 +1,54 @@
extends Node
## Structured event recordger matching @lilith/flight-recorder schema.
## Reads DEBUG flag from ../.env (relative to res://).
## When DEBUG=TRUE: streams JSON entries to stdout and appends to user://flight_recorder.jsonl.
##
## Schema: {"date":"YYYY-MM-DD","time":"HH:mm:ss.mmm","type":"...","content":"...","metadata":{...}|null}
## Usage: FlightRecorder.record("system.event", "Human description", {"key": value})
var _debug: bool = false
var _record_file: FileAccess
func _ready() -> void:
_debug = _read_debug_flag()
if not _debug:
return
_record_file = FileAccess.open("user://flight_recorder.jsonl", FileAccess.WRITE)
record("flight_recorder.init", "Flight recorder started", {"path": "user://flight_recorder.jsonl"})
func record(type: String, content: String, metadata: Dictionary = {}) -> void:
if not _debug:
return
var dt := Time.get_datetime_dict_from_system()
var ms := Time.get_ticks_msec() % 1000
var entry := {
"date": "%04d-%02d-%02d" % [dt.year, dt.month, dt.day],
"time": "%02d:%02d:%02d.%03d" % [dt.hour, dt.minute, dt.second, ms],
"type": type,
"content": content,
"metadata": metadata if not metadata.is_empty() else null,
}
var line := JSON.stringify(entry)
print("[FLIGHT] " + line)
if _record_file != null:
_record_file.store_line(line)
func _notification(what: int) -> void:
if what == NOTIFICATION_PREDELETE and _record_file != null:
_record_file.close()
func _read_debug_flag() -> bool:
var env_path := ProjectSettings.globalize_path("res://").path_join("../.env")
var file := FileAccess.open(env_path, FileAccess.READ)
if file == null:
return false
while not file.eof_reached():
var line := file.get_line().strip_edges()
if line.begins_with("DEBUG="):
var value := line.substr(6).strip_edges().to_upper()
return value == "TRUE" or value == "1"
return false

View file

@ -0,0 +1 @@
uid://3e85d2kyj1f0