feat: add creator search option and update related functionality across models and UI

This commit is contained in:
Will Miao
2025-08-06 18:32:57 +08:00
parent c74033b9c0
commit 2c6f9d8602
8 changed files with 31 additions and 2 deletions

View File

@@ -177,6 +177,7 @@ class BaseModelRoutes(ABC):
'filename': request.query.get('search_filename', 'true').lower() == 'true', 'filename': request.query.get('search_filename', 'true').lower() == 'true',
'modelname': request.query.get('search_modelname', 'true').lower() == 'true', 'modelname': request.query.get('search_modelname', 'true').lower() == 'true',
'tags': request.query.get('search_tags', 'false').lower() == 'true', 'tags': request.query.get('search_tags', 'false').lower() == 'true',
'creator': request.query.get('search_creator', 'false').lower() == 'true',
'recursive': request.query.get('recursive', 'false').lower() == 'true', 'recursive': request.query.get('recursive', 'false').lower() == 'true',
} }

View File

@@ -200,6 +200,22 @@ class BaseModelService(ABC):
search_results.append(item) search_results.append(item)
continue continue
# Search by creator
civitai = item.get('civitai')
creator_username = ''
if civitai and isinstance(civitai, dict):
creator = civitai.get('creator')
if creator and isinstance(creator, dict):
creator_username = creator.get('username', '')
if search_options.get('creator', False) and creator_username:
if fuzzy_search:
if fuzzy_match(creator_username, search):
search_results.append(item)
continue
elif search.lower() in creator_username.lower():
search_results.append(item)
continue
return search_results return search_results
async def _apply_specific_filters(self, data: List[Dict], **kwargs) -> List[Dict]: async def _apply_specific_filters(self, data: List[Dict], **kwargs) -> List[Dict]:

View File

@@ -50,7 +50,8 @@ VALID_LORA_TYPES = ['lora', 'locon', 'dora']
# Civitai model tags in priority order for subfolder organization # Civitai model tags in priority order for subfolder organization
CIVITAI_MODEL_TAGS = [ CIVITAI_MODEL_TAGS = [
'character', 'style', 'concept', 'clothing', 'base model', 'character', 'style', 'concept', 'clothing',
# 'base model', # exclude 'base model'
'poses', 'background', 'tool', 'vehicle', 'buildings', 'poses', 'background', 'tool', 'vehicle', 'buildings',
'objects', 'assets', 'animal', 'action' 'objects', 'assets', 'animal', 'action'
] ]

View File

@@ -47,7 +47,7 @@ def get_lora_info(lora_name):
# No event loop is running, we can use asyncio.run() # No event loop is running, we can use asyncio.run()
return asyncio.run(_get_lora_info_async()) return asyncio.run(_get_lora_info_async())
def fuzzy_match(text: str, pattern: str, threshold: float = 0.7) -> bool: def fuzzy_match(text: str, pattern: str, threshold: float = 0.85) -> bool:
""" """
Check if text matches pattern using fuzzy matching. Check if text matches pattern using fuzzy matching.
Returns True if similarity ratio is above threshold. Returns True if similarity ratio is above threshold.

View File

@@ -628,6 +628,9 @@ export class BaseModelApiClient {
if (pageState.searchOptions.tags !== undefined) { if (pageState.searchOptions.tags !== undefined) {
params.append('search_tags', pageState.searchOptions.tags.toString()); params.append('search_tags', pageState.searchOptions.tags.toString());
} }
if (pageState.searchOptions.creator !== undefined) {
params.append('search_creator', pageState.searchOptions.creator.toString());
}
params.append('recursive', (pageState.searchOptions?.recursive ?? false).toString()); params.append('recursive', (pageState.searchOptions?.recursive ?? false).toString());
} }
} }

View File

@@ -318,6 +318,7 @@ export class SearchManager {
filename: options.filename || false, filename: options.filename || false,
modelname: options.modelname || false, modelname: options.modelname || false,
tags: options.tags || false, tags: options.tags || false,
creator: options.creator || false,
recursive: recursive recursive: recursive
}; };
} else if (this.currentPage === 'checkpoints') { } else if (this.currentPage === 'checkpoints') {
@@ -325,6 +326,7 @@ export class SearchManager {
filename: options.filename || false, filename: options.filename || false,
modelname: options.modelname || false, modelname: options.modelname || false,
tags: options.tags || false, tags: options.tags || false,
creator: options.creator || false,
recursive: recursive recursive: recursive
}; };
} }

View File

@@ -37,6 +37,7 @@ export const state = {
filename: true, filename: true,
modelname: true, modelname: true,
tags: false, tags: false,
creator: false,
recursive: false recursive: false
}, },
filters: { filters: {
@@ -83,6 +84,7 @@ export const state = {
searchOptions: { searchOptions: {
filename: true, filename: true,
modelname: true, modelname: true,
creator: false,
recursive: false recursive: false
}, },
filters: { filters: {
@@ -110,6 +112,7 @@ export const state = {
filename: true, filename: true,
modelname: true, modelname: true,
tags: false, tags: false,
creator: false,
recursive: false recursive: false
}, },
filters: { filters: {

View File

@@ -86,15 +86,18 @@
<div class="search-option-tag active" data-option="filename">Filename</div> <div class="search-option-tag active" data-option="filename">Filename</div>
<div class="search-option-tag active" data-option="modelname">Checkpoint Name</div> <div class="search-option-tag active" data-option="modelname">Checkpoint Name</div>
<div class="search-option-tag active" data-option="tags">Tags</div> <div class="search-option-tag active" data-option="tags">Tags</div>
<div class="search-option-tag" data-option="creator">Creator</div>
{% elif request.path == '/embeddings' %} {% elif request.path == '/embeddings' %}
<div class="search-option-tag active" data-option="filename">Filename</div> <div class="search-option-tag active" data-option="filename">Filename</div>
<div class="search-option-tag active" data-option="modelname">Embedding Name</div> <div class="search-option-tag active" data-option="modelname">Embedding Name</div>
<div class="search-option-tag active" data-option="tags">Tags</div> <div class="search-option-tag active" data-option="tags">Tags</div>
<div class="search-option-tag" data-option="creator">Creator</div>
{% else %} {% else %}
<!-- Default options for LoRAs page --> <!-- Default options for LoRAs page -->
<div class="search-option-tag active" data-option="filename">Filename</div> <div class="search-option-tag active" data-option="filename">Filename</div>
<div class="search-option-tag active" data-option="modelname">Model Name</div> <div class="search-option-tag active" data-option="modelname">Model Name</div>
<div class="search-option-tag active" data-option="tags">Tags</div> <div class="search-option-tag active" data-option="tags">Tags</div>
<div class="search-option-tag" data-option="creator">Creator</div>
{% endif %} {% endif %}
</div> </div>
</div> </div>