Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion apps/web/e2e/data/routes.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -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) => ({
Expand Down
10 changes: 2 additions & 8 deletions apps/web/src/features/metrics/SandboxMetricsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -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),
})}
</span>
Expand Down Expand Up @@ -318,12 +317,7 @@ function RuntimeTable({ rows, showProject, showNode, onOpen }: { rows: HostedRun
{showNode ? <td><NodeLink node={row.node} /></td> : null}
{sampleGap ? <td colSpan={2} className="table-muted">{t(`sandbox.reason.${sampleGap}`)}</td> : <>
<td>
{cpu?.usage_cores != null ? (
<span className="table-meter">
<Meter value={cpu.usage_cores} limit={cpu.capacity_cores} label={t("sandbox.cpu")} />
<span>{formatCores(cpu.usage_cores, locale)} / {t("sandbox.cores", { value: formatCores(cpu.capacity_cores, locale) })}</span>
</span>
) : 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.
<span className="table-meter">
<Meter value={cpu.utilization_ratio} limit={1} label={t("sandbox.cpu")} />
Expand Down
3 changes: 1 addition & 2 deletions apps/web/src/features/metrics/sandbox-runtime.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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", () => {
Expand Down
2 changes: 0 additions & 2 deletions apps/web/src/features/metrics/sandbox-runtime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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)),
Expand Down
2 changes: 1 addition & 1 deletion apps/web/src/features/overview/test-fixtures.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
6 changes: 2 additions & 4 deletions apps/web/src/features/sessions/SessionRuntimeSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -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;
Expand Down
4 changes: 2 additions & 2 deletions apps/web/src/i18n/locales/en/metrics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
1 change: 0 additions & 1 deletion apps/web/src/i18n/locales/en/sessions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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}}",
Expand Down
4 changes: 2 additions & 2 deletions apps/web/src/i18n/locales/zh-CN/metrics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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: "未运行",
Expand Down
1 change: 0 additions & 1 deletion apps/web/src/i18n/locales/zh-CN/sessions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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}}",
Expand Down
5 changes: 0 additions & 5 deletions apps/web/src/lib/format.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
5 changes: 0 additions & 5 deletions contracts/agents-api/core.openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -2672,7 +2668,6 @@ definitions:
x-nullable: true
required:
- capacity_cores
- usage_cores
- usage_seconds_total
- utilization_ratio
type: object
Expand Down
2 changes: 0 additions & 2 deletions contracts/agents-api/runtime-observability-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -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": {
Expand Down Expand Up @@ -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`
Expand Down
1 change: 0 additions & 1 deletion contracts/agents-api/v1/runtime_observations.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"`
}

Expand Down
4 changes: 1 addition & 3 deletions contracts/agents-api/zh/runtime-observability-api.md
Original file line number Diff line number Diff line change
@@ -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 页面。
Expand Down Expand Up @@ -54,7 +54,6 @@ Authorization: Bearer <Core key>
"cpu": {
"usage_seconds_total": 482.75,
"capacity_cores": 2.0,
"usage_cores": null,
"utilization_ratio": null
},
"memory": {
Expand Down Expand Up @@ -128,7 +127,6 @@ Authorization: Bearer <Core key>
| --- | --- |
| `usage_seconds_total` | 当前计算实例代次的累计 CPU 秒数(Docker、microsandbox)。 |
| `capacity_cores` | 已配置的 CPU 容量,大于零。 |
| `usage_cores` | 始终为 null。 |
| `utilization_ratio` | 提供商报告的 `capacity_cores` 占比(E2B),不进行钳制;对于报告累计 CPU 时间的提供商为 null。 |

### `memory` {#memory}
Expand Down
4 changes: 2 additions & 2 deletions packages/agents-client/src/admin-client.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};
Expand All @@ -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 },
};
Expand Down
8 changes: 4 additions & 4 deletions packages/agents-client/src/client.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ function runtimeObservation(overrides: Record<string, unknown> = {}): Record<str
cpu: {
usage_seconds_total: 0,
capacity_cores: 2,
usage_cores: null,

utilization_ratio: null,
},
memory: { usage_bytes: 0, limit_bytes: 1024 },
Expand Down Expand Up @@ -2771,13 +2771,13 @@ describe("OpenAIAgentsClient", () => {
["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,
Expand Down
1 change: 0 additions & 1 deletion packages/agents-client/src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
Expand Down
3 changes: 1 addition & 2 deletions packages/agents-client/src/generated/core-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion services/core/internal/api/runtime_observations_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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}`,
} {
Expand Down
Loading