phase 1
This commit is contained in:
105
core/api/detect/config.py
Normal file
105
core/api/detect/config.py
Normal file
@@ -0,0 +1,105 @@
|
||||
"""
|
||||
Runtime config endpoint for the detection pipeline.
|
||||
|
||||
GET /detect/config — read current config
|
||||
PUT /detect/config — update config (takes effect on next run)
|
||||
GET /detect/config/stages — list stage palette with config fields
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
|
||||
from fastapi import APIRouter
|
||||
from pydantic import BaseModel
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
router = APIRouter(prefix="/detect", tags=["detect"])
|
||||
|
||||
# In-memory config — persists until server restart.
|
||||
# Phase 12+ moves this to DB.
|
||||
_runtime_config: dict = {}
|
||||
|
||||
|
||||
class ConfigUpdate(BaseModel):
|
||||
detection: dict | None = None
|
||||
ocr: dict | None = None
|
||||
resolver: dict | None = None
|
||||
escalation: dict | None = None
|
||||
preprocessing: dict | None = None
|
||||
|
||||
|
||||
class StageConfigInfo(BaseModel):
|
||||
name: str
|
||||
label: str
|
||||
description: str
|
||||
category: str
|
||||
config_fields: list[dict]
|
||||
reads: list[str]
|
||||
writes: list[str]
|
||||
|
||||
|
||||
@router.get("/config")
|
||||
def read_config():
|
||||
return _runtime_config
|
||||
|
||||
|
||||
@router.put("/config")
|
||||
def write_config(update: ConfigUpdate):
|
||||
changes = update.model_dump(exclude_none=True)
|
||||
for section, values in changes.items():
|
||||
if section not in _runtime_config:
|
||||
_runtime_config[section] = {}
|
||||
_runtime_config[section].update(values)
|
||||
|
||||
logger.info("Config updated: %s", list(changes.keys()))
|
||||
return _runtime_config
|
||||
|
||||
|
||||
@router.get("/config/stages", response_model=list[StageConfigInfo])
|
||||
def list_stage_configs():
|
||||
"""Return the stage palette with config field metadata for the editor."""
|
||||
from detect.stages import list_stages
|
||||
|
||||
result = []
|
||||
for stage in list_stages():
|
||||
info = _stage_to_info(stage)
|
||||
result.append(info)
|
||||
return result
|
||||
|
||||
|
||||
@router.get("/config/stages/{stage_name}", response_model=StageConfigInfo)
|
||||
def get_stage_config(stage_name: str):
|
||||
"""Return config field metadata for a single stage."""
|
||||
from detect.stages import get_stage
|
||||
|
||||
try:
|
||||
stage = get_stage(stage_name)
|
||||
except KeyError:
|
||||
from fastapi import HTTPException
|
||||
raise HTTPException(status_code=404, detail=f"Unknown stage: {stage_name}")
|
||||
return _stage_to_info(stage)
|
||||
|
||||
|
||||
def _stage_to_info(stage) -> StageConfigInfo:
|
||||
return StageConfigInfo(
|
||||
name=stage.name,
|
||||
label=stage.label,
|
||||
description=stage.description,
|
||||
category=stage.category,
|
||||
config_fields=[
|
||||
{
|
||||
"name": f.name,
|
||||
"type": f.type,
|
||||
"default": f.default,
|
||||
"description": f.description,
|
||||
"min": f.min,
|
||||
"max": f.max,
|
||||
"options": f.options,
|
||||
}
|
||||
for f in stage.config_fields
|
||||
],
|
||||
reads=stage.io.reads,
|
||||
writes=stage.io.writes,
|
||||
)
|
||||
Reference in New Issue
Block a user