From edc25b45b8f34af801ea3ce549a228120d279b9a Mon Sep 17 00:00:00 2001 From: Divided by Zer0 Date: Mon, 20 Nov 2023 01:02:51 +0100 Subject: [PATCH] feat: Adds worker selection (#116) feat: report request ID --- LucidCreations.tscn | 264 ++++++++++++++---- PopupInfoPanel.gd | 3 + StableHordeClient.gd | 19 +- Workers.gd | 226 +++++++++++++++ addons/stable_horde_client/AIImageTexture.gd | 6 +- addons/stable_horde_client/plugin.gd | 4 + .../stable_horde_client.gd | 14 +- .../stable_horde_workers.gd | 66 +++++ bus.gd | 2 + project.godot | 16 +- theme/assets/iconpark/worker.svg | 1 + 11 files changed, 557 insertions(+), 64 deletions(-) create mode 100644 Workers.gd create mode 100644 addons/stable_horde_client/stable_horde_workers.gd create mode 100644 theme/assets/iconpark/worker.svg diff --git a/LucidCreations.tscn b/LucidCreations.tscn index a05682c..c39ea89 100644 --- a/LucidCreations.tscn +++ b/LucidCreations.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=56 format=2] +[gd_scene load_steps=59 format=2] [ext_resource path="res://StableHordeClient.gd" type="Script" id=1] [ext_resource path="res://addons/stable_horde_client/stable_horde_client.gd" type="Script" id=2] @@ -44,6 +44,9 @@ [ext_resource path="res://theme/components/dark/small_normal_0.tres" type="StyleBox" id=42] [ext_resource path="res://src/Lora/TextualInversion.gd" type="Script" id=43] [ext_resource path="res://src/Lora/TIInject.tscn" type="PackedScene" id=44] +[ext_resource path="res://addons/stable_horde_client/stable_horde_workers.gd" type="Script" id=45] +[ext_resource path="res://Workers.gd" type="Script" id=46] +[ext_resource path="res://theme/assets/iconpark/worker.svg" type="Texture" id=47] [sub_resource type="StreamTexture" id=9] flags = 4 @@ -439,8 +442,10 @@ text = "Cancel" [node name="img2img" type="VBoxContainer" parent="Margin/Panel/Display/Panels/Controls"] unique_name_in_owner = true visible = false -margin_right = 300.0 -margin_bottom = 236.0 +margin_left = 10.0 +margin_top = 10.0 +margin_right = 330.0 +margin_bottom = 340.0 rect_min_size = Vector2( 320, 0 ) size_flags_horizontal = 3 size_flags_vertical = 3 @@ -448,7 +453,7 @@ custom_constants/separation = 8 [node name="Img2ImgEnabled" type="CheckButton" parent="Margin/Panel/Display/Panels/Controls/img2img"] unique_name_in_owner = true -margin_right = 300.0 +margin_right = 320.0 margin_bottom = 28.0 text = "Enabled" @@ -507,7 +512,7 @@ visible = false margin_left = 10.0 margin_top = 10.0 margin_right = 330.0 -margin_bottom = 360.0 +margin_bottom = 400.0 rect_min_size = Vector2( 320, 0 ) size_flags_horizontal = 3 size_flags_vertical = 3 @@ -851,9 +856,9 @@ showcase_index = 1 [node name="TextualInversions" type="VBoxContainer" parent="Margin/Panel/Display/Panels/Controls/Basic"] unique_name_in_owner = true -margin_top = 84.0 +margin_top = 124.0 margin_right = 320.0 -margin_bottom = 116.0 +margin_bottom = 156.0 script = ExtResource( 43 ) [node name="TIAutocompleteHBC" type="HBoxContainer" parent="Margin/Panel/Display/Panels/Controls/Basic/TextualInversions"] @@ -862,7 +867,7 @@ margin_bottom = 32.0 [node name="TISelectLabel" type="Label" parent="Margin/Panel/Display/Panels/Controls/Basic/TextualInversions/TIAutocompleteHBC"] margin_top = 6.0 -margin_right = 50.0 +margin_right = 127.0 margin_bottom = 26.0 rect_min_size = Vector2( 50, 0 ) custom_fonts/font = ExtResource( 40 ) @@ -870,7 +875,7 @@ text = "Textual Inversions" [node name="TIAutoComplete" parent="Margin/Panel/Display/Panels/Controls/Basic/TextualInversions/TIAutocompleteHBC" instance=ExtResource( 26 )] unique_name_in_owner = true -margin_left = 54.0 +margin_left = 131.0 margin_right = 248.0 margin_bottom = 32.0 size_flags_horizontal = 3 @@ -1021,15 +1026,15 @@ showcase_index = 1 [node name="TrustedWorkers" type="CheckButton" parent="Margin/Panel/Display/Panels/Controls/Basic"] unique_name_in_owner = true -margin_top = 124.0 +margin_top = 164.0 margin_right = 320.0 -margin_bottom = 152.0 +margin_bottom = 192.0 text = "Require Trusted Workers" [node name="HBC" type="HBoxContainer" parent="Margin/Panel/Display/Panels/Controls/Basic"] -margin_top = 160.0 +margin_top = 200.0 margin_right = 320.0 -margin_bottom = 188.0 +margin_bottom = 228.0 [node name="NSFW" type="CheckButton" parent="Margin/Panel/Display/Panels/Controls/Basic/HBC"] unique_name_in_owner = true @@ -1048,9 +1053,9 @@ pressed = true text = "Censored" [node name="HBC2" type="HBoxContainer" parent="Margin/Panel/Display/Panels/Controls/Basic"] -margin_top = 196.0 +margin_top = 236.0 margin_right = 320.0 -margin_bottom = 224.0 +margin_bottom = 264.0 [node name="LoadFromDisk" parent="Margin/Panel/Display/Panels/Controls/Basic/HBC2" instance=ExtResource( 16 )] margin_right = 158.0 @@ -1067,15 +1072,15 @@ text = "Save All" [node name="PP" parent="Margin/Panel/Display/Panels/Controls/Basic" instance=ExtResource( 22 )] unique_name_in_owner = true -margin_top = 232.0 +margin_top = 272.0 margin_right = 320.0 -margin_bottom = 314.0 +margin_bottom = 354.0 [node name="SubmitRatings" type="Button" parent="Margin/Panel/Display/Panels/Controls/Basic"] unique_name_in_owner = true -margin_top = 322.0 +margin_top = 362.0 margin_right = 320.0 -margin_bottom = 350.0 +margin_bottom = 390.0 size_flags_horizontal = 3 disabled = true text = "Submit Ratings" @@ -1184,19 +1189,18 @@ visible = false margin_left = 10.0 margin_top = 10.0 margin_right = 330.0 -margin_bottom = 360.0 +margin_bottom = 378.0 rect_min_size = Vector2( 320, 0 ) script = ExtResource( 11 ) [node name="VBC" type="VBoxContainer" parent="Margin/Panel/Display/Panels/Controls/Options"] margin_right = 320.0 -margin_bottom = 350.0 +margin_bottom = 368.0 custom_constants/separation = 8 [node name="VBCRight" type="VBoxContainer" parent="Margin/Panel/Display/Panels/Controls/Options/VBC"] -margin_top = 215.0 margin_right = 320.0 -margin_bottom = 310.0 +margin_bottom = 95.0 size_flags_horizontal = 3 custom_constants/separation = 8 @@ -1245,8 +1249,9 @@ scroll_active = false selection_enabled = true [node name="VBCLeft" type="VBoxContainer" parent="Margin/Panel/Display/Panels/Controls/Options/VBC"] +margin_top = 103.0 margin_right = 320.0 -margin_bottom = 205.0 +margin_bottom = 368.0 size_flags_horizontal = 3 custom_constants/separation = 8 @@ -1260,9 +1265,9 @@ text = "API Key (Register)" fit_content_height = true [node name="APIHBC" type="HBoxContainer" parent="Margin/Panel/Display/Panels/Controls/Options/VBC/VBCLeft"] -margin_top = 25.0 +margin_top = 29.0 margin_right = 320.0 -margin_bottom = 53.0 +margin_bottom = 57.0 [node name="APIKey" type="LineEdit" parent="Margin/Panel/Display/Panels/Controls/Options/VBC/VBCLeft/APIHBC"] unique_name_in_owner = true @@ -1287,16 +1292,16 @@ script = ExtResource( 21 ) [node name="SaveDirLabel" type="Label" parent="Margin/Panel/Display/Panels/Controls/Options/VBC/VBCLeft"] unique_name_in_owner = true -margin_top = 57.0 +margin_top = 65.0 margin_right = 320.0 -margin_bottom = 77.0 +margin_bottom = 85.0 text = "Image Save Directory" autowrap = true [node name="HBoxContainer" type="HBoxContainer" parent="Margin/Panel/Display/Panels/Controls/Options/VBC/VBCLeft"] -margin_top = 81.0 +margin_top = 93.0 margin_right = 320.0 -margin_bottom = 109.0 +margin_bottom = 121.0 [node name="SaveDir" type="LineEdit" parent="Margin/Panel/Display/Panels/Controls/Options/VBC/VBCLeft/HBoxContainer"] unique_name_in_owner = true @@ -1315,23 +1320,23 @@ margin_bottom = 28.0 [node name="RememberPrompt" type="CheckButton" parent="Margin/Panel/Display/Panels/Controls/Options/VBC/VBCLeft"] unique_name_in_owner = true -margin_top = 113.0 +margin_top = 129.0 margin_right = 320.0 -margin_bottom = 141.0 +margin_bottom = 157.0 text = "Remember Prompt" [node name="LargerValues" type="CheckButton" parent="Margin/Panel/Display/Panels/Controls/Options/VBC/VBCLeft"] unique_name_in_owner = true -margin_top = 145.0 +margin_top = 165.0 margin_right = 320.0 -margin_bottom = 173.0 +margin_bottom = 193.0 text = "Larger Values" [node name="LoadSeedFromDisk" type="CheckButton" parent="Margin/Panel/Display/Panels/Controls/Options/VBC/VBCLeft"] unique_name_in_owner = true -margin_top = 177.0 +margin_top = 201.0 margin_right = 320.0 -margin_bottom = 205.0 +margin_bottom = 229.0 text = "Recover Saved Seeds" [node name="Shared" type="CheckButton" parent="Margin/Panel/Display/Panels/Controls/Options/VBC/VBCLeft"] @@ -1344,10 +1349,9 @@ text = "Share Images" [node name="WipeCache" type="Button" parent="Margin/Panel/Display/Panels/Controls/Options/VBC/VBCLeft"] unique_name_in_owner = true -margin_left = 273.0 -margin_top = 25.0 +margin_top = 237.0 margin_right = 320.0 -margin_bottom = 53.0 +margin_bottom = 265.0 text = "Wipe CivitAI Cache" [node name="Information" type="VBoxContainer" parent="Margin/Panel/Display/Panels/Controls"] @@ -1433,20 +1437,167 @@ margin_top = 350.0 margin_right = 300.0 margin_bottom = 350.0 +[node name="Workers" type="MarginContainer" parent="Margin/Panel/Display/Panels/Controls"] +margin_left = 10.0 +margin_top = 10.0 +margin_right = 330.0 +margin_bottom = 340.0 + +[node name="WorkersVBC" type="VBoxContainer" parent="Margin/Panel/Display/Panels/Controls/Workers"] +unique_name_in_owner = true +margin_right = 320.0 +margin_bottom = 330.0 +script = ExtResource( 46 ) + +[node name="WorkersAutocompleteHBC" type="HBoxContainer" parent="Margin/Panel/Display/Panels/Controls/Workers/WorkersVBC"] +margin_right = 320.0 +margin_bottom = 32.0 + +[node name="WorkerSelectLabel" type="Label" parent="Margin/Panel/Display/Panels/Controls/Workers/WorkersVBC/WorkersAutocompleteHBC"] +margin_top = 6.0 +margin_right = 52.0 +margin_bottom = 26.0 +rect_min_size = Vector2( 50, 0 ) +custom_fonts/font = ExtResource( 40 ) +text = "Worker" + +[node name="WorkerAutoComplete" parent="Margin/Panel/Display/Panels/Controls/Workers/WorkersVBC/WorkersAutocompleteHBC" instance=ExtResource( 26 )] +unique_name_in_owner = true +margin_left = 56.0 +margin_right = 284.0 +margin_bottom = 32.0 +size_flags_horizontal = 3 +text = "" +placeholder_text = "Worker Name or ID" +seek_keys = [ "name" ] +description_keys = [ "performance", "trusted" ] +description_format = "{item}: {performance}" +popup_position = 1 + +[node name="ShowAllWorkers" type="Button" parent="Margin/Panel/Display/Panels/Controls/Workers/WorkersVBC/WorkersAutocompleteHBC"] +unique_name_in_owner = true +margin_left = 288.0 +margin_right = 320.0 +margin_bottom = 32.0 +rect_min_size = Vector2( 32, 32 ) +icon = ExtResource( 15 ) +flat = true +icon_align = 1 +expand_icon = true + +[node name="StableHordeWorkers" type="HTTPRequest" parent="Margin/Panel/Display/Panels/Controls/Workers/WorkersVBC/WorkersAutocompleteHBC"] +unique_name_in_owner = true +script = ExtResource( 45 ) + +[node name="SelectedWorkers" type="RichTextLabel" parent="Margin/Panel/Display/Panels/Controls/Workers/WorkersVBC"] +unique_name_in_owner = true +visible = false +margin_top = 36.0 +margin_right = 320.0 +margin_bottom = 86.0 +rect_min_size = Vector2( 0, 50 ) +bbcode_enabled = true +fit_content_height = true + +[node name="WorkerInfoCard" type="PopupPanel" parent="Margin/Panel/Display/Panels/Controls/Workers/WorkersVBC/SelectedWorkers"] +unique_name_in_owner = true +margin_left = 157.0 +margin_top = -63.0 +margin_right = 757.0 +margin_bottom = 62.0 +custom_styles/panel = ExtResource( 39 ) + +[node name="VBoxContainer" type="VBoxContainer" parent="Margin/Panel/Display/Panels/Controls/Workers/WorkersVBC/SelectedWorkers/WorkerInfoCard"] +margin_left = 10.0 +margin_top = 10.0 +margin_right = 590.0 +margin_bottom = 115.0 + +[node name="WorkerInfoLabel" type="RichTextLabel" parent="Margin/Panel/Display/Panels/Controls/Workers/WorkersVBC/SelectedWorkers/WorkerInfoCard/VBoxContainer"] +unique_name_in_owner = true +margin_right = 500.0 +margin_bottom = 105.0 +rect_min_size = Vector2( 500, 0 ) +bbcode_enabled = true +bbcode_text = "This is some model info + +blah blah blah + +url: https://example.com" +text = "This is some model info + +blah blah blah + +url: https://example.com" +fit_content_height = true + +[node name="WorkerTriggerSelection" type="PopupMenu" parent="Margin/Panel/Display/Panels/Controls/Workers/WorkersVBC/SelectedWorkers"] +unique_name_in_owner = true +margin_left = 49.0 +margin_top = -90.0 +margin_right = 69.0 +margin_bottom = -70.0 +hide_on_checkable_item_selection = false + +[node name="WorkerSelect" type="OptionButton" parent="Margin/Panel/Display/Panels/Controls/Workers/WorkersVBC"] +unique_name_in_owner = true +visible = false +margin_top = 36.0 +margin_right = 320.0 +margin_bottom = 64.0 +text = "Any model" +clip_text = true +items = [ "Any model", null, false, 0, null, "stable_diffusion", null, false, 1, null ] +selected = 0 + +[node name="WorkerPopupInfo" type="PopupPanel" parent="Margin/Panel/Display/Panels/Controls/Workers/WorkersVBC"] +unique_name_in_owner = true +margin_top = 36.0 +margin_right = 512.0 +margin_bottom = 153.0 + +[node name="WorkerPopupInfoLabel" type="RichTextLabel" parent="Margin/Panel/Display/Panels/Controls/Workers/WorkersVBC/WorkerPopupInfo"] +unique_name_in_owner = true +margin_left = 6.0 +margin_top = 6.0 +margin_right = 506.0 +margin_bottom = 111.0 +rect_min_size = Vector2( 500, 0 ) +bbcode_enabled = true +bbcode_text = "This is some model info + +blah blah blah + +url: https://example.com" +text = "This is some model info + +blah blah blah + +url: https://example.com" +fit_content_height = true +deselect_on_focus_loss_enabled = false + +[node name="BlockList" type="CheckButton" parent="Margin/Panel/Display/Panels/Controls/Workers/WorkersVBC"] +unique_name_in_owner = true +margin_top = 36.0 +margin_right = 320.0 +margin_bottom = 64.0 +text = "Blocklist?" + [node name="Footer" type="HBoxContainer" parent="Margin/Panel"] margin_top = 826.0 margin_right = 1568.0 margin_bottom = 868.0 [node name="Config" type="PanelContainer" parent="Margin/Panel/Footer"] -margin_right = 144.0 +margin_right = 178.0 margin_bottom = 42.0 size_flags_vertical = 8 [node name="Buttons" type="HBoxContainer" parent="Margin/Panel/Footer/Config"] margin_left = 6.0 margin_top = 6.0 -margin_right = 138.0 +margin_right = 172.0 margin_bottom = 36.0 size_flags_vertical = 4 @@ -1483,20 +1634,31 @@ icon = ExtResource( 18 ) icon_align = 1 expand_icon = true -[node name="Advanced" type="Button" parent="Margin/Panel/Footer/Config/Buttons"] +[node name="Workers" type="Button" parent="Margin/Panel/Footer/Config/Buttons"] margin_left = 102.0 margin_right = 132.0 margin_bottom = 30.0 rect_min_size = Vector2( 30, 30 ) toggle_mode = true group = SubResource( 10 ) +icon = ExtResource( 47 ) +icon_align = 1 +expand_icon = true + +[node name="Advanced" type="Button" parent="Margin/Panel/Footer/Config/Buttons"] +margin_left = 136.0 +margin_right = 166.0 +margin_bottom = 30.0 +rect_min_size = Vector2( 30, 30 ) +toggle_mode = true +group = SubResource( 10 ) icon = ExtResource( 35 ) icon_align = 1 expand_icon = true [node name="About" type="PanelContainer" parent="Margin/Panel/Footer"] -margin_left = 148.0 -margin_right = 224.0 +margin_left = 182.0 +margin_right = 258.0 margin_bottom = 42.0 size_flags_vertical = 8 @@ -1529,7 +1691,7 @@ icon_align = 1 expand_icon = true [node name="Progress" type="MarginContainer" parent="Margin/Panel/Footer"] -margin_left = 228.0 +margin_left = 262.0 margin_right = 1568.0 margin_bottom = 42.0 size_flags_horizontal = 3 @@ -1537,7 +1699,7 @@ size_flags_horizontal = 3 [node name="GenerationsProcessing" type="ProgressBar" parent="Margin/Panel/Footer/Progress"] unique_name_in_owner = true modulate = Color( 1, 1, 1, 0.392157 ) -margin_right = 1340.0 +margin_right = 1306.0 margin_bottom = 42.0 mouse_filter = 2 size_flags_horizontal = 3 @@ -1548,7 +1710,7 @@ max_value = 20.0 [node name="GenerationsDone" type="ProgressBar" parent="Margin/Panel/Footer/Progress"] unique_name_in_owner = true -margin_right = 1340.0 +margin_right = 1306.0 margin_bottom = 42.0 mouse_filter = 2 size_flags_horizontal = 3 @@ -1558,7 +1720,7 @@ custom_styles/bg = SubResource( 17 ) max_value = 20.0 [node name="Margin" type="MarginContainer" parent="Margin/Panel/Footer/Progress"] -margin_right = 1340.0 +margin_right = 1306.0 margin_bottom = 42.0 custom_constants/margin_right = 8 custom_constants/margin_top = 2 @@ -1568,7 +1730,7 @@ custom_constants/margin_bottom = 2 [node name="Labels" type="HBoxContainer" parent="Margin/Panel/Footer/Progress/Margin"] margin_left = 8.0 margin_top = 2.0 -margin_right = 1332.0 +margin_right = 1298.0 margin_bottom = 40.0 size_flags_horizontal = 3 size_flags_vertical = 3 @@ -1590,7 +1752,7 @@ scroll_active = false [node name="VBC" type="VBoxContainer" parent="Margin/Panel/Footer/Progress/Margin/Labels"] margin_left = 100.0 margin_top = 10.0 -margin_right = 1324.0 +margin_right = 1290.0 margin_bottom = 28.0 size_flags_horizontal = 3 size_flags_vertical = 4 @@ -1598,7 +1760,7 @@ custom_constants/separation = 0 [node name="StatusText" type="RichTextLabel" parent="Margin/Panel/Footer/Progress/Margin/Labels/VBC"] unique_name_in_owner = true -margin_right = 1224.0 +margin_right = 1190.0 margin_bottom = 18.0 rect_min_size = Vector2( 0, 18 ) size_flags_horizontal = 3 diff --git a/PopupInfoPanel.gd b/PopupInfoPanel.gd index 60c8534..ff837f8 100644 --- a/PopupInfoPanel.gd +++ b/PopupInfoPanel.gd @@ -37,6 +37,9 @@ const DESCRIPTIONS = { "ShowAllLoras": "Will display an list of all known LoRas, from which to select one manually.", "ShowAllTIs": "Will display an list of all known Textual Inversions, from which to select one manually.", "WipeCache": "Will remove all CivitAI cached information. You will have to search for your loras once more after this.", + "BlockList": "When enabled, the workers specified will NOT be used for generations (This option requires upfront kudos). When disabled only the workers specified will be used for the generation.", + "WorkerAutoComplete": "Specify workers to use for this generation. Use the toggle below to specify using them as an allowlist or a blocklist. When models are selected, only workers which can generate any of those models will be shown.", + "ShowAllWorkers": "Press this button to display and select available workers for your selected model.", } const META_DESCRIPTIONS = { diff --git a/StableHordeClient.gd b/StableHordeClient.gd index 6381cb8..3503622 100644 --- a/StableHordeClient.gd +++ b/StableHordeClient.gd @@ -75,8 +75,9 @@ onready var control_type = $"%ControlType" onready var image_is_control = $"%ImageIsControl" # model onready var model = $"%Model" -onready var lora = $"%Lora" -onready var ti = $"%TextualInversions" +onready var lora:LoraSelection = $"%Lora" +onready var ti:TISelection = $"%TextualInversions" +onready var workers: WorkerSelection = $"%WorkersVBC" # post-processing onready var pp = $"%PP" # ratings @@ -334,7 +335,7 @@ func _on_StatusText_meta_clicked(meta): OS.shell_open("https://www.patreon.com/db0") "worker": # warning-ignore:return_value_discarded - OS.shell_open("https://github.com/db0/AI-Horde/blob/main/README_StableHorde.md#joining-the-horde") + OS.shell_open("https://github.com/Haidra-Org/AI-Horde/blob/main/README_StableHorde.md#joining-the-horde") func _on_ControlNet_meta_clicked(meta): match meta: @@ -360,7 +361,8 @@ func _get_test_images(n = 10) -> Array: OS.get_unix_time(), "none", img, - 'Test Image ID') + 'Test Image ID', + "Test Request ID") new_texture.create_from_image(img) test_array.append(new_texture) return(test_array) @@ -527,6 +529,9 @@ func _connect_hover_signals() -> void: $"%FetchTIsFromCivitAI", $"%ShowAllTIs", $"%WipeCache", + $"%BlockList", + $"%WorkerAutoComplete", + $"%ShowAllWorkers", ]: node.connect("mouse_entered", EventBus, "_on_node_hovered", [node]) node.connect("mouse_exited", EventBus, "_on_node_unhovered", [node]) @@ -655,7 +660,11 @@ func _accept_settings() -> void: var tis = ti.selected_tis_list globals.set_setting("tis",tis) stable_horde_client.set("tis", tis) - print_debug(tis) + var wks = workers.get_worker_ids() + globals.set_setting("workers", workers.selected_workers_list, "Options") + globals.set_setting("blocklist", workers.blocklist, "Options") + stable_horde_client.set("workers", wks) + stable_horde_client.set("worker_blacklist", workers.blocklist) stable_horde_client.set("api_key", options.get_api_key()) stable_horde_client.set("karras", karras.pressed) globals.set_setting("karras", karras.pressed) diff --git a/Workers.gd b/Workers.gd new file mode 100644 index 0000000..9d6d12a --- /dev/null +++ b/Workers.gd @@ -0,0 +1,226 @@ +class_name WorkerSelection +extends Control + +signal worker_modified(workers_list) + +var selected_workers_list : Array = [] +var worker_refresh: float +var current_models := [] +var blocklist := false + +onready var worker_auto_complete = $"%WorkerAutoComplete" +onready var selected_workers = $"%SelectedWorkers" +onready var show_all_workers = $"%ShowAllWorkers" + +onready var worker_select = $"%WorkerSelect" +onready var stable_horde_workers := $"%StableHordeWorkers" +onready var worker_info_card := $"%WorkerInfoCard" +onready var worker_info_label := $"%WorkerInfoLabel" +onready var popup_info := $"%WorkerPopupInfo" +onready var popup_info_label := $"%WorkerPopupInfoLabel" +onready var block_list = $"%BlockList" + + +func _ready(): +# warning-ignore:return_value_discarded + EventBus.connect("model_selected",self,"on_model_selection_changed") + # warning-ignore:return_value_discarded + stable_horde_workers.connect("workers_retrieved",self, "_on_workers_retrieved") + # warning-ignore:return_value_discarded + worker_auto_complete.connect("item_selected", self,"_on_worker_selected") + + selected_workers.connect("meta_clicked",self,"_on_selected_workers_meta_clicked") + selected_workers.connect("meta_hover_started",self,"_on_selected_workers_meta_hover_started") + selected_workers.connect("meta_hover_ended",self,"_on_selected_workers_meta_hover_ended") + worker_info_label.connect("meta_clicked",self,"_on_worker_info_workers_meta_clicked") + show_all_workers.connect("pressed",self,"_on_show_all_workers_pressed") + block_list.connect("toggled",self,"_on_blocklist_enabled") +# warning-ignore:return_value_discarded + worker_info_card.connect("hide",self,"_on_workers_info_card_hide") + yield(get_tree().create_timer(0.2), "timeout") + selected_workers_list = globals.config.get_value("Options", "workers", []) + blocklist = globals.config.get_value("Options", "blocklist", false) + block_list.pressed = blocklist + _update_selected_workers_label() + _emit_selected_workers() + + +func _process(delta): + worker_refresh += delta + if worker_refresh > 30: + worker_refresh = 0 + stable_horde_workers.get_workers() + +func _on_workers_retrieved(_worker_reference: Dictionary): + worker_auto_complete.selections = stable_horde_workers.get_workers_with_models(current_models) + + +func get_worker_reference(worker_name: String) -> Dictionary: + return stable_horde_workers.get_worker_info(worker_name) + + +func get_worker_performance(worker_name: String) -> Dictionary: + var worker = get_worker_reference(worker_name) + var default_perf_dict = { + "performance": float(worker["performance"].split(' ')[0]), + "uptime": worker["uptime"] / (60*60*24), + } + return(default_perf_dict) + +func _on_request_initiated(): + stable_horde_workers.get_workers() + +func _show_worker_details(worker_name: String) -> void: + var worker_reference := get_worker_reference(worker_name) + if worker_reference.empty(): + worker_info_label.bbcode_text = "No worker info could not be retrieved at this time." + else: + var perf = _get_worker_performance(worker_name) + var fmt = { + "id": worker_reference['id'], + "description": worker_reference['name'], + "version": worker_reference['bridge_agent'], + "trusted": worker_reference['trusted'], + "info": worker_reference.get('info'), + "team": worker_reference['team']['name'], + "models": ', '.join(worker_reference["models"]), + "health_color": "#" + perf["health_color"], + "trusted_color": "#" + perf["trusted_color"], + "performance": worker_reference['performance'], + } + if worker_reference["models"].size() > 30: + fmt["models"] = worker_reference["models"].size() + if not worker_reference.get('info'): + fmt["info"] = "N/A" + var label_text = "Name: {description}\nID: {id}\nVersion: {version}\n".format(fmt)\ + + "Trusted: [color={trusted_color}]{trusted}[/color].\n".format(fmt)\ + + "Performance: [color={health_color}]{performance}[/color].\n".format(fmt)\ + + "Models: {models}.\n\n".format(fmt)\ + + "Info: {info}".format(fmt) + worker_info_label.bbcode_text = label_text + worker_info_card.rect_size = Vector2(0,0) + worker_info_card.popup() + worker_info_card.rect_global_position = get_global_mouse_position() + Vector2(30,-worker_info_card.rect_size.y/2) + +func _get_worker_performance(worker_name: String) -> Dictionary: + var worker_performance := get_worker_performance(worker_name) + var worker_reference := get_worker_reference(worker_name) + var healthy := Color(0,1,0) + var unhealthy := Color(1,0,0) + # Any speed above 1MPS is decent. However below 1MPS we consider it unhealthy + var health_pct = worker_performance["performance"] + if worker_performance["performance"] > 1.0: + health_pct = 1 + var normalized = (health_pct - 0.5) / (1.0 - 0.5) + if normalized < 0: + normalized = 0 + var health_color := unhealthy.linear_interpolate(healthy,normalized) + var trusted_color = Color(0,1,0) + if not worker_reference['trusted']: + trusted_color = Color(1,1,0) + return { + "health_color": health_color.to_html(false), + "trusted_color": trusted_color.to_html(false), + } + +func replace_workers(workers_list: Array) -> void: + selected_workers_list = workers_list + _update_selected_workers_label() + _emit_selected_workers() + +func _on_worker_selected(worker_name: String) -> void: + if selected_workers_list.size() >= 5: + return + if worker_name in selected_workers_list: + return + selected_workers_list.append(worker_name) + _update_selected_workers_label() + _emit_selected_workers() + +func _get_selected_workers() -> Array: + var worker_defs = [] + for worker_name in selected_workers_list: + worker_defs.append(get_worker_reference(worker_name)) + return worker_defs + +func _emit_selected_workers() -> void: + EventBus.emit_signal("worker_selected", _get_selected_workers()) + emit_signal("worker_modified", _get_selected_workers()) + +func _update_selected_workers_label() -> void: + var bbtext := [] + var indexes_to_remove = [] + for index in range(selected_workers_list.size()): + var worker_text = "[url={worker_hover}]{worker_name}[/url] ([url={worker_remove}]X[/url])" + var worker_name = selected_workers_list[index] + # This might happen for example when we changed the current models + # and some of the workers don't support them + var matching_model_workers = stable_horde_workers.get_workers_with_models(current_models) + if current_models.size() > 0 and not matching_model_workers.has(worker_name): + indexes_to_remove.append(index) + continue + var worker_fmt = { + "worker_name": worker_name, + "worker_hover": 'hover:' + str(index), + "worker_remove": 'delete:' + str(index), + } + bbtext.append(worker_text.format(worker_fmt)) + selected_workers.bbcode_text = ", ".join(bbtext) + indexes_to_remove.invert() + for index in indexes_to_remove: + selected_workers_list.remove(index) + if selected_workers_list.size() > 0: + selected_workers.show() + else: + selected_workers.hide() + +func _on_selected_workers_meta_clicked(meta) -> void: + var meta_split = meta.split(":") + match meta_split[0]: + "hover": + _show_worker_details(selected_workers_list[int(meta_split[1])]) + "delete": + selected_workers_list.remove(int(meta_split[1])) + _update_selected_workers_label() + _emit_selected_workers() + +func _on_selected_workers_meta_hover_started(meta: String) -> void: + var meta_split = meta.split(":") + var info = '' + match meta_split[0]: + "hover": + info = "WorkerHover" + "delete": + info = "WorkerDelete" + EventBus.emit_signal("rtl_meta_hovered",selected_workers,info) + +func _on_selected_workers_meta_hover_ended(_meta: String) -> void: + EventBus.emit_signal("rtl_meta_unhovered",selected_workers) + +func _on_lora_info_workers_meta_clicked(meta) -> void: +# warning-ignore:return_value_discarded + OS.shell_open(meta) + +func _on_show_all_workers_pressed() -> void: + worker_auto_complete.select_from_all() + +func on_model_selection_changed(models_list) -> void: + current_models = models_list + worker_auto_complete.selections = stable_horde_workers.get_workers_with_models(current_models) + _update_selected_workers_label() + +func _on_blocklist_enabled(value) -> void: + blocklist = value + +func get_worker_ids() -> Array: + var idlist = [] + for worker_name in selected_workers_list: + var worker_reference := get_worker_reference(worker_name) + idlist.append(worker_reference["id"]) + return idlist + +class WorkerSorter: + static func sort(m1, m2): + if m1["fmt"]["name"] < m2["fmt"]["name"]: + return true + return false diff --git a/addons/stable_horde_client/AIImageTexture.gd b/addons/stable_horde_client/AIImageTexture.gd index 1943117..3a689d8 100644 --- a/addons/stable_horde_client/AIImageTexture.gd +++ b/addons/stable_horde_client/AIImageTexture.gd @@ -31,6 +31,7 @@ var attributes: Dictionary var timestamp: float var image_horde_id: String var control_type: String +var request_id: String func _init( _prompt: String, @@ -42,7 +43,8 @@ func _init( _timestamp: float, _control_type: String, _image: Image, - _image_horde_id: String) -> void: + _image_horde_id: String, + _request_id: String) -> void: ._init() prompt = _prompt attributes = _imgen_params.duplicate(true) @@ -64,6 +66,8 @@ func _init( image = _image image_horde_id = _image_horde_id timestamp = _timestamp + request_id = _request_id + attributes['request_id'] = _request_id # This can be used to provide metadata for the source image in img2img requests func set_source_image_path(image_path: String) -> void: diff --git a/addons/stable_horde_client/plugin.gd b/addons/stable_horde_client/plugin.gd index a5bcd5c..5dfd67a 100644 --- a/addons/stable_horde_client/plugin.gd +++ b/addons/stable_horde_client/plugin.gd @@ -5,12 +5,14 @@ extends EditorPlugin const SH_HTTP_CLIENT_NAME = "StableHordeHTTPRequest" const SH_CLIENT_NAME = "StableHordeClient" const SH_MODELS_NAME = "StableHordeModels" +const SH_WORKERS_NAME = "StableHordeWorkers" const SH_LOGIN_NAME = "StableHordeLogin" const SH_RATE_GEN_NAME = "StableHordeRateGeneration" const INHERITANCE = "StableHordeHTTPRequest" const SH_HTTP_CLIENT = preload("stable_horde_httpclient.gd") const SH_CLIENT = preload("stable_horde_client.gd") const SH_MODELS = preload("stable_horde_models.gd") +const SH_WORKERS = preload("stable_horde_workers.gd") const SH_LOGIN = preload("stable_horde_login.gd") const SH_RATE_GEN = preload("stable_horde_rate_generation.gd") const ICON = preload("icon.png") @@ -20,6 +22,7 @@ func _enter_tree(): add_custom_type(SH_HTTP_CLIENT_NAME, "HTTPRequest", SH_HTTP_CLIENT, ICON) add_custom_type(SH_CLIENT_NAME, INHERITANCE, SH_CLIENT, ICON) add_custom_type(SH_MODELS_NAME, INHERITANCE, SH_MODELS, ICON) + add_custom_type(SH_WORKERS_NAME, INHERITANCE, SH_WORKERS, ICON) add_custom_type(SH_LOGIN_NAME, INHERITANCE, SH_LOGIN, ICON) add_custom_type(SH_RATE_GEN_NAME, INHERITANCE, SH_RATE_GEN, ICON) @@ -28,5 +31,6 @@ func _exit_tree(): remove_custom_type(SH_HTTP_CLIENT_NAME) remove_custom_type(SH_CLIENT_NAME) remove_custom_type(SH_MODELS_NAME) + remove_custom_type(SH_WORKERS_NAME) remove_custom_type(SH_LOGIN_NAME) remove_custom_type(SH_RATE_GEN_NAME) diff --git a/addons/stable_horde_client/stable_horde_client.gd b/addons/stable_horde_client/stable_horde_client.gd index ad319c0..b4ce5b0 100644 --- a/addons/stable_horde_client/stable_horde_client.gd +++ b/addons/stable_horde_client/stable_horde_client.gd @@ -98,6 +98,8 @@ export(bool) var r2 := true export(bool) var shared := true export(String, "none", "canny", "hed", "depth", "normal", "openpose", "seg", "scribble", "fakescribbles", "hough") var control_type := "none" export(bool) var dry_run := false +export(Array) var workers := [] +export(bool) var worker_blacklist := false var all_image_textures := [] var latest_image_textures := [] @@ -151,10 +153,11 @@ func generate(replacement_prompt := '', replacement_params := {}) -> void: "r2": r2, "shared": shared, "dry_run": dry_run, - "workers": [ - "ba9937fb-8558-4d42-9059-926de5f0fe4e", #pama - "dc0704ab-5b42-4c65-8471-561be16ad696", #portal - ], # debug + "workers": workers, + "worker_blacklist": worker_blacklist, +# "workers": [ +# "dc0704ab-5b42-4c65-8471-561be16ad696", #portal +# ], # debug } # print_debug(submit_dict) if source_image: @@ -286,7 +289,8 @@ func prepare_aitexture(imgbuffer: PoolByteArray, img_dict: Dictionary, timestamp timestamp, control_type, image, - img_dict["id"]) + img_dict["id"], + async_request_id) texture.create_from_image(image) latest_image_textures.append(texture) # Avoid keeping all images in RAM. Until I find a reason for it. diff --git a/addons/stable_horde_client/stable_horde_workers.gd b/addons/stable_horde_client/stable_horde_workers.gd new file mode 100644 index 0000000..f610f1d --- /dev/null +++ b/addons/stable_horde_client/stable_horde_workers.gd @@ -0,0 +1,66 @@ +class_name StableHordeWorkers +extends StableHordeHTTPRequest + +signal workers_retrieved(workers_list) + +var worker_results := [] +var workers_by_id := {} +var workers_by_name := {} +var workers_retrieved = false + +func _ready() -> void: + get_workers() + + +func get_workers() -> void: + if state != States.READY: + print_debug("Workers currently working. Cannot do more than 1 request at a time with the same Stable Horde Models.") + return + state = States.WORKING + var error = request(aihorde_url + "/api/v2/workers?type=image", [], false, HTTPClient.METHOD_GET) + if error != OK: + var error_msg := "Something went wrong when initiating the stable horde request" + push_error(error_msg) + state = States.READY + emit_signal("request_failed",error_msg) + + +# Function to overwrite to process valid return from the horde +func process_request(json_ret) -> void: + if typeof(json_ret) != TYPE_ARRAY: + var error_msg : String = "Unexpected worker format received" + push_error("Unexpected worker format received" + ': ' + json_ret) + emit_signal("request_failed",error_msg) + state = States.READY + return + worker_results = json_ret + for worker in worker_results: + workers_by_id[worker.id] = worker + workers_by_name[worker.name] = worker + emit_signal("workers_retrieved", workers_by_name) + state = States.READY + +func emit_models_retrieved() -> void: + emit_signal("workers_retrieved", worker_results) + +func get_worker_info(worker_string: String) -> Dictionary: + if workers_by_id.has(worker_string): + return workers_by_id[worker_string] + if workers_by_name.has(worker_string): + return workers_by_name[worker_string] + return {} + +func is_worker(worker_string: String) -> bool: + return not get_worker_info(worker_string).empty() + +func get_workers_with_models(models: Array) -> Dictionary: +# for w in workers_by_name: +# print([w]) + if models.size() == 0: + return workers_by_name + var ret: Dictionary = {} + for worker_name in workers_by_name: + for model in models: + if workers_by_name[worker_name]["models"].has(model.name) and not ret.has(worker_name): + ret[worker_name] = workers_by_name[worker_name] + return ret diff --git a/bus.gd b/bus.gd index a3d69b7..ed6e094 100644 --- a/bus.gd +++ b/bus.gd @@ -20,6 +20,8 @@ signal shared_toggled() signal lora_selected(lora_details) # warning-ignore:unused_signal signal model_selected(model_details) +# warning-ignore:unused_signal +signal worker_selected(worker_details) signal kudos_calculated(kudos) signal generation_completed signal cache_wipe_requested diff --git a/project.godot b/project.godot index 3fd01c8..b12951d 100644 --- a/project.godot +++ b/project.godot @@ -104,6 +104,11 @@ _global_script_classes=[ { "language": "GDScript", "path": "res://addons/stable_horde_client/stable_horde_rate_generation.gd" }, { +"base": "StableHordeHTTPRequest", +"class": "StableHordeWorkers", +"language": "GDScript", +"path": "res://addons/stable_horde_client/stable_horde_workers.gd" +}, { "base": "Control", "class": "TISelection", "language": "GDScript", @@ -118,6 +123,11 @@ _global_script_classes=[ { "class": "Utils", "language": "GDScript", "path": "res://Utils.gd" +}, { +"base": "Control", +"class": "WorkerSelection", +"language": "GDScript", +"path": "res://Workers.gd" } ] _global_script_class_icons={ "AIImageTexture": "", @@ -139,9 +149,11 @@ _global_script_class_icons={ "StableHordeModelShowcase": "", "StableHordeModels": "", "StableHordeRateGeneration": "", +"StableHordeWorkers": "", "TISelection": "", "ToolConsts": "", -"Utils": "" +"Utils": "", +"WorkerSelection": "" } [application] @@ -166,7 +178,7 @@ window/stretch/aspect="keep" [editor_plugins] -enabled=PoolStringArray( ) +enabled=PoolStringArray( "res://addons/stable_horde_client/plugin.cfg" ) [global] diff --git a/theme/assets/iconpark/worker.svg b/theme/assets/iconpark/worker.svg new file mode 100644 index 0000000..1cb8771 --- /dev/null +++ b/theme/assets/iconpark/worker.svg @@ -0,0 +1 @@ + \ No newline at end of file