Integrate zinit SDK: ZinitLifecycle for UI, logging via zinit, remove legacy scripts #9

Closed
opened 2026-03-10 09:28:27 +00:00 by timur · 3 comments
Owner

Context

Parent issue: lhumina_code/hero_os#24
Related: lhumina_code/hero_rpc#7, lhumina_code/home#6

hero_embedder_server already has ZinitLifecycle (Pattern B) with full run/start/stop/status/logs subcommands.

Important: In-process long-running operations (corpus download/load, batch embedding, model loading, namespace ops) stay as in-process async tasks. They work with in-memory state (cached ONNX models, HNSW vector indexes, redb connections) and cannot be externalized to zinit subprocess jobs. However, they should log through zinit for centralized visibility.


1. Add ZinitLifecycle to hero_embedder_ui

File: crates/hero_embedder_ui/src/main.rs (~225 lines)

Currently a standalone Axum server with basic CLI arg parsing — no subcommands, no zinit integration.

Improvement: Add ZinitLifecycle (non-OpenRPC binary pattern):

hero_embedder_ui <COMMAND>
  run      Start via zinit + stream logs + stop on Ctrl-C
  start    Register with zinit and start in background
  stop     Stop the zinit-managed service
  serve    Run the server process (internal — zinit calls this)
  status   Query zinit for service status
  logs     Fetch service logs from zinit

Update Makefile:

start: build
	cargo run -p hero_embedder_server -- start
	cargo run -p hero_embedder_ui -- start

stop:
	@cargo run -p hero_embedder_server -- stop 2>/dev/null || true
	@cargo run -p hero_embedder_ui -- stop 2>/dev/null || true

2. Replace custom OperationLogger with zinit logs

File: crates/hero_embedder_lib/src/logging.rs (~250 lines)

Custom circular-buffer logger (500 entries) with operation types: Embed, Search, Rerank, LoadCorpus, IndexAdd/Delete/Clear, NamespaceCreate/Delete, KVS*, Vectors*. Queryable via logs.get, logs.clear, logs.stream RPC methods.

Improvement: Forward operation logs to zinit via logs.insert() with structured source names.

Log source naming convention

Operation Zinit Log Source
Embedding hero_embedder.embed.{namespace}
Search hero_embedder.search.{namespace}
Reranking hero_embedder.rerank.{namespace}
Corpus download hero_embedder.corpus.download
Corpus load hero_embedder.corpus.load.{namespace}
Namespace CRUD hero_embedder.namespace.{action}
Model loading hero_embedder.init.model
Index operations hero_embedder.index.{namespace}

3. Health checks in zinit service registration

Configure HTTP health checks for both services in their ZinitLifecycle registration:

  • Server: /health on Unix socket (already has structured health response)
  • UI: /health on Unix socket

4. Remove legacy run.sh

File: run.sh — Manual server startup with quality level selection, bypasses zinit.

Remove. All execution goes through hero_embedder_server run (zinit-managed). Quality level should be an env var or arg on serve.


Summary

Area Current Target
Server lifecycle ZinitLifecycle Done
UI lifecycle Standalone ZinitLifecycle subcommands
Operation logging Custom 500-entry buffer Forward to zinit logs.insert()
Health checks Basic endpoints, not wired to zinit Zinit health check config
Legacy scripts run.sh exists Remove
In-process ops spawn_blocking / async tasks Stay in-process, log through zinit

Acceptance Criteria

  • hero_embedder_ui has run/start/stop/status/logs/serve subcommands
  • Operation logs forwarded to zinit with structured source names
  • Health checks configured in zinit service registration
  • run.sh removed
  • Makefile manages both services via binary subcommands
## Context Parent issue: https://forge.ourworld.tf/lhumina_code/hero_os/issues/24 Related: https://forge.ourworld.tf/lhumina_code/hero_rpc/issues/7, https://forge.ourworld.tf/lhumina_code/home/issues/6 hero_embedder_server **already has `ZinitLifecycle`** (Pattern B) with full `run`/`start`/`stop`/`status`/`logs` subcommands. **Important:** In-process long-running operations (corpus download/load, batch embedding, model loading, namespace ops) stay as in-process async tasks. They work with in-memory state (cached ONNX models, HNSW vector indexes, redb connections) and cannot be externalized to zinit subprocess jobs. However, they should **log through zinit** for centralized visibility. --- ## 1. Add `ZinitLifecycle` to hero_embedder_ui **File:** `crates/hero_embedder_ui/src/main.rs` (~225 lines) Currently a standalone Axum server with basic CLI arg parsing — no subcommands, no zinit integration. **Improvement:** Add `ZinitLifecycle` (non-OpenRPC binary pattern): ``` hero_embedder_ui <COMMAND> run Start via zinit + stream logs + stop on Ctrl-C start Register with zinit and start in background stop Stop the zinit-managed service serve Run the server process (internal — zinit calls this) status Query zinit for service status logs Fetch service logs from zinit ``` Update Makefile: ```makefile start: build cargo run -p hero_embedder_server -- start cargo run -p hero_embedder_ui -- start stop: @cargo run -p hero_embedder_server -- stop 2>/dev/null || true @cargo run -p hero_embedder_ui -- stop 2>/dev/null || true ``` --- ## 2. Replace custom OperationLogger with zinit logs **File:** `crates/hero_embedder_lib/src/logging.rs` (~250 lines) Custom circular-buffer logger (500 entries) with operation types: Embed, Search, Rerank, LoadCorpus, IndexAdd/Delete/Clear, NamespaceCreate/Delete, KVS*, Vectors*. Queryable via `logs.get`, `logs.clear`, `logs.stream` RPC methods. **Improvement:** Forward operation logs to zinit via `logs.insert()` with structured source names. ### Log source naming convention | Operation | Zinit Log Source | |-----------|------------------| | Embedding | `hero_embedder.embed.{namespace}` | | Search | `hero_embedder.search.{namespace}` | | Reranking | `hero_embedder.rerank.{namespace}` | | Corpus download | `hero_embedder.corpus.download` | | Corpus load | `hero_embedder.corpus.load.{namespace}` | | Namespace CRUD | `hero_embedder.namespace.{action}` | | Model loading | `hero_embedder.init.model` | | Index operations | `hero_embedder.index.{namespace}` | --- ## 3. Health checks in zinit service registration Configure HTTP health checks for both services in their `ZinitLifecycle` registration: - Server: `/health` on Unix socket (already has structured health response) - UI: `/health` on Unix socket --- ## 4. Remove legacy `run.sh` **File:** `run.sh` — Manual server startup with quality level selection, bypasses zinit. Remove. All execution goes through `hero_embedder_server run` (zinit-managed). Quality level should be an env var or arg on `serve`. --- ## Summary | Area | Current | Target | |------|---------|--------| | Server lifecycle | ✅ ZinitLifecycle | ✅ Done | | UI lifecycle | ❌ Standalone | ZinitLifecycle subcommands | | Operation logging | Custom 500-entry buffer | Forward to zinit `logs.insert()` | | Health checks | Basic endpoints, not wired to zinit | Zinit health check config | | Legacy scripts | `run.sh` exists | Remove | | In-process ops | `spawn_blocking` / async tasks | **Stay in-process**, log through zinit | ## Acceptance Criteria - [ ] hero_embedder_ui has `run`/`start`/`stop`/`status`/`logs`/`serve` subcommands - [ ] Operation logs forwarded to zinit with structured source names - [ ] Health checks configured in zinit service registration - [ ] `run.sh` removed - [ ] Makefile manages both services via binary subcommands
Author
Owner

Correction: scope of zinit jobs vs in-process operations

After further discussion, the recommendation to convert most in-process operations to zinit jobs was incorrect. Zinit jobs are subprocess-based — they spawn external commands. Most hero_embedder operations work with in-memory state (cached ONNX models, vector indexes, redb connections) and cannot be externalized to subprocesses.

What should NOT become zinit jobs (stays in-process)

  • Corpus load / batch embedding (items 2) — Uses cached ONNX embedder loaded in memory, writes to in-process HNSW index and redb store. Cannot serialize model + index state to a subprocess.
  • Corpus download (item 1) — While the download itself is subprocess-friendly, the decompression → redb write pipeline uses the in-process database connection.
  • Namespace create/delete (item 6) — Uses in-process redb + filesystem state.
  • Model loading at startup (item 3) — Loads ONNX models into the server's memory space. This is inherently in-process.

What SHOULD use zinit

Area Zinit Feature Still Valid
UI lifecycle ZinitLifecycle for hero_embedder_ui Yes (item 5)
Logging logs.insert() with structured source names Yes (item 4)
Health checks Health check in zinit service registration Yes
Model download Could be a zinit job — it shells out to curl Partial — the curl download part only
Legacy run.sh removal All execution via ZinitLifecycle Yes (item 7)

Revised summary

The core improvements are:

  1. ZinitLifecycle for hero_embedder_ui (server already done)
  2. Logging through zinit — replace/supplement custom OperationLogger with zinit logs.insert() using structured source names
  3. Health checks in zinit service registration
  4. Remove legacy run.sh

In-process long-running operations (corpus load, embedding, model loading) stay as async tasks but should log through zinit for centralized visibility.

## Correction: scope of zinit jobs vs in-process operations After further discussion, the recommendation to convert most in-process operations to **zinit jobs** was incorrect. Zinit jobs are subprocess-based — they spawn external commands. Most hero_embedder operations work with **in-memory state** (cached ONNX models, vector indexes, redb connections) and cannot be externalized to subprocesses. ### What should NOT become zinit jobs (stays in-process) - **Corpus load / batch embedding** (items 2) — Uses cached ONNX embedder loaded in memory, writes to in-process HNSW index and redb store. Cannot serialize model + index state to a subprocess. - **Corpus download** (item 1) — While the download itself is subprocess-friendly, the decompression → redb write pipeline uses the in-process database connection. - **Namespace create/delete** (item 6) — Uses in-process redb + filesystem state. - **Model loading at startup** (item 3) — Loads ONNX models into the server's memory space. This is inherently in-process. ### What SHOULD use zinit | Area | Zinit Feature | Still Valid | |------|--------------|-------------| | **UI lifecycle** | `ZinitLifecycle` for hero_embedder_ui | ✅ Yes (item 5) | | **Logging** | `logs.insert()` with structured source names | ✅ Yes (item 4) | | **Health checks** | Health check in zinit service registration | ✅ Yes | | **Model download** | Could be a zinit job — it shells out to `curl` | ✅ Partial — the curl download part only | | **Legacy run.sh removal** | All execution via `ZinitLifecycle` | ✅ Yes (item 7) | ### Revised summary The core improvements are: 1. **`ZinitLifecycle`** for hero_embedder_ui (server already done) 2. **Logging through zinit** — replace/supplement custom `OperationLogger` with zinit `logs.insert()` using structured source names 3. **Health checks** in zinit service registration 4. **Remove legacy `run.sh`** In-process long-running operations (corpus load, embedding, model loading) stay as async tasks but should **log through zinit** for centralized visibility.
timur changed title from Use zinit jobs API for long-running operations, integrate logging with zinit, add lifecycle to UI to Integrate zinit SDK: ZinitLifecycle for UI, logging via zinit, remove legacy scripts 2026-03-10 11:26:53 +00:00
Author
Owner

Implementation audit — code is correct, build fixed

Audited all uncommitted changes:

  • UI lifecycle module created (hero_embedder_ui/src/lifecycle.rs) using ServiceBuilder/ActionBuilder/RetryPolicyBuilder correct
  • zinit_client.rs wraps zinit RPC with graceful fallback (no-op when zinit unavailable) — correct pattern. Has create_job method defined but never called — dead code, harmless
  • jobs.rs provides RPC handlers for in-process job progress tracking (state.job_progress) — correct, these are NOT zinit subprocess jobs
  • logging.rs forwards operation logs to zinit via logs.insert() with structured source names — correct
  • run.sh deleted per plan
  • No zinit jobs API misuse — corpus load, embedding, etc. all stay in-process with job_progress HashMap

Build fix applied: Added missing hero_rpc_openrpc workspace dependency and SDK crate dependency. Pre-existing issue (unrelated to zinit work) — the openrpc_client! macro generates code requiring this crate. Workspace now compiles clean (cargo check --workspace passes).

Remaining: code is uncommitted. Needs to be committed and pushed.

## Implementation audit — code is correct, build fixed Audited all uncommitted changes: - **UI lifecycle module** created (`hero_embedder_ui/src/lifecycle.rs`) using `ServiceBuilder`/`ActionBuilder`/`RetryPolicyBuilder` — ✅ correct - **`zinit_client.rs`** wraps zinit RPC with graceful fallback (no-op when zinit unavailable) — ✅ correct pattern. Has `create_job` method defined but **never called** — dead code, harmless - **`jobs.rs`** provides RPC handlers for **in-process** job progress tracking (`state.job_progress`) — ✅ correct, these are NOT zinit subprocess jobs - **`logging.rs`** forwards operation logs to zinit via `logs.insert()` with structured source names — ✅ correct - **`run.sh` deleted** — ✅ per plan - **No zinit jobs API misuse** — corpus load, embedding, etc. all stay in-process with `job_progress` HashMap **Build fix applied:** Added missing `hero_rpc_openrpc` workspace dependency and SDK crate dependency. Pre-existing issue (unrelated to zinit work) — the `openrpc_client!` macro generates code requiring this crate. Workspace now compiles clean (`cargo check --workspace` passes). Remaining: code is uncommitted. Needs to be committed and pushed.
timur closed this issue 2026-03-10 11:43:24 +00:00
Author
Owner

All items implemented and pushed to development branch (commit 642f88a):

  • ZinitLifecycle for hero_embedder_ui (server was already done)
  • Zinit logging via zinit_client.rs wrapper with graceful fallback
  • In-process job progress tracking via jobs.rs + state.job_progress
  • Operation logs forwarded to zinit logs.insert() with structured source names
  • Legacy run.sh removed
  • Makefile updated
  • Fixed pre-existing SDK build issue (missing hero_rpc_openrpc dep)
  • Workspace compiles clean

Closing.

All items implemented and pushed to `development` branch (commit 642f88a): - ✅ ZinitLifecycle for hero_embedder_ui (server was already done) - ✅ Zinit logging via `zinit_client.rs` wrapper with graceful fallback - ✅ In-process job progress tracking via `jobs.rs` + `state.job_progress` - ✅ Operation logs forwarded to zinit `logs.insert()` with structured source names - ✅ Legacy `run.sh` removed - ✅ Makefile updated - ✅ Fixed pre-existing SDK build issue (missing `hero_rpc_openrpc` dep) - ✅ Workspace compiles clean Closing.
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
lhumina_code/hero_embedder#9
No description provided.