refactor(backend): ♻️ Improve backend client implementations in llm_client.gd, stt_client.gd, and tts_client.gd with cleaner structure, better error handling, and optimized service integration
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
This commit is contained in:
parent
83fc5e6ccd
commit
0dcf4a596f
3 changed files with 83 additions and 90 deletions
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue