Expose profile context metadata for llama.cpp model discovery

This commit is contained in:
Mikei386 committed 2026-10-02 20:49:21 +02:00
1 parent a7b30b62d2
commit 1f8ffe2e72
3 files changed
+61 -2

No files matched your search

+34 -1
View File
@@ -10,6 +10,7 @@ import re
import socket
import threading
import time
from urllib.parse import urlsplit,parse_qs
from http.server import BaseHTTPRequestHandler,ThreadingHTTPServer
from api_compat import normalize_chat,CompatibilityError
from profiles import generation_parameters
@@ -151,7 +152,29 @@ class Endpoint:
if not row['runnable']:raise APIError('Profil derzeit nicht ausführbar: '+' '.join(row['blockers']),503,'model_unavailable')
return row
def model_list(self,kind="chat"):
return dict(object='list',data=[dict(id='athena-image' if kind=='image' else p['name'],object='model',created=int(p['updated_at']),owned_by='athena-deck') for p in self.rows() if p['enabled'] and p['runnable'] and p['kind']==kind])
data=[]
for p in self.rows():
if not (p['enabled'] and p['runnable'] and p['kind']==kind):continue
row=dict(id='athena-image' if kind=='image' else p['name'],object='model',created=int(p['updated_at']),owned_by='athena-deck')
if kind=='chat':
params=p.get('parameters',{})
row.update(context_window=int(params.get('context',8192)),parallel_slots=int(params.get('slots',1)),context_scope='shared')
data.append(row)
return dict(object='list',data=data)
def model_properties(self,name):
# Discovery is configuration-only: never start or probe a model worker.
p=self.find_profile(name,'chat')
params=p.get('parameters',{})
model=p.get('model') or {}
identity=' '.join(str(model.get(k,'')) for k in ('repo','file','filename','name')).lower()
known_template=bool(re.search(r'qwen|gemma',identity))
return dict(model=p['name'],n_ctx=int(params.get('context',8192)),
default_generation_settings=dict(n_ctx=int(params.get('context',8192)),params={}),
total_slots=int(params.get('slots',1)),context_scope='shared',
modalities=dict(vision=bool(params.get('vision_projector'))),
chat_template_caps=dict(supports_tool_calls=known_template,
supports_typed_content=True,
supports_reasoning_effort='qwen' in identity))
class APIHTTPServer(ThreadingHTTPServer):
daemon_threads=True
@@ -206,6 +229,16 @@ class APIHandler(BaseHTTPRequestHandler):
with ep.lock:
if not ep.allowed():raise APIError('Endpunkt wird gestoppt.',503,'endpoint_stopping')
ep.inflight+=1;admitted=True
parsed=urlsplit(self.path)
if self.command=='GET' and (parsed.path=='/models' or (parsed.path=='/v1/models' and ep.scheduler.gpu_mode!='video')):
return self.send(ep.model_list())
if self.command=='GET' and parsed.path=='/props':
query=parse_qs(parsed.query)
name=query.get('model',[None])[0]
if not name:raise APIError('model ist für Router-Eigenschaften erforderlich.')
return self.send(ep.model_properties(name))
if self.command=='GET' and parsed.path=='/health':
return self.send({'status':'ok','service':'athena-deck-api'})
if ep.scheduler.gpu_mode=='separator' and self.path not in ('/v1/embeddings','/v1/embeddings/models'):raise APIError('Audio-Trennung aktiv. Für Chat, Bilder oder Musik die Betriebsart wechseln.',503,'separator_mode_active')
if ep.video and ep.scheduler.gpu_mode not in ('llm','music') and self.path not in ('/v1/embeddings','/v1/embeddings/models'):
if ep.scheduler.gpu_mode=='switching':raise APIError('Moduswechsel läuft.',503,'mode_switching')