-
Notifications
You must be signed in to change notification settings - Fork 2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Tag overhaul #13
base: main
Are you sure you want to change the base?
Tag overhaul #13
Conversation
Added a system to manage loading/saving of tags. Creation/deletion is still pending, but shouldn't be hard to add. There seems to be an issue with loading the UI tabs, I'm not sure if it's caused by code within this extension or some weird caching issue with Gradio or A1111. Additional changes: - Cleaned up some code and extracted (mostly) all paths into it's own file - Fixed .gitignore (you should never be ignoring everything) NOTE: My changes are simply added on the side of everything else as of now, this PR is not merge ready.
Yeah, I see what you're saying, that's annoying... is there a way to stop caching for specific components rendered? And if so, what effect does this have on the rest of the program? I have a feeling there has to be a way to stop the caching if we really want to by passing some sort of argument to the components upon creation, but I don't know what that argument/process is. |
From my limited research, I wasn't able to find anything in the source code that's available to be used in the scope of extensions. There might be something, though, so feel free to have a look. Digging through the issues, I found this AUTOMATIC1111/stable-diffusion-webui#13981, wasn't very helpful though. For now, I implemented a function to manually edit the cache, but that still doesn't fix the issue about having to add a suffix to each label for it to work... |
Seeing as elements with labels are cached, using a dataframe seems to be the most practical path forward. There are still a few issues with this approach however. Mainly the fact that you can't seem to edit the row_count property of a dataframe programmatically (at least I couldn't figure out how)
Another issue I happened to stumble upon is that Gradio does not allow you to dynamically add/remove components after initialization... |
Yeah, Gradio elements are super weird. For the current tag editing / search functionality, all I'm doing is changing the visibility of the textbox and save button elements dynamically based on if the textbox has the searched-for tag(s) in it or not. That's why I'm now kinda on the fence about displaying by Tags then having textboxes of the file names because you can't add the extra network to your prompt from the tag editor so I at least don't see a reason to explicitly display them like that. I prefer the current design because you can change the tags on an extra network by extra network basis, so it makes the process of removing many tags from an extra network a lot more streamlined than displaying by tag. I still think we should store it as a json with tags as the keys and they hold a list of model paths, but the presentation to the use I think should be different than the underlying representation. Regarding making and deleting elements dynamically, if that's the route we need to go then we'll have to create a custom interface using html, css, and javascript to support dynamic control over ui elements. |
My main problem about displaying by models is the fact you can have a lot of models, in my case I have well over 300 LoRAs. Going through and adding the same tags for each and every one of those models is a lot of work, which is why I would rather define tags, and apply those to the models through some well designed UI. Ofcourse, the final decision is up to you, but I think the extension would benefit from atleast having it be an option. |
I see the value in organizing by model and organizing by tag, as both come with their respective trade-offs; It becomes increasingly cumbersome to add multiple different tags to the same file when organizing by tag and it becomes increasingly cumbersome to add the same tag to multiple files when organizing by model. Therefore, I think we should do both. Have one tab for the Tag Editor for editing by model and another tab for the Tag Editor for editing by tag. We'll only have one backend tag file data structure representation, but the user can decide how they want the information to be displayed based on their needs. I think we should store the information on the backend the way you describe it above, so file names are values in a list with the tag as the key. We can have it so that when you edit the files of one tag or edit the tags of one file, it updates accordingly on the other tab so that everything is consistent when the "save" button is clicked. I think this method of having two different ways of organizing is the best method, but let me know what your thoughts are about this. |
While exploring other ways of designing the interface, I asked ChatGPT and these are some examples of the same problem and the solutions that it presented: Email Clients (Gmail, Outlook): Email clients often allow users to organize emails by folders (akin to tagging by model) and also offer tagging or labeling features to categorize emails. This dual approach helps users manage large volumes of emails more effectively. Photo Management Software (Google Photos, Adobe Lightroom): These applications allow users to tag photos and also organize them into albums. The tagging feature is particularly useful for finding all photos with a common subject, while albums are great for organizing photos from a specific event or project. File Management Systems (Windows Explorer, macOS Finder): Modern operating systems offer both folder-based organization and tagging capabilities. Users can assign multiple tags to files and also organize them into folders, providing flexibility in file management. Content Management Systems (WordPress, Drupal): These platforms often offer categories (for hierarchical organization) and tags (for non-hierarchical organization), allowing content creators to organize posts and articles in multiple ways. TL;DR - The main idea is that each file gets a set of descriptors, in our case tags, and then each file is sorted into a category like a folder. A feature request for bulk tagging (see this issue) was already proposed and I believe it's relevant to this problem. While I believe that the Two-Tabs approach is the best UI design implementation, I think we might be able to expand upon the idea or iterate on it with a file-system-like design. Again, let me know what you think of this, I absolutely agree that being able to organize by tag instead of model is valuable, but I see the good in having both vs having to compromise with one over the other. |
Did a lot of work and research on UI responsiveness and layout, as well as fleshing out the tag system a little bit. - Re-did UI layout (still temporary) - Added a rudimentary control flow for loading, saving and editing tags - Added a function to ensure no duplicate keys are saved - Added search controls - Added an auto save feature
I agree, but I don't think two tabs are strictly necessary. If you don't mind, I'd like you to check out the extension with my latest changes. I moved the UI towards a data-table format, which at least in my opinion makes it way better to look at. This will also allow us to easily be able to swap between any display format. Ideally, I would like the last column to be similar to a Dropdown component with the If you have any concerns or input regarding my changes, please let me know. |
Ok, I took a look at your branch, here are my thoughts:
Overall, great work! I can see that a lot of thought has gone into this and I will say I didn't even think to use a Gradio table for the tag representation so that's super clever. I know you have a lot of work on your own fork, but I've made over 30 commits since you started doing this so I would prefer you make a new branch on this repo under this issue and copied your work to that so that you're developing on top of the latest version of the extension, not an outdated one. I think this is also important because the current version of the extension on your fork is missing key directories (mainly the javascript folder) that it relies on to exist so that the javascript override files can be copied from staging to that particular folder to allow for the model cards to be searched properly. These exist on my repo so we shouldn't run into any issues if you start a new branch and copy your work. Also, I know it's a WIP but there are a couple things that are not handled that I didn't want to go overlooked:
Finally, I know there's not really a standard right now for reviewing PR's between the two of us, but because this is such a drastic change to both the UI and the backend, I'd appreciate if when you feel like you have a production-ready implementation you open a PR for me to review so that we make sure there's not weird issues and bugs that pop up. |
Thanks for the feedback. I feel like I should address some of these points.
It does. If it didn't work for you, then that's an unintended bug. def search(search_term: str):
if search_term == '' or search_term is None:
return to_dataframe()
filtered_tags = []
for tag in tags:
if search_term in f'{tag.name} {tag.description} {tag.models}':
filtered_tags.append(tag)
return to_dataframe(filtered_tags)
I'm not sure if I understand what you're getting at here. I mentioned adding a custom component that displays arrays as an embedded multiselect dropdown component within the cells. If this is not what you meant, then I'd appreciate if you could elaborate a bit more.
It shouldn't really be necessary. I'll keep updating my branch from main and solve any merge conflicts that appear. It's actually kind of counter-intuitive to create a new branch, as we will lose the commit history of this one, making it harder to track down eventual bugs. I intend to continue developing on this PR until I feel like we have something production-ready. If there is any specific reason you want me to create a new branch, let me know.
Nice catch, I'll have that fixed ASAP.
Yeah, the codebase for the tag manager is still in an early stage. It should be seeing improvments with every commit I make from now on out.
I just want to assure you that I will never merge anything into main without having it reviewed first. That's a key part of developing with git anyway. As mentioned above, if you don't have any specific reason to create a new PR/branch, I'll continue working on this one and request your review once I feel it is ready to be merged. I also want to go mention some additional details that need to be addressed. We need to think about users updating from an older version of the extension. Some system should be put in place to auto-update from the old system to the new one. While on that topic, it might be useful to also think about future-proofing, in case we want to update the tag data structure again for some reason. |
I read you loud and clear, sounds like you’ve got your own stuff covered, I’ll leave you to it👍 For implementing the update rollout logic, that’s fairly simple as we can add some startup stuff that checks if the current “network_descriptions” folder exists and write all of the current tags to the tags.json folder. I would say that we shouldn’t forcefully delete the network_descriptions folder but should print a warning or something saying that the network_descriptions tag scheme has been deprecated (and maybe remove the files in a future update🤷♂️) As for future proofing, I’m not quite sure where to start, so if you have some ideas and concerns throw them at me and I’ll start working on a way to patch them. Finally, for the in-cell dropdown you mentioned, I’m not quite sure what your vision for that is, but later today I’ll comment a quick drawn mock-up of the vision I have to give you a better idea. |
Regarding future proofing, one thing that I just thought of is that we should have an interface as a facade to the tag file read and write so that we can make changes to the backend with affecting the frontend; Something like a ‘read_all_tags()’ function that always returns a dict of tags as keys and lists of models as values, same for writing tags to the database. This way, all we would have to change is the read and write logic in the interfaces and the frontend works just the same. |
This commit fixes an issue with saving. Everything now seems to be saving properly, and the auto-save feature works as intended. You can now also search by tags using the new tag system (currently only on LoRAs, but it should work for most networks)
When you mention an interface, were you thinking something along the lines of this? class TagManagerAPI():
from enum import Enum
import scripts.helpers.tag_manager as tm
class DisplayMethod(Enum):
TAG = 0
MODEL = 1
def read_all_tags() -> list[list[str]]:
pass
def search() -> list[list[str]]:
pass
def add_row() -> list[list[str]]:
pass
def del_row() -> list[list[str]]:
pass
def save() -> list[list[str]]:
pass Where the UI interacts with the API object, and the API object interacts with the back-end. |
For organizing, we could either add a I can see the convinience with having that extra piece of UI. Personally, I don't like how the dropdown component feels, so I wanted to avoid them as much as possible. However, this seems like an intuitive way to edit tags on the fly. As for the display type, my thought process behind that was just editing the data on demand. We still only need to load the tags once, but the data will be reorganized before making it to the front/back-end. It will add some slight computational load when reading/writing tags, but it should be negligible unless you have thousands of tags. I was thinking this would be a better way of achieving the "two-tab" approach we discussed earlier. |
This commit adds a modular API interface to handle control-flow between the back-end and the front-end. The main purpose it serves is to control the data structure to allow for more dynamic data presentation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Review... Very Close to Finished!!
Still fails on fresh install when "network_descriptions" folder doesn't exist
I really like where the extras menu is!
Auto-saving works! Models, tags, and descriptions with spaces are saved properly!
Sorting alphabetically vs # tags works! It's a nice touch to sort the tags in "By Model" mode, what do you think of sorting alphabetically instead of reverse-alphabetically?
Tags are not properly converted from the old to the new system
- On clean install, text files are generated for all extra networks, which I don't think we should be generating any more new txt files
- Even then, the tags from the text files generated are not added to the tags.json file, it only has the example tag
On clean install, extra network cards on the main UI tab are hidden for some reason
- In the past this was caused by the wrong *.js file being loaded for the webui, but I'm not sure why this happened here
After auto-saving and refreshing the page (not reloading the UI) the table doesn't reflect the saved tags from tags.json, it only shows the example tag
- Is there a disconnect with the table in memory and the table in the file? At what point are the tags read from the file into memory to display on the table?
- Adding a new row immediately updates the table to the tags from the file, so somewhere it's just not updating after a page refresh
Clicking the "auto save" doesn't immediately save the current state of the table, a change needs to be made before it saves
You can only search one term/word/model/etc. at a time
- I know it's a bit cumbersome to implement searching via multiple keywords, so that's a feature I'd be willing to add after this rework gets merged
Adding a tag after removing all tags and saving adds a new_tag and an empty tag as an empty row
- Saving adds an empty tag to the backend, I'm not sure how impactful this is to the program so it's not a huge issue
When in "By Model" mode it's not super clear what the "remove" button does...
I realise my commit history says 9 hours ago, but a lot of these commits were made about half an hour ago. I am currently working as we speak :D I haven't been able to replicate the issues you're facing with the table headers not being displayed properly - is this a SD.Next exclusive issue? I will get around to adding a fail check for the network_descriptions folder, and the old tag system will be completely disabled when I feel confident the update logic works flawlessly. I am a bit unsure how to fix the add/remove buttons as of now. However, I do agree it should not add a new Tag python object, but instead a new row following whichever tag representation you're using. Finally, the existence of an empty tag in the backend is neccessary to store models without tags. Alternatively, we can also create a tag with a unique placeholder name such as Also, on a side note. A1111 added a feature that adds model descriptions to search_terms, effectively invalidating this extension lol (see your PR on stable-diffusion-webui-extensions for more details). Personally, I still think this extension provides value in terms of accessibility - our UI is a lot more intuitive than A1111's metadata editor. EDIT:
Are you on automatic1111 1.8+? Chances are it's broken on earlier versions. |
- Add/Remove buttons now adds a new row based on the display mode - Remove now supports a row index (default: -1)
While auto-save is a nice feature to have, it doesn't really work well in the sense that the table in memory will always be misaligned with the textual representation. I am in favour of removing it completely.
Ok, I can take a look at the javascript and see what's up... if automatic is adding more stuff to the search terms like us then the flag is probably broken as well, I'll see if I can't come up with a fix for that. Also I did all of my testing on a1111, so I think I might have had an outdated version |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I got a NotImplementedError() on format_tag_data() after clean install
- Caused by self.display_mode being None when running, unknown why it's None
- The option is still a string of 1 or 0 on the backend so the if-statements aren't catching and the function is returning None due to reaching the end of the function
- This specific issue was because my config file still had a 1 or 0 option, the new backend works with comparisons...
- To be safe I think all of the if-statements should be if-else like it's assumed that something will go wrong so at least there's a default option to fall back on instead of missing all of the if-statements and causing errors
- Enums and ints aren't interchangeable, but they are used interchangeably in the code, causing comparisons to miss and if-statements to note execute, resulting in the NotImplementedError()
I like the addition of the row index, the buttons adding/removing rows is fantastic!
- I really like the ability to remove a specific index, so I think it'd be very useful to have an index column to help indicate index and row associations
Adding tags then refreshing extra networks allows searching for the new tag! Amazing!
- I'm awful at remembering model names, so I'm trying to think of a way to allow the user to search for model names they have then copy the name to add it to a row in the tag editor...
- This becomes especially important when you have files that show up like "0282 Blooming Blessing.v2" on the model card but are inserted into the prompt textarea as "0282 Blooming Blessing_v2", so it becomes confusing what the actual model name is
- Specifically for this case, I was not able to match the name of the model to a tag and could not apply the tag to the model, so I assume there's an issue with the numbers, spaces, periods, or v2 characters on our backend where the name of the file is not what it appears to be to the user
after having reviewed this PR a few times now, I'm starting to really appreciate the monumental effort you've put into this PR, so fantastic work and thanks for taking this on Cruxial0!
not sure if this is too late, I didn't read the entire conversation irrc the rule in A41 for gradio component is "if a gradio component has a
a usere can change the values buy using the the importent bit ui_loadsave code example # for a single component
test_cb = gr.Checkbox()
test_cb.do_not_save_to_config = True
# you have lots of components because applying the attributes in and loop
c_list = []
c1 = gr.XXX()
c2 = gr.XXX()
...
c_list = [c1, c2, .....]
[setattr(c, 'do_not_save_to_config ', True) for c in c_list]
|
I like to clarify even though someone added a similar feature in base webui you and this does not prevent this extension from being added to the index I only mention about the upcoming future in dev brunch as a heads up so that you people are aware
|
I knew there probably had to be a way of disabling the caching, but I wasn't able to find it. Thanks for clearing that up! As for 1.8 support, are you sure you tested the latest version? The error message you posted is the exact same as the one I got before implementing the patch. I am not able to reproduce as of 0055952 being merged. |
did a another test just now to make sure KeyError: 'shorthash' |
This is getting very close to done... nsfw filter doesn't work
Tags are duplicated when switching from "By Model" to "By Tag" and tags added and saved in "By Tag" are not displayed when switching to "By Model" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See previous comment ^^^
Update function no longer scans directories with a deprecated counterpart
Not able to replicate this. Is this on A1111 or SD.Next? I fixed an issue where tags would be overwritten if the old folders were regenerated, which would happen if you were to change branches. Not sure if that's related to the issues you're having or not. |
closes #10
Added a system to manage loading/saving of tags. Creation/deletion is still pending, but shouldn't be hard to add.
There seems to be an issue with loading the UI tabs, I'm not sure if it's caused by code within this extension or some weird caching issue with Gradio or A1111.
Additional changes:
NOTE: My changes are simply added on the side of everything else as of now, this PR is not merge ready. Feel free to make changes here.