phase 5: edge transforms, soleprint-ui rename, infra fixes

- pipeline edge transforms: stages can declare accepted_transforms,
  edges carry a transform dict, runner injects per-stage and nodes
  apply (e.g. invert_mask before edge detection); editable from UI
  via PUT /config/edge-transform
- rename mpr-ui-framework -> soleprint-ui (now an external package
  synced via .spr from /home/mariano/wdir/spr); add @vue-flow/core
  and uplot to detection-app so linked package resolves them
- Tiltfile guards kubectl context, k8s commands pin --context kind-mpr
- kind-config: gateway on hostPort 30080 (Caddy fronts mpr.local.ar)
- modelgen: pyproject.toml, .spr marker, dict default_factory support
This commit is contained in:
2026-04-29 05:31:08 -03:00
parent 55e83e4203
commit 020f3540d3
35 changed files with 414 additions and 1747 deletions

View File

@@ -38,6 +38,14 @@ class StageOutputHintInfo(BaseModel):
src_format: str = "png"
class TransformOptionInfo(BaseModel):
key: str
type: str
default: object = False
label: str = ""
description: str = ""
class StageConfigInfo(BaseModel):
name: str
label: str
@@ -45,6 +53,7 @@ class StageConfigInfo(BaseModel):
category: str
config_fields: list[dict]
output_hints: list[StageOutputHintInfo] = []
accepted_transforms: list[TransformOptionInfo] = []
reads: list[str]
writes: list[str]
@@ -87,6 +96,51 @@ def get_pipeline_config(profile_name: str):
return profile["pipeline"]
class UpdateEdgeTransformRequest(BaseModel):
profile_name: str = "soccer_broadcast"
source_stage: str
target_stage: str
transform: dict
@router.put("/config/edge-transform")
def update_edge_transform(req: UpdateEdgeTransformRequest):
"""Update the transform on an edge in a profile's pipeline config."""
from uuid import UUID
from core.db.models import Profile
from core.db.connection import get_session
from sqlmodel import select
from fastapi import HTTPException
with get_session() as session:
stmt = select(Profile).where(Profile.name == req.profile_name)
profile = session.exec(stmt).first()
if not profile:
raise HTTPException(status_code=404, detail=f"Profile not found: {req.profile_name}")
pipeline = dict(profile.pipeline)
edges = pipeline.get("edges", [])
found = False
for edge in edges:
if edge.get("source") == req.source_stage and edge.get("target") == req.target_stage:
edge["transform"] = req.transform
found = True
break
if not found:
raise HTTPException(
status_code=404,
detail=f"Edge not found: {req.source_stage}{req.target_stage}",
)
pipeline["edges"] = edges
profile.pipeline = pipeline
session.commit()
return {"status": "updated", "edge": f"{req.source_stage}{req.target_stage}", "transform": req.transform}
@router.get("/config/stages", response_model=list[StageConfigInfo])
def list_stage_configs():
"""Return the stage palette with config field metadata for the editor."""
@@ -137,6 +191,13 @@ def _stage_to_info(stage) -> StageConfigInfo:
)
for h in getattr(stage, "output_hints", [])
],
accepted_transforms=[
TransformOptionInfo(
key=t.key, type=t.type, default=t.default,
label=t.label, description=t.description,
)
for t in getattr(stage, "accepted_transforms", [])
],
reads=stage.io.reads,
writes=stage.io.writes,
)