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()