Isolate client reasoning normalization in API compatibility adapter

This commit is contained in:
Mikei386
2026-09-29 10:11:02 +02:00
parent f337ade65a
commit dad67acad8
8 changed files with 62 additions and 12 deletions
+11 -6
View File
@@ -10,6 +10,7 @@ import socket
import threading
import time
from http.server import BaseHTTPRequestHandler,ThreadingHTTPServer
from api_compat import normalize_chat,CompatibilityError
from inference import InferenceError
from stt import read_upload
@@ -136,9 +137,15 @@ class APIHandler(BaseHTTPRequestHandler):
protocol_version='HTTP/1.1'
def setup(self):super().setup();self.connection.settimeout(15);self.sent=False
def log_message(self,*args):pass
def compatibility_headers(self):
info=getattr(self,'compatibility',{})
if info:
self.send_header('X-Athena-Reasoning-Requested',info['requested'])
self.send_header('X-Athena-Reasoning-Effective',info['effective'])
self.send_header('X-Athena-Reasoning-Semantics',info['semantics'])
def send(self,payload,status=200):
body=json.dumps(payload).encode();self.sent=True
self.send_response(status);self.send_header('Content-Type','application/json');self.send_header('Content-Length',str(len(body)));self.send_header('Cache-Control','no-store');self.send_header('Connection','close');self.end_headers();self.wfile.write(body);self.close_connection=True
self.send_response(status);self.compatibility_headers();self.send_header('Content-Type','application/json');self.send_header('Content-Length',str(len(body)));self.send_header('Cache-Control','no-store');self.send_header('Connection','close');self.end_headers();self.wfile.write(body);self.close_connection=True
def failure(self,exc):
if self.sent:return
self.send({'error':{'message':str(exc),'type':getattr(exc,'code','server_error'),'param':None,'code':getattr(exc,'code','worker_unavailable')}},getattr(exc,'status',503))
@@ -211,9 +218,8 @@ class APIHandler(BaseHTTPRequestHandler):
self.sent=True;self.send_response(200);self.send_header('Content-Type','audio/wav');self.send_header('Content-Length',str(len(body)));self.send_header('Cache-Control','no-store');self.send_header('Connection','close');self.end_headers();self.wfile.write(body);return
raise APIError('TTS-Zeitlimit überschritten.',504)
def chat(self,ep,data):
supported={'model','messages','stream','stream_options','temperature','top_p','top_k','max_tokens','max_completion_tokens','stop','seed','tools','tool_choice','parallel_tool_calls','response_format','presence_penalty','frequency_penalty','logprobs','top_logprobs','user','n','reasoning_effort'}
if set(data)-supported:raise APIError('Nicht unterstützte Chat-Felder: '+', '.join(sorted(set(data)-supported)))
if 'reasoning_effort' in data and data['reasoning_effort'] is not None and (not isinstance(data['reasoning_effort'],str) or data['reasoning_effort'] not in ('none','minimal','low','medium','high','xhigh')):raise APIError('Ungültiger reasoning_effort-Wert.')
try:data,self.compatibility=normalize_chat(data)
except CompatibilityError as exc:raise APIError(str(exc)) from None
if type(data.get('stream',False)) is not bool:raise APIError('stream muss true oder false sein.')
if data.get('n',1)!=1:raise APIError('Zunächst wird n=1 unterstützt.')
messages=data.get('messages')
@@ -241,7 +247,6 @@ class APIHandler(BaseHTTPRequestHandler):
def allowed():return ep.allowed() and any(p['id']==profile['id'] and p['revision']==profile['revision'] and p['enabled'] for p in ep.rows())
with ep.scheduler.lease(key,profile['parameters']['slots'],lambda:ep.worker.ensure(profile),allowed=allowed):
body=dict(data)
if body.get('reasoning_effort') is None:body.pop('reasoning_effort',None)
for field in ('temperature','top_p','top_k'):body.setdefault(field,profile['parameters'][field])
conn,key=ep.worker.connect()
try:
@@ -254,7 +259,7 @@ class APIHandler(BaseHTTPRequestHandler):
if len(raw)>16*1024*1024:raise InferenceError('Modellantwort überschreitet 16 MiB.')
return self.send(json.loads(raw))
self.sent=True;self.connection.settimeout(30)
self.send_response(200);self.send_header('Content-Type','text/event-stream');self.send_header('Cache-Control','no-store');self.send_header('Connection','close');self.end_headers()
self.send_response(200);self.compatibility_headers();self.send_header('Content-Type','text/event-stream');self.send_header('Cache-Control','no-store');self.send_header('Connection','close');self.end_headers()
deadline=time.monotonic()+600
while time.monotonic()<deadline:
chunk=response.read1(8192)