diff --git a/cli/api-interrogate.py b/cli/api-interrogate.py index 211980046..cd345834b 100755 --- a/cli/api-interrogate.py +++ b/cli/api-interrogate.py @@ -9,6 +9,7 @@ import sys import os import asyncio import filetype +from types import SimpleNamespace from PIL import Image from util import log, Map import sdapi @@ -65,22 +66,21 @@ async def interrogate(f): stats['captions'][word] = stats['captions'][word] + 1 if word in stats['captions'] else 1 else: log.error({ 'interrogate clip error': res }) - # run booru - json.model = 'deepdanbooru' - res = await sdapi.post('/sdapi/v1/interrogate', json) + # run tagger (DeepBooru) + tagger_req = SimpleNamespace(image=json.image, model='deepbooru', show_scores=True) + res = await sdapi.post('/sdapi/v1/tagger', tagger_req) keywords = {} - if 'caption' in res: - for term in res.caption.split(', '): - term = term.replace('(', '').replace(')', '').replace('\\', '').split(':') - if len(term) < 2: - continue - keywords[term[0]] = term[1] - keywords = dict(sorted(keywords.items(), key=lambda x:x[1], reverse=True)) - for word in keywords.items(): - stats['keywords'][word[0]] = stats['keywords'][word[0]] + 1 if word[0] in stats['keywords'] else 1 - log.info({ 'interrogate keywords': keywords }) + if 'scores' in res and res.scores: + keywords = dict(sorted(res.scores.items(), key=lambda x: x[1], reverse=True)) + for word in keywords: + stats['keywords'][word] = stats['keywords'][word] + 1 if word in stats['keywords'] else 1 + log.info({'interrogate keywords': keywords}) + elif 'tags' in res: + for tag in res.tags.split(', '): + stats['keywords'][tag] = stats['keywords'][tag] + 1 if tag in stats['keywords'] else 1 + log.info({'interrogate tags': res.tags}) else: - log.error({ 'interrogate booru error': res }) + log.error({'interrogate tagger error': res}) return caption, keywords, style diff --git a/cli/test-caption-api.py b/cli/test-caption-api.py index b79ec3c8b..a9d5090ae 100755 --- a/cli/test-caption-api.py +++ b/cli/test-caption-api.py @@ -3,7 +3,7 @@ Caption API Test Suite Comprehensive tests for all Caption API endpoints and parameters: -- GET/POST /sdapi/v1/interrogate (OpenCLiP/DeepBooru) +- GET/POST /sdapi/v1/interrogate (OpenCLiP) - POST /sdapi/v1/vqa (VLM Captioning with annotated images) - GET /sdapi/v1/vqa/models, /sdapi/v1/vqa/prompts - POST /sdapi/v1/tagger @@ -302,13 +302,7 @@ class CaptionAPITest: self.log_fail(f"Expected list, got {type(data)}") return - # Test 2: Contains deepdanbooru - if 'deepdanbooru' in data: - self.log_pass("Contains 'deepdanbooru'") - else: - self.log_fail("Missing 'deepdanbooru'") - - # Test 3: Contains OpenCLIP models (format: arch/dataset) + # Test 2: Contains OpenCLIP models (format: arch/dataset) clip_models = [m for m in data if '/' in m] if clip_models: self.log_pass(f"Contains {len(clip_models)} OpenCLIP models") @@ -316,36 +310,6 @@ class CaptionAPITest: else: self.log_skip("No OpenCLIP models found (may need to download)") - # ========================================================================= - # TEST: POST /sdapi/v1/interrogate - DeepBooru - # ========================================================================= - def test_interrogate_post_deepbooru(self): - """Test DeepBooru interrogation.""" - print("\n" + "=" * 70) - print("TEST: POST /sdapi/v1/interrogate (DeepBooru)") - print("=" * 70) - - t0 = time.time() - data = self.post('/sdapi/v1/interrogate', { - 'image': self.image_b64, - 'model': 'deepdanbooru' - }) - elapsed = time.time() - t0 - - if 'error' in data: - self.log_skip(f"DeepBooru: {data.get('reason', 'failed')} (model may not be loaded)") - return - - caption = data.get('caption', '') - if caption and not self.is_error_answer(caption): - caption_preview = caption[:80] + '...' if len(caption) > 80 else caption - self.log_pass(f"DeepBooru returns caption ({elapsed:.1f}s)") - self.log_info(f"Caption: {caption_preview}") - elif self.is_error_answer(caption): - self.log_fail(f"DeepBooru returned error: {caption}") - else: - self.log_fail("DeepBooru returned empty caption") - # ========================================================================= # TEST: POST /sdapi/v1/interrogate - OpenCLIP Modes # ========================================================================= @@ -2323,7 +2287,6 @@ class CaptionAPITest: # Interrogate tests self.test_interrogate_list_models() - self.test_interrogate_post_deepbooru() self.test_interrogate_post_clip_modes() self.test_interrogate_analyze() self.test_interrogate_invalid_inputs() diff --git a/modules/api/endpoints.py b/modules/api/endpoints.py index d8df7f531..64b8b738d 100644 --- a/modules/api/endpoints.py +++ b/modules/api/endpoints.py @@ -98,16 +98,17 @@ def get_interrogate(): Returns model identifiers for use with POST /sdapi/v1/interrogate. **Model Types:** - - `deepdanbooru`: Anime-style image tagger returning comma-separated tags - OpenCLIP models: Format `architecture/pretrained_dataset` (e.g., `ViT-L-14/openai`) + For anime-style tagging (WaifuDiffusion, DeepBooru), use `/sdapi/v1/tagger` instead. + **Example Response:** ```json - ["deepdanbooru", "ViT-L-14/openai", "ViT-H-14/laion2b_s32b_b79k"] + ["ViT-L-14/openai", "ViT-H-14/laion2b_s32b_b79k"] ``` """ from modules.interrogate.openclip import refresh_clip_models - return ['deepdanbooru'] + refresh_clip_models() + return refresh_clip_models() def get_schedulers(): from modules.sd_samplers import list_samplers diff --git a/modules/api/models.py b/modules/api/models.py index d16719c75..86eb14886 100644 --- a/modules/api/models.py +++ b/modules/api/models.py @@ -368,11 +368,11 @@ class ResStatus(BaseModel): class ReqInterrogate(BaseModel): """Request model for OpenCLIP/BLIP image interrogation. - Analyze image using CLIP model via OpenCLIP to generate prompts, - or use DeepDanbooru for anime-style tagging. + Analyze image using CLIP model via OpenCLIP to generate prompts. + For anime-style tagging, use /sdapi/v1/tagger with WaifuDiffusion or DeepBooru. """ image: str = Field(default="", title="Image", description="Image to interrogate. Must be a Base64 encoded string containing the image data (PNG/JPEG).") - model: str = Field(default="ViT-L-14/openai", title="Model", description="OpenCLIP model to use. Use 'deepdanbooru' or 'deepbooru' for anime tagging. Get available models from GET /sdapi/v1/interrogate.") + model: str = Field(default="ViT-L-14/openai", title="Model", description="OpenCLIP model to use. Get available models from GET /sdapi/v1/interrogate.") clip_model: str = Field(default="ViT-L-14/openai", title="CLIP Model", description="CLIP model used for image-text similarity matching. Larger models (ViT-L, ViT-H) are more accurate but slower and use more VRAM.") blip_model: str = Field(default="blip-large", title="Caption Model", description="BLIP model used to generate the initial image caption. The caption model describes the image content which CLIP then enriches with style and flavor terms.") mode: str = Field(default="best", title="Mode", description="Interrogation mode. Fast: Quick caption with minimal flavor terms. Classic: Standard interrogation with balanced quality and speed. Best: Most thorough analysis, slowest but highest quality. Negative: Generate terms to use as negative prompt.")