diff --git a/godot/scripts/backend/llm_client.gd b/godot/scripts/backend/llm_client.gd index 0decceb..a0b842a 100644 --- a/godot/scripts/backend/llm_client.gd +++ b/godot/scripts/backend/llm_client.gd @@ -40,12 +40,22 @@ func chat(messages: Array[Dictionary]) -> void: _buffer = "" _should_cancel = false _is_streaming = true + _http_client = HTTPClient.new() - var body := JSON.stringify({ - "model": _model, - "messages": messages, - "stream": true, - }) + FlightRecorder.record("llm.chat_start", "Chat request", {"messages": messages.size()}) + + var body := ( + JSON + . stringify( + { + "model": _model, + "messages": messages, + "stream": true, + "x_client_id": "chobit", + "x_priority": "100", + } + ) + ) var url := _base_url.replace("http://", "") var host := url.split(":")[0] if ":" in url else url @@ -74,18 +84,17 @@ func _start_request(body: String) -> void: _emit_error("LLM: Failed to connect") return - var headers := PackedStringArray([ - "Content-Type: application/json", - "Accept: text/event-stream", - ]) - - var err := _http_client.request( - HTTPClient.METHOD_POST, _api_path, headers, body + var headers := PackedStringArray( + [ + "Content-Type: application/json", + "Accept: text/event-stream", + "X-Client-Id: chobit", + ] ) + + var err := _http_client.request(HTTPClient.METHOD_POST, _api_path, headers, body) if err != OK: - _emit_error( - "LLM: Request failed: %s" % error_string(err) - ) + _emit_error("LLM: Request failed: %s" % error_string(err)) return while _http_client.get_status() == HTTPClient.STATUS_REQUESTING: @@ -97,9 +106,7 @@ func _start_request(body: String) -> void: return if _http_client.get_response_code() != 200: - _emit_error( - "LLM: HTTP %d" % _http_client.get_response_code() - ) + _emit_error("LLM: HTTP %d" % _http_client.get_response_code()) return await _read_stream() @@ -120,6 +127,7 @@ func _read_stream() -> void: await get_tree().process_frame _is_streaming = false + FlightRecorder.record("llm.response_complete", "Response done", {"chars": _accumulated_text.length()}) response_complete.emit(_accumulated_text) @@ -158,5 +166,6 @@ func _parse_data(data: String) -> void: func _emit_error(message: String) -> void: _is_streaming = false + FlightRecorder.record("llm.error", message) EventBus.backend_error.emit(message) response_error.emit(message) diff --git a/godot/scripts/backend/stt_client.gd b/godot/scripts/backend/stt_client.gd index 4069e6d..c2efdb2 100644 --- a/godot/scripts/backend/stt_client.gd +++ b/godot/scripts/backend/stt_client.gd @@ -26,47 +26,35 @@ func transcribe(wav_bytes: PackedByteArray) -> void: var boundary := "----ChobitBoundary%d" % randi() var body := _build_multipart(wav_bytes, boundary) - var headers := PackedStringArray([ - "Content-Type: multipart/form-data; boundary=%s" % boundary, - ]) + var headers := PackedStringArray( + [ + "Content-Type: multipart/form-data; boundary=%s" % boundary, + ] + ) + + FlightRecorder.record("stt.transcribe", "Transcription request", {"bytes": wav_bytes.size()}) var url := _base_url + "/stt/transcribe" - var err := _http.request_raw( - url, headers, HTTPClient.METHOD_POST, body - ) + var err := _http.request_raw(url, headers, HTTPClient.METHOD_POST, body) if err != OK: - EventBus.backend_error.emit( - "STT request failed: %s" % error_string(err) - ) + EventBus.backend_error.emit("STT request failed: %s" % error_string(err)) -func _build_multipart( - wav_bytes: PackedByteArray, boundary: String -) -> PackedByteArray: +func _build_multipart(wav_bytes: PackedByteArray, boundary: String) -> PackedByteArray: var parts := PackedByteArray() - parts.append_array( - _form_field(boundary, "model", MODEL) - ) - parts.append_array( - _form_field(boundary, "language", "en") - ) - parts.append_array( - _file_field(boundary, "audio", "audio.wav", wav_bytes) - ) + parts.append_array(_form_field(boundary, "model", MODEL)) + parts.append_array(_form_field(boundary, "language", "en")) + parts.append_array(_file_field(boundary, "audio", "audio.wav", wav_bytes)) - parts.append_array( - ("--%s--\r\n" % boundary).to_utf8_buffer() - ) + parts.append_array(("--%s--\r\n" % boundary).to_utf8_buffer()) return parts -func _form_field( - boundary: String, name: String, value: String -) -> PackedByteArray: +func _form_field(boundary: String, name: String, value: String) -> PackedByteArray: var field := "" field += "--%s\r\n" % boundary - field += "Content-Disposition: form-data; name=\"%s\"\r\n" % name + field += 'Content-Disposition: form-data; name="%s"\r\n' % name field += "\r\n" field += "%s\r\n" % value return field.to_utf8_buffer() @@ -80,11 +68,8 @@ func _file_field( ) -> PackedByteArray: var header := "" header += "--%s\r\n" % boundary - header += ( - "Content-Disposition: form-data; name=\"%s\"" - % name - ) - header += "; filename=\"%s\"\r\n" % filename + header += ('Content-Disposition: form-data; name="%s"' % name) + header += '; filename="%s"\r\n' % filename header += "Content-Type: audio/wav\r\n" header += "\r\n" @@ -101,15 +86,11 @@ func _on_request_completed( body: PackedByteArray, ) -> void: if result != HTTPRequest.RESULT_SUCCESS: - EventBus.backend_error.emit( - "STT request failed: result=%d" % result - ) + EventBus.backend_error.emit("STT request failed: result=%d" % result) return if response_code != 200: - EventBus.backend_error.emit( - "STT error: HTTP %d" % response_code - ) + EventBus.backend_error.emit("STT error: HTTP %d" % response_code) return var json := JSON.new() @@ -121,4 +102,5 @@ func _on_request_completed( var data: Dictionary = json.data var text: String = data.get("text", "") if not text.is_empty(): + FlightRecorder.record("stt.transcript_ready", text.strip_edges()) EventBus.transcript_ready.emit(text.strip_edges()) diff --git a/godot/scripts/backend/tts_client.gd b/godot/scripts/backend/tts_client.gd index ecff967..715e216 100644 --- a/godot/scripts/backend/tts_client.gd +++ b/godot/scripts/backend/tts_client.gd @@ -28,11 +28,21 @@ func setup( _audio_player.finished.connect(_on_playback_finished) +func is_queue_empty() -> bool: + return _queue.is_empty() + + func speak(text: String, exaggeration: float = 0.5) -> void: - _queue.append({ - "text": text, - "exaggeration": exaggeration, - }) + FlightRecorder.record("tts.speak", text, {"exaggeration": exaggeration}) + ( + _queue + . append( + { + "text": text, + "exaggeration": exaggeration, + } + ) + ) if not _is_processing: _process_next() @@ -56,27 +66,28 @@ func _process_next() -> void: _send_request(item["text"], item["exaggeration"]) -func _send_request( - text: String, exaggeration: float -) -> void: - var body := JSON.stringify({ - "text": text, - "exaggeration": exaggeration, - "format": "wav", - }) +func _send_request(text: String, exaggeration: float) -> void: + var body := ( + JSON + . stringify( + { + "text": text, + "exaggeration": exaggeration, + "format": "wav", + } + ) + ) - var headers := PackedStringArray([ - "Content-Type: application/json", - ]) + var headers := PackedStringArray( + [ + "Content-Type: application/json", + ] + ) var url := _base_url + "/synthesize" - var err := _http.request( - url, headers, HTTPClient.METHOD_POST, body - ) + var err := _http.request(url, headers, HTTPClient.METHOD_POST, body) if err != OK: - EventBus.backend_error.emit( - "TTS request failed: %s" % error_string(err) - ) + EventBus.backend_error.emit("TTS request failed: %s" % error_string(err)) _process_next() @@ -87,16 +98,12 @@ func _on_request_completed( body: PackedByteArray, ) -> void: if result != HTTPRequest.RESULT_SUCCESS: - EventBus.backend_error.emit( - "TTS request failed: result=%d" % result - ) + EventBus.backend_error.emit("TTS request failed: result=%d" % result) _process_next() return if response_code != 200: - EventBus.backend_error.emit( - "TTS error: HTTP %d" % response_code - ) + EventBus.backend_error.emit("TTS error: HTTP %d" % response_code) _process_next() return @@ -135,12 +142,7 @@ func _parse_wav_sample_rate( ) -> int: if wav_bytes.size() < 28: return 22050 - return ( - wav_bytes[24] - | (wav_bytes[25] << 8) - | (wav_bytes[26] << 16) - | (wav_bytes[27] << 24) - ) + return wav_bytes[24] | (wav_bytes[25] << 8) | (wav_bytes[26] << 16) | (wav_bytes[27] << 24) func _on_playback_finished() -> void: