Expose profile context metadata for llama.cpp model discovery
This commit is contained in:
1 parent
a7b30b62d2
commit
1f8ffe2e72
3 files changed
+61
-2
No files matched your search
+34
-1
@@ -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')
|
||||
|
||||
Reference in new issue
Block a user