From 2ac5407ba1c3ddc089e7aa0277739423a52f056f Mon Sep 17 00:00:00 2001 From: SaladDay <1203511142@qq.com> Date: Sat, 24 Oct 2026 02:12:35 +0000 Subject: [PATCH 1/2] Report CPU only as the Runtime history ratio --- apps/web/e2e/data/routes.mjs | 6 ++-- .../features/metrics/SandboxMetricsPage.tsx | 15 ++-------- .../features/metrics/sandbox-runtime.test.ts | 4 +-- .../src/features/metrics/sandbox-runtime.ts | 6 ++-- .../src/features/overview/test-fixtures.ts | 1 - .../sessions/SessionRuntimeSection.tsx | 5 +--- apps/web/src/i18n/locales/en/metrics.ts | 1 - apps/web/src/i18n/locales/en/sessions.ts | 3 +- apps/web/src/i18n/locales/zh-CN/metrics.ts | 1 - apps/web/src/i18n/locales/zh-CN/sessions.ts | 3 +- contracts/agents-api/core.openapi.yaml | 29 ------------------- .../agents-api/runtime-observability-api.md | 16 ---------- contracts/agents-api/sandbox-deployment.md | 2 +- .../agents-api/v1/runtime_observations.go | 7 ----- .../zh/runtime-observability-api.md | 18 +----------- contracts/agents-api/zh/sandbox-deployment.md | 4 +-- docs/web/index.md | 2 +- docs/zh/web/index.md | 4 +-- .../agents-client/src/admin-client.test.ts | 2 -- packages/agents-client/src/client.test.ts | 16 ---------- packages/agents-client/src/client.ts | 22 ++------------ .../agents-client/src/generated/core-api.ts | 12 ++------ packages/agents-client/src/types.ts | 5 ++-- .../core/internal/api/runtime_observations.go | 3 -- .../internal/api/runtime_observations_test.go | 13 ++++----- 25 files changed, 31 insertions(+), 169 deletions(-) diff --git a/apps/web/e2e/data/routes.mjs b/apps/web/e2e/data/routes.mjs index 8f585d2a8..adddd9dea 100644 --- a/apps/web/e2e/data/routes.mjs +++ b/apps/web/e2e/data/routes.mjs @@ -100,11 +100,11 @@ export function buildDemo(now = Math.floor(Date.now() / 1000), publicUrl = "http }); const observations = sessions.map((session) => { const base = { id: session.id, object: "agent.runtime_observation", session_id: session.id, resolved_at: now }; - if (session.environment.type === "none") return { ...base, environment_id: null, mode: "none", provider_type: null, instance: { kind: "none", allocation_id: null, device_id: null, connection_generation: null }, lifecycle_state: null, status: "unsupported", reason: "runtime_mode_not_observable", allocation_created_at: null, observed_at: null, started_at: null, cpu: null, memory: null }; - if (session.environment.type === "self_hosted") return { ...base, environment_id: session.environment.id, mode: "self_hosted", provider_type: null, instance: { kind: "self_hosted_connection", allocation_id: null, device_id: null, connection_generation: null }, lifecycle_state: null, status: "unsupported", reason: "runtime_mode_not_observable", allocation_created_at: null, observed_at: null, started_at: null, cpu: null, memory: null }; + if (session.environment.type === "none") return { ...base, environment_id: null, mode: "none", provider_type: null, instance: { kind: "none", allocation_id: null, device_id: null, connection_generation: null }, lifecycle_state: null, status: "unsupported", reason: "runtime_mode_not_observable", allocation_created_at: null, observed_at: null, started_at: null, memory: null }; + if (session.environment.type === "self_hosted") return { ...base, environment_id: session.environment.id, mode: "self_hosted", provider_type: null, instance: { kind: "self_hosted_connection", allocation_id: null, device_id: null, connection_generation: null }, lifecycle_state: null, status: "unsupported", reason: "runtime_mode_not_observable", allocation_created_at: null, observed_at: null, started_at: null, memory: null }; const allocation = allocations.find((entry) => entry.session_id === session.id); const sleeping = allocation.compute_phase === "suspended"; - return { ...base, environment_id: session.environment.id, mode: "openai_hosted", provider_type: "docker", instance: { kind: "managed_allocation", allocation_id: allocation.id, device_id: null, connection_generation: null }, lifecycle_state: sleeping ? "sleeping" : "active", status: "observed", reason: null, allocation_created_at: session.created_at, observed_at: now - 2, started_at: session.created_at, cpu: sleeping ? null : { usage_seconds_total: 600 + Math.floor(rand() * 4000), capacity_cores: 2, utilization_ratio: null }, memory: sleeping ? null : { usage_bytes: Math.floor((0.4 + rand() * 1.4) * 2 ** 30), limit_bytes: 2 * 2 ** 30 } }; + return { ...base, environment_id: session.environment.id, mode: "openai_hosted", provider_type: "docker", instance: { kind: "managed_allocation", allocation_id: allocation.id, device_id: null, connection_generation: null }, lifecycle_state: sleeping ? "sleeping" : "active", status: "observed", reason: null, allocation_created_at: session.created_at, observed_at: now - 2, started_at: session.created_at, memory: sleeping ? null : { usage_bytes: Math.floor((0.4 + rand() * 1.4) * 2 ** 30), limit_bytes: 2 * 2 ** 30 } }; }); // Added after everything else is generated, so the seeded data above does not change. agents.push(...providerAgentDefinitions.map((definition, index) => ({ diff --git a/apps/web/src/features/metrics/SandboxMetricsPage.tsx b/apps/web/src/features/metrics/SandboxMetricsPage.tsx index b0f5160d4..e2af69bff 100644 --- a/apps/web/src/features/metrics/SandboxMetricsPage.tsx +++ b/apps/web/src/features/metrics/SandboxMetricsPage.tsx @@ -23,7 +23,7 @@ import { TableSkeleton } from "../../components/Skeleton"; import { useFailureToast } from "../../components/Toast"; import { ListToolbar, listSummary, NameCell, SearchField } from "../../components/list-ui"; import { useConsoleNavigation } from "../../lib/console-navigation"; -import { formatBytes, formatClock, formatCompact, formatDuration, formatInteger, formatPercent, MISSING } from "../../lib/format"; +import { formatBytes, formatClock, formatCompact, formatDuration, formatInteger, MISSING } from "../../lib/format"; import { projectClient, ProjectName, useProjects } from "../../lib/projects"; import { coreError } from "../../lib/core-error"; import { loadRuntimeDurableSnapshot, RUNTIME_DURABLE_RANGES, RuntimeDurableHistoryIncompleteError, type RuntimeDurableRange } from "../dashboard/runtime-history"; @@ -277,7 +277,6 @@ function RuntimeTable({ rows, showProject, showNode, onOpen }: { rows: HostedRun {t("sandbox.status")}{t("sandbox.statusHelp")} {showProject ? {tCommon("project.column")} : null} {showNode ? {t("sandbox.node")} : null} - {t("sandbox.cpu")} {t("sandbox.memory")} {t("sandbox.uptime")} {t("sandbox.tokens")} @@ -288,7 +287,6 @@ function RuntimeTable({ rows, showProject, showNode, onOpen }: { rows: HostedRun const { observation, session } = row; const observed = observation.status === "observed"; const sampleGap = runtimeSampleGap(observation); - const cpu = observed ? observation.cpu : null; const memory = observed ? observation.memory : null; const open = () => onOpen(observation.session_id); return ( @@ -299,16 +297,7 @@ function RuntimeTable({ rows, showProject, showNode, onOpen }: { rows: HostedRun {showProject ? : null} {showNode ? : null} - {sampleGap ? {t(`sandbox.reason.${sampleGap}`)} : <> - - {cpu?.utilization_ratio != null ? ( - // Providers that report only a utilization ratio: show it as a share of the sandbox's CPU. - - - {formatPercent(cpu.utilization_ratio, locale)} - - ) : {MISSING}} - + {sampleGap ? {t(`sandbox.reason.${sampleGap}`)} : <> {memory?.usage_bytes != null ? ( diff --git a/apps/web/src/features/metrics/sandbox-runtime.test.ts b/apps/web/src/features/metrics/sandbox-runtime.test.ts index c5edb27d6..28ae40263 100644 --- a/apps/web/src/features/metrics/sandbox-runtime.test.ts +++ b/apps/web/src/features/metrics/sandbox-runtime.test.ts @@ -56,10 +56,10 @@ describe("hosted Runtime projections", () => { expect(one.sessions.map((entry) => entry.id)).toEqual(["b"]); }); - it("sums only reported CPU and memory and counts lifecycle states", () => { + it("sums only reported memory and counts lifecycle states", () => { expect(hostedRuntimeUsage(load.observations)).toEqual({ hosted: 3, active: 1, sleeping: 1, pending: 1, observed: 2, - cpuCapacityCores: 2, memoryUsageBytes: 100, memoryLimitBytes: 400, + memoryUsageBytes: 100, memoryLimitBytes: 400, }); }); diff --git a/apps/web/src/features/metrics/sandbox-runtime.ts b/apps/web/src/features/metrics/sandbox-runtime.ts index ce03cb329..768f45716 100644 --- a/apps/web/src/features/metrics/sandbox-runtime.ts +++ b/apps/web/src/features/metrics/sandbox-runtime.ts @@ -9,7 +9,7 @@ import { type OwnedRuntimeObservation } from "../../lib/admin-view"; * Hosted Runtimes across every project: the observations come from the Web * API's deployment-wide list (each labelled with its project); the Sessions * they belong to are read by ID through their project, bounded. Only - * Core-managed hosted sandboxes report CPU and memory. + * Core-managed hosted sandboxes report memory. */ /** Most hosted Sessions read by ID on one load; the rest are listed by ID only. */ @@ -97,9 +97,8 @@ export interface HostedRuntimeUsage { sleeping: number; /** Transitioning or waiting for an allocation. */ pending: number; - /** Runtimes with a current CPU and memory sample. */ + /** Runtimes with a current memory sample. */ observed: number; - cpuCapacityCores: number | null; memoryUsageBytes: number | null; memoryLimitBytes: number | null; } @@ -114,7 +113,6 @@ export function hostedRuntimeUsage(observations: readonly OwnedRuntimeObservatio sleeping: hosted.filter((observation) => observation.lifecycle_state === "sleeping").length, pending: hosted.filter((observation) => observation.lifecycle_state === "pending" || observation.lifecycle_state === "transitioning").length, observed: observed.length, - cpuCapacityCores: sumKnown(observed.map((observation) => observation.cpu?.capacity_cores)), memoryUsageBytes: sumKnown(observed.map((observation) => observation.memory?.usage_bytes)), memoryLimitBytes: sumKnown(observed.map((observation) => observation.memory?.limit_bytes)), }; diff --git a/apps/web/src/features/overview/test-fixtures.ts b/apps/web/src/features/overview/test-fixtures.ts index ba4ce669d..98229483c 100644 --- a/apps/web/src/features/overview/test-fixtures.ts +++ b/apps/web/src/features/overview/test-fixtures.ts @@ -51,7 +51,6 @@ export function hostedObservation(sessionId: string, projectId: string, override environment_id: `env_${sessionId}`, mode: "openai_hosted", provider_type: "docker", instance: { kind: "managed_allocation", allocation_id: `alloc_${sessionId}`, device_id: null, connection_generation: null }, lifecycle_state: "active", status: "observed", reason: null, allocation_created_at: 100, observed_at: 1_000, started_at: 400, - cpu: { usage_seconds_total: 10, capacity_cores: 2, utilization_ratio: null }, memory: { usage_bytes: 100, limit_bytes: 400 }, project_id: projectId, ...overrides, diff --git a/apps/web/src/features/sessions/SessionRuntimeSection.tsx b/apps/web/src/features/sessions/SessionRuntimeSection.tsx index 9799a20ee..0488f5e8e 100644 --- a/apps/web/src/features/sessions/SessionRuntimeSection.tsx +++ b/apps/web/src/features/sessions/SessionRuntimeSection.tsx @@ -6,7 +6,7 @@ import { useTranslation } from "react-i18next"; import { failedLast, useFailureToast } from "../../components/Toast"; import { DashboardSkeleton } from "../../components/Skeleton"; import { Kpi, KpiStrip, Section, SegmentedControl } from "../../components/console-ui"; -import { formatBytes, formatDateTime, formatPercent, formatRelative, MISSING } from "../../lib/format"; +import { formatBytes, formatDateTime, formatRelative, MISSING } from "../../lib/format"; import { coreError } from "../../lib/core-error"; import { RuntimeCharts } from "../metrics/RuntimeCharts"; import { runtimeSampleGap } from "../metrics/sandbox-runtime"; @@ -83,9 +83,7 @@ export function SessionRuntimeSection({ // The same state and sample reason as Sandbox metrics' Hosted Runtimes. const stateLabel = observation?.lifecycle_state ? tMetrics(`sandbox.lifecycle.${observation.lifecycle_state}`) : MISSING; const sampleGap = observation ? runtimeSampleGap(observation) : null; - const cpu = observation?.status === "observed" ? observation.cpu : null; const memory = observation?.status === "observed" ? observation.memory : null; - const cpuValue = cpu?.utilization_ratio != null ? formatPercent(cpu.utilization_ratio, locale) : MISSING; const memoryValue = memory?.usage_bytes != null ? memory.limit_bytes != null ? `${formatBytes(memory.usage_bytes)} / ${formatBytes(memory.limit_bytes)}` : formatBytes(memory.usage_bytes) : MISSING; @@ -107,7 +105,6 @@ export function SessionRuntimeSection({ > - {formatRelative(observedAt, Math.floor(Date.now() / 1000), locale)}} tone={sampleGap ? "warning" : undefined} /> diff --git a/apps/web/src/i18n/locales/en/metrics.ts b/apps/web/src/i18n/locales/en/metrics.ts index 4ada3db8e..66e191a70 100644 --- a/apps/web/src/i18n/locales/en/metrics.ts +++ b/apps/web/src/i18n/locales/en/metrics.ts @@ -211,7 +211,6 @@ export const metrics = { runtimeUnavailable: "Runtime observations are unavailable. {{reason}}", runtimeStale: "Refresh failed; showing the previous observations. {{reason}}", runtimePartial: "Session details were not read for {{unread}} Runtimes (over the read limit) and could not be read for {{failed}}. Those rows show the Session ID only.", - cpu: "CPU", cores: "{{value}} cores", memory: "Memory", disk: "Disk", diff --git a/apps/web/src/i18n/locales/en/sessions.ts b/apps/web/src/i18n/locales/en/sessions.ts index 0f14224dc..2ac68318a 100644 --- a/apps/web/src/i18n/locales/en/sessions.ts +++ b/apps/web/src/i18n/locales/en/sessions.ts @@ -118,9 +118,8 @@ export const sessions = { }, runtime: { title: "Runtime", - help: "CPU and memory of this Session's hosted sandbox as observed by Core, and the samples Core retained for the chosen range.", + help: "Current memory of this Session's hosted sandbox as observed by Core, and the CPU and memory samples Core retained for the chosen range.", state: "State", - cpu: "CPU", memory: "Memory", observed: "Observed", range: "Time range", diff --git a/apps/web/src/i18n/locales/zh-CN/metrics.ts b/apps/web/src/i18n/locales/zh-CN/metrics.ts index 4d9405e28..b17eccf8e 100644 --- a/apps/web/src/i18n/locales/zh-CN/metrics.ts +++ b/apps/web/src/i18n/locales/zh-CN/metrics.ts @@ -211,7 +211,6 @@ export const metrics = { runtimeUnavailable: "Runtime 观测不可用。{{reason}}", runtimeStale: "刷新失败,显示的是上次的观测。{{reason}}", runtimePartial: "有 {{unread}} 个 Runtime 超过读取上限、{{failed}} 个读取失败,未读取 Session 详情,这些行只显示 Session ID。", - cpu: "CPU", cores: "{{value}} 核", memory: "内存", disk: "磁盘", diff --git a/apps/web/src/i18n/locales/zh-CN/sessions.ts b/apps/web/src/i18n/locales/zh-CN/sessions.ts index b7b5c9bea..f8ffc6a85 100644 --- a/apps/web/src/i18n/locales/zh-CN/sessions.ts +++ b/apps/web/src/i18n/locales/zh-CN/sessions.ts @@ -120,9 +120,8 @@ export const sessions = { }, runtime: { title: "Runtime", - help: "Core 观测到的此 Session 托管沙箱的 CPU 和内存,以及所选时间范围内 Core 保留的采样。", + help: "Core 观测到的此 Session 托管沙箱当前内存,以及所选时间范围内 Core 保留的 CPU 和内存采样。", state: "状态", - cpu: "CPU", memory: "内存", observed: "观测时间", range: "时间范围", diff --git a/contracts/agents-api/core.openapi.yaml b/contracts/agents-api/core.openapi.yaml index 967570464..0a95caad4 100644 --- a/contracts/agents-api/core.openapi.yaml +++ b/contracts/agents-api/core.openapi.yaml @@ -66,10 +66,6 @@ definitions: minimum: 0 type: integer x-nullable: true - cpu: - allOf: - - $ref: '#/definitions/v1.RuntimeCPUObservation' - x-nullable: true disk: allOf: - $ref: '#/definitions/api.RuntimeDiskObservation' @@ -143,7 +139,6 @@ definitions: type: string required: - allocation_created_at - - cpu - disk - environment_id - id @@ -2694,25 +2689,6 @@ definitions: required: - type type: object - v1.RuntimeCPUObservation: - properties: - capacity_cores: - minimum: 5e-324 - type: number - x-nullable: true - usage_seconds_total: - minimum: 0 - type: number - x-nullable: true - utilization_ratio: - minimum: 0 - type: number - x-nullable: true - required: - - capacity_cores - - usage_seconds_total - - utilization_ratio - type: object v1.RuntimeHistory: properties: coverage: @@ -3056,10 +3032,6 @@ definitions: minimum: 0 type: integer x-nullable: true - cpu: - allOf: - - $ref: '#/definitions/v1.RuntimeCPUObservation' - x-nullable: true environment_id: format: uuid type: string @@ -3126,7 +3098,6 @@ definitions: type: string required: - allocation_created_at - - cpu - environment_id - id - instance diff --git a/contracts/agents-api/runtime-observability-api.md b/contracts/agents-api/runtime-observability-api.md index 70a22d616..d5066a752 100644 --- a/contracts/agents-api/runtime-observability-api.md +++ b/contracts/agents-api/runtime-observability-api.md @@ -49,11 +49,6 @@ The list has one row for every Session of every Project that is not deleted, inc "resolved_at": 1789953021, "observed_at": 1789953020, "started_at": 1789951220, - "cpu": { - "usage_seconds_total": 482.75, - "capacity_cores": 2.0, - "utilization_ratio": null - }, "memory": { "usage_bytes": 805306368, "limit_bytes": 2147483648 @@ -95,7 +90,6 @@ This returns one `RuntimeObservation`, without `disk`. It accepts no query param | `resolved_at` | integer | Unix seconds when Core resolved this row. | | `observed_at` | integer or null | Unix seconds of the provider sample; null without a sample. | | `started_at` | integer or null | Unix seconds when the current compute incarnation started. | -| `cpu` | object or null | Null when no CPU value was observed. | | `memory` | object or null | Null when no memory value was observed. | `lifecycle_state` comes from Core's allocation records, never from the sample: @@ -117,16 +111,6 @@ This returns one `RuntimeObservation`, without `disk`. It accepts no query param | `device_id` | The Runtime device bound to the managed allocation, when there is one; null otherwise. | | `connection_generation` | Always null: Core does not observe self-hosted connections. | -### `cpu` - -All fields are finite nonnegative numbers or null. Zero is an observed zero; null is unavailable. - -| Field | Meaning | -| --- | --- | -| `usage_seconds_total` | Cumulative CPU seconds of the current compute incarnation (Docker, microsandbox). | -| `capacity_cores` | Configured CPU capacity, greater than zero. | -| `utilization_ratio` | Provider-reported share of `capacity_cores` (E2B), not clamped; null for providers that report cumulative CPU time. | - ### `memory` `usage_bytes` and `limit_bytes` are safe JSON integers or null. Zero usage is observed zero; `limit_bytes` is at least 1, and an unknown or unlimited limit is null. diff --git a/contracts/agents-api/sandbox-deployment.md b/contracts/agents-api/sandbox-deployment.md index 22f6c0740..c9dd555ee 100644 --- a/contracts/agents-api/sandbox-deployment.md +++ b/contracts/agents-api/sandbox-deployment.md @@ -227,7 +227,7 @@ Some fields keep one name across providers but differ in meaning, or do not appl | Node `retained`, `snapshots`, `max_retained` | Suspended sandboxes are `retained` minus `active`; `snapshots` also includes retained snapshots during restore or cleanup | Not applicable | Docker never suspends: `retained` equals `active`, `snapshots` is 0 and `max_retained` equals `max_active` | | Node `host.available_disk_bytes` | Free space on the filesystem of the node state directory; sandbox disks have their own quotas | Not applicable | Free space on the filesystem of the node state directory, not a container's disk | | Allocation `compute_phase`, `compute_phase_changed_at` | Includes `suspended`; the timestamp describes the current phase, not snapshot expiry | Not applicable: no node allocations | Always `disabled`, counted as running until release; the time is the allocation's creation | -| Runtime observation `cpu`, `memory` | From the VM: `cpu.usage_seconds_total`, CPU and memory limits, memory usage | From E2B metrics: `cpu.utilization_ratio` and `capacity_cores`, memory usage and limit; no cumulative CPU time | From Docker stats: `cpu.usage_seconds_total`, CPU and memory limits, memory usage | +| Runtime observation `memory` | From the VM: memory limit and usage | From E2B metrics: memory usage and limit | From Docker stats: memory limit and usage | | Runtime observation `disk` | `null` | E2B `diskUsed` and `diskTotal`; `null` when the template does not report them | `null`: no disk quota | | Runtime observation `lifecycle_state: sleeping` | While suspended | Never | Never | | Runtime history CPU | Derived from cumulative CPU time | Mean of the utilization ratios E2B reported in each bucket | Derived from cumulative CPU time | diff --git a/contracts/agents-api/v1/runtime_observations.go b/contracts/agents-api/v1/runtime_observations.go index cc8c6084d..adefd888c 100644 --- a/contracts/agents-api/v1/runtime_observations.go +++ b/contracts/agents-api/v1/runtime_observations.go @@ -15,7 +15,6 @@ type RuntimeObservation struct { ResolvedAt int64 `json:"resolved_at" binding:"required" minimum:"0"` ObservedAt *int64 `json:"observed_at" extensions:"x-nullable" binding:"required" minimum:"0"` StartedAt *int64 `json:"started_at" extensions:"x-nullable" binding:"required" minimum:"0"` - CPU *RuntimeCPUObservation `json:"cpu" extensions:"x-nullable" binding:"required"` Memory *RuntimeMemoryObservation `json:"memory" extensions:"x-nullable" binding:"required"` } @@ -26,12 +25,6 @@ type RuntimeInstance struct { ConnectionGeneration *string `json:"connection_generation" extensions:"x-nullable" binding:"required" format:"uuid"` } -type RuntimeCPUObservation struct { - UsageSecondsTotal *float64 `json:"usage_seconds_total" extensions:"x-nullable" binding:"required" minimum:"0"` - CapacityCores *float64 `json:"capacity_cores" extensions:"x-nullable" binding:"required" minimum:"5e-324"` - UtilizationRatio *float64 `json:"utilization_ratio" extensions:"x-nullable" binding:"required" minimum:"0"` -} - type RuntimeMemoryObservation struct { UsageBytes *uint64 `json:"usage_bytes" extensions:"x-nullable" binding:"required" minimum:"0"` LimitBytes *uint64 `json:"limit_bytes" extensions:"x-nullable" binding:"required" minimum:"1"` diff --git a/contracts/agents-api/zh/runtime-observability-api.md b/contracts/agents-api/zh/runtime-observability-api.md index 8c45bda02..e03bd4b29 100644 --- a/contracts/agents-api/zh/runtime-observability-api.md +++ b/contracts/agents-api/zh/runtime-observability-api.md @@ -1,7 +1,7 @@ --- title: "Runtime 遥测 API" source: contracts/agents-api/runtime-observability-api.md -source_hash: 17bdd8f0821388294d9454d9b6640a33df850756d34c40d0ee5b1551173fef07 +source_hash: 7dcff114daa6c74d1ee08709f2f68d1b8d97c7225991669fb6431c684e34c26b --- Core 通过 `/core/v1` 下的只读管理员路由报告托管 Runtime 和沙箱节点所使用的信息:当前 Runtime 观测值、单个 Session 的已存储 Runtime 历史记录,以及沙箱节点的主机观测值和历史记录。读取操作绝不创建、唤醒、续期或更改计算资源,也绝不向历史记录添加样本。[Runtime observability](runtime-observability.md) 定义了 Core 如何采集和保留这些值;[Console API usage](../../../docs/zh/web/console-api-usage.md) 列出了读取这些值的 Web 页面。 @@ -51,11 +51,6 @@ Authorization: Bearer "resolved_at": 1789953021, "observed_at": 1789953020, "started_at": 1789951220, - "cpu": { - "usage_seconds_total": 482.75, - "capacity_cores": 2.0, - "utilization_ratio": null - }, "memory": { "usage_bytes": 805306368, "limit_bytes": 2147483648 @@ -97,7 +92,6 @@ Authorization: Bearer | `resolved_at` | integer | Core 解析此行时的 Unix 秒数。 | | `observed_at` | integer 或 null | 提供商样本的 Unix 秒数;无样本时为 null。 | | `started_at` | integer 或 null | 当前计算实例代次启动时的 Unix 秒数。 | -| `cpu` | object 或 null | 未观测到 CPU 值时为 null。 | | `memory` | object 或 null | 未观测到内存值时为 null。 | `lifecycle_state` 来自 Core 的分配记录,绝不来自样本: @@ -119,16 +113,6 @@ Authorization: Bearer | `device_id` | 存在时,为绑定到托管分配的 Runtime 设备;其他情况下为 null。 | | `connection_generation` | 始终为 null:Core 不观测自托管连接。 | -### `cpu` {#cpu} - -所有字段均为有限非负数或 null。0 表示观测到的零;null 表示不可用。 - -| 字段 | 含义 | -| --- | --- | -| `usage_seconds_total` | 当前计算实例代次的累计 CPU 秒数(Docker、microsandbox)。 | -| `capacity_cores` | 已配置的 CPU 容量,大于零。 | -| `utilization_ratio` | 提供商报告的 `capacity_cores` 占比(E2B),不进行钳制;对于报告累计 CPU 时间的提供商为 null。 | - ### `memory` {#memory} `usage_bytes` 和 `limit_bytes` 是 JSON 安全整数或 null。使用量为零表示观测到零;`limit_bytes` 至少为 1,未知或无限制的限额为 null。 diff --git a/contracts/agents-api/zh/sandbox-deployment.md b/contracts/agents-api/zh/sandbox-deployment.md index 8ddf657b2..3487b491e 100644 --- a/contracts/agents-api/zh/sandbox-deployment.md +++ b/contracts/agents-api/zh/sandbox-deployment.md @@ -1,7 +1,7 @@ --- title: "沙箱部署" source: contracts/agents-api/sandbox-deployment.md -source_hash: 673ae35810faa99f2a3fa480f6b487b4b8a3e7a21a47a4b1fa9c22df6bc53845 +source_hash: 279d5e0a8587dd866ff19f4d653fc91ab3f6026cf043bca57af104c34ecfc45c --- 沙箱部署为 Core 管理的 `openai_hosted` 执行选择沙箱提供商和每个沙箱的资源。PostgreSQL 为每个安装维护一个当前有效选择;Web 和 Core API 写入同一配置。节点文件保存其已安装副本和特定于主机的路径,且不能覆盖其资源。Runtime 不属于该选择:每个节点运行其所属安装验证过的 Runtime。该选择独立于 Harness。部署可以保持未配置状态,没有节点;此时它拒绝托管准入。 @@ -232,7 +232,7 @@ Core 用一条规则,从节点主机和单个沙箱的大小推导推荐值。 | 节点 `retained`、`snapshots`、`max_retained` | 已暂停沙箱数为 `retained` 减去 `active`;`snapshots` 还包含恢复或清理期间保留的快照 | 不适用 | Docker 绝不暂停:`retained` 等于 `active`,`snapshots` 为 0,`max_retained` 等于 `max_active` | | 节点 `host.available_disk_bytes` | 节点状态目录所在文件系统的可用空间;沙箱磁盘有自己的配额 | 不适用 | 节点状态目录所在文件系统的可用空间,而不是容器的磁盘 | | 分配 `compute_phase`、`compute_phase_changed_at` | 包含 `suspended`;时间表示当前阶段的起点,不是快照到期时间 | 不适用:没有节点分配 | 始终为 `disabled`,在释放前计为运行中;该时间为分配创建时间 | -| Runtime 观测 `cpu`、`memory` | 来自 VM:`cpu.usage_seconds_total`、CPU 和内存限制、内存使用量 | 来自 E2B 指标:`cpu.utilization_ratio` 和 `capacity_cores`、内存使用量和限制;无累计 CPU 时间 | 来自 Docker stats:`cpu.usage_seconds_total`、CPU 和内存限制、内存使用量 | +| Runtime 观测 `memory` | 来自 VM:内存限制和使用量 | 来自 E2B 指标:内存使用量和限制 | 来自 Docker stats:内存限制和使用量 | | Runtime 观测 `disk` | `null` | E2B `diskUsed` 和 `diskTotal`;模板未报告时为 `null` | `null`:无磁盘配额 | | Runtime 观测 `lifecycle_state: sleeping` | 暂停期间 | 从不 | 从不 | | Runtime 历史 CPU | 根据累计 CPU 时间派生 | 每个时间桶中 E2B 所报告使用率比值的平均值 | 根据累计 CPU 时间派生 | diff --git a/docs/web/index.md b/docs/web/index.md index df93900a9..186bb186e 100644 --- a/docs/web/index.md +++ b/docs/web/index.md @@ -21,7 +21,7 @@ Signing in opens the Overview. While any step is still to do, its **Getting star | Monitor | Overview | Service status, running Sessions, **Sandbox capacity** and work needing attention; 24-hour Session activity; Core and its nodes as a topology, each opening its page; Needs attention: failed Sessions and Sessions waiting for their caller, active in the last 24 hours, each with its one-line reason; usage by Project | | Monitor | Core metrics | Whether Core can run Turns: execution slots, the Turn queue and its p95 wait, interrupted Turns, connected daemons, database latency, CPU and resident memory against their limits, background jobs; Go heap, goroutines and the database pool under **Diagnostics** | | Monitor | Agent metrics | Requests, errors, duration, tokens, models, tools, Agents and API keys over 1 h, 6 h, 24 h or 7 d | -| Monitor | Sandbox metrics | Hosted Runtimes across Projects over time, active against total, each with its status (active, suspended, changing state, waiting for allocation or stopped) and its CPU and memory observations; an E2B deployment adds the sandboxes Core holds in E2B's cloud | +| Monitor | Sandbox metrics | Hosted Runtimes across Projects over time, active against total, each with its status (active, suspended, changing state, waiting for allocation or stopped) and its memory observation and CPU and memory history; an E2B deployment adds the sandboxes Core holds in E2B's cloud | | Monitor | Request log | Recent HTTP requests to Core, their status, elapsed time and safe error codes; [scope and retention](../../contracts/agents-api/admin-api.md#request-log) | | Monitor | Session log | Every Session, and each Session's read-only conversation, trace and Turns with the classified reason of a failure and its next step; a self-hosted Session's page also manages its executor credentials and gives the command that connects a host | | Resources | Agents, Environment templates, Skills, Files, Vaults | Inspection and permitted deletion, with the Project and the creating key of each resource | diff --git a/docs/zh/web/index.md b/docs/zh/web/index.md index 80d3906d0..a1e140659 100644 --- a/docs/zh/web/index.md +++ b/docs/zh/web/index.md @@ -1,7 +1,7 @@ --- title: "Web 控制台" source: docs/web/index.md -source_hash: c28ab8e8f10d5cb7ac6aea42ef4f9a111007a3833bf7949c037b2ce71a50ac19 +source_hash: eacc43b23569a21ee55d858602846ec93626432c81acba0792a94b0e1f0e1848 --- Web 是 OpenAgentCore 安装的管理员控制台:运维人员用 Core 密钥登录,查看健康状态和用量,检查应用创建的内容,并管理项目、密钥、节点和设置。应用使用项目 API 密钥调用 `/v1`,不使用 Web。 @@ -23,7 +23,7 @@ Web 是 OpenAgentCore 安装的管理员控制台:运维人员用 Core 密钥 | 监控 | 概览 | 服务状态、运行中的 Session、**沙箱容量**和待处理工作;24 小时 Session 活动;以拓扑展示 Core 与节点,各自打开对应页面;需要处理:过去 24 小时内活跃的失败或等待调用方的 Session,每行附一行原因;按项目统计的用量 | | 监控 | Core 监控 | Core 能否运行 Turn:执行槽位、Turn 队列及其 p95 等待时间、被中断的 Turn、已连接守护进程、数据库延迟、CPU 与常驻内存及其限制、后台任务;Go 堆、goroutine 和数据库连接池位于 **诊断** | | 监控 | Agent 监控 | 1 小时、6 小时、24 小时或 7 天的请求、错误、耗时、token、模型、工具、Agent 和 API 密钥 | -| 监控 | 沙箱监控 | 各项目托管 Runtime 随时间的变化,显示活跃数量与总数,以及各自的状态(活跃、已挂起、切换中、等待分配或已停止)和 CPU、内存观测;E2B 部署还显示 Core 在 E2B 云中持有的沙箱 | +| 监控 | 沙箱监控 | 各项目托管 Runtime 随时间的变化,显示活跃数量与总数,以及各自的状态(活跃、已挂起、切换中、等待分配或已停止)、内存观测和 CPU、内存历史;E2B 部署还显示 Core 在 E2B 云中持有的沙箱 | | 监控 | 请求日志 | 到达 Core 的近期 HTTP 请求、状态、耗时和安全错误码;[范围及保留方式](../../../contracts/agents-api/zh/admin-api.md#request-log) | | 监控 | Session 日志 | 全部 Session;各 Session 的只读对话、跟踪、Turn、失败分类原因及下一步操作;自托管 Session 页面还管理执行器凭据并提供主机连接命令 | | 资源 | Agent、Environment 模板、Skills、文件、Vault | 检查和允许的删除操作,显示各资源所属项目及创建密钥 | diff --git a/packages/agents-client/src/admin-client.test.ts b/packages/agents-client/src/admin-client.test.ts index c6827ddad..541650322 100644 --- a/packages/agents-client/src/admin-client.test.ts +++ b/packages/agents-client/src/admin-client.test.ts @@ -439,7 +439,6 @@ describe("AdminClient project monitoring", () => { instance: { kind: "managed_allocation", allocation_id: resourceId, device_id: keyId, connection_generation: null }, lifecycle_state: "active", status: "observed", reason: null, allocation_created_at: 10, resolved_at: 30, observed_at: 20, started_at: 10, - cpu: { usage_seconds_total: 0, capacity_cores: 2, utilization_ratio: null }, memory: { usage_bytes: 0, limit_bytes: 1024 }, disk: null, }; @@ -462,7 +461,6 @@ describe("AdminClient project monitoring", () => { instance: { kind: "managed_allocation", allocation_id: resourceId, device_id: keyId, connection_generation: null }, lifecycle_state: "active", status: "observed", reason: null, allocation_created_at: 10, resolved_at: 30, observed_at: 20, started_at: 10, - cpu: { usage_seconds_total: null, capacity_cores: 2, utilization_ratio: 0.1955 }, memory: { usage_bytes: 183836672, limit_bytes: 2079141888 }, disk: { usage_bytes: 1593188352, limit_bytes: 23511863296 }, }; diff --git a/packages/agents-client/src/client.test.ts b/packages/agents-client/src/client.test.ts index f9a1d817d..108f866d9 100644 --- a/packages/agents-client/src/client.test.ts +++ b/packages/agents-client/src/client.test.ts @@ -133,11 +133,6 @@ function runtimeObservation(overrides: Record = {}): Record { await expect(client.retrieveRuntimeObservation(runtimeProjectId, runtimeSessionId)).resolves.toMatchObject({ id: runtimeSessionId, - cpu: { usage_seconds_total: 0 }, memory: { usage_bytes: 0 }, }); expect(String(calls[0]?.input)).toBe( @@ -2756,7 +2750,6 @@ describe("OpenAIAgentsClient", () => { allocation_created_at: null, observed_at: null, started_at: null, - cpu: null, memory: null, }); const client = new AdminClient({ fetch: recordingFetch(jsonResponse(value), []) }); @@ -2769,15 +2762,6 @@ describe("OpenAIAgentsClient", () => { ["invalid status/reason", () => ({ ...runtimeObservation(), status: "observed", reason: "sample_timeout" })], ["invalid lifecycle state", () => ({ ...runtimeObservation(), lifecycle_state: "paused" })], ["invalid mode/instance", () => ({ ...runtimeObservation(), mode: "none" })], - ["negative CPU", () => ({ ...runtimeObservation(), cpu: { - usage_seconds_total: -1, capacity_cores: 2, utilization_ratio: null, - } })], - ["non-numeric CPU", () => ({ ...runtimeObservation(), cpu: { - usage_seconds_total: "NaN", capacity_cores: 2, utilization_ratio: null, - } })], - ["zero CPU capacity", () => ({ ...runtimeObservation(), cpu: { - usage_seconds_total: 1, capacity_cores: 0, utilization_ratio: null, - } })], ["unsafe memory", () => ({ ...runtimeObservation(), memory: { usage_bytes: Number.MAX_SAFE_INTEGER + 1, limit_bytes: 1024, } })], diff --git a/packages/agents-client/src/client.ts b/packages/agents-client/src/client.ts index de493c103..b1984d011 100644 --- a/packages/agents-client/src/client.ts +++ b/packages/agents-client/src/client.ts @@ -24,7 +24,7 @@ import { verbosityResourceValues, } from "./generated/public-api"; import { - runtimeCPUObservationFields, runtimeInstanceFields, runtimeMemoryObservationFields, runtimeObservationFields, + runtimeInstanceFields, runtimeMemoryObservationFields, runtimeObservationFields, runtimeObservationLifecycleStateValues, runtimeObservationModeValues, runtimeObservationReasonValues, runtimeObservationStatusValues, } from "./generated/core-api"; import { projectTokenUsage } from "./usage-projection"; @@ -931,7 +931,7 @@ export function projectRuntimeObservation(value: unknown, expectedSessionId?: st observedAt > value.resolved_at )) || (!observed && ( - observedAt !== null || startedAt !== null || value.cpu !== null || value.memory !== null + observedAt !== null || startedAt !== null || value.memory !== null )) || (value.status === "unsupported" && ( (!isNone && !isSelfHosted) || value.reason !== "runtime_mode_not_observable" @@ -943,22 +943,6 @@ export function projectRuntimeObservation(value: unknown, expectedSessionId?: st (allocationCreatedAt !== null && allocationCreatedAt > value.resolved_at) ) return invalidRuntimeObservation(); - let cpu: RuntimeObservation["cpu"] = null; - if (value.cpu !== null) { - if (!observed || !isRecord(value.cpu) || !exactFields(value.cpu, runtimeCPUObservationFields)) { - return invalidRuntimeObservation(); - } - cpu = { - usage_seconds_total: nullableRuntimeNumber(value.cpu.usage_seconds_total), - capacity_cores: nullableRuntimeNumber(value.cpu.capacity_cores), - utilization_ratio: nullableRuntimeNumber(value.cpu.utilization_ratio), - }; - if ( - Object.values(cpu).every((entry) => entry === null) || - (cpu.capacity_cores !== null && cpu.capacity_cores === 0) - ) return invalidRuntimeObservation(); - } - let memory: RuntimeObservation["memory"] = null; if (value.memory !== null) { if (!observed || !isRecord(value.memory) || !exactFields(value.memory, runtimeMemoryObservationFields)) { @@ -983,7 +967,7 @@ export function projectRuntimeObservation(value: unknown, expectedSessionId?: st status: value.status, reason: value.reason as RuntimeObservation["reason"], lifecycle_state: value.lifecycle_state as RuntimeObservation["lifecycle_state"], allocation_created_at: allocationCreatedAt, resolved_at: value.resolved_at, - observed_at: observedAt, started_at: startedAt, cpu, memory, + observed_at: observedAt, started_at: startedAt, memory, } as RuntimeObservation; } diff --git a/packages/agents-client/src/generated/core-api.ts b/packages/agents-client/src/generated/core-api.ts index 074141c2f..be0433809 100644 --- a/packages/agents-client/src/generated/core-api.ts +++ b/packages/agents-client/src/generated/core-api.ts @@ -26,7 +26,6 @@ export interface AdminRuntimeObservation { export const adminRuntimeObservationFields = ["observation", "project_id"] as const; export interface AdminRuntimeObservationDetail { allocation_created_at: number | null; - cpu: RuntimeCPUObservation | null; disk: RuntimeDiskObservation | null; environment_id: string | null; id: string; @@ -43,7 +42,7 @@ export interface AdminRuntimeObservationDetail { started_at: number | null; status: AdminRuntimeObservationDetailStatus; } -export const adminRuntimeObservationDetailFields = ["allocation_created_at", "cpu", "disk", "environment_id", "id", "instance", "lifecycle_state", "memory", "mode", "object", "observed_at", "provider_type", "reason", "resolved_at", "session_id", "started_at", "status"] as const; +export const adminRuntimeObservationDetailFields = ["allocation_created_at", "disk", "environment_id", "id", "instance", "lifecycle_state", "memory", "mode", "object", "observed_at", "provider_type", "reason", "resolved_at", "session_id", "started_at", "status"] as const; export const adminRuntimeObservationDetailLifecycleStateValues = ["active", "sleeping", "transitioning", "pending", "stopped"] as const; export type AdminRuntimeObservationDetailLifecycleState = (typeof adminRuntimeObservationDetailLifecycleStateValues)[number]; export const adminRuntimeObservationDetailModeValues = ["none", "self_hosted", "openai_hosted"] as const; @@ -674,12 +673,6 @@ export interface RolloutNodes { export const rolloutNodesFields = ["failed", "preparing", "ready", "unknown"] as const; export const rolloutStateValues = ["settled", "preparing"] as const; export type RolloutState = (typeof rolloutStateValues)[number]; -export interface RuntimeCPUObservation { - capacity_cores: number | null; - usage_seconds_total: number | null; - utilization_ratio: number | null; -} -export const runtimeCPUObservationFields = ["capacity_cores", "usage_seconds_total", "utilization_ratio"] as const; export interface RuntimeDiskObservation { limit_bytes: number | null; usage_bytes: number | null; @@ -782,7 +775,6 @@ export interface RuntimeMemoryObservation { export const runtimeMemoryObservationFields = ["limit_bytes", "usage_bytes"] as const; export interface RuntimeObservation { allocation_created_at: number | null; - cpu: RuntimeCPUObservation | null; environment_id: string | null; id: string; instance: RuntimeInstance; @@ -798,7 +790,7 @@ export interface RuntimeObservation { started_at: number | null; status: RuntimeObservationStatus; } -export const runtimeObservationFields = ["allocation_created_at", "cpu", "environment_id", "id", "instance", "lifecycle_state", "memory", "mode", "object", "observed_at", "provider_type", "reason", "resolved_at", "session_id", "started_at", "status"] as const; +export const runtimeObservationFields = ["allocation_created_at", "environment_id", "id", "instance", "lifecycle_state", "memory", "mode", "object", "observed_at", "provider_type", "reason", "resolved_at", "session_id", "started_at", "status"] as const; export const runtimeObservationLifecycleStateValues = ["active", "sleeping", "transitioning", "pending", "stopped"] as const; export type RuntimeObservationLifecycleState = (typeof runtimeObservationLifecycleStateValues)[number]; export const runtimeObservationModeValues = ["none", "self_hosted", "openai_hosted"] as const; diff --git a/packages/agents-client/src/types.ts b/packages/agents-client/src/types.ts index 11f39200f..bf9d10b2e 100644 --- a/packages/agents-client/src/types.ts +++ b/packages/agents-client/src/types.ts @@ -14,7 +14,7 @@ import type { TextParam, TextResource, TokenUsageResource, TurnResource, VaultCredentialResource, VaultResource, VaultStatusParam, } from "./generated/public-api"; import type { - CoreHarness as CoreHarnessResource, ModelConfigurationSupport as ModelConfigurationSupportResource, RuntimeCPUObservation, + CoreHarness as CoreHarnessResource, ModelConfigurationSupport as ModelConfigurationSupportResource, RuntimeMemoryObservation, RuntimeObservationLifecycleState, RuntimeObservationReason, } from "./generated/core-api"; @@ -735,7 +735,7 @@ export interface CreateSessionStreamOptions extends StreamOptions { // Generated /core/v1 types keep their schema names in ./generated/core-api. export type { - HarnessModelConfiguration, RuntimeCPUObservation, RuntimeHistory, RuntimeMemoryObservation, RuntimeObservationReason, SessionExecutionConfiguration, + HarnessModelConfiguration, RuntimeHistory, RuntimeMemoryObservation, RuntimeObservationReason, SessionExecutionConfiguration, } from "./generated/core-api"; export type RuntimeUnavailableReason = Exclude; @@ -764,7 +764,6 @@ export interface RuntimeObservedObservation extends RuntimeObservationBase { allocation_created_at: number | null; observed_at: number; started_at: number | null; - cpu: RuntimeCPUObservation | null; memory: RuntimeMemoryObservation | null; } diff --git a/services/core/internal/api/runtime_observations.go b/services/core/internal/api/runtime_observations.go index 2d8e94606..7af9eadae 100644 --- a/services/core/internal/api/runtime_observations.go +++ b/services/core/internal/api/runtime_observations.go @@ -132,9 +132,6 @@ func runtimeObservationResponse(observation runtimeobs.Observation) (v1.RuntimeO startedAt := observation.Sample.StartedAt.Unix() result.StartedAt = &startedAt } - if observation.Sample.CPUUsageSecondsTotal != nil || observation.Sample.CPUCapacityCores != nil || observation.Sample.CPUUtilizationRatio != nil { - result.CPU = &v1.RuntimeCPUObservation{UsageSecondsTotal: observation.Sample.CPUUsageSecondsTotal, CapacityCores: observation.Sample.CPUCapacityCores, UtilizationRatio: observation.Sample.CPUUtilizationRatio} - } if observation.Sample.MemoryUsageBytes != nil || observation.Sample.MemoryLimitBytes != nil { result.Memory = &v1.RuntimeMemoryObservation{UsageBytes: observation.Sample.MemoryUsageBytes, LimitBytes: observation.Sample.MemoryLimitBytes} } diff --git a/services/core/internal/api/runtime_observations_test.go b/services/core/internal/api/runtime_observations_test.go index b7cf4670f..1ac74c8ee 100644 --- a/services/core/internal/api/runtime_observations_test.go +++ b/services/core/internal/api/runtime_observations_test.go @@ -85,7 +85,7 @@ func TestRuntimeObservationRoutesUseSessionIdentityAndExactNullability(t *testin t.Fatalf("observation returned %d: %s", response.Code, response.Body) } var value v1.RuntimeObservation - if json.Unmarshal(response.Body.Bytes(), &value) != nil || value.ID != sessionID || value.SessionID != sessionID || value.Instance.Kind != "none" || value.EnvironmentID != nil || value.ProviderType != nil || value.ObservedAt != nil || value.CPU != nil || value.Memory != nil || value.Reason == nil || *value.Reason != "runtime_mode_not_observable" { + if json.Unmarshal(response.Body.Bytes(), &value) != nil || value.ID != sessionID || value.SessionID != sessionID || value.Instance.Kind != "none" || value.EnvironmentID != nil || value.ProviderType != nil || value.ObservedAt != nil || value.Memory != nil || value.Reason == nil || *value.Reason != "runtime_mode_not_observable" { t.Fatalf("invalid unsupported observation: %s", response.Body) } } @@ -104,16 +104,15 @@ func TestRuntimeObservationRoutesRejectQueries(t *testing.T) { } func TestRuntimeObservationResponsePreservesObservedZero(t *testing.T) { - zeroCPU := float64(0) zeroMemory := uint64(0) now := time.Date(2026, 9, 22, 8, 0, 0, 0, time.UTC) sessionID, environmentID := uuid.NewString(), uuid.NewString() value, err := runtimeObservationResponse(runtimeobs.Observation{ Target: runtimeobs.Target{SessionID: sessionID, EnvironmentID: environmentID, Mode: runtimeobs.ModeManaged, Instance: runtimeobs.Instance{AllocationID: uuid.NewString(), DeviceID: uuid.NewString(), AllocationState: "running", ComputePhase: "running", AllocationCreatedAt: now.Add(-time.Hour)}}, Status: runtimeobs.StatusObserved, ProviderType: "docker", ResolvedAt: now, - Sample: &runtimeobs.Sample{ObservedAt: now, CPUUsageSecondsTotal: &zeroCPU, MemoryUsageBytes: &zeroMemory}, + Sample: &runtimeobs.Sample{ObservedAt: now, MemoryUsageBytes: &zeroMemory}, }) - if err != nil || value.LifecycleState == nil || *value.LifecycleState != "active" || value.CPU == nil || value.CPU.UsageSecondsTotal == nil || *value.CPU.UsageSecondsTotal != 0 || value.Memory == nil || value.Memory.UsageBytes == nil || *value.Memory.UsageBytes != 0 { + if err != nil || value.LifecycleState == nil || *value.LifecycleState != "active" || value.Memory == nil || value.Memory.UsageBytes == nil || *value.Memory.UsageBytes != 0 { t.Fatalf("observed zero was lost: %+v %v", value, err) } } @@ -170,15 +169,14 @@ func TestRuntimeObservationResponseRejectsTimesOutsidePublicContract(t *testing. func timePointer(value time.Time) *time.Time { return &value } -func TestAdminRuntimeObservationProjectsReportedUtilizationAndDisk(t *testing.T) { +func TestAdminRuntimeObservationProjectsDisk(t *testing.T) { now := time.Date(2026, 9, 25, 10, 31, 37, 0, time.UTC) - ratio, cores := .1955, 2.0 memoryUsed, memoryTotal, diskUsed, diskTotal := uint64(183836672), uint64(2079141888), uint64(1593188352), uint64(23511863296) observation := runtimeobs.Observation{ Target: runtimeobs.Target{SessionID: uuid.NewString(), EnvironmentID: uuid.NewString(), Mode: runtimeobs.ModeManaged, Instance: runtimeobs.Instance{AllocationID: uuid.NewString(), AllocationState: "running", ComputePhase: "disabled", AllocationCreatedAt: now.Add(-time.Minute)}}, Status: runtimeobs.StatusObserved, ProviderType: "e2b", ResolvedAt: now, - Sample: &runtimeobs.Sample{ObservedAt: now, StartedAt: timePointer(now.Add(-time.Minute)), CPUUtilizationRatio: &ratio, CPUCapacityCores: &cores, + Sample: &runtimeobs.Sample{ObservedAt: now, StartedAt: timePointer(now.Add(-time.Minute)), MemoryUsageBytes: &memoryUsed, MemoryLimitBytes: &memoryTotal, DiskUsageBytes: &diskUsed, DiskLimitBytes: &diskTotal}, } value, err := adminRuntimeObservationResponse(observation) @@ -187,7 +185,6 @@ func TestAdminRuntimeObservationProjectsReportedUtilizationAndDisk(t *testing.T) } raw, _ := json.Marshal(value) for _, want := range []string{ - `"cpu":{"usage_seconds_total":null,"capacity_cores":2,"utilization_ratio":0.1955}`, `"memory":{"usage_bytes":183836672,"limit_bytes":2079141888}`, `"disk":{"usage_bytes":1593188352,"limit_bytes":23511863296}`, } { From 7f776358be60079fd1fd11a1b326e0562e4c8142 Mon Sep 17 00:00:00 2001 From: SaladDay <1203511142@qq.com> Date: Sat, 24 Oct 2026 02:51:22 +0000 Subject: [PATCH 2/2] Drop the observation CPU from the sandbox runtime fixtures --- apps/web/src/features/metrics/sandbox-runtime.test.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/web/src/features/metrics/sandbox-runtime.test.ts b/apps/web/src/features/metrics/sandbox-runtime.test.ts index 28ae40263..b73c657a4 100644 --- a/apps/web/src/features/metrics/sandbox-runtime.test.ts +++ b/apps/web/src/features/metrics/sandbox-runtime.test.ts @@ -5,7 +5,7 @@ import type { SandboxAllocation } from "@oac/agents-client"; import { hostedObservation, node, session } from "../overview/test-fixtures"; import { hostedRuntimeRows, hostedRuntimeUsage, loadHostedRuntimes, matchesRuntime, runtimeSampleGap, runtimeSnapshot } from "./sandbox-runtime"; -const none = { ...hostedObservation("plain", "p1"), mode: "none", instance: { kind: "none", allocation_id: null, device_id: null, connection_generation: null }, lifecycle_state: null, status: "unsupported", reason: "runtime_mode_not_observable", cpu: null, memory: null } as unknown as ReturnType; +const none = { ...hostedObservation("plain", "p1"), mode: "none", instance: { kind: "none", allocation_id: null, device_id: null, connection_generation: null }, lifecycle_state: null, status: "unsupported", reason: "runtime_mode_not_observable", memory: null } as unknown as ReturnType; describe("loadHostedRuntimes", () => { it("keeps hosted observations, reads their Sessions through their project and bounds the reads", async () => { @@ -37,8 +37,8 @@ describe("hosted Runtime projections", () => { const load = { observations: [ hostedObservation("a", "p1"), - hostedObservation("b", "p2", { lifecycle_state: "sleeping", status: "observed", cpu: null, memory: null }), - hostedObservation("c", "p2", { lifecycle_state: "pending", status: "unavailable", reason: "allocation_pending", cpu: null, memory: null, observed_at: null }), + hostedObservation("b", "p2", { lifecycle_state: "sleeping", status: "observed", memory: null }), + hostedObservation("c", "p2", { lifecycle_state: "pending", status: "unavailable", reason: "allocation_pending", memory: null, observed_at: null }), ], sessions: new Map([["a", session("a", { metadata: { title: "Nightly build" } })], ["b", session("b")]]), unread: 0, @@ -77,7 +77,7 @@ describe("hosted Runtime projections", () => { }); it("names a missing sample only for a sandbox Core records as active", () => { - const missing = { status: "unavailable", observed_at: null, started_at: null, cpu: null, memory: null } as const; + const missing = { status: "unavailable", observed_at: null, started_at: null, memory: null } as const; expect(runtimeSampleGap(hostedObservation("a", "p", { ...missing, reason: "sample_timeout" }))).toBe("sample_timeout"); expect(runtimeSampleGap(hostedObservation("b", "p", { ...missing, lifecycle_state: "sleeping", reason: "runtime_not_running" }))).toBeNull(); expect(runtimeSampleGap(hostedObservation("c", "p"))).toBeNull();