Close local release-quality gaps and reconcile workplan status
All checks were successful
CI Smoke / host-smoke (push) Successful in 1s
CI Smoke / container-smoke (push) Successful in 2s

Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a0e332-3365-77c0-8491-084e9ea33ac1
This commit is contained in:
tegwick 2026-09-27 17:36:27 +02:00
parent 37436bb562
commit 7cd633986e
56 changed files with 462 additions and 237 deletions

View file

@ -30,14 +30,14 @@ import time
import uuid
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
from pathlib import Path
from typing import Optional
from typing import Any
from urllib.parse import parse_qs, urlsplit
from llm_connect._diagnostics import capture_diagnostics
from llm_connect.adapter import LLMAdapter
from llm_connect.exceptions import (
LLMBudgetExceededError,
LLMAPIError,
LLMBudgetExceededError,
LLMConfigurationError,
LLMError,
LLMRateLimitError,
@ -48,15 +48,21 @@ from llm_connect.profiles import ProfiledLLMAdapter, default_runtime_profiles
from llm_connect.usage import maybe_record_usage, suppress_auto_usage_record
class _AdapterHTTPServer(ThreadingHTTPServer):
adapter: LLMAdapter
class _Handler(BaseHTTPRequestHandler):
"""Request handler — adapter injected via server.adapter."""
def log_message(self, format, *args): # suppress default access log
server: _AdapterHTTPServer
def log_message(self, format: str, *args: Any) -> None: # suppress default access log
pass
# ── GET ────────────────────────────────────────────────────────
def do_GET(self):
def do_GET(self) -> None:
parsed = urlsplit(self.path)
if parsed.path == "/health":
self._respond(200, {"status": "ok"})
@ -65,7 +71,7 @@ class _Handler(BaseHTTPRequestHandler):
# ── POST ───────────────────────────────────────────────────────
def do_POST(self):
def do_POST(self) -> None:
parsed = urlsplit(self.path)
if parsed.path != "/execute":
self._respond(404, {"error": "not found"})
@ -96,7 +102,7 @@ class _Handler(BaseHTTPRequestHandler):
diagnostics_enabled = debug_enabled or bool(audit_dir)
try:
with capture_diagnostics(diagnostics_enabled) as diagnostics:
adapter = self.server.adapter # type: ignore[attr-defined]
adapter = self.server.adapter
if not adapter.validate_config(config):
raise LLMConfigurationError(
"Adapter rejected RunConfig",
@ -152,9 +158,9 @@ class LLMServer:
host: str = "127.0.0.1",
port: int = 8080,
) -> None:
self._httpd = ThreadingHTTPServer((host, port), _Handler)
self._httpd.adapter = adapter # type: ignore[attr-defined]
self._thread: Optional[threading.Thread] = None
self._httpd = _AdapterHTTPServer((host, port), _Handler)
self._httpd.adapter = adapter
self._thread: threading.Thread | None = None
@property
def port(self) -> int:
@ -163,7 +169,7 @@ class LLMServer:
@property
def host(self) -> str:
return self._httpd.server_address[0]
return str(self._httpd.server_address[0])
def start(self) -> None:
"""Start serving in a daemon background thread."""
@ -185,7 +191,7 @@ class LLMServer:
def _build_adapter(
provider: str,
model: Optional[str],
model: str | None,
*,
enable_profiles: bool = True,
strict_profiles: bool = False,
@ -240,7 +246,7 @@ def _error_response(exc: Exception) -> tuple[int, dict]:
def _error_body(code: str, exc: Exception) -> dict:
body = {
body: dict[str, Any] = {
"error": code,
"message": _sanitize_text(_message(exc)),
"type": exc.__class__.__name__,
@ -260,7 +266,7 @@ def _message(exc: Exception) -> str:
def _safe_context(context: dict) -> dict:
safe = {}
safe: dict[str, Any] = {}
for key, value in context.items():
lowered = str(key).lower()
if any(secret_word in lowered for secret_word in ("key", "secret", "token", "password")):
@ -321,7 +327,7 @@ def _safe_filename(value: str) -> str:
return re.sub(r"[^A-Za-z0-9_.-]+", "-", value).strip("-") or "response"
def main(argv=None) -> None:
def main(argv: list[str] | None = None) -> None:
parser = argparse.ArgumentParser(
prog="python -m llm_connect.server",
description="Start llm_connect HTTP serve mode.",