diff --git a/README.md b/README.md index 9ffb824af..d7401acc3 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,12 @@ OpenAgentCore runs AI agents on your own infrastructure behind the OpenAI Agents - **Every part is replaceable.** Sandboxes, harnesses and model providers plug in through defined protocols. +## Screenshots + +| Overview | Agent metrics | +| --- | --- | +| ![Deployment overview](docs/assets/console-overview-en.png) | ![Agent metrics](docs/assets/console-agent-metrics-en.png) | + ## Install On a Linux amd64 host with Docker and Python 3.9+: diff --git a/README.zh-CN.md b/README.zh-CN.md index 54ee16fff..6b8719432 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -23,6 +23,12 @@ OpenAgentCore 在你自己的基础设施上运行 AI Agent,对外提供 OpenA 也可以在你自己的 Linux、macOS 或 Windows 机器上工作。 - **每个部件都可替换。** 沙箱、Harness 和模型供应商都通过既定协议接入。 +## 界面预览 + +| 概览 | Agent 监控 | +| --- | --- | +| ![部署概览](docs/assets/console-overview-zh.png) | ![Agent 监控](docs/assets/console-agent-metrics-zh.png) | + ## 安装 在已准备 Docker 和 Python 3.9+ 的 Linux amd64 主机上: diff --git a/apps/web/README.md b/apps/web/README.md index f64cb7170..1ee6dd241 100644 --- a/apps/web/README.md +++ b/apps/web/README.md @@ -157,3 +157,26 @@ pnpm --filter @agents-core-web/web build These checks cover the application source. Browser acceptance through `services/core-console` is tracked in the [frontend roadmap](../../docs/web/roadmap.md). Required repository checks are documented in [CONTRIBUTING.md](../../CONTRIBUTING.md). + +### README screenshots + +The browser fixture has an opt-in scene for Overview and Agent metrics, including +five available nodes. From the repository root, start these in separate terminals: + +```sh +OAC_WEB_SCREENSHOT_DEMO=1 AGENTS_FIXTURE_PORT=18394 node apps/web/e2e/fixture-console.mjs +``` + +```sh +OAC_WEB_DEV_PROXY_TARGET=http://127.0.0.1:18394 pnpm --filter @agents-core-web/web exec vite --host 127.0.0.1 --mode test --port 4394 +``` + +Open `http://127.0.0.1:4394` in Chrome and sign in with the fixture-only key +`fixture-core-key-3f9a2c71`. Capture Overview and Agent metrics in light mode, +once in English and once in Chinese using the console language menu. Check that +all five nodes load and metrics have no partial-data warning before capturing. +For a remote preview, forward port 4394 over SSH and capture in local Chrome. +Keep the original resolution, crop browser chrome and add a plain macOS-style +window bar. The four PNGs in `docs/assets/console-*.png` are linked by the matching +README and included in the distribution manifest. Normal acceptance data and +production builds do not enable this scene. diff --git a/apps/web/e2e/fixture-console.mjs b/apps/web/e2e/fixture-console.mjs index 0a7884417..350589fc6 100644 --- a/apps/web/e2e/fixture-console.mjs +++ b/apps/web/e2e/fixture-console.mjs @@ -10,6 +10,7 @@ import { buildAdmin } from "./data/admin.mjs"; import { coreMetrics } from "./data/core-metrics.mjs"; import { buildResources } from "./data/resources.mjs"; import { buildDemo } from "./data/routes.mjs"; +import { buildScreenshotDemo } from "../screenshots/data.mjs"; const port = Number(process.env.AGENTS_FIXTURE_PORT ?? 18092); const SESSION_COOKIE = "core_console=fixture-session"; @@ -94,8 +95,9 @@ function e2bDeployment() { function reset(mode = "login", fresh = false, sandbox = "configured", nodes = "demo", address = "public", credentials = "configured", installers = true, artifacts = "docker,microsandbox") { // Self-hosted Sessions get their remote_url from public_url, as in Core. - const base = buildDemo(undefined, address === "local" ? LOCAL_URL : PUBLIC_URL); + const screenshots = process.env.OAC_WEB_SCREENSHOT_DEMO === "1"; const now = Math.floor(Date.now() / 1000); + const base = (screenshots ? buildScreenshotDemo : buildDemo)(now, address === "local" ? LOCAL_URL : PUBLIC_URL); const resources = buildResources(now, base.agents, base.sessions); const admin = buildAdmin(now, base, resources); // A fresh install: no project, Session or Runtime yet; Getting started leads. diff --git a/apps/web/screenshots/data.mjs b/apps/web/screenshots/data.mjs new file mode 100644 index 000000000..41d4fee33 --- /dev/null +++ b/apps/web/screenshots/data.mjs @@ -0,0 +1,52 @@ +// Synthetic README scenes. Only the explicitly opted-in browser fixture uses them. +import { buildDemo } from "../e2e/data/routes.mjs"; + +export function buildScreenshotDemo(now, publicUrl) { + const data = buildDemo(now, publicUrl); + for (const session of data.sessions) { + session.status = "idle"; + session.error = null; + session.required_actions = []; + } + const pending = [ + ["Support triage", "approve_refund", "Review refund request"], + ["Incident responder", "approve_rollback", "Approve checkout rollback"], + ].map(([name, action, title], index) => { + const session = data.sessions.find((entry) => entry.agent.name === name); + session.status = "requires_action"; + session.metadata = { title }; + session.last_active_at = now - 180 - index * 240; + const turn = data.turns.get(session.id).at(-1); + if (turn) Object.assign(turn, { status: "waiting", completed_at: null, error: null }); + session.required_actions = [{ type: "function_call", call_id: `demo-approval-${index}`, turn_id: turn?.id, name: action, arguments: {} }]; + return session; + }); + for (const session of data.sessions.filter((entry) => !pending.includes(entry)).slice(0, 6)) { + session.status = "in_progress"; + session.last_active_at = now - 20; + const turn = data.turns.get(session.id).at(-1); + if (turn) Object.assign(turn, { status: "in_progress", completed_at: null, error: null, usage: null }); + } + data.nodes.push( + { ...data.nodes[0], id: "node-us-west" }, + { ...data.nodes[0], id: "node-eu-west" }, + ); + data.allocations.forEach((allocation, index) => { + allocation.node_id = data.nodes[index % data.nodes.length].id; + }); + data.nodes.forEach((node, index) => { + const own = data.allocations.filter((entry) => entry.node_id === node.id); + const running = own.filter((entry) => entry.compute_phase === "running").length; + Object.assign(node, { + name: ["worker-eu-01", "worker-us-02", "worker-ap-03", "worker-us-04", "worker-eu-05"][index], + online: true, provider_ready: true, rollout: { state: "ready", ready_generation: 1 }, + active: running, running, retained: own.length - running, snapshots: own.length - running, + reserved: 0, cleanup_pending: 0, max_active: 8, max_retained: 16, + available_memory_bytes: 32 * 2 ** 30, available_disk_bytes: 240 * 2 ** 30, + last_seen_at: new Date((now - 4) * 1000).toISOString(), + }); + delete node.diagnostic; + }); + data.sessions.sort((a, b) => b.created_at - a.created_at); + return data; +} diff --git a/docs/assets/console-agent-metrics-en.png b/docs/assets/console-agent-metrics-en.png new file mode 100644 index 000000000..c7c050f35 Binary files /dev/null and b/docs/assets/console-agent-metrics-en.png differ diff --git a/docs/assets/console-agent-metrics-zh.png b/docs/assets/console-agent-metrics-zh.png new file mode 100644 index 000000000..b63eed99c Binary files /dev/null and b/docs/assets/console-agent-metrics-zh.png differ diff --git a/docs/assets/console-overview-en.png b/docs/assets/console-overview-en.png new file mode 100644 index 000000000..7164a92ae Binary files /dev/null and b/docs/assets/console-overview-en.png differ diff --git a/docs/assets/console-overview-zh.png b/docs/assets/console-overview-zh.png new file mode 100644 index 000000000..916bd2943 Binary files /dev/null and b/docs/assets/console-overview-zh.png differ diff --git a/scripts/core-distribution-manifest.py b/scripts/core-distribution-manifest.py index 8377cb6ce..b1ee1479a 100644 --- a/scripts/core-distribution-manifest.py +++ b/scripts/core-distribution-manifest.py @@ -54,6 +54,10 @@ "docs/assets/openagentcore-banner.jpeg", "docs/assets/architecture.png", "docs/assets/development-architecture.png", + "docs/assets/console-overview-en.png", + "docs/assets/console-overview-zh.png", + "docs/assets/console-agent-metrics-en.png", + "docs/assets/console-agent-metrics-zh.png", ) REPOSITORY_URL = "https://github.com/MiniMax-AI/parsar-core" MARKDOWN_LINK = re.compile(r"(!?)\[((?:[^\[\]]|\[[^\]]*\])*)\]\(([^)\s]+)((?:\s+\"[^\"]*\")?)\)")