feat(api): solver service Phase 3b — server endpoints, job definitions, and result cache

Add server-side solver service module with REST API endpoints, database
schema, job definitions, and runner result caching.

New files:
- migrations/021_solver_results.sql: solver_results table with upsert constraint
- internal/db/solver_results.go: SolverResultRepository (Upsert, GetByItem, GetByItemRevision)
- internal/api/solver_handlers.go: solver API handlers and maybeCacheSolverResult hook
- jobdefs/assembly-solve.yaml: manual solve job definition
- jobdefs/assembly-validate.yaml: auto-validate on revision creation
- jobdefs/assembly-kinematic.yaml: manual kinematic simulation job

Modified:
- internal/config/config.go: SolverConfig struct with max_context_size_mb, default_timeout
- internal/modules/modules.go, loader.go: register solver module (depends on jobs)
- internal/db/jobs.go: ListSolverJobs helper with definition_name prefix filter
- internal/api/handlers.go: wire SolverResultRepository into Server
- internal/api/routes.go: /api/solver/* routes + /api/items/{partNumber}/solver/results
- internal/api/runner_handlers.go: async result cache hook on job completion

API endpoints:
- POST   /api/solver/jobs          — submit solver job (editor)
- GET    /api/solver/jobs          — list solver jobs with filters
- GET    /api/solver/jobs/{id}     — get solver job status
- POST   /api/solver/jobs/{id}/cancel — cancel solver job (editor)
- GET    /api/solver/solvers       — registry of available solvers
- GET    /api/items/{pn}/solver/results — cached results for item

Also fixes pre-existing test compilation errors (missing workflows param
in NewServer calls across 6 test files).
This commit is contained in:
Forbes
2026-02-20 12:08:34 -06:00
parent ed1ac45e12
commit 5f144878d6
20 changed files with 853 additions and 10 deletions

View File

@@ -19,6 +19,7 @@ type Config struct {
Auth AuthConfig `yaml:"auth"`
Jobs JobsConfig `yaml:"jobs"`
Workflows WorkflowsConfig `yaml:"workflows"`
Solver SolverConfig `yaml:"solver"`
Modules ModulesConfig `yaml:"modules"`
}
@@ -32,6 +33,7 @@ type ModulesConfig struct {
FreeCAD *ModuleToggle `yaml:"freecad"`
Jobs *ModuleToggle `yaml:"jobs"`
DAG *ModuleToggle `yaml:"dag"`
Solver *ModuleToggle `yaml:"solver"`
}
// ModuleToggle holds an optional enabled flag. The pointer allows
@@ -146,6 +148,14 @@ type WorkflowsConfig struct {
Directory string `yaml:"directory"` // default /etc/silo/workflows
}
// SolverConfig holds assembly solver service settings.
type SolverConfig struct {
DefaultSolver string `yaml:"default_solver"`
MaxContextSizeMB int `yaml:"max_context_size_mb"`
DefaultTimeout int `yaml:"default_timeout"`
AutoDiagnoseOnCommit bool `yaml:"auto_diagnose_on_commit"`
}
// OdooConfig holds Odoo ERP integration settings.
type OdooConfig struct {
Enabled bool `yaml:"enabled"`
@@ -204,6 +214,12 @@ func Load(path string) (*Config, error) {
if cfg.Workflows.Directory == "" {
cfg.Workflows.Directory = "/etc/silo/workflows"
}
if cfg.Solver.MaxContextSizeMB == 0 {
cfg.Solver.MaxContextSizeMB = 10
}
if cfg.Solver.DefaultTimeout == 0 {
cfg.Solver.DefaultTimeout = 300
}
// Override with environment variables
if v := os.Getenv("SILO_DB_HOST"); v != "" {
@@ -221,6 +237,9 @@ func Load(path string) (*Config, error) {
if v := os.Getenv("SILO_STORAGE_ROOT_DIR"); v != "" {
cfg.Storage.Filesystem.RootDir = v
}
if v := os.Getenv("SILO_SOLVER_DEFAULT"); v != "" {
cfg.Solver.DefaultSolver = v
}
// Auth defaults
if cfg.Auth.LDAP.UserAttr == "" {