Expose model capability hints and configurable vision projector placement
This commit is contained in:
+15
-6
@@ -39,11 +39,13 @@ def repo_id(value):
|
||||
def file_role(filename):
|
||||
"""One classification shared by library UI and profile validation."""
|
||||
name=filename.lower();parts=PurePosixPath(name).parts
|
||||
if any(p in ('text_encoder','text_encoders') for p in parts) or re.search(r'(?:^|[/_-])(?:text.encoder|qwen3vl|t5xxl|clip_l|clip_g)(?:[/_.-]|$)',name):
|
||||
if re.search(r'(?:^|[/_-])mmproj(?:[/_.-]|$)',name):
|
||||
role,label='vision_projector','Vision-Projektor'
|
||||
elif any(p in ('text_encoder','text_encoders') for p in parts) or re.search(r'(?:^|[/_-])(?:text.encoder|qwen3vl|t5xxl|clip_l|clip_g)(?:[/_.-]|$)',name):
|
||||
role,label='text_encoder','Textencoder'
|
||||
elif 'vae' in parts or re.search(r'(?:^|[/_-])vae(?:[/_.-]|$)',name):
|
||||
role,label='vae','VAE'
|
||||
elif re.search(r'(?:^|[/_-])(?:mmproj|lora|adapter)(?:[/_.-]|$)',name):
|
||||
elif re.search(r'(?:^|[/_-])(?:lora|adapter)(?:[/_.-]|$)',name):
|
||||
role,label='auxiliary','Zusatzkomponente'
|
||||
elif name.endswith(('.gguf','.safetensors')):
|
||||
role,label='model','Modelldatei'
|
||||
@@ -58,6 +60,13 @@ def is_derived_model(model):
|
||||
tags=model.get('tags') or []
|
||||
return any(isinstance(tag,str) and (tag.startswith('base_model:') or tag.lower() in {'lora','peft','adapter','merge','mergekit','finetune','fine-tuned','quantized','gguf','gptq','awq'}) for tag in tags)
|
||||
|
||||
def capabilities(model):
|
||||
tags={t.lower() for t in (model.get('tags') or []) if isinstance(t,str)}
|
||||
vision=bool(tags & {'image-text-to-text','image-to-text','vision','multimodal'}) or model.get('pipeline_tag') in ('image-text-to-text','image-to-text')
|
||||
files=any(file_role(x.get('rfilename',''))['role']=='vision_projector' for x in (model.get('siblings') or []))
|
||||
tools=bool(tags & {'tool-use','tool-calling','function-calling','function_calling'})
|
||||
return dict(vision='Hinweis laut Hub' if vision else 'Projektordatei vorhanden' if files else 'Unbekannt',tools='Hinweis laut Hub' if tools else 'Unbekannt')
|
||||
|
||||
class Catalog:
|
||||
def __init__(self,root):
|
||||
self.root=Path(root);self.lock=threading.RLock();self.job=None;self.cancel=threading.Event()
|
||||
@@ -105,11 +114,11 @@ class Catalog:
|
||||
elif '/' in q:direct=repo_id(q)
|
||||
if direct:
|
||||
item=metadata('/api/models/'+direct)
|
||||
return dict(models=[dict(repo=item['id'],downloads=item.get('downloads'),gated=item.get('gated',False),created_at=item.get('createdAt'))],base_only=base_only,scanned=1,notice='Direkt ausgewähltes Repository: Kategorie- und Basismodellfilter werden für diesen Treffer nicht angewendet.')
|
||||
return dict(models=[dict(repo=item['id'],downloads=item.get('downloads'),gated=item.get('gated',False),created_at=item.get('createdAt'),capabilities=capabilities(item))],base_only=base_only,scanned=1,notice='Direkt ausgewähltes Repository: Kategorie- und Basismodellfilter werden für diesen Treffer nicht angewendet.')
|
||||
if q.lower().endswith('.gguf'):
|
||||
q=q[:-5];notice='GGUF-Dateiname als Repository-Suchbegriff verwendet. Die Datei wählst du anschließend in den Modelldetails.'
|
||||
query=dict(search=q,filter=KINDS[kind],limit=100 if base_only else 20,sort='createdAt' if sort=='newest' else 'downloads',direction=-1)
|
||||
if base_only:query['expand']=['downloads','createdAt','gated','cardData','tags']
|
||||
query['expand']=['downloads','createdAt','gated','cardData','tags','pipeline_tag']
|
||||
rows=metadata('/api/models?'+urllib.parse.urlencode(query,doseq=True))
|
||||
if not rows and q and kind=='chat':
|
||||
fallback=dict(query,filter='gguf',expand=['downloads','createdAt','gated','cardData','tags','pipeline_tag'])
|
||||
@@ -118,7 +127,7 @@ class Catalog:
|
||||
if rows:notice='GGUF-Sprachmodelle ohne reguläres Kategorie-Tag gefunden. Bei aktiver Basismodellauswahl bleiben Quantisierungen ausgeblendet.'
|
||||
scanned=len(rows)
|
||||
if base_only:rows=[x for x in rows if not is_derived_model(x)]
|
||||
return {'models':[dict(repo=x['id'],downloads=x.get('downloads'),gated=x.get('gated',False),created_at=x.get('createdAt')) for x in rows[:20]],'base_only':base_only,'scanned':scanned,'notice':notice}
|
||||
return {'models':[dict(repo=x['id'],downloads=x.get('downloads'),gated=x.get('gated',False),created_at=x.get('createdAt'),capabilities=capabilities(x)) for x in rows[:20]],'base_only':base_only,'scanned':scanned,'notice':notice}
|
||||
def files(self,repo):
|
||||
repo=repo_id(repo)
|
||||
with self.cache_lock:
|
||||
@@ -134,7 +143,7 @@ class Catalog:
|
||||
size=f.get('size',f.get('lfs',{}).get('size'))
|
||||
if not isinstance(size,int) or size<1:continue
|
||||
files.append(dict(name=name,size=size,sha256=f.get('lfs',{}).get('sha256')))
|
||||
result=dict(repo=repo,revision=revision,files=files,gated=data.get('gated',False),license=(data.get('cardData') or {}).get('license'),url='https://huggingface.co/'+repo)
|
||||
result=dict(capabilities=capabilities(data),repo=repo,revision=revision,files=files,gated=data.get('gated',False),license=(data.get('cardData') or {}).get('license'),url='https://huggingface.co/'+repo)
|
||||
with self.cache_lock:
|
||||
if len(self.cache)>=64:self.cache.pop(next(iter(self.cache)))
|
||||
self.cache[repo]=(time.monotonic(),result)
|
||||
|
||||
Reference in New Issue
Block a user