optional FastAPI service skeleton

This commit is contained in:
2026-05-06 19:30:49 +02:00
parent f4f77b2eeb
commit e53bc4144d
8 changed files with 352 additions and 10 deletions

View File

@@ -0,0 +1,5 @@
"""Optional FastAPI service adapter for Kontextual Engine."""
from .app import ServiceRuntime, create_app
__all__ = ["ServiceRuntime", "create_app"]

View File

@@ -0,0 +1,123 @@
"""Versioned FastAPI service skeleton.
The service layer is intentionally thin: route handlers translate HTTP
requests into service/runtime contracts and must not own domain behavior.
"""
from __future__ import annotations
from dataclasses import dataclass, field
from importlib import metadata
from typing import Any
from kontextual_engine.adapters.memory import InMemoryAssetRegistryRepository
from kontextual_engine.core import utc_now
from kontextual_engine.ports import AssetRegistryRepository
API_VERSION = "v1"
OPENAPI_VERSION = "1.0.0"
@dataclass
class ServiceRuntime:
repository: AssetRegistryRepository = field(default_factory=InMemoryAssetRegistryRepository)
api_version: str = API_VERSION
service_name: str = "kontextual-engine"
started_at: str = field(default_factory=lambda: utc_now().isoformat())
@property
def package_version(self) -> str:
try:
return metadata.version("kontextual-engine")
except metadata.PackageNotFoundError:
return "0.1.0"
def health(self) -> dict[str, Any]:
return {
"status": "ok",
"service": self.service_name,
"api_version": self.api_version,
"package_version": self.package_version,
"started_at": self.started_at,
}
def readiness(self) -> dict[str, Any]:
checks: dict[str, dict[str, Any]] = {}
try:
asset_count = len(self.repository.list_assets())
checks["asset_registry"] = {
"status": "ok",
"repository": type(self.repository).__name__,
"asset_count": asset_count,
}
except Exception as exc:
checks["asset_registry"] = {
"status": "error",
"repository": type(self.repository).__name__,
"error_type": type(exc).__name__,
"message": str(exc),
}
ready = all(item["status"] == "ok" for item in checks.values())
return {
"status": "ready" if ready else "not_ready",
"ready": ready,
"service": self.service_name,
"api_version": self.api_version,
"checks": checks,
}
def version(self) -> dict[str, Any]:
return {
"service": self.service_name,
"api_version": self.api_version,
"package_version": self.package_version,
"openapi_version": OPENAPI_VERSION,
}
def create_app(runtime: ServiceRuntime | None = None):
try:
from fastapi import FastAPI
except ImportError as exc: # pragma: no cover - exercised when optional extra is absent
raise RuntimeError(
"FastAPI service dependencies are not installed. Install kontextual-engine[service]."
) from exc
runtime = runtime or ServiceRuntime()
app = FastAPI(
title="Kontextual Engine Service API",
version=OPENAPI_VERSION,
openapi_url="/openapi.json",
docs_url="/docs",
redoc_url="/redoc",
)
app.state.kontextual_runtime = runtime
@app.get("/health", tags=["system"])
def health() -> dict[str, Any]:
return runtime.health()
@app.get("/ready", tags=["system"])
def ready() -> dict[str, Any]:
return runtime.readiness()
@app.get("/version", tags=["system"])
def version() -> dict[str, Any]:
return runtime.version()
prefix = f"/api/{runtime.api_version}"
@app.get(f"{prefix}/health", tags=["system"])
def versioned_health() -> dict[str, Any]:
return runtime.health()
@app.get(f"{prefix}/ready", tags=["system"])
def versioned_ready() -> dict[str, Any]:
return runtime.readiness()
@app.get(f"{prefix}/version", tags=["system"])
def versioned_version() -> dict[str, Any]:
return runtime.version()
return app