156 lines
4.8 KiB
GDScript
156 lines
4.8 KiB
GDScript
extends Control
|
|
|
|
@onready var _prompt_edit: TextEdit = $MarginContainer/Scroll/VBox/PromptEdit
|
|
@onready var main: Control = $"."
|
|
@onready var _http: HTTPRequest = $HTTPRequest
|
|
@onready var output_text: RichTextLabel = $MarginContainer/Scroll/VBox/OutputText
|
|
@onready var option_button: OptionButton = $MarginContainer/Scroll/VBox/OptionButton
|
|
@onready var generate_button: Button = $MarginContainer/Scroll/VBox/GenerateRow/GenerateButton
|
|
@onready var helper = $VBoxContainer/SubViewportContainer/SubViewport/base_node/helper
|
|
|
|
const API_URL := "https://apphubai.wolke.uni-greifswald.de/v1" #TODO insert your url to the llm / api-gateway here
|
|
const API_TOKEN := ""
|
|
const API_MODEL := "unsloth/gemma-4-31B-it-qat-GGUF:UD-Q4_K_XL"
|
|
const MAX_RESPONSE_TOKENS := 200
|
|
var document_id
|
|
|
|
signal closing_chatbot
|
|
|
|
|
|
func _ready():
|
|
_http.request_completed.connect(Callable(self, "_on_request_completed"))
|
|
_http.timeout = 180.0
|
|
document_id = option_button.selected
|
|
helper.play_idle()
|
|
|
|
|
|
func _unhandled_input(event: InputEvent) -> void:
|
|
if not main.visible:
|
|
return
|
|
print(option_button.selected)
|
|
if event.is_action_pressed("drop"):
|
|
get_viewport().set_input_as_handled()
|
|
_on_close_pressed()
|
|
|
|
#if event.is_action_pressed("advance_dialogue"):
|
|
# get_viewport().set_input_as_handled()
|
|
# _on_generate_pressed()
|
|
|
|
|
|
func _on_generate_pressed() -> void:
|
|
var user_text := _prompt_edit.text.strip_edges()
|
|
document_id = option_button.selected
|
|
TextToSpeech._stop_dialogue()
|
|
if user_text.is_empty():
|
|
print("Prompt ist leer")
|
|
return
|
|
|
|
var document := _load_txt("res://addons/godot_llama/demo/documents/%s.md" % [document_id]).strip_edges()
|
|
var combined_user_message := user_text
|
|
if not document.is_empty():
|
|
combined_user_message = "Dokument:\n%s\n\nFrage: %s" % [document, user_text]
|
|
|
|
var body := {
|
|
"model": API_MODEL,
|
|
"messages": [
|
|
{"role": "system", "content": tr("CHATBOT_SYSTEM_PROMPT")},
|
|
{"role": "user", "content": combined_user_message},
|
|
],
|
|
"max_tokens": MAX_RESPONSE_TOKENS,
|
|
"temperature": 0.1,
|
|
}
|
|
var headers := PackedStringArray([
|
|
"Content-Type: application/json",
|
|
"Authorization: Bearer %s" % API_TOKEN,
|
|
])
|
|
var endpoint := API_URL.trim_suffix("/") + "/chat/completions"
|
|
var err := _http.request(endpoint, headers, HTTPClient.METHOD_POST, JSON.stringify(body))
|
|
if err != OK:
|
|
_stop_helper_animation()
|
|
output_text.text = _request_failed("HTTP %s" % error_string(err))
|
|
generate_button.disabled = false
|
|
return
|
|
|
|
$VBoxContainer/SubViewportContainer/SubViewport/AnimationPlayer.play("walk_helper")
|
|
helper.play_think_walk()
|
|
generate_button.disabled = true
|
|
|
|
func _on_request_completed(
|
|
result: int,
|
|
response_code: int,
|
|
_headers: PackedStringArray,
|
|
body: PackedByteArray
|
|
) -> void:
|
|
_stop_helper_animation()
|
|
generate_button.disabled = false
|
|
var txt := body.get_string_from_utf8()
|
|
|
|
if result != HTTPRequest.RESULT_SUCCESS:
|
|
output_text.text = _request_failed("result %s" % result)
|
|
return
|
|
if response_code != 200:
|
|
output_text.text = _request_failed("HTTP %s" % response_code)
|
|
push_warning("Chatbot HTTP %s: %s" % [response_code, txt.left(300)])
|
|
return
|
|
|
|
var json = JSON.parse_string(txt)
|
|
var response_text := _extract_reply(json)
|
|
if response_text.is_empty():
|
|
output_text.text = _request_failed("empty response")
|
|
return
|
|
|
|
output_text.text = response_text
|
|
TextToSpeech._on_dialogue({"text": response_text})
|
|
|
|
func _extract_reply(json) -> String:
|
|
if typeof(json) != TYPE_DICTIONARY:
|
|
return ""
|
|
if json.has("choices") and json["choices"] is Array and not json["choices"].is_empty():
|
|
var choice = json["choices"][0]
|
|
if choice is Dictionary and choice.get("message") is Dictionary:
|
|
return str(choice["message"].get("content", "")).strip_edges()
|
|
if json.has("content"):
|
|
return str(json["content"]).strip_edges()
|
|
if json.get("message") is Dictionary:
|
|
return str(json["message"].get("content", "")).strip_edges()
|
|
return ""
|
|
|
|
func _request_failed(detail: String) -> String:
|
|
if TranslationServer.get_locale().begins_with("en"):
|
|
return "The request failed (%s)." % detail
|
|
return "Die Anfrage ist fehlgeschlagen (%s)." % detail
|
|
|
|
func _stop_helper_animation() -> void:
|
|
$VBoxContainer/SubViewportContainer/SubViewport/AnimationPlayer.stop()
|
|
helper.play_idle()
|
|
|
|
func _load_txt(path: String) -> String:
|
|
var file := FileAccess.open(path, FileAccess.READ)
|
|
print(file)
|
|
if file == null:
|
|
return ""
|
|
|
|
var content := file.get_as_text()
|
|
file.close()
|
|
|
|
return content
|
|
|
|
func _on_close_pressed() -> void:
|
|
main.visible = false
|
|
Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)
|
|
TextToSpeech._stop_dialogue()
|
|
Global.chatbot_opened = false
|
|
TextToSpeech._stop_dialogue()
|
|
closing_chatbot.emit()
|
|
|
|
|
|
func _on_prompt_edit_gui_input(event: InputEvent) -> void:
|
|
if event is InputEventKey and event.pressed:
|
|
print(event.keycode)
|
|
if event.keycode == KEY_ENTER:
|
|
if event.shift_pressed:
|
|
_prompt_edit.insert_text_at_caret("\n")
|
|
else:
|
|
_on_generate_pressed()
|
|
accept_event()
|