- Rust 45.7%
- JavaScript 19.3%
- Python 16.8%
- HTML 13.2%
- CSS 4.9%
|
All checks were successful
lab publish / publish (push) Successful in 12m5s
The lab installer now installs to ~/.local/bin instead of ~/hero/bin. Point GITHUB_PATH at the new location so the release publish works again. Signed-by: mik-tf <mik-tf@noreply.invalid> |
||
|---|---|---|
| .forgejo/workflows | ||
| crates | ||
| examples | ||
| scripts | ||
| .gitignore | ||
| buildenv.sh | ||
| Cargo.lock | ||
| Cargo.toml | ||
| Cargo.toml.hero_builder_backup | ||
| PRD.md | ||
| PURPOSE.md | ||
| README.md | ||
hero_logic
A runtime for runnable, observable, resumable Python flows.
A flow is a Python function decorated with @flow(...). It has typed inputs and outputs. When you run it, hero_logic spawns a sandboxed Python subprocess, captures a live span tree of every step, persists the result, and renders the run as a graph you can read at any depth.
Flows compose recursively: a step calls another flow as a function (in-process, default) or as a separate Play (spawned, opt-in). Any step at any depth can pause — exit cleanly with state persisted — to wait for input from a user, a webhook, a scheduled trigger, or any other source. When a resume arrives, the play re-runs; every previously-completed step replays from cache, the pause returns the resume payload, execution continues. No long-blocked subprocesses, no lost work, no double side-effects.
Around the runtime: versioned workflows, saved input presets, per-version benchmarks, a searchable library of flows, and a web admin with Monaco source + live graph view + pause/resume input forms.
For the full design see PRD.md. This README is the orientation.
What problems it solves
- Visibility into multi-step automation. Watch a complex run, live, as a tree — including nested sub-flows, fan-outs, retries, replays. A reader who doesn't know Python can still see what happened.
- Resumable workflows without long-running processes. A flow can ask a user for a clarification, wait for an external webhook, or pause for a scheduled trigger — and the subprocess exits cleanly until the resume arrives.
- Reproducible runs with cost / latency tracking. Pin Plays to specific versions; benchmark versions against realistic inputs; let the runtime pick the right version per request based on cost/speed/accuracy weights.
- A growing library of solved problems. Every saved Workflow is a callable primitive. The agent searches the library before generating fresh code; user-confirmed flows save back as reusable entries.
The dominant current consumer is AI-agent orchestration (service_agent and friends), but hero_logic is not AI-specific. Anything that benefits from typed I/O + step visibility + saved inputs + benchmarks + pauses qualifies.
Core API (Python)
Authoring surface, all exported from hero_tracing:
from hero_tracing import flow, instrument, ask_user
@flow(name="my_flow", inputs={"prompt": {"type":"string","required":True}},
outputs={"answer": {"type":"string"}})
def main(prompt):
ai = instrument(HeroAibrokerClient()) # opt-in per-RPC spans
selection = flow.invoke("pick_service", prompt=prompt) # in-process sub-flow
if not selection["confident"]:
selection = ask_user.choice("Which service?", # pause for human input
options=selection["candidates"])
result = flow.invoke("run_against_service", # spawned sub-flow → own Play
service=selection, prompt=prompt, spawn=True)
return {"answer": result["summary"]}
@flow— every call becomes a span; declares typed I/O; outputs are memoized for replayflow.step(name, **tags)/flow.span(...)— context-manager spans (use sparingly; prefer@flow)flow.invoke(name, **inputs, spawn=False)— call a sub-flow (in-process or spawned)flow.pause(name, schema=..., ui=...)— generic pause; returns the resume payloadask_user.text / number / choice / multi_choice / confirm— UI-flavored helpers overflow.pauseinstrument(client)— opt-in per-RPC spansflow.Failed— clean "this didn't work" exception for spansflow.current_span.tag(k, v)/.log(text)— attach data to the current span
See PRD §3 for the full surface.
RPC surface (LogicService)
JSON-RPC 2.0 over HTTP/1.1 over UDS at ~/hero/var/sockets/hero_logic/rpc.sock. Generated typed clients live at ~/.hero/var/router/python/hero_logic_client.py.
workflow_create / workflow_get / workflow_list / workflow_update / workflow_delete
workflow_create_version / workflow_set_current_version / workflow_version_fetch
play_start / play_run_async / play_status / play_wait / play_cancel
play_resume / play_pending_resumes
example_upsert / example_fetch / example_to_input_data / example_list / example_delete
benchmark_list_for_workflow / benchmark_list_for_version / benchmark_latest_for_version
pick_version
flow_library_search
See PRD §8 for parameters and return shapes.
Where things live
hero_logic/
├── PRD.md # full design spec
├── README.md # this file
├── examples/ # E2E driver scripts (start play, respond to pauses, assert)
└── crates/
├── hero_logic/ # CLI binary + RPC server binary + executor
│ ├── src/main.rs # hero_logic CLI (--start / --stop / --info)
│ ├── src/bin/hero_logic_server.rs # JSON-RPC server (rpc.sock)
│ ├── src/engine/ # Python executor, span socket, replay
│ ├── src/seed_flows/ # bundled @flow Python sources
│ ├── schemas/logic/logic.oschema # types + RPC source of truth
│ └── sdk/python/hero_tracing.py # embedded; staged on server startup
└── hero_logic_admin/ # Axum dashboard (admin.sock)
├── src/main.rs
└── templates/ # Askama HTML
Runtime paths the executor touches:
~/hero/var/sockets/hero_logic/{rpc.sock,admin.sock}— service sockets~/.hero/var/flows/sdk/hero_tracing.py— staged on every server start~/.hero/var/router/python/— generated RPC clients (owned by hero_router)~/.hero/var/plays/{play_sid}/work/— per-Play workdir + replay cache files/tmp/spans-{play_sid}.sock— per-Play span socket
Binaries
| Binary | Kind | Socket | Purpose |
|---|---|---|---|
hero_logic |
cli | — | Registration + control (--start / --stop / --info) |
hero_logic_server |
server | hero_logic/rpc.sock |
JSON-RPC, executor, span listener |
hero_logic_admin |
admin | hero_logic/admin.sock |
Web dashboard |
All three follow the herolib_base pattern: service.toml at the crate root, embedded via service_base!(), validated and printed on startup, sockets prepared and stale-cleaned before bind. See PRD §13.
Build, install, run
lab reads each binary's service.toml and drives the whole build / install / start / stop lifecycle on top of hero_proc:
lab service logic --install # build + install all declared binaries to ~/hero/bin
lab service logic --start # register with hero_proc and start server + admin
lab service logic --status # status of all binaries
lab service logic --stop # stop all binaries
For local development without hero_proc, run a binary directly:
RUST_LOG=debug cargo run -p hero_logic --bin hero_logic_server # server in foreground
cargo run -p hero_logic_admin # admin UI in foreground
cargo test --workspace # tests
hero_proc must be running before lab service logic --start. Admin UI: http://localhost:9820 when proxied through hero_router, or via ~/hero/var/sockets/hero_logic/admin.sock.
How a run flows end-to-end
- Author writes a
@flow-decorated Python function; saves as aWorkflow+ firstWorkflowVersion. play_startvalidates inputs, writes aPlay, binds/tmp/spans-{sid}.sock, spawnspython3withPYTHONPATHpointing at the staged SDK + generated clients.- The subprocess imports
hero_tracing, the boot stub calls the@flow-decorated entry; every step emits JSONL span events over the socket. - The server persists spans incrementally and pushes them via SSE to the admin UI's graph view.
- If a step calls
flow.pause(...)→ subprocess exits clean,Play.status = awaiting_resume, aResumeRequestis persisted. - UI (or webhook / cron / another service) calls
play_resume(play_sid, resume_id, payload)→ server spawns a fresh subprocess; cached step outputs short-circuit@flowcalls; the pause returns the resume payload; the run continues. Repeats per pause until the play reaches a terminal status.
For details on the replay contract and step memoization, see PRD §4–5.
Tips for AI agents picking this up
- The PRD is the source of truth. Read it before changing anything structural.
crates/hero_logic/schemas/logic/logic.oschemais the source of truth for types + RPC. Edit the schema, regenerate, then implement handlers — not the other way around.- The Python authoring surface is
crates/hero_logic/sdk/python/hero_tracing.py. Wire protocol lives there too. - The executor is
crates/hero_logic/src/engine/python_executor.rs(spawn, sandbox, boot stub) andengine/span_socket.rs(listener, persistence). - Seed flows in
crates/hero_logic/src/seed_flows/are working reference implementations of the authoring patterns.service_agent.pyis the primary example. /examples/is the E2E test surface — runnable scripts that drive a play through its full lifecycle including resumes.
License
Apache-2.0