# Quickstart: Chat UI with NPCs (with Streaming Support) # Save as QuickstartChat.gd and attach to a Control node extends Control @onready var chat_display = RichTextLabel.new() @onready var input_field = LineEdit.new() @onready var send_button = Button.new() @onready var streaming_toggle = CheckBox.new() var current_npc: GumYumNPC var is_streaming: bool = false func _ready(): # Setup UI setup_ui() # Configure API GumyumClient.api_key = "gumyum-pk-your-public-key" GumyumClient.api_secret = "gumyum-sk-your-secret-key" GumyumClient.base_url = "https://npc.gumyum.com" # Spawn a random NPC add_system_message("Connecting to GumYum API...") GumyumClient.npcs.spawn("kingdom", 42, null, _on_npc_spawned) # universe # seed # npc_id (null = random) func setup_ui(): # Create a VBox container var vbox = VBoxContainer.new() vbox.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT) add_child(vbox) # Title var title = Label.new() title.text = "GumYum NPC Chat (Quickstart)" title.add_theme_font_size_override("font_size", 24) vbox.add_child(title) # Streaming toggle streaming_toggle.text = "Enable streaming (typewriter effect)" streaming_toggle.toggled.connect(_on_streaming_toggled) vbox.add_child(streaming_toggle) # Chat display (scrollable) chat_display.bbcode_enabled = true chat_display.scroll_following = true chat_display.custom_minimum_size = Vector2(0, 400) vbox.add_child(chat_display) # Input container var hbox = HBoxContainer.new() vbox.add_child(hbox) # Input field input_field.placeholder_text = "Type your message..." input_field.size_flags_horizontal = Control.SIZE_EXPAND_FILL input_field.text_submitted.connect(_on_text_submitted) hbox.add_child(input_field) # Send button send_button.text = "Send" send_button.pressed.connect(_on_send_pressed) hbox.add_child(send_button) func _on_npc_spawned(npc: GumYumNPC): if npc == null: add_system_message("[color=red]Failed to spawn NPC![/color]") return current_npc = npc # Connect streaming signals for this NPC current_npc.dialogue_stream_started.connect(_on_stream_started) current_npc.dialogue_chunk_received.connect(_on_chunk_received) current_npc.dialogue_stream_ended.connect(_on_stream_ended) add_system_message( "[color=green]Connected to " + npc.name + " the " + npc.profession + "[/color]" ) input_field.editable = true send_button.disabled = false input_field.grab_focus() func _on_streaming_toggled(button_pressed: bool): add_system_message( "[color=yellow]Streaming " + ("enabled" if button_pressed else "disabled") + "[/color]" ) func _on_text_submitted(text: String): send_message(text) func _on_send_pressed(): send_message(input_field.text) func send_message(text: String): if text.is_empty() or current_npc == null or is_streaming: return add_player_message(text) input_field.text = "" # Disable input while processing input_field.editable = false send_button.disabled = true # Check if streaming is enabled var use_streaming = streaming_toggle.button_pressed # Send to NPC with or without streaming current_npc.chat_with_history(text, 0.8, 1500, use_streaming, _on_chat_response) # temperature # max_tokens # streaming based on toggle # Set streaming flag if streaming is enabled if use_streaming: is_streaming = true func _on_chat_response(response): if response.has("error"): add_system_message("[color=red]Error: " + response.error + "[/color]") # Re-enable input on error is_streaming = false input_field.editable = true send_button.disabled = false input_field.grab_focus() elif not streaming_toggle.button_pressed: # Non-streaming response if response.has("choices") and response.choices.size() > 0: var content = response.choices[0]["message"]["content"] add_npc_message(content) # Re-enable input input_field.editable = true send_button.disabled = false input_field.grab_focus() # Streaming-specific functions func _on_stream_started(): # Show NPC is typing chat_display.append_text("[b][color=cyan]" + current_npc.name + ":[/color][/b] ") func _on_chunk_received(chunk: String): # Append each chunk as it arrives chat_display.append_text(chunk) # Auto-scroll to bottom chat_display.scroll_to_line(chat_display.get_line_count() - 1) func _on_stream_ended(_full_response: String, _context: Dictionary, mood_transition: Dictionary): chat_display.append_text("\n\n") # Check for mood changes if mood_transition.has("new_mood"): chat_display.append_text("[i](mood: " + mood_transition.new_mood + ")[/i]\n\n") # Re-enable input is_streaming = false input_field.editable = true send_button.disabled = false input_field.grab_focus() func add_player_message(text: String): chat_display.append_text("[b]You:[/b] " + text + "\n\n") func add_npc_message(text: String): chat_display.append_text( "[b][color=cyan]" + current_npc.name + ":[/color][/b] " + text + "\n\n" ) func add_system_message(text: String): chat_display.append_text("[i]" + text + "[/i]\n\n")