182 lines
4.9 KiB
GDScript
182 lines
4.9 KiB
GDScript
# 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")
|