340 lines
13 KiB
Markdown
340 lines
13 KiB
Markdown
# OpenVINO NPU classifier/router dry-run contract
|
|
|
|
Status: specification for dry-run prototype refresh
|
|
Target port: `127.0.0.1:18819`
|
|
Owner context: Atlas/Hermes local assistant sidecar evaluation
|
|
|
|
This service is an advisory classifier for Atlas/Hermes automation hints. It may suggest labels such as tool-needed, memory-candidate type, urgency, workflow category, and safety-confirmation-required, but it must not make or enforce live routing, memory, tool, or safety decisions without a separate explicit approval from Will.
|
|
|
|
## Recommended model and runtime
|
|
|
|
Recommended v1 runtime: small local Python HTTP/CLI service backed by the existing OpenVINO NPU embeddings service on `127.0.0.1:18817`.
|
|
|
|
Recommended v1 model shape:
|
|
|
|
- Primary signal: `bge-base-en-v1.5-int8-ov` embeddings from the live embeddings service.
|
|
- Classifier layer: inspectable deterministic rules plus cosine similarity against curated synthetic/prototype utterances.
|
|
- Model label: `bge-base-en-v1.5-int8-ov/prototype-router-v0`.
|
|
- Device proof: request-level `npu_busy_delta_us` from `:18817` plus direct sysfs before/after reads from `/sys/class/accel/accel0/device/npu_busy_time_us`.
|
|
|
|
Why this is preferred for the dry run:
|
|
|
|
1. It reuses the already-live NPU embeddings path rather than adding a second model conversion/runtime dependency before contract validation.
|
|
2. Rules and prototypes are transparent enough for safety-sensitive routing hints; a reviewer can inspect why a message was labeled.
|
|
3. It avoids fine-tuning or training on private Atlas/Hermes transcripts.
|
|
4. It keeps the service small, localhost-only, and easy to start/stop during smoke tests.
|
|
5. It produces NPU activity through the embeddings path while making clear that final decision logic remains advisory.
|
|
|
|
Defer a dedicated NPU sequence-classification model such as TinyBERT/MiniLM until the dry-run labels and thresholds have been evaluated against synthetic fixtures and explicitly-approved non-private examples. If pursued later, use OpenVINO Runtime/Optimum export with fixed input shapes suitable for NPU, and keep the rule layer for safety gates.
|
|
|
|
## Non-goals and safety invariants
|
|
|
|
The service must not:
|
|
|
|
- Change Hermes/Atlas model routing, gateway routing, memory writes, tool-use permissions, or safety-confirmation behavior.
|
|
- Restart, stop, enable, or persist any live Atlas/Hermes/gateway/RAG service.
|
|
- Bind to anything broader than `127.0.0.1` by default.
|
|
- Mutate Chroma/vector collections, trigger reindexing, or write to RAG state.
|
|
- Process private document/image directories or private transcript dumps for smoke testing.
|
|
- Log raw prompts by default beyond normal foreground stderr during local review.
|
|
- Claim NPU success from HTTP 200 alone.
|
|
|
|
## Endpoint contract
|
|
|
|
All HTTP endpoints are local-only by default.
|
|
|
|
Base URL:
|
|
|
|
```text
|
|
http://127.0.0.1:18819
|
|
```
|
|
|
|
### GET `/healthz`, `/health`, `/readyz`, `/`
|
|
|
|
Purpose: liveness/readiness metadata.
|
|
|
|
Response fields:
|
|
|
|
- `status`: `starting | ok`
|
|
- `service`: `atlas-router-classifier`
|
|
- `version`: service version string
|
|
- `mode`: always `dry_run`
|
|
- `model`: model/runtime label
|
|
- `embed_url`: upstream embeddings URL
|
|
- `device`: expected to say `NPU-via-embedding-service` or equivalent
|
|
- `labels`: supported label names
|
|
- `embedding_dim`: embedding dimension after warmup
|
|
- `prototype_count`: number of synthetic prototype examples loaded
|
|
- `prototype_npu_busy_delta_us`: warmup delta reported by upstream embeddings, if available
|
|
- `npu_busy_time_us`: current sysfs counter value, if readable
|
|
- `warnings`: list of non-fatal warnings
|
|
|
|
A healthy service is not enough to prove NPU execution. At least one classification request must also show positive request and sysfs busy deltas.
|
|
|
|
### GET `/v1/labels`
|
|
|
|
Purpose: publish schema information without dumping private examples.
|
|
|
|
Response fields:
|
|
|
|
- `model`
|
|
- `thresholds`
|
|
- `tool_needed`: recommended threshold `0.72`
|
|
- `memory_candidate`: recommended threshold `0.78`
|
|
- `safety_confirmation_required`: recommended threshold `0.80`
|
|
- `workflow_category`: recommended threshold `0.52`
|
|
- `enums`
|
|
- `memory_candidate`: `none`, `user_preference`, `durable_user_fact`, `environment_fact`, `workflow_convention`, `skill_candidate`
|
|
- `urgency`: `low`, `normal`, `high`, `critical`
|
|
- `workflow_category`: `chat`, `research`, `coding`, `debugging`, `devops`, `smart_home`, `media`, `note_taking`, `productivity`, `kanban`, `unknown`
|
|
- `prototype_ids`: names of curated synthetic prototype buckets
|
|
|
|
### POST `/v1/classify`
|
|
|
|
Purpose: classify one user/task message for advisory dry-run hints.
|
|
|
|
Request:
|
|
|
|
```json
|
|
{
|
|
"id": "optional-trace-id",
|
|
"text": "Urgent: check whether port 18817 is listening and inspect systemd logs.",
|
|
"context": {
|
|
"platform": "cli",
|
|
"source": "user"
|
|
},
|
|
"options": {
|
|
"include_evidence": true,
|
|
"include_embedding_debug": false,
|
|
"dry_run": true
|
|
}
|
|
}
|
|
```
|
|
|
|
Required behavior:
|
|
|
|
- Reject empty text with HTTP 400.
|
|
- Default `dry_run` to true.
|
|
- Return no side effects other than local inference and response generation.
|
|
- Include evidence by default unless `include_evidence=false`.
|
|
- Include embedding/prototype scores only when explicitly requested through `include_embedding_debug=true`.
|
|
|
|
Response:
|
|
|
|
```json
|
|
{
|
|
"id": "optional-trace-id",
|
|
"model": "bge-base-en-v1.5-int8-ov/prototype-router-v0",
|
|
"created": 1780590000,
|
|
"duration_ms": 12.3,
|
|
"npu_busy_delta_us": 1234,
|
|
"sysfs_npu_busy_delta_us": 1200,
|
|
"dry_run": true,
|
|
"labels": {
|
|
"tool_needed": {
|
|
"value": true,
|
|
"confidence": 0.84,
|
|
"threshold": 0.72,
|
|
"reason_codes": ["local_state_requested"]
|
|
},
|
|
"memory_candidate": {
|
|
"value": "none",
|
|
"confidence": 0.31,
|
|
"threshold": 0.78,
|
|
"reason_codes": []
|
|
},
|
|
"urgency": {
|
|
"value": "high",
|
|
"confidence": 0.84,
|
|
"scores": {"low": 0.0, "normal": 0.2, "high": 0.84, "critical": 0.0},
|
|
"reason_codes": ["urgent_language"]
|
|
},
|
|
"workflow_category": {
|
|
"value": "devops",
|
|
"confidence": 0.86,
|
|
"scores": {"devops": 0.86, "unknown": 0.14}
|
|
},
|
|
"safety_confirmation_required": {
|
|
"value": false,
|
|
"confidence": 0.0,
|
|
"threshold": 0.8,
|
|
"reason_codes": []
|
|
}
|
|
},
|
|
"warnings": [],
|
|
"evidence": []
|
|
}
|
|
```
|
|
|
|
### POST `/v1/batch_classify`
|
|
|
|
Purpose: classify a bounded batch of non-private synthetic or explicitly-approved messages.
|
|
|
|
Request:
|
|
|
|
```json
|
|
{
|
|
"items": [
|
|
{"id": "m1", "text": "What time is it in Seattle right now?"},
|
|
{"id": "m2", "text": "Restart the live Atlas gateway and switch primary routing."}
|
|
],
|
|
"options": {"include_evidence": false, "dry_run": true}
|
|
}
|
|
```
|
|
|
|
Response:
|
|
|
|
- `model`
|
|
- `duration_ms`
|
|
- aggregate `npu_busy_delta_us`
|
|
- `results`: array of `/v1/classify` responses
|
|
|
|
Batch limits for prototype review:
|
|
|
|
- Keep batches small; the prototype rejects empty batches and batches larger than `OPENVINO_CLASSIFIER_MAX_BATCH_SIZE` (default `32`).
|
|
- Use only synthetic fixtures unless Will explicitly approves a real non-private sample set.
|
|
- Do not retain request bodies to disk.
|
|
|
|
## CLI contract
|
|
|
|
The same implementation should support foreground review from the service directory:
|
|
|
|
```bash
|
|
cd /home/will/lab/swarm/openvino-classifier-npu
|
|
/home/will/.venvs/npu/bin/python router_classifier.py \
|
|
--host 127.0.0.1 \
|
|
--port 18819 \
|
|
--embed-url http://127.0.0.1:18817/v1/embeddings
|
|
```
|
|
|
|
Required flags/env:
|
|
|
|
- `--host` / `OPENVINO_CLASSIFIER_HOST`; default `127.0.0.1`.
|
|
- `--port` / `OPENVINO_CLASSIFIER_PORT`; default `18819`.
|
|
- `--embed-url` / `OPENVINO_CLASSIFIER_EMBED_URL`; default `http://127.0.0.1:18817/v1/embeddings`.
|
|
- `--timeout-s` / `OPENVINO_CLASSIFIER_TIMEOUT_S`; default `30`.
|
|
- `--max-batch-size` / `OPENVINO_CLASSIFIER_MAX_BATCH_SIZE`; default `32`.
|
|
- `--no-warmup` to defer prototype embedding until first request.
|
|
|
|
A future dedicated CLI mode may be added for one-shot JSONL classification, but foreground HTTP review is sufficient for the dry-run contract.
|
|
|
|
## Synthetic smoke-test plan
|
|
|
|
Preconditions:
|
|
|
|
1. Confirm `:18817` embeddings service is healthy.
|
|
2. Confirm `:18819` is not already listening.
|
|
3. Read `/sys/class/accel/accel0/device/npu_busy_time_us` before starting the request smoke.
|
|
4. Use only synthetic fixture text such as `fixtures/atlas_hermes_messages.jsonl`.
|
|
|
|
Unit/schema smoke, no NPU dependency:
|
|
|
|
```bash
|
|
cd /home/will/lab/swarm
|
|
/home/will/.venvs/npu/bin/python -m unittest discover -s openvino-classifier-npu/tests -v
|
|
```
|
|
|
|
Foreground service smoke:
|
|
|
|
```bash
|
|
ss -ltnp | grep ':18819\b' || true
|
|
cd /home/will/lab/swarm/openvino-classifier-npu
|
|
/home/will/.venvs/npu/bin/python router_classifier.py --host 127.0.0.1 --port 18819
|
|
```
|
|
|
|
From another shell:
|
|
|
|
```bash
|
|
curl -fsS http://127.0.0.1:18819/healthz | jq .
|
|
curl -fsS http://127.0.0.1:18819/v1/labels | jq .
|
|
curl -fsS http://127.0.0.1:18819/v1/classify \
|
|
-H 'Content-Type: application/json' \
|
|
-d '{"id":"smoke-devops","text":"Urgent: check whether port 18817 is listening and inspect systemd logs.","options":{"include_evidence":true,"dry_run":true}}' | jq .
|
|
curl -fsS http://127.0.0.1:18819/v1/classify \
|
|
-H 'Content-Type: application/json' \
|
|
-d '{"id":"smoke-safety","text":"Restart the live Atlas gateway and switch primary routing to the new classifier.","options":{"include_evidence":true,"dry_run":true}}' | jq .
|
|
```
|
|
|
|
Expected label checks:
|
|
|
|
- `smoke-devops`: `tool_needed.value=true`, `urgency.value=high`, `workflow_category.value=devops`.
|
|
- `smoke-safety`: `safety_confirmation_required.value=true`, no actual restart or routing change.
|
|
- Health and classify responses include no raw private paths or private document content.
|
|
|
|
Shutdown:
|
|
|
|
- Stop the foreground server with Ctrl-C.
|
|
- Re-run `ss -ltnp | grep ':18819\b' || true` and confirm no listener remains.
|
|
|
|
## NPU busy-time verification plan
|
|
|
|
Use sysfs plus service response fields; do not accept HTTP 200 alone.
|
|
|
|
```bash
|
|
BUSY=/sys/class/accel/accel0/device/npu_busy_time_us
|
|
before=$(cat "$BUSY")
|
|
response=$(curl -fsS http://127.0.0.1:18819/v1/classify \
|
|
-H 'Content-Type: application/json' \
|
|
-d '{"id":"npu-proof","text":"Check current systemd service status for the embeddings service.","options":{"include_evidence":false,"dry_run":true}}')
|
|
after=$(cat "$BUSY")
|
|
echo "$response" | jq '{npu_busy_delta_us, sysfs_npu_busy_delta_us, warnings}'
|
|
echo "outer_sysfs_npu_busy_delta_us=$((after-before))"
|
|
```
|
|
|
|
Optional localhost smoke helper, after starting the foreground service:
|
|
|
|
```bash
|
|
/home/will/.venvs/npu/bin/python openvino-classifier-npu/smoke_classifier.py \
|
|
--base-url http://127.0.0.1:18819
|
|
```
|
|
|
|
Acceptance for an NPU-backed classification request:
|
|
|
|
- HTTP request succeeds.
|
|
- Response `npu_busy_delta_us > 0` from upstream embeddings.
|
|
- Response `sysfs_npu_busy_delta_us > 0` when sysfs is readable.
|
|
- Outer shell `after-before > 0`.
|
|
- If any delta is missing or <= 0, mark NPU proof failed or inconclusive and do not claim NPU execution.
|
|
|
|
## Docs and diagram implications
|
|
|
|
If this prototype is refreshed or reviewed, update documentation to show:
|
|
|
|
- Live baseline remains RAG `:18810`, RAG health `:18814`, Whisper NPU `:18816`, and embeddings `:18817`.
|
|
- Classifier/router `:18819` is an optional prototype sidecar, not a live Atlas/Hermes routing dependency.
|
|
- Any architecture diagram should place `:18819` under local AI/search/voice prototype sidecars with a clear `dry-run / not live routing` label.
|
|
- Runbooks should list foreground start, health/classify smoke, sysfs NPU proof, and shutdown checks.
|
|
- Service catalog entries should state `not installed/enabled` until Will approves persistent service enablement.
|
|
- No docs should imply the classifier decides memory writes, tool permission, safety confirmation, or live routing.
|
|
|
|
Relevant docs inventory:
|
|
|
|
- `docs/swarm-infrastructure.md`
|
|
- `docs/swarm-infrastructure.html`
|
|
- `docs/diagram-maintenance.md`
|
|
- `swarm-common/obsidian-vault/will/will-shared-zap/Runbooks/OpenVINO NPU Services Runbook.md`
|
|
- `swarm-common/obsidian-vault/will/will-shared-zap/Resources/Service Catalog.md`
|
|
|
|
## No-go / defer criteria
|
|
|
|
Do not proceed to implementation refresh, persistent service enablement, or live integration if any of the following hold:
|
|
|
|
- `:18817` embeddings is unavailable and no approved NPU embedding fallback exists.
|
|
- `/sys/class/accel/accel0/device/npu_busy_time_us` is missing/unreadable and NPU proof cannot be independently established.
|
|
- Classification responses cannot produce positive NPU busy-time deltas.
|
|
- `:18819` is already occupied by an unknown or live service.
|
|
- Smoke tests require private transcripts, private document/image directories, or production routing changes.
|
|
- Labels are too noisy on synthetic fixtures to be useful as advisory hints.
|
|
- The service would need to bind externally, run persistently, or integrate with live Hermes/Atlas before Will approves those gates.
|
|
- Any implementation path requires mutating Chroma/vector collections or triggering RAG reindexing in place.
|
|
|
|
## Implementation handoff notes
|
|
|
|
Recommended next engineer actions:
|
|
|
|
1. Verify or refresh `openvino-classifier-npu/router_classifier.py` to match this contract.
|
|
2. Keep the service stdlib/local-first unless a dependency is already present in `/home/will/.venvs/npu`.
|
|
3. Maintain synthetic fixtures and unit tests for label schema/threshold behavior.
|
|
4. Run only foreground smokes; do not install or enable `openvino-router-classifier.service`.
|
|
5. Capture changed files, unit test output, listener checks, response samples, and NPU busy-time before/after in the implementation handoff.
|