Add owned OpenAI endpoint and coordinated native model switching

This commit is contained in:
Mikei386
2026-09-28 20:24:12 +02:00
parent 8d03a9a979
commit ff5d36252a
25 changed files with 952 additions and 177 deletions
+2 -2
View File
@@ -12,8 +12,8 @@ RUN git clone https://github.com/Comfy-Org/ComfyUI.git /opt/deck-comfy \
&& /opt/deck-image-python/bin/pip freeze > /opt/deck-comfy/deck-requirements.lock
WORKDIR /app
COPY deploy/image-requirements.lock /app/deploy/image-requirements.lock
COPY docker_support.py image_encoder_node.py image_runtime.py image_test.py profiles.py capacity.py server.py runtime.py catalog.py auth.py collect_hardware.py demo.py /app/
COPY docker-ui.js image-test-ui.js profiles-ui.js index.html app.js studio.js runtime-ui.js catalog-ui.js style.css login.html login.js access-ui.js network-ui.js /app/
COPY endpoint.py inference.py docker_support.py image_encoder_node.py image_runtime.py image_test.py profiles.py capacity.py server.py runtime.py catalog.py auth.py collect_hardware.py /app/
COPY endpoint-ui.js docker-ui.js image-test-ui.js profiles-ui.js index.html app.js studio.js runtime-ui.js catalog-ui.js style.css login.html login.js access-ui.js network-ui.js /app/
COPY network/__init__.py network/client.py network/config.py network/rpc.py /app/network/
ENV PYTHONDONTWRITEBYTECODE=1 PYTHONUNBUFFERED=1 HOME=/tmp \
DECK_BIND_HOST=0.0.0.0 DECK_STATE_DIR=/var/lib/deck \
+25 -7
View File
@@ -14,7 +14,7 @@ import urllib.request
ROOT = Path(__file__).resolve().parent.parent
LABEL = 'de.casaderoll.athena-deck.standalone'
FILES = ['docker_support.py','docker-ui.js','deploy/docker_helper.py','deploy/setup_docker_helper.py','image_encoder_node.py','image_runtime.py','image_test.py','image-test-ui.js','profiles.py','profiles-ui.js','capacity.py','runtime.py','runtime-ui.js','catalog.py','catalog-ui.js','server.py','auth.py','collect_hardware.py','demo.py','index.html','app.js','studio.js','style.css','login.html','login.js','access-ui.js','network-ui.js','network/__init__.py','network/client.py','network/config.py','network/rpc.py','deploy/Dockerfile','deploy/image-requirements.lock']
FILES = ['endpoint.py','inference.py','endpoint-ui.js','docker_support.py','docker-ui.js','deploy/docker_helper.py','deploy/setup_docker_helper.py','image_encoder_node.py','image_runtime.py','image_test.py','image-test-ui.js','profiles.py','profiles-ui.js','capacity.py','runtime.py','runtime-ui.js','catalog.py','catalog-ui.js','server.py','auth.py','collect_hardware.py','index.html','app.js','studio.js','style.css','login.html','login.js','access-ui.js','network-ui.js','network/__init__.py','network/client.py','network/config.py','network/rpc.py','deploy/Dockerfile','deploy/image-requirements.lock']
def run(*args, check=True, interactive=False):
@@ -98,6 +98,9 @@ def launch(config):
'-p','127.0.0.1:'+str(config['port'])+':8108',
'--health-cmd', 'python3 -c "import urllib.request,json; assert json.load(urllib.request.urlopen(\'http://127.0.0.1:8108/api/v1/auth/status\',timeout=3))[\'initialized\']"',
'--health-interval','30s','--health-timeout','5s','--health-retries','3']
if config.get('api_ports'):
args+=['-e','DECK_API_BIND=0.0.0.0','-e','DECK_API_PORTS='+','.join(map(str,config['api_ports']))]
for port in config['api_ports']:args+=['-p',f'127.0.0.1:{port}:{port}']
if config.get('development_setup'):
args[args.index('--health-cmd')+1] = 'python3 -c "import urllib.request; urllib.request.urlopen(\'http://127.0.0.1:8108/api/v1/auth/status\',timeout=3)"'
# Explicit isolated development bootstrap, reachable only through host loopback/SSH.
@@ -151,6 +154,9 @@ def install(args):
if base.exists():
raise RuntimeError('Installationsverzeichnis existiert bereits. Es wird nicht überschrieben.')
free_port(args.port)
api_ports=parse_api_ports(args.api_ports or '8120-8124')
if args.port in api_ports:raise RuntimeError('Verwaltungsport und API-Ports müssen verschieden sein.')
for port in api_ports:free_port(port)
parent=base.parent
while not parent.exists():parent=parent.parent
if shutil.disk_usage(parent).free < 25*1024**3:
@@ -162,7 +168,7 @@ def install(args):
for sub in ('state','models','backups','bootstrap'):
(base/sub).mkdir(mode=0o700)
os.chown(base/'state',65534,65534)
config=dict(owner=LABEL,base=str(base),name=args.name,port=args.port,image=image,previous_image=None,gpu_telemetry=args.gpu_telemetry,image_runtime=True,docker_helper=True)
config=dict(owner=LABEL,base=str(base),name=args.name,port=args.port,api_ports=api_ports,image=image,previous_image=None,gpu_telemetry=args.gpu_telemetry,image_runtime=True,docker_helper=True)
run(sys.executable,str(ROOT/'deploy/setup_docker_helper.py'),'--client-uid','65534','--client-gid','65534')
write_manifest(base,config)
provision(config)
@@ -177,23 +183,26 @@ def install(args):
print('Generierter API-Token: geschützte Datei '+str(base/'bootstrap/api-token.txt')+'. In Passwortmanager übernehmen und Datei danach entfernen.')
def update(base, rollback=False, force=False):
def update(base, rollback=False, force=False, api_ports=None):
config=load_manifest(base)
previous=owned(config['name'],base)
if not previous:
raise RuntimeError('Kein installierter Deck-Container vorhanden.')
selected_ports=config.get('api_ports',[]) if api_ports is None else parse_api_ports(api_ports)
if config['port'] in selected_ports:raise RuntimeError('Verwaltungsport und API-Ports müssen verschieden sein.')
for port in set(selected_ports)-set(config.get('api_ports',[])):free_port(port)
image=config.get('previous_image') if rollback else build()
if not image:raise RuntimeError('Kein vorheriger Build gespeichert.')
if image==config['image'] and not force:
if image==config['image'] and selected_ports==config.get('api_ports',[]) and not force:
print('Dieser Quellstand ist bereits installiert.');return
backup_name=config['name']+'-previous'
if inspect(backup_name):raise RuntimeError('Rückfall-Containername belegt. Keine Änderung ausgeführt.')
stamp=str(time.time_ns())
shutil.copytree(base/'state',base/'backups'/stamp,ignore=shutil.ignore_patterns('models','runtime','runtime-verification','image-tests','image-verification','image-verification-*','image-runtime'))
shutil.copytree(base/'state',base/'backups'/stamp,ignore=shutil.ignore_patterns('models','runtime','runtime-verification','image-tests','image-verification','image-verification-*','image-runtime','router-verification-*'))
was_running=previous['State']['Running']
if was_running:run('docker','stop','--time','15',config['name'])
run('docker','rename',config['name'],backup_name)
new=dict(config,image=image,previous_image=config['image'])
new=dict(config,image=image,previous_image=config['image'],api_ports=selected_ports)
try:
launch(new);ready(new);write_manifest(base,new)
except Exception:
@@ -206,6 +215,14 @@ def update(base, rollback=False, force=False):
print('Nur Athena Deck wurde aktualisiert. Vorheriges Image bleibt für --rollback erhalten.')
def parse_api_ports(value):
import re
if not isinstance(value,str) or not re.fullmatch(r'[0-9]{4,5}(?:-[0-9]{4,5})?',value):raise RuntimeError('API-Port oder kleiner Portbereich erwartet, z. B. 8120-8124.')
parts=[int(x) for x in value.split('-')];start=parts[0];end=parts[-1]
if not 1024<=start<=end<=65535 or end-start>=16:raise RuntimeError('Maximal 16 nicht privilegierte API-Ports verwenden.')
return list(range(start,end+1))
def main():
parser=argparse.ArgumentParser(description='Athena Deck auf Debian 12/13 getrennt installieren. Kein WireGuard, keine Host-Paket- oder Treiberänderungen.')
actions=parser.add_mutually_exclusive_group(required=True)
@@ -214,6 +231,7 @@ def main():
parser.add_argument('--directory',type=Path,default=Path('/opt/athena-deck-standalone'))
parser.add_argument('--name',default='athena-deck-standalone')
parser.add_argument('--port',type=int,default=8110)
parser.add_argument('--api-ports',help='Separater API-Port oder Bereich, z. B. 8120-8124. Installationsstandard: 8120-8124; Updates behalten den bisherigen Bereich.')
parser.add_argument('--gpu-telemetry',action='store_true',help='Vorhandenes NVIDIA Container Toolkit für Messwerte und Fit-Prüfung nutzen; installiert keine Treiber.')
args=parser.parse_args()
import re
@@ -248,7 +266,7 @@ def main():
if item:run('docker','start',config['name'])
else:launch(config)
ready(config);print('Deck bereit.')
elif args.update or args.rollback:update(args.directory,args.rollback)
elif args.update or args.rollback:update(args.directory,args.rollback,api_ports=args.api_ports)
if __name__=='__main__':
try:main()