refactor(caption): unify tagger settings and reorganize Caption Tab UI

Consolidate WD14 and DeepBooru tagger settings into unified options:
- Merge wd14_general_threshold + deepbooru_score_threshold → tagger_threshold
- Merge wd14_include_rating + deepbooru_include_rating → tagger_include_rating
- Rename interrogate_score → tagger_show_scores
- Rename tagger_escape → tagger_escape_brackets
- Rename CLiP → OpenCLiP in caption type choices

UI reorganization:
- Add Interrogate tab to Caption Tab with default caption type selector
- Move interrogate_offload to Model Offloading section as "Offload caption models"
- Hide Interrogate settings section (all settings now in Caption Tab UI)
- Update locale_en.json for OpenCLiP naming

Code improvements:
- DeepBooru tag_multi() now accepts same parameters as WD14 for unified interface
- Fix setting references in interrogate.py for consolidated settings
- Add comprehensive tagger test suite (cli/test-tagger.py)
This commit is contained in:
CalamitousFelicitousness
2026-01-21 02:45:12 +00:00
parent 656e86a962
commit becb19319d
7 changed files with 989 additions and 90 deletions
+33 -12
View File
@@ -85,28 +85,29 @@ def tagger_batch_wrapper(model_name, batch_files, batch_folder, batch_str, save_
def update_tagger_ui(model_name):
"""Update UI controls based on selected tagger model.
When DeepBooru is selected, character_threshold and include_rating are disabled
since DeepBooru doesn't support separate character threshold or rating tags.
When DeepBooru is selected, character_threshold is disabled since DeepBooru
doesn't support separate character threshold.
"""
from modules.interrogate import tagger
is_db = tagger.is_deepbooru(model_name)
return [
gr.update(interactive=not is_db), # character_threshold
gr.update(interactive=not is_db, value=False if is_db else None), # include_rating
gr.update(), # include_rating - now supported by both taggers
]
def update_tagger_params(model_name, general_threshold, character_threshold, include_rating, max_tags, sort_alpha, use_spaces, escape_brackets, exclude_tags):
def update_tagger_params(model_name, general_threshold, character_threshold, include_rating, max_tags, sort_alpha, use_spaces, escape_brackets, exclude_tags, show_scores):
"""Save all tagger parameters to shared.opts when UI controls change."""
shared.opts.wd14_model = model_name
shared.opts.wd14_general_threshold = float(general_threshold)
shared.opts.tagger_threshold = float(general_threshold)
shared.opts.wd14_character_threshold = float(character_threshold)
shared.opts.wd14_include_rating = bool(include_rating)
shared.opts.tagger_include_rating = bool(include_rating)
shared.opts.tagger_max_tags = int(max_tags)
shared.opts.tagger_sort_alpha = bool(sort_alpha)
shared.opts.tagger_use_spaces = bool(use_spaces)
shared.opts.tagger_escape = bool(escape_brackets)
shared.opts.tagger_escape_brackets = bool(escape_brackets)
shared.opts.tagger_exclude_tags = str(exclude_tags)
shared.opts.tagger_show_scores = bool(show_scores)
shared.opts.save()
@@ -138,6 +139,12 @@ def update_vlm_model_params(vlm_model, vlm_system):
shared.opts.save()
def update_default_caption_type(caption_type):
"""Save the default caption type to shared.opts."""
shared.opts.interrogate_default_type = str(caption_type)
shared.opts.save()
def create_ui():
shared.log.debug('UI initialize: tab=caption')
with gr.Row(equal_height=False, variant='compact', elem_classes="caption", elem_id="caption_tab"):
@@ -200,7 +207,7 @@ def create_ui():
btn_vlm_caption_batch = gr.Button("Batch Caption", variant='primary', elem_id="btn_vlm_caption_batch")
with gr.Row():
btn_vlm_caption = gr.Button("Caption", variant='primary', elem_id="btn_vlm_caption")
with gr.Tab("CLiP Interrogate", elem_id='tab_clip_interrogate'):
with gr.Tab("OpenCLiP", elem_id='tab_clip_interrogate'):
with gr.Row():
clip_model = gr.Dropdown([], value=shared.opts.interrogate_clip_model, label='CLiP Model', elem_id='clip_clip_model')
ui_common.create_refresh_button(clip_model, openclip.refresh_clip_models, lambda: {"choices": openclip.refresh_clip_models()}, 'clip_models_refresh')
@@ -250,17 +257,19 @@ def create_ui():
wd_unload_btn = gr.Button(value='Unload', elem_id='wd_unload', variant='secondary')
with gr.Accordion(label='Tagger: Advanced Options', open=True, visible=True):
with gr.Row():
wd_general_threshold = gr.Slider(label='General threshold', value=shared.opts.wd14_general_threshold, minimum=0.0, maximum=1.0, step=0.01, elem_id='wd_general_threshold')
wd_general_threshold = gr.Slider(label='General threshold', value=shared.opts.tagger_threshold, minimum=0.0, maximum=1.0, step=0.01, elem_id='wd_general_threshold')
wd_character_threshold = gr.Slider(label='Character threshold', value=shared.opts.wd14_character_threshold, minimum=0.0, maximum=1.0, step=0.01, elem_id='wd_character_threshold')
with gr.Row():
wd_max_tags = gr.Slider(label='Max tags', value=shared.opts.tagger_max_tags, minimum=1, maximum=512, step=1, elem_id='wd_max_tags')
wd_include_rating = gr.Checkbox(label='Include rating', value=shared.opts.wd14_include_rating, elem_id='wd_include_rating')
wd_include_rating = gr.Checkbox(label='Include rating', value=shared.opts.tagger_include_rating, elem_id='wd_include_rating')
with gr.Row():
wd_sort_alpha = gr.Checkbox(label='Sort alphabetically', value=shared.opts.tagger_sort_alpha, elem_id='wd_sort_alpha')
wd_use_spaces = gr.Checkbox(label='Use spaces', value=shared.opts.tagger_use_spaces, elem_id='wd_use_spaces')
wd_escape = gr.Checkbox(label='Escape brackets', value=shared.opts.tagger_escape, elem_id='wd_escape')
wd_escape = gr.Checkbox(label='Escape brackets', value=shared.opts.tagger_escape_brackets, elem_id='wd_escape')
with gr.Row():
wd_exclude_tags = gr.Textbox(label='Exclude tags', value=shared.opts.tagger_exclude_tags, placeholder='Comma-separated tags to exclude', elem_id='wd_exclude_tags')
with gr.Row():
wd_show_scores = gr.Checkbox(label='Show confidence scores', value=shared.opts.tagger_show_scores, elem_id='wd_show_scores')
gr.HTML('<style>#wd_character_threshold:has(input:disabled), #wd_include_rating:has(input:disabled) { opacity: 0.5; }</style>')
with gr.Accordion(label='Tagger: Batch', open=False, visible=True):
with gr.Row():
@@ -277,6 +286,14 @@ def create_ui():
btn_wd_tag_batch = gr.Button("Batch Tag", variant='primary', elem_id="btn_wd_tag_batch")
with gr.Row():
btn_wd_tag = gr.Button("Tag", variant='primary', elem_id="btn_wd_tag")
with gr.Tab("Interrogate", elem_id='tab_interrogate'):
with gr.Row():
default_caption_type = gr.Radio(
choices=["VLM", "OpenCLiP", "Tagger"],
value=shared.opts.interrogate_default_type,
label="Default Caption Type",
elem_id="default_caption_type"
)
with gr.Column(variant='compact', elem_id='interrogate_output'):
with gr.Row(elem_id='interrogate_output_prompt'):
prompt = gr.Textbox(label="Answer", lines=12, placeholder="ai generated image description")
@@ -320,7 +337,7 @@ def create_ui():
wd_model.change(fn=update_tagger_ui, inputs=[wd_model], outputs=[wd_character_threshold, wd_include_rating], show_progress=False)
# Save tagger parameters to shared.opts when UI controls change
tagger_inputs = [wd_model, wd_general_threshold, wd_character_threshold, wd_include_rating, wd_max_tags, wd_sort_alpha, wd_use_spaces, wd_escape, wd_exclude_tags]
tagger_inputs = [wd_model, wd_general_threshold, wd_character_threshold, wd_include_rating, wd_max_tags, wd_sort_alpha, wd_use_spaces, wd_escape, wd_exclude_tags, wd_show_scores]
wd_model.change(fn=update_tagger_params, inputs=tagger_inputs, outputs=[], show_progress=False)
wd_general_threshold.change(fn=update_tagger_params, inputs=tagger_inputs, outputs=[], show_progress=False)
wd_character_threshold.change(fn=update_tagger_params, inputs=tagger_inputs, outputs=[], show_progress=False)
@@ -330,6 +347,7 @@ def create_ui():
wd_use_spaces.change(fn=update_tagger_params, inputs=tagger_inputs, outputs=[], show_progress=False)
wd_escape.change(fn=update_tagger_params, inputs=tagger_inputs, outputs=[], show_progress=False)
wd_exclude_tags.change(fn=update_tagger_params, inputs=tagger_inputs, outputs=[], show_progress=False)
wd_show_scores.change(fn=update_tagger_params, inputs=tagger_inputs, outputs=[], show_progress=False)
# Save CLiP model parameters to shared.opts when UI controls change
clip_model_inputs = [clip_model, blip_model, clip_mode]
@@ -342,6 +360,9 @@ def create_ui():
vlm_model.change(fn=update_vlm_model_params, inputs=vlm_model_inputs, outputs=[], show_progress=False)
vlm_system.change(fn=update_vlm_model_params, inputs=vlm_model_inputs, outputs=[], show_progress=False)
# Save default caption type to shared.opts when UI control changes
default_caption_type.change(fn=update_default_caption_type, inputs=[default_caption_type], outputs=[], show_progress=False)
for tabname, button in copy_interrogate_buttons.items():
generation_parameters_copypaste.register_paste_params_button(generation_parameters_copypaste.ParamBinding(paste_button=button, tabname=tabname, source_text_component=prompt, source_image_component=image,))
generation_parameters_copypaste.add_paste_fields("caption", image, None)