diff --git a/.github/workflows/ci-v2-production.yml b/.github/workflows/ci-v2-production.yml index 8c43e75..f3b87af 100644 --- a/.github/workflows/ci-v2-production.yml +++ b/.github/workflows/ci-v2-production.yml @@ -47,7 +47,7 @@ jobs: server /data - run: uv lock --check - run: uv sync --frozen --extra postgres --extra server --extra s3 --extra redis --extra otel - - run: uv run ruff check src tests scripts examples + - run: uv run ruff check src tests scripts deploy/reference_app.py - run: uv run ruff format --check src tests example - run: uv run mypy src/rath - name: Audit exact production dependency set diff --git a/.github/workflows/release-v2-ga.yml b/.github/workflows/release-v2-ga.yml index 3decda9..2a8b78e 100644 --- a/.github/workflows/release-v2-ga.yml +++ b/.github/workflows/release-v2-ga.yml @@ -120,7 +120,7 @@ jobs: uv lock --check uv sync --frozen --all-extras --all-groups uv run ruff format --check src tests example - uv run ruff check src tests scripts examples + uv run ruff check src tests scripts deploy/reference_app.py uv run mypy --no-incremental src/rath uv run pytest -q -n auto \ -m "not live_llm and not opensandbox and not openviking" diff --git a/.github/workflows/release-v2-rc.yml b/.github/workflows/release-v2-rc.yml index c3509e7..39e5559 100644 --- a/.github/workflows/release-v2-rc.yml +++ b/.github/workflows/release-v2-rc.yml @@ -55,7 +55,7 @@ jobs: uv lock --check uv sync --frozen --all-extras --all-groups uv run ruff format --check src tests example - uv run ruff check src tests scripts examples + uv run ruff check src tests scripts deploy/reference_app.py uv run mypy --no-incremental src/rath uv run pytest -q -n auto -m "not live_llm and not opensandbox and not openviking" uv run python scripts/export_openapi_v2.py --output "$RUNNER_TEMP/openapi-v2.json" diff --git a/README.md b/README.md index 96c39fa..a4dd876 100644 --- a/README.md +++ b/README.md @@ -54,18 +54,40 @@ Most agent frameworks begin with an agent loop. OpenRath begins with **Session** OpenRath is designed for this: many agents collaborating across many branchable sessions, while still tracing every role, workspace, memory write, and final output. -## v2.0.0 durable runtime (release candidate) - -The `2.0.0rc1` candidate adds explicit `@step` / `@router` execution plans, durable -Runs and checkpoints, effect reconciliation, tenant-scoped Agent Server APIs, -and governed Provider/Tool/Sandbox/Memory adapters. The HTTP contract is -currently **Beta**; v1 JSONL imports are historical and cannot resume an active -Run. +## OpenRath v2.0.0: Built for Production + +The defining change in OpenRath v2.0.0 is that OpenRath moves beyond a +composable Python framework and becomes a durable runtime designed for +production deployment. The existing Session-first Python API remains intact; +the release adds a production execution and operations layer around it. + +| Production concern | What OpenRath provides | +| --- | --- | +| Durable execution | Explicit `@step` / `@router` boundaries compile into immutable execution plans. Runs, Events, and Checkpoints survive process and worker restarts. | +| Resilient workers | Leases, fencing, retries, cancellation, deadlines, and resumable queues prevent stale workers from silently committing new state. | +| Controlled side effects | An Effect Ledger records outcomes and idempotency keys. Ambiguous non-idempotent effects stop in `NEEDS_REVIEW` instead of being replayed blindly. | +| Human decisions | Durable Interrupts pause a Run for approval or input and resume it without rebuilding hidden loop state. | +| Security and tenancy | Agent Server tokens carry explicit action grants; tenant/project scope, policy checks, secret references, trust labels, and audit remain separate boundaries. | +| Production operations | PostgreSQL is the durable source of truth, Redis can accelerate signaling, S3-compatible storage holds artifacts, and health, migration, telemetry, container, and Kubernetes references are included. | + +```text +@step / @router + | + v +ExecutionPlan -> Run -> Event -> Checkpoint + | | + | +-> Interrupt / Effect Ledger + | + +-> PostgreSQL durable state + +-> optional Redis signals + +-> S3-compatible artifacts + | + v + Agent Server HTTP + SSE +``` -Embedded mode is intended for a trusted process. Agent Server mode is the -strict durable profile: tokens need explicit action grants, object access is -tenant/project scoped, and synchronous steps cannot declare a preemptive -timeout. Use an async step or isolated executor for enforceable deadlines. +Embedded mode remains useful inside a trusted process. Agent Server mode is the +strict production profile: ```python runtime = LocalRuntime( @@ -76,12 +98,28 @@ runtime = LocalRuntime( server = AgentServer(store, runtime, auth=auth, audit_sink=audit) ``` -Production PostgreSQL schema migration is a separate operation: -`openrath-migrate` followed by `openrath-migrate --check`. Runtime identities -do not need DDL privileges. See -[`deploy/docs/operations-v2.md`](deploy/docs/operations-v2.md), -[`deploy/docs/migration-v2.md`](deploy/docs/migration-v2.md), and the generated -[`deploy/docs/openapi-v2.json`](deploy/docs/openapi-v2.json). +Install the production profile and run schema migration as a separate +operation: + +```bash +pip install "openrath[server,postgres]" +openrath-migrate +openrath-migrate --check +``` + +Runtime identities do not need DDL privileges. Tokens need explicit action +grants, object access is tenant/project scoped, and synchronous steps cannot +declare a preemptive timeout; use an async step or isolated executor when a +deadline must be enforced. + +OpenRath v2.0.0 is designed for production deployment while keeping interface +maturity explicit: the Agent Server HTTP surface remains **Beta**, and v1 JSONL +imports are historical records rather than resumable active Runs. Deployment, +migration, security, and operations guidance lives in +[`deploy/`](deploy/), including +[`operations-v2.md`](deploy/docs/operations-v2.md), +[`migration-v2.md`](deploy/docs/migration-v2.md), and the generated +[`openapi-v2.json`](deploy/docs/openapi-v2.json).
@@ -344,6 +382,10 @@ python example/01_hello_agent.py
Read [`example/README.md`](example/README.md) for setup details and shared helpers.
+For complete, production-shaped scenarios with fixed inputs, durable state, QA,
+and committed deliverables, see
+[`Rath-Team/OpenRath-Example`](https://github.com/Rath-Team/OpenRath-Example).
+
---
## Docs and Links
diff --git a/README_zh.md b/README_zh.md
index 87ea319..a6195ef 100644
--- a/README_zh.md
+++ b/README_zh.md
@@ -54,16 +54,67 @@
OpenRath 为此而设计:多个 Agent 在多个可分支 Session 上协作,同时仍能追踪每个 role、workspace、memory 写入和最终输出。
-## v2.0.0 durable runtime(候选版本)
+## OpenRath v2.0.0:面向生产环境
+
+OpenRath v2.0.0 最重要的变化,是 OpenRath 从一个可组合的 Python 框架,
+进入可部署、可恢复、可运维的生产环境。原有以 Session 为核心的 Python API
+保持不变;这个版本在其外层增加了一套生产级执行与运维体系。
+
+| 生产环境关注点 | OpenRath 提供的能力 |
+| --- | --- |
+| 持久化执行 | 显式的 `@step` / `@router` 边界会编译成不可变执行计划;Run、Event 与 Checkpoint 能跨进程和 Worker 重启保留。 |
+| Worker 故障恢复 | Lease、Fencing、Retry、Cancellation、Deadline 与可恢复队列,防止过期 Worker 静默提交新状态。 |
+| 受控副作用 | Effect Ledger 记录执行结果与幂等键;无法确认的非幂等副作用会停在 `NEEDS_REVIEW`,而不是被盲目重放。 |
+| 人工决策 | 持久化 Interrupt 可以暂停 Run 等待审批或输入,并在不重建隐藏循环状态的情况下恢复。 |
+| 安全与租户隔离 | Agent Server Token 使用显式 Action Grant;Tenant/Project Scope、Policy、Secret Reference、Trust Label 与 Audit 保持为独立边界。 |
+| 生产运维 | PostgreSQL 是持久化事实来源,Redis 可加速信号传递,S3 兼容存储保存 Artifact,并提供健康检查、迁移、遥测、容器与 Kubernetes 参考。 |
+
+```text
+@step / @router
+ |
+ v
+ExecutionPlan -> Run -> Event -> Checkpoint
+ | |
+ | +-> Interrupt / Effect Ledger
+ |
+ +-> PostgreSQL 持久化状态
+ +-> 可选 Redis 信号
+ +-> S3 兼容 Artifact
+ |
+ v
+ Agent Server HTTP + SSE
+```
+
+Embedded mode 仍适合可信进程内部使用。Agent Server mode 是严格的生产
+Profile:
-`2.0.0rc1` 新增显式 `@step` / `@router` 执行计划、durable Run 与
-Checkpoint、effect reconciliation、tenant-scoped Agent Server API,以及受
-治理的 Provider/Tool/Sandbox/Memory adapter。HTTP contract 在 RC 阶段仍为
-**Beta**;v1 JSONL 导入仅作为历史记录,不能恢复 active Run。
+```python
+runtime = LocalRuntime(
+ store,
+ effect_ledger=ledger,
+ production_mode=True,
+)
+server = AgentServer(store, runtime, auth=auth, audit_sink=audit)
+```
-Embedded mode 面向可信本地进程。Agent Server mode 是严格的 durable
-profile:token 必须具有显式 action grants,对象访问按 tenant/project
-隔离;需要强制 deadline 时应使用 async step 或 isolated executor。
+安装生产 Profile,并将数据库 Schema 迁移作为独立操作执行:
+
+```bash
+pip install "openrath[server,postgres]"
+openrath-migrate
+openrath-migrate --check
+```
+
+运行时身份不需要 DDL 权限。Token 必须拥有显式 Action Grant,对象访问按
+Tenant/Project 隔离;同步 Step 不能声明抢占式超时,需要强制 Deadline 时应使用
+Async Step 或隔离执行器。
+
+OpenRath v2.0.0 面向生产部署,同时明确标注接口成熟度:Agent Server HTTP
+接口仍为 **Beta**;v1 JSONL 导入属于历史记录,不能恢复 Active Run。部署、
+迁移、安全和运维指南位于 [`deploy/`](deploy/),包括
+[`operations-v2.md`](deploy/docs/operations-v2.md)、
+[`migration-v2.md`](deploy/docs/migration-v2.md) 和生成的
+[`openapi-v2.json`](deploy/docs/openapi-v2.json)。
@@ -326,6 +377,9 @@ python example/01_hello_agent.py
阅读 [`example/README.md`](example/README.md) 获取设置细节和共享 helpers。
+如需查看包含固定输入、持久化状态、质量检查和已提交交付物的完整生产化场景,
+请访问 [`Rath-Team/OpenRath-Example`](https://github.com/Rath-Team/OpenRath-Example)。
+
---
## 文档与链接
diff --git a/deploy/compose/compose.yaml b/deploy/compose/compose.yaml
index 650ab63..a4dc8d4 100644
--- a/deploy/compose/compose.yaml
+++ b/deploy/compose/compose.yaml
@@ -26,7 +26,7 @@ services:
dockerfile: docker/Dockerfile
image: openrath:2.0.0rc1
environment:
- OPENRATH_APP: examples.v2_server_app:app
+ OPENRATH_APP: reference_app:app
OPENRATH_POSTGRES_DSN: postgresql://openrath:${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD}@postgres:5432/openrath
OPENRATH_TOKEN: ${OPENRATH_TOKEN:?set OPENRATH_TOKEN}
OPENRATH_TENANT_ID: ${OPENRATH_TENANT_ID:-default}
@@ -61,7 +61,7 @@ services:
dockerfile: docker/Dockerfile
image: openrath:2.0.0rc1
entrypoint: ["openrath-worker"]
- command: ["--app", "examples.v2_server_app:server"]
+ command: ["--app", "reference_app:server"]
environment:
OPENRATH_POSTGRES_DSN: postgresql://openrath:${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD}@postgres:5432/openrath
OPENRATH_TOKEN: ${OPENRATH_TOKEN:?set OPENRATH_TOKEN}
diff --git a/deploy/kubernetes/openrath.yaml b/deploy/kubernetes/openrath.yaml
index 7272c64..aea9d88 100644
--- a/deploy/kubernetes/openrath.yaml
+++ b/deploy/kubernetes/openrath.yaml
@@ -15,7 +15,7 @@ kind: ConfigMap
metadata:
name: openrath
data:
- OPENRATH_APP: examples.v2_server_app:app
+ OPENRATH_APP: reference_app:app
OPENRATH_EMBEDDED_WORKER: "false"
OPENRATH_DB_SCHEMA: openrath
OPENRATH_TENANT_ID: default
@@ -163,7 +163,7 @@ spec:
image: ghcr.io/rath-team/openrath:2.0.0rc1
# Release automation must replace the review tag with image@sha256.
imagePullPolicy: Always
- command: ["openrath-worker", "--app", "examples.v2_server_app:server"]
+ command: ["openrath-worker", "--app", "reference_app:server"]
envFrom:
- configMapRef:
name: openrath
diff --git a/examples/v2_server_app.py b/deploy/reference_app.py
similarity index 97%
rename from examples/v2_server_app.py
rename to deploy/reference_app.py
index 026854c..cf09d74 100644
--- a/examples/v2_server_app.py
+++ b/deploy/reference_app.py
@@ -1,4 +1,4 @@
-"""Minimal standalone OpenRath v2 reference application."""
+"""Minimal deployable OpenRath v2 reference application."""
from __future__ import annotations
diff --git a/docker/Dockerfile b/docker/Dockerfile
index 13623f6..27e5a4a 100644
--- a/docker/Dockerfile
+++ b/docker/Dockerfile
@@ -32,7 +32,7 @@ RUN groupadd --system --gid 10001 openrath \
&& useradd --system --uid 10001 --gid openrath --home /app openrath
WORKDIR /app
COPY --from=builder /opt/venv /opt/venv
-COPY examples ./examples
+COPY deploy/reference_app.py ./reference_app.py
USER 10001:10001
EXPOSE 8000
ENTRYPOINT ["openrath-server"]
diff --git a/tests/deployment/test_reference_manifests.py b/tests/deployment/test_reference_manifests.py
index e949282..f59cf9c 100644
--- a/tests/deployment/test_reference_manifests.py
+++ b/tests/deployment/test_reference_manifests.py
@@ -28,7 +28,7 @@ def test_kubernetes_template_covers_workloads_and_dns() -> None:
def test_reference_server_enables_audit_and_explicit_grants() -> None:
- app = Path("examples/v2_server_app.py").read_text(encoding="utf-8")
+ app = Path("deploy/reference_app.py").read_text(encoding="utf-8")
compose = Path("deploy/compose/compose.yaml").read_text(encoding="utf-8")
assert "StructuredAuditSink()" in app
assert 'os.environ["OPENRATH_GRANTS"]' in app