Add exclusive LLM and video modes with isolated LTX worker and job API
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Athena Deck prototype: loopback API, owned model processes, read-only telemetry."""
|
||||
from video_runtime import VideoRuntime
|
||||
from video import Video
|
||||
from execution_setup import assess as execution_assess
|
||||
import argparse
|
||||
import os
|
||||
@@ -104,6 +106,13 @@ class Server(ThreadingHTTPServer):
|
||||
except ValueError as exc:self.endpoint.error=str(exc)
|
||||
self.chat_tests=ChatTests(self.profiles,self.worker,self.scheduler)
|
||||
self.auto_tests=AutoTests(self.catalog.root.parent/'auto-tests.json',self.profiles,self.worker,self.scheduler)
|
||||
self.video_runtime=VideoRuntime(self.catalog.root.parent/'video-runtime')
|
||||
def stop_gpu_work():
|
||||
self.auto_tests.stop();self.chat_tests.stop();self.image_tests.stop();self.tts_tests.stop();self.worker.stop()
|
||||
self.video=Video(self.catalog.root.parent/'video',self.profiles,self.video_runtime,self.scheduler,stop_gpu_work)
|
||||
self.profiles.video_blockers=self.video.blockers
|
||||
self.profiles.video_runtime_ready=lambda:self.video_runtime.status()["installed"]
|
||||
self.endpoint.video=self.video
|
||||
self.sessions = {}
|
||||
self.login_attempts = []
|
||||
self.auth_lock = threading.Lock()
|
||||
@@ -245,7 +254,7 @@ class Handler(BaseHTTPRequestHandler):
|
||||
return self.headers.get('Host') in allowed
|
||||
|
||||
def execution_setup(self, model, profile=None):
|
||||
installed={'stt':bool(self.server.runtime.status().get('active')),'chat':bool(self.server.runtime.status().get('active')),'image':self.server.image_runtime.status().get('installed',False),'audio':self.server.tts_runtime.status().get('installed',False)}
|
||||
installed={'stt':bool(self.server.runtime.status().get('active')),'chat':bool(self.server.runtime.status().get('active')),'image':self.server.image_runtime.status().get('installed',False),'audio':self.server.tts_runtime.status().get('installed',False),'video':self.server.video_runtime.status().get('installed',False)}
|
||||
return execution_assess(model,installed,profile)
|
||||
|
||||
def do_GET(self):
|
||||
@@ -259,7 +268,7 @@ class Handler(BaseHTTPRequestHandler):
|
||||
return self.respond({'error':'Anmeldung erforderlich.'},401)
|
||||
if not self.authenticated() and self.path == '/':
|
||||
return self.respond((ROOT/'login.html').read_bytes(), mime='text/html; charset=utf-8')
|
||||
routes = {'/stt-ui.js':('stt-ui.js','text/javascript'),'/tts-ui.js':('tts-ui.js','text/javascript'),'/auto-test-ui.js':('auto-test-ui.js','text/javascript'),'/chat-test-ui.js':('chat-test-ui.js','text/javascript'),'/endpoint-ui.js':('endpoint-ui.js','text/javascript'),'/docker-ui.js': ('docker-ui.js','text/javascript'), '/': ('index.html', 'text/html; charset=utf-8'), '/app.js': ('app.js', 'text/javascript'), '/style.css': ('style.css', 'text/css'), '/network-ui.js': ('network-ui.js', 'text/javascript'), '/access-ui.js': ('access-ui.js', 'text/javascript'), '/studio.js': ('studio.js', 'text/javascript'), '/catalog-ui.js': ('catalog-ui.js','text/javascript'), '/runtime-ui.js': ('runtime-ui.js','text/javascript'), '/profiles-ui.js': ('profiles-ui.js','text/javascript'), '/image-test-ui.js': ('image-test-ui.js','text/javascript')}
|
||||
routes = {'/video-ui.js':('video-ui.js','text/javascript'),'/stt-ui.js':('stt-ui.js','text/javascript'),'/tts-ui.js':('tts-ui.js','text/javascript'),'/auto-test-ui.js':('auto-test-ui.js','text/javascript'),'/chat-test-ui.js':('chat-test-ui.js','text/javascript'),'/endpoint-ui.js':('endpoint-ui.js','text/javascript'),'/docker-ui.js': ('docker-ui.js','text/javascript'), '/': ('index.html', 'text/html; charset=utf-8'), '/app.js': ('app.js', 'text/javascript'), '/style.css': ('style.css', 'text/css'), '/network-ui.js': ('network-ui.js', 'text/javascript'), '/access-ui.js': ('access-ui.js', 'text/javascript'), '/studio.js': ('studio.js', 'text/javascript'), '/catalog-ui.js': ('catalog-ui.js','text/javascript'), '/runtime-ui.js': ('runtime-ui.js','text/javascript'), '/profiles-ui.js': ('profiles-ui.js','text/javascript'), '/image-test-ui.js': ('image-test-ui.js','text/javascript')}
|
||||
if self.path in routes:
|
||||
name, mime = routes[self.path]
|
||||
return self.respond((ROOT/name).read_bytes(), mime=mime)
|
||||
@@ -276,6 +285,16 @@ class Handler(BaseHTTPRequestHandler):
|
||||
if urlsplit(self.path).path == '/api/v1/tts/audio':
|
||||
try:return self.respond(self.server.tts_tests.audio(parse_qs(urlsplit(self.path).query).get('id',[''])[0]),mime='audio/wav')
|
||||
except (OSError,ValueError):return self.respond({'error':'Audio nicht verfügbar.'},404)
|
||||
if urlsplit(self.path).path == '/api/v1/video/content':
|
||||
try:
|
||||
path=self.server.video.result(parse_qs(urlsplit(self.path).query).get('id',[''])[0])
|
||||
self.send_response(200);self.send_header('Content-Type','video/mp4');self.send_header('Content-Length',str(path.stat().st_size));self.send_header('Cache-Control','no-store');self.end_headers()
|
||||
with path.open('rb') as source:
|
||||
while chunk:=source.read(1024*1024):self.wfile.write(chunk)
|
||||
return
|
||||
except ValueError as exc:return self.respond({'error':str(exc)},404)
|
||||
if self.path == '/api/v1/video':return self.respond(self.server.video.status())
|
||||
if self.path == '/api/v1/video-runtime':return self.respond(self.server.video_runtime.status())
|
||||
if self.path == '/api/v1/image-runtime':return self.respond(self.server.image_runtime.status())
|
||||
if self.path == '/api/v1/image-tests':return self.respond(self.server.image_tests.status())
|
||||
if urlsplit(self.path).path == '/api/v1/image-tests/image':
|
||||
@@ -389,6 +408,16 @@ class Handler(BaseHTTPRequestHandler):
|
||||
if data:raise ValueError('Keine Parameter erwartet.')
|
||||
return self.respond(self.server.chat_tests.stop() if self.path.endswith('/cancel') else self.server.chat_tests.unload())
|
||||
except ValueError as exc:return self.respond({'error':str(exc)},400)
|
||||
if self.path in ('/api/v1/video/mode','/api/v1/video/profile','/api/v1/video/generate','/api/v1/video-runtime/install','/api/v1/video-runtime/cancel'):
|
||||
try:
|
||||
data=self.read_json()
|
||||
if self.path.endswith('/mode') and set(data)=={'mode'}:return self.respond(self.server.video.switch(data['mode']))
|
||||
if self.path.endswith('/profile') and set(data)=={'id'}:return self.respond(self.server.video.select(data['id']))
|
||||
if self.path.endswith('/generate'):return self.respond(self.server.video.generate(data),202)
|
||||
if data:raise ValueError('Ungültige Video-Parameter.')
|
||||
return self.respond(self.server.video_runtime.start() if self.path.endswith('/install') else self.server.video_runtime.stop())
|
||||
except ValueError as exc:return self.respond({'error':str(exc)},400)
|
||||
except Exception:return self.respond({'error':'Video-Aktion fehlgeschlagen.'},503)
|
||||
if self.path in ('/api/v1/endpoint/start','/api/v1/endpoint/stop','/api/v1/endpoint/config','/api/v1/endpoint/profile'):
|
||||
try:
|
||||
data=self.read_json();ep=self.server.endpoint
|
||||
@@ -446,6 +475,7 @@ class Handler(BaseHTTPRequestHandler):
|
||||
if self.path == '/api/v1/profiles/delete':
|
||||
try:
|
||||
data=self.read_json()
|
||||
if self.server.video.selected==data.get('id') and self.server.video.status()['state'] in ('ready','switching'):raise ValueError('Aktives Videoprofil zuerst durch Wechsel auf LLM entladen.')
|
||||
with self.server.image_tests.lock, self.server.tts_tests.lock, self.server.stt.lock:
|
||||
stt_job=self.server.stt.job
|
||||
if stt_job and stt_job.get("state")=="running" and stt_job.get("profile_id")==data.get("id"):raise ValueError("Dieses STT-Profil wird gerade ausgeführt. Zuerst den Auftrag beenden.")
|
||||
@@ -507,6 +537,8 @@ def main():
|
||||
finally:
|
||||
server.auto_tests.stop()
|
||||
server.chat_tests.stop()
|
||||
server.video.close()
|
||||
server.video_runtime.stop()
|
||||
server.endpoint.close()
|
||||
server.stt.stop()
|
||||
server.tts_tests.stop()
|
||||
|
||||
Reference in New Issue
Block a user