From 70d90e85eb0e43c9dbd5446aa07243d4035a98ad Mon Sep 17 00:00:00 2001 From: Lyon <88232613+pikasTech@users.noreply.github.com> Date: Sun, 21 Jun 2026 12:20:25 +0800 Subject: [PATCH] fix: emit workbench sse accepted span (#1816) --- internal/cloud/server-workbench-http.ts | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/internal/cloud/server-workbench-http.ts b/internal/cloud/server-workbench-http.ts index a46f6f48..9ad3a408 100644 --- a/internal/cloud/server-workbench-http.ts +++ b/internal/cloud/server-workbench-http.ts @@ -15,6 +15,7 @@ import { } from "./server-http-utils.ts"; import { createWorkbenchReadModel } from "./workbench-read-model.ts"; import { createWorkbenchTurnProjection, createWorkbenchTurnTimingProjection, durableTraceStatus, RUNNING_STATUSES, TERMINAL_STATUSES } from "./workbench-turn-projection.ts"; +import { emitHttpServerRequestSpan } from "./otel-trace.ts"; const DEFAULT_PAGE_LIMIT = 50; const DEFAULT_SESSION_LIST_LIMIT = 20; @@ -131,6 +132,7 @@ export async function handleWorkbenchRealtimeHttp(request, response, url, option heartbeatMs, outboxMode: typeof (options.runtimeStore ?? null)?.readWorkbenchProjectionOutbox === "function" }); + emitWorkbenchRealtimeAcceptedOtelSpan(request, options.env); if (activeTraceId) { await (perf ? perf.measure("workbench_initial_trace", () => writeTraceRealtimeSnapshot({ writeEvent, options, actor: auth.actor, traceId: activeTraceId, reason: "initial" })) : writeTraceRealtimeSnapshot({ writeEvent, options, actor: auth.actor, traceId: activeTraceId, reason: "initial" })); const runtimeStore = options.runtimeStore ?? null; @@ -245,6 +247,25 @@ function attachWorkbenchRealtimeOtelContext(request, fields = {}) { }; } +function emitWorkbenchRealtimeAcceptedOtelSpan(request, env = process.env) { + const context = request?.hwlabHttpRequestContext; + if (!context?.traceId) return; + const endedAtMs = Date.now(); + void emitHttpServerRequestSpan(context, env, { + startTimeMs: context.startedAtMs, + endTimeMs: endedAtMs, + statusCode: 200, + method: "GET", + route: "/v1/workbench/events", + attributes: { + ...(context.otelAttributes ?? {}), + "http.closed_by": "sse-accepted", + "workbench.sse.lifecycle": "accepted", + "workbench.sse.accept_latency_ms": Math.max(0, endedAtMs - Number(context.startedAtMs ?? endedAtMs)) + } + }).catch(() => undefined); +} + function realtimeEventCreatedAt(payload) { const candidates = [payload?.event?.createdAt, payload?.snapshot?.lastEvent?.createdAt, payload?.turn?.updatedAt]; for (const value of candidates) {