File size: 4,624 Bytes
c216121
 
 
 
 
 
 
 
 
 
 
7b2a37a
c216121
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7b2a37a
 
 
6bc6ef3
c216121
 
ab93852
ae32abe
 
ab93852
ae32abe
 
7b2a37a
ab93852
c216121
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7b2a37a
 
 
c216121
 
 
 
 
7b2a37a
c216121
 
 
 
7b2a37a
 
 
 
c216121
7b2a37a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
c216121
 
 
7b2a37a
 
c216121
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
---
title: SolverForge Field Service Routing
emoji: 🧰
colorFrom: indigo
colorTo: blue
sdk: docker
app_port: 7860
pinned: false
license: apache-2.0
short_description: SolverForge field-service routing example
---

# SolverForge FSR

`solverforge-fsr` is a SolverForge field-service routing example with retained
jobs, route geometry, technician schedules, and a browser map workspace.

It answers one concrete question:

"Given technicians, service visits, skills, parts, shifts, territories, and
road-network travel, which technician should serve each visit and in what
order?"

## Hugging Face Space Deployment

This repo is Docker-Space ready. Hugging Face reads the README front matter,
builds `Dockerfile`, and expects the app to bind `PORT=7860`.

Local Space-equivalent commands:

```sh
make space-build
make space-run
```

Full local readiness check:

```sh
make ci-local
```

The Space image is self-contained: it builds against published crates.io
packages, copies `static/`, `solver.toml`, and `solverforge.app.toml`, and runs
the release binary `solverforge_fsr`.

## Versioning

The app package version is `1.0.1`; the release binary is `solverforge_fsr`.

- CLI version used to scaffold this project: `2.0.3`
- SolverForge runtime target for this scaffold: `solverforge 0.11.1`
- SolverForge UI target for this scaffold: `solverforge-ui 0.6.5`
- SolverForge maps target for this scaffold: `solverforge-maps 2.1.4`
- Runtime dependency currently wired into `Cargo.toml`: `crates.io: solverforge 0.11.1`
- Frontend UI dependency currently wired into `Cargo.toml`: `crates.io: solverforge-ui 0.6.5`
- Maps dependency currently wired into `Cargo.toml`: `crates.io: solverforge-maps 2.1.4`

This project was scaffolded by `solverforge-cli`, and it currently targets `SolverForge crate target 0.11.1` through the configured crate dependency targets.

`solverforge-core` is a direct dependency because the 0.11 custom
incremental-constraint API exposes `ConstraintRef`, while the top-level facade
does not re-export that type.

## What SolverForge Is Doing Here

- `ServiceVisit` is a problem fact: a customer job the solver must place in a
  technician route.
- `TechnicianRoute` is the planning entity: each technician owns one mutable
  route.
- `TechnicianRoute.visits` is the list planning variable.
- `FieldServicePlan` is the planning solution.
- Constraints score assignment coverage, route reachability, shift capacity,
  required skills, required parts, time windows, priority slack, territory
  affinity, travel time, and workload balance.
- `solver.toml` selects the construction and local-search policy.

The app ships one standard Bergamo field-service dataset and prepares full
routing data when a job is created so snapshots can expose per-segment geometry.

## Quick Start

```sh
make run-release
```

Then open `http://localhost:7860`.

To inspect the command surface:

```sh
make help
```

## Development

```sh
# Add a new constraint
solverforge generate constraint my_rule --unary --hard

# Add a domain entity
solverforge generate entity worker --planning-variable shift_idx

# Add a problem fact
solverforge generate fact location

# Remove a resource
solverforge destroy constraint my_rule
```

## Project Structure

| Directory | Purpose |
|-----------|--------|
| `src/domain/` | Planning entities, facts, and solution struct |
| `src/constraints/` | Constraint definitions (scored by the solver) |
| `src/solver/` | Solver service and configuration |
| `src/api/` | HTTP routes and DTOs |
| `src/data/` | Data loading and generation |
| `static/` | Browser UI, SolverForge UI config, and generated UI model |
| `Dockerfile` | Multi-stage Rust 1.95 Alpine build for the Hugging Face Docker Space |
| `Makefile` | Local development, validation, and Space image commands |
| `solverforge.app.toml` | Scaffolded app/domain contract |
| `solver.toml` | Solver configuration (termination, phases) |

## REST API

- `GET /health`
- `GET /info`
- `GET /demo-data`
- `GET /demo-data/{id}`
- `POST /jobs`
- `GET /jobs/{id}`
- `GET /jobs/{id}/status`
- `GET /jobs/{id}/snapshot`
- `GET /jobs/{id}/analysis`
- `GET /jobs/{id}/routes`
- `POST /jobs/{id}/pause`
- `POST /jobs/{id}/resume`
- `POST /jobs/{id}/cancel`
- `DELETE /jobs/{id}`
- `GET /jobs/{id}/events`

Route geometry is returned by `/jobs/{id}/routes` with segment-level geometry
status. Unreachable road-network legs are reported as non-routed segments, so
one infeasible leg does not hide the rest of the snapshot geometry.

## Validation

Standard validation:

```sh
make test
```

Full local Space validation:

```sh
make ci-local
```