Decompose: Add HTTP service layer (FastAPI) exposing the EmbroideryPipeline #31
Labels
No labels
blocked:children
blocked:depends-101
blocked:depends-103
blocked:depends-104
blocked:needs-attention
blocked:needs-decision
blocked:no-changes
blocked:preflight
bug
chore
feature-request
in-progress
lint-cleanup
needs-decompose
performance
quality
quickfix
security/medium
triage-approved
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
Mark/easystitch#31
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Add HTTP service layer (FastAPI) exposing the EmbroideryPipeline
Context
EasyStitch's
EmbroideryPipeline(src/main_pipeline.py) is a complete 5-stage CAMengine. Today it's only invokable via the CLI (
src/main.py) or by directimport — there's no HTTP layer.
A consumer (ThreadCraft,
Mark/Kimi_Agent_Brother_10_Digitizing_App) is about tostart using easyStitch as a service. The integration ticket is
Kimi_Agent_Brother_10_Digitizing_App#11.Goal
Add a thin FastAPI service that wraps the pipeline and exposes a typed JSON API.
Pipeline stage reference
src/raster/src/svg_annotation/src/intent_resolver/src/embroidery_converter/src/post_processing/src/output_generators/The HTTP service wraps stages 1–6 as endpoints, with
/v1/digitizecovering1–5 and
/v1/exportcovering 6 (taking pre-computed stitch objects fromthe client).
Decomposition
Too large for a single fix; the 5 phases below are each shippable
independently. The first is the skeleton + health, the others layer
endpoints on top.
P1: Service skeleton + healthcheck
Set up the FastAPI app structure and a working
/healthzendpoint. Thisphase is fully isolated — no pipeline integration yet.
Files:
src/service/__init__.pysrc/service/app.py(FastAPI app factory, CORS config, routerregistration stubs,
/healthzhandler)src/service/config.py(pydantic settings:PORT,MAX_UPLOAD_MB,LOG_LEVEL,WORKER_POOL_SIZE,CORS_ALLOWED_ORIGINS)src/service/models.py(pydantic request/response models, emptyfor now)
Acceptance:
uvicorn src.service.app:app --reloadstarts on port 8080.GET /healthzreturns{"status": "ok", "version": "<git-sha>"}with200.
(
http://localhost:3000) and a configurable prod origin.tests/test_service_health.py) passes.CMD ["uvicorn", "src.service.app:app", ...].Why first: every other phase adds a router under this skeleton, so
the skeleton must be in place before they can be tested.
P2: POST /v1/digitize
Wire the 5-stage pipeline to a single endpoint. This is the core of the
service.
Files:
src/service/app.py(add/v1/digitizeroute)src/service/models.py(DigitizeRequest, DigitizeResponsepydantic models)
src/service/handlers/digitize.py(route handler, callsEmbroideryPipeline.process_bytes(), formats response)Acceptance:
POST /v1/digitizewith a 1024×1024 PNG and default settings returnsa valid
DigitizeResponse(regions, stitchObjects, stats) within 5seconds on a 4-core dev box.
stats.stagesbreakdown (depends on issue#35 — the
stagesdict population fix — being merged first).format emit fails) return a structured
503with the failing stagenamed in the body. No 500s on expected failures.
Depends on: P1 (skeleton). Soft-depends on #32 (importable pipeline)
and #33 (process_bytes method) — if those aren't done first, this
phase has to call
process(input_path)and write temp files, whichadds friction. Land them first for cleanliness.
P3: POST /v1/export
Take pre-computed stitch objects (already in the client or returned by
P2) and emit a binary embroidery file in the requested format.
Files:
src/service/app.py(add/v1/exportroute)src/service/models.py(ExportRequest, ExportResponse orbinary response)
src/service/handlers/export.pyAcceptance:
POST /v1/exportwith stitch objects andformat=pesreturns avalid PES file body. Header check: starts with
#PES0001or thecorrect v1 magic for the requested PES version.
format=dst,format=exp,format=jefeach return a valid filewith the correct magic bytes for that format.
X-Stitch-CountandX-Color-Countheaders.transformation in the service layer).
Depends on: P1 (skeleton), P2 (routing pattern). Independent of
P2's data path — accepts stitch objects from any source.
P4: POST /v1/threads/resolve + GET /v1/threads/catalog
ΔE-2000 thread palette lookup. Two endpoints:
POST /v1/threads/resolve— input: array of hex colors, output:nearest catalog entries.
GET /v1/threads/catalog— output: the embedded Brother PR-10thread catalog (one-shot fetch, client caches).
Files:
src/service/app.py(add the two routes)src/service/models.py(ResolveRequest, ResolveResponse,CatalogResponse)
src/service/handlers/threads.pyAcceptance:
POST /v1/threads/resolvewith["#aabbcc", "#112233"]returnsthe nearest 2 catalog entries with
deltaEvalues.GET /v1/threads/catalogreturns the full PR-10 catalog as JSON(~79 entries).
time < 50ms for resolve).
src/embroidery_converter/thread_manager.py— no new algorithm.Depends on: P1 (skeleton). Independent of P2/P3 (different code
path).
Note: requires the Brother PR-10 profile from #36 so the catalog
source is correct. The catalog can be sourced separately if #36 is
in flight.
P5: Dockerfile + deployment config
Production-ready container build and node_hub deployment manifest.
Files:
Dockerfile(addCMDfor uvicorn, ensure deps areinstalled in runtime stage, pin base image tag)
node-hub.app.yml(or whatever format node_hub expects) fordeploying easyStitch as a managed service
docker-compose.ymlfor local dev (optional, but useful fortesting the full service)
docs/deployment.mdcovering Docker, node_hub, env varsAcceptance:
docker build -t easystitch-service . && docker run -p 8080:8080 easystitch-servicestarts a working service.node-hub.app.ymlregisters the service, setsCORS_ALLOWED_ORIGINScorrectly, and
node_hubhealth-checks it successfully.docs/deployment.mdcovers: building, running, env vars, commonerrors.
python:3.11-slimbase + scipy + scikit-image is ~1.5 GB; that's fine).
Depends on: P1 (skeleton) so the Dockerfile has something to run.
P2/P3/P4 not required — the image with only P1 is shippable, and the
endpoints come online as those phases land.
Cross-cutting concerns (handled across all phases, not in one phase)
service implementation honors those values across all endpoints.
LOG_LEVEL=infobydefault; per-request UUID for correlation).
{"error": "...", "stage": "...", "request_id": "..."}across all endpoints.Add HTTP service layer (FastAPI) exposing the EmbroideryPipelineto Decompose: Add HTTP service layer (FastAPI) exposing the EmbroideryPipelineThis issue has been decomposed into the following sub-issues:
/healthzendpoint. This phase iPOST /v1/threads/resolve— inpEach sub-issue has been labeled with
triage-approvedand is ready for queue routing.