diff --git a/apps/web/e2e/data/routes.mjs b/apps/web/e2e/data/routes.mjs index c0806d2f8..8f585d2a8 100644 --- a/apps/web/e2e/data/routes.mjs +++ b/apps/web/e2e/data/routes.mjs @@ -104,7 +104,7 @@ export function buildDemo(now = Math.floor(Date.now() / 1000), publicUrl = "http 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 }; 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, usage_cores: Number((rand() * 1.6).toFixed(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, 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 } }; }); // 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 332dcbdf1..b413064a1 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, formatCores, formatDuration, formatInteger, formatPercent, MISSING } from "../../lib/format"; +import { formatBytes, formatClock, formatCompact, formatDuration, formatInteger, formatPercent, 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"; @@ -241,7 +241,6 @@ function HostedRuntimeSection({ state, stale, fleet, range, onOpen }: { state: R active: formatInteger(usage.active, locale), n: formatInteger(usage.hosted, locale), sleeping: formatInteger(usage.sleeping, locale), - cpu: usage.cpuUsageCores === null ? MISSING : t("sandbox.cores", { value: formatCores(usage.cpuUsageCores, locale) }), memory: usage.memoryUsageBytes === null ? MISSING : formatBytes(usage.memoryUsageBytes), })} @@ -318,12 +317,7 @@ function RuntimeTable({ rows, showProject, showNode, onOpen }: { rows: HostedRun {showNode ? : null} {sampleGap ? {t(`sandbox.reason.${sampleGap}`)} : <> - {cpu?.usage_cores != null ? ( - - - {formatCores(cpu.usage_cores, locale)} / {t("sandbox.cores", { value: formatCores(cpu.capacity_cores, locale) })} - - ) : cpu?.utilization_ratio != null ? ( + {cpu?.utilization_ratio != null ? ( // Providers that report only a utilization ratio: show it as a share of the sandbox's CPU. diff --git a/apps/web/src/features/metrics/sandbox-runtime.test.ts b/apps/web/src/features/metrics/sandbox-runtime.test.ts index 876da9605..c5edb27d6 100644 --- a/apps/web/src/features/metrics/sandbox-runtime.test.ts +++ b/apps/web/src/features/metrics/sandbox-runtime.test.ts @@ -59,9 +59,8 @@ describe("hosted Runtime projections", () => { it("sums only reported CPU and memory and counts lifecycle states", () => { expect(hostedRuntimeUsage(load.observations)).toEqual({ hosted: 3, active: 1, sleeping: 1, pending: 1, observed: 2, - cpuUsageCores: 0.5, cpuCapacityCores: 2, memoryUsageBytes: 100, memoryLimitBytes: 400, + cpuCapacityCores: 2, memoryUsageBytes: 100, memoryLimitBytes: 400, }); - expect(hostedRuntimeUsage([load.observations[1]!]).cpuUsageCores).toBeNull(); }); it("places each Runtime on its host and derives uptime only from a current sample", () => { diff --git a/apps/web/src/features/metrics/sandbox-runtime.ts b/apps/web/src/features/metrics/sandbox-runtime.ts index 5fff4518e..ce03cb329 100644 --- a/apps/web/src/features/metrics/sandbox-runtime.ts +++ b/apps/web/src/features/metrics/sandbox-runtime.ts @@ -99,7 +99,6 @@ export interface HostedRuntimeUsage { pending: number; /** Runtimes with a current CPU and memory sample. */ observed: number; - cpuUsageCores: number | null; cpuCapacityCores: number | null; memoryUsageBytes: number | null; memoryLimitBytes: number | null; @@ -115,7 +114,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, - cpuUsageCores: sumKnown(observed.map((observation) => observation.cpu?.usage_cores)), 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 135f60cae..ba4ce669d 100644 --- a/apps/web/src/features/overview/test-fixtures.ts +++ b/apps/web/src/features/overview/test-fixtures.ts @@ -51,7 +51,7 @@ 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, usage_cores: 0.5, utilization_ratio: null }, + 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 ba871fce9..9799a20ee 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, formatCores, formatDateTime, formatPercent, formatRelative, MISSING } from "../../lib/format"; +import { formatBytes, formatDateTime, formatPercent, formatRelative, MISSING } from "../../lib/format"; import { coreError } from "../../lib/core-error"; import { RuntimeCharts } from "../metrics/RuntimeCharts"; import { runtimeSampleGap } from "../metrics/sandbox-runtime"; @@ -85,9 +85,7 @@ export function SessionRuntimeSection({ 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?.usage_cores != null && cpu.capacity_cores != null - ? t("runtime.cores", { used: formatCores(cpu.usage_cores, locale), capacity: formatCores(cpu.capacity_cores, locale) }) - : cpu?.utilization_ratio != null ? formatPercent(cpu.utilization_ratio, locale) : MISSING; + 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; diff --git a/apps/web/src/i18n/locales/en/metrics.ts b/apps/web/src/i18n/locales/en/metrics.ts index 5abbf61aa..059054738 100644 --- a/apps/web/src/i18n/locales/en/metrics.ts +++ b/apps/web/src/i18n/locales/en/metrics.ts @@ -247,8 +247,8 @@ export const metrics = { historyBudget: "Charts cover at most {{limit}} Sessions.", historyMixed: "Sessions returned different bucket widths.", }, - runtimeMeta: "{{active}} of {{n}} active · CPU {{cpu}} · memory {{memory}}", - runtimeMetaSuspended: "{{active}} of {{n}} active · {{sleeping}} suspended · CPU {{cpu}} · memory {{memory}}", + runtimeMeta: "{{active}} of {{n}} active · memory {{memory}}", + runtimeMetaSuspended: "{{active}} of {{n}} active · {{sleeping}} suspended · memory {{memory}}", reason: { allocation_pending: "Allocation pending", runtime_not_running: "Not running", diff --git a/apps/web/src/i18n/locales/en/sessions.ts b/apps/web/src/i18n/locales/en/sessions.ts index 3384645fe..0f14224dc 100644 --- a/apps/web/src/i18n/locales/en/sessions.ts +++ b/apps/web/src/i18n/locales/en/sessions.ts @@ -125,7 +125,6 @@ export const sessions = { observed: "Observed", range: "Time range", ranges: { "1h": "1h", "6h": "6h", "24h": "24h" }, - cores: "{{used}} of {{capacity}} cores", observationFailed: "The Runtime observation could not be loaded: {{reason}}", historyUnavailable: "Core keeps no Runtime history for this Session.", historyFailed: "Runtime history could not be loaded: {{reason}}", diff --git a/apps/web/src/i18n/locales/zh-CN/metrics.ts b/apps/web/src/i18n/locales/zh-CN/metrics.ts index 6c37faea6..c9fc0fe28 100644 --- a/apps/web/src/i18n/locales/zh-CN/metrics.ts +++ b/apps/web/src/i18n/locales/zh-CN/metrics.ts @@ -247,8 +247,8 @@ export const metrics = { historyBudget: "图表最多覆盖 {{limit}} 个 Session。", historyMixed: "各 Session 返回的时间桶宽度不一致。", }, - runtimeMeta: "{{n}} 个中 {{active}} 个活跃 · CPU {{cpu}} · 内存 {{memory}}", - runtimeMetaSuspended: "{{n}} 个中 {{active}} 个活跃 · {{sleeping}} 个挂起 · CPU {{cpu}} · 内存 {{memory}}", + runtimeMeta: "{{n}} 个中 {{active}} 个活跃 · 内存 {{memory}}", + runtimeMetaSuspended: "{{n}} 个中 {{active}} 个活跃 · {{sleeping}} 个挂起 · 内存 {{memory}}", reason: { allocation_pending: "等待分配", runtime_not_running: "未运行", diff --git a/apps/web/src/i18n/locales/zh-CN/sessions.ts b/apps/web/src/i18n/locales/zh-CN/sessions.ts index cdec25270..b7b5c9bea 100644 --- a/apps/web/src/i18n/locales/zh-CN/sessions.ts +++ b/apps/web/src/i18n/locales/zh-CN/sessions.ts @@ -127,7 +127,6 @@ export const sessions = { observed: "观测时间", range: "时间范围", ranges: { "1h": "1 小时", "6h": "6 小时", "24h": "24 小时" }, - cores: "{{used}} / {{capacity}} 核", observationFailed: "无法加载 Runtime 观测:{{reason}}", historyUnavailable: "Core 没有保留此 Session 的 Runtime 历史。", historyFailed: "无法加载 Runtime 历史:{{reason}}", diff --git a/apps/web/src/lib/format.ts b/apps/web/src/lib/format.ts index b9e28984f..3a98a88f0 100644 --- a/apps/web/src/lib/format.ts +++ b/apps/web/src/lib/format.ts @@ -51,11 +51,6 @@ export function formatDuration(seconds: number | null | undefined): string { return `${Math.floor(hours / 24)}d ${hours % 24}h`; } -export function formatCores(value: number | null | undefined, locale?: string): string { - if (value === null || value === undefined || !Number.isFinite(value)) return MISSING; - return value.toLocaleString(locale, { maximumFractionDigits: value < 10 ? 2 : 1 }); -} - /** Unix seconds of an RFC 3339 timestamp, for the formatters below; null when absent or unparseable. */ export function epochSeconds(value: string | null | undefined): number | null { if (!value) return null; diff --git a/contracts/agents-api/core.openapi.yaml b/contracts/agents-api/core.openapi.yaml index 182963b6d..ac053cee4 100644 --- a/contracts/agents-api/core.openapi.yaml +++ b/contracts/agents-api/core.openapi.yaml @@ -2658,10 +2658,6 @@ definitions: minimum: 5e-324 type: number x-nullable: true - usage_cores: - minimum: 0 - type: number - x-nullable: true usage_seconds_total: minimum: 0 type: number @@ -2672,7 +2668,6 @@ definitions: x-nullable: true required: - capacity_cores - - usage_cores - usage_seconds_total - utilization_ratio type: object diff --git a/contracts/agents-api/runtime-observability-api.md b/contracts/agents-api/runtime-observability-api.md index 1cfab9864..70a22d616 100644 --- a/contracts/agents-api/runtime-observability-api.md +++ b/contracts/agents-api/runtime-observability-api.md @@ -52,7 +52,6 @@ The list has one row for every Session of every Project that is not deleted, inc "cpu": { "usage_seconds_total": 482.75, "capacity_cores": 2.0, - "usage_cores": null, "utilization_ratio": null }, "memory": { @@ -126,7 +125,6 @@ All fields are finite nonnegative numbers or null. Zero is an observed zero; nul | --- | --- | | `usage_seconds_total` | Cumulative CPU seconds of the current compute incarnation (Docker, microsandbox). | | `capacity_cores` | Configured CPU capacity, greater than zero. | -| `usage_cores` | Always null. | | `utilization_ratio` | Provider-reported share of `capacity_cores` (E2B), not clamped; null for providers that report cumulative CPU time. | ### `memory` diff --git a/contracts/agents-api/v1/runtime_observations.go b/contracts/agents-api/v1/runtime_observations.go index 8e9fef69e..cc8c6084d 100644 --- a/contracts/agents-api/v1/runtime_observations.go +++ b/contracts/agents-api/v1/runtime_observations.go @@ -29,7 +29,6 @@ type RuntimeInstance struct { 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"` - UsageCores *float64 `json:"usage_cores" extensions:"x-nullable" binding:"required" minimum:"0"` UtilizationRatio *float64 `json:"utilization_ratio" extensions:"x-nullable" binding:"required" minimum:"0"` } diff --git a/contracts/agents-api/zh/runtime-observability-api.md b/contracts/agents-api/zh/runtime-observability-api.md index 23040c051..8c45bda02 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: 4a9c00d1eed9fe51f9e8f2f08e00f1709ddc55eb1c650ed3a5380f44a5b71e1c +source_hash: 17bdd8f0821388294d9454d9b6640a33df850756d34c40d0ee5b1551173fef07 --- Core 通过 `/core/v1` 下的只读管理员路由报告托管 Runtime 和沙箱节点所使用的信息:当前 Runtime 观测值、单个 Session 的已存储 Runtime 历史记录,以及沙箱节点的主机观测值和历史记录。读取操作绝不创建、唤醒、续期或更改计算资源,也绝不向历史记录添加样本。[Runtime observability](runtime-observability.md) 定义了 Core 如何采集和保留这些值;[Console API usage](../../../docs/zh/web/console-api-usage.md) 列出了读取这些值的 Web 页面。 @@ -54,7 +54,6 @@ Authorization: Bearer "cpu": { "usage_seconds_total": 482.75, "capacity_cores": 2.0, - "usage_cores": null, "utilization_ratio": null }, "memory": { @@ -128,7 +127,6 @@ Authorization: Bearer | --- | --- | | `usage_seconds_total` | 当前计算实例代次的累计 CPU 秒数(Docker、microsandbox)。 | | `capacity_cores` | 已配置的 CPU 容量,大于零。 | -| `usage_cores` | 始终为 null。 | | `utilization_ratio` | 提供商报告的 `capacity_cores` 占比(E2B),不进行钳制;对于报告累计 CPU 时间的提供商为 null。 | ### `memory` {#memory} diff --git a/packages/agents-client/src/admin-client.test.ts b/packages/agents-client/src/admin-client.test.ts index b1205cbd6..c6827ddad 100644 --- a/packages/agents-client/src/admin-client.test.ts +++ b/packages/agents-client/src/admin-client.test.ts @@ -439,7 +439,7 @@ 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, usage_cores: null, utilization_ratio: null }, + cpu: { usage_seconds_total: 0, capacity_cores: 2, utilization_ratio: null }, memory: { usage_bytes: 0, limit_bytes: 1024 }, disk: null, }; @@ -462,7 +462,7 @@ 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, usage_cores: null, utilization_ratio: 0.1955 }, + 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 621ce525a..a14312838 100644 --- a/packages/agents-client/src/client.test.ts +++ b/packages/agents-client/src/client.test.ts @@ -136,7 +136,7 @@ function runtimeObservation(overrides: Record = {}): Record { ["invalid lifecycle state", () => ({ ...runtimeObservation(), lifecycle_state: "paused" })], ["invalid mode/instance", () => ({ ...runtimeObservation(), mode: "none" })], ["negative CPU", () => ({ ...runtimeObservation(), cpu: { - usage_seconds_total: -1, capacity_cores: 2, usage_cores: null, utilization_ratio: null, + usage_seconds_total: -1, capacity_cores: 2, utilization_ratio: null, } })], ["non-numeric CPU", () => ({ ...runtimeObservation(), cpu: { - usage_seconds_total: "NaN", capacity_cores: 2, usage_cores: null, utilization_ratio: null, + usage_seconds_total: "NaN", capacity_cores: 2, utilization_ratio: null, } })], ["zero CPU capacity", () => ({ ...runtimeObservation(), cpu: { - usage_seconds_total: 1, capacity_cores: 0, usage_cores: null, utilization_ratio: null, + 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 a25b4743c..de493c103 100644 --- a/packages/agents-client/src/client.ts +++ b/packages/agents-client/src/client.ts @@ -951,7 +951,6 @@ export function projectRuntimeObservation(value: unknown, expectedSessionId?: st cpu = { usage_seconds_total: nullableRuntimeNumber(value.cpu.usage_seconds_total), capacity_cores: nullableRuntimeNumber(value.cpu.capacity_cores), - usage_cores: nullableRuntimeNumber(value.cpu.usage_cores), utilization_ratio: nullableRuntimeNumber(value.cpu.utilization_ratio), }; if ( diff --git a/packages/agents-client/src/generated/core-api.ts b/packages/agents-client/src/generated/core-api.ts index f8d1b5c52..0db806dad 100644 --- a/packages/agents-client/src/generated/core-api.ts +++ b/packages/agents-client/src/generated/core-api.ts @@ -657,11 +657,10 @@ export const rolloutStateValues = ["settled", "preparing"] as const; export type RolloutState = (typeof rolloutStateValues)[number]; export interface RuntimeCPUObservation { capacity_cores: number | null; - usage_cores: number | null; usage_seconds_total: number | null; utilization_ratio: number | null; } -export const runtimeCPUObservationFields = ["capacity_cores", "usage_cores", "usage_seconds_total", "utilization_ratio"] as const; +export const runtimeCPUObservationFields = ["capacity_cores", "usage_seconds_total", "utilization_ratio"] as const; export interface RuntimeDiskObservation { limit_bytes: number | null; usage_bytes: number | null; diff --git a/services/core/internal/api/runtime_observations_test.go b/services/core/internal/api/runtime_observations_test.go index 227db91ac..b7cf4670f 100644 --- a/services/core/internal/api/runtime_observations_test.go +++ b/services/core/internal/api/runtime_observations_test.go @@ -187,7 +187,7 @@ func TestAdminRuntimeObservationProjectsReportedUtilizationAndDisk(t *testing.T) } raw, _ := json.Marshal(value) for _, want := range []string{ - `"cpu":{"usage_seconds_total":null,"capacity_cores":2,"usage_cores":null,"utilization_ratio":0.1955}`, + `"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}`, } {