@dan-ai-studio/dshopencodego 0.1.77 → 0.1.79
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +3 -3
- package/README.md +3 -3
- package/lib/client.js +35 -10
- package/lib/index.js +57 -20
- package/lib/types/request-log/store.d.ts +34 -13
- package/package.json +1 -1
package/README.en.md
CHANGED
|
@@ -144,15 +144,15 @@ The API key comes from the Harness credential store (reference name `OPENCODE_GO
|
|
|
144
144
|
- **Two entries, one page**:
|
|
145
145
|
- **A sidebar icon → a full page in the main column**: every request across sessions. The log is a process-wide thing, so the sidebar is where it belongs — a fixed, always-visible spot, and the same pattern the Harness's own plugin manager uses.
|
|
146
146
|
- **A "Request log" View inside the conversation**: this session's requests, in place — answering "why was that turn slow" without leaving the chat.
|
|
147
|
-
- It is a page, not a floating card: the head, the summary (requests / failures / mean latency / mean first token / mean tok/s / mean cache hit / connection reuse) and the filters stay put while the table scrolls. Each row carries time, model, session, input/output/cache, **cache hit**, **tok/s**, first token, latency and status; clicking one expands its HTTP detail (method, protocol, URL, body size, redacted headers, request shape, result and usage, reasoning split, cache writes, per-request cost, content span). Filters: model, result, slow requests, session; groups: session, model, result; **click a column head to sort by it**, click again to flip.
|
|
147
|
+
- It is a page, not a floating card: the head, the summary (requests / failures / mean latency / mean first token / mean tok/s / mean cache hit / connection reuse) and the filters stay put while the table scrolls. Each row carries time, model, session, input/output/cache, **cache hit**, **tok/s**, first token, latency and status; clicking one expands its HTTP detail (method, protocol, URL, body size, **preparation**, redacted headers, request shape, result and usage, reasoning split, cache writes, per-request cost, content span). **Preparation** is the local time from request start to the HTTP send — large means the slowness is local (cold catalog / images / history conversion); small with a large first token means the gateway / model side is slow. Filters: model, result, slow requests, session; groups: session, model, result; **click a column head to sort by it**, click again to flip.
|
|
148
148
|
- **tok/s** is the output rate over the decode window (first token to the end), on the same scale as the Harness's own per-turn reading (reasoning tokens included). A request that produced no token has no rate — none is invented. **A burst delivery has no rate either**: when the gateway holds a whole turn and flushes it at the end (common with reasoning models: 8 seconds of thinking invisible on the wire, then a hundred tokens in a few milliseconds), a sub-100ms content span is delivery shape, not generation speed — it shows as — and stays out of the mean.
|
|
149
149
|
- The session column names the **session title** (from the session catalog; falls back to the id, whose full spelling stays one hover away). The session filter matches titles and ids; session groups are headed the same way. **Clicking a title opens that session** (an archived one refuses, and says so).
|
|
150
150
|
- **Cache hit** is the share of input tokens served from cache (cache reads over uncached input plus cache reads). No reported input means no share — none is invented either.
|
|
151
151
|
- **The In column is uncached new input**: the gateway reports a prompt as "uncached plus cache hits" and the log records only the new part — a fully prefix-cached turn reads as a tiny input with a 100% hit, which is not a bug. Hover a column head for its definition.
|
|
152
152
|
- **Content split**: below the body size the detail shows **Text X / Images Y** — images estimated as base64 per deduped attachment (each capped at the route's encoded target), text as the residual, so the two always sum to the total. Image-dominated turns read at a glance.
|
|
153
153
|
- Session ids are **always stored whole**: an id is a conversation identity, not a credential, and a truncated one reads two conversations as one.
|
|
154
|
-
- The log is a ring buffer (oldest dropped first) that dies with the process; snapshots are read on
|
|
155
|
-
- The credential never enters a record: the recorder takes no headers argument at all, only the allowlisted session id and user agent. Failed requests are recorded with their error code; a
|
|
154
|
+
- The log is a ring buffer (oldest dropped first) that dies with the process; snapshots are read on open and on manual refresh, plus once a second while any request is running, so a page nobody opens costs no traffic.
|
|
155
|
+
- The credential never enters a record: the recorder takes no headers argument at all, only the allowlisted session id and user agent. A request gets a "Requesting" placeholder row the moment it starts (the status column counts its seconds) and the row is completed in place when it ends. Failed requests are recorded with their error code; a stream the consumer abandoned early is recorded as `ABORTED` instead of vanishing silently.
|
|
156
156
|
|
|
157
157
|
**Images**: an over-budget image is refused explicitly rather than silently downscaled or dropped. The budgets ride one preset `imageBudgetPreset` (three choices, default `dsh`), not three numbers:
|
|
158
158
|
|
package/README.md
CHANGED
|
@@ -143,15 +143,15 @@ API Key 通过 Harness 凭证库提供(引用名 `OPENCODE_GO_API_KEY`),
|
|
|
143
143
|
- **两个入口,同一个页面**:
|
|
144
144
|
- **侧边栏图标 → 主区域整页**:跨会话看全部请求。日志是进程级的数据,放在侧边栏最合理——位置固定、常驻可见,和 DSH 自己的插件管理器同一个范式。
|
|
145
145
|
- **会话里的「请求日志」标签页**:就地看**本会话**的请求,回答"刚才那轮为什么慢"不用离开对话。
|
|
146
|
-
- 页面是一整页而不是浮层:标题、统计(请求数/失败数/平均耗时/平均首字/平均 tok/s/平均缓存命中/连接复用率)、筛选栏固定在上方,只有表格自己滚动。每行含时间、模型、会话、输入/输出/缓存、**缓存命中**、**tok/s**、首字、耗时与状态;点一行展开 HTTP
|
|
146
|
+
- 页面是一整页而不是浮层:标题、统计(请求数/失败数/平均耗时/平均首字/平均 tok/s/平均缓存命中/连接复用率)、筛选栏固定在上方,只有表格自己滚动。每行含时间、模型、会话、输入/输出/缓存、**缓存命中**、**tok/s**、首字、耗时与状态;点一行展开 HTTP 明细(方法、协议、地址、请求体大小、**准备耗时**、脱敏后的请求头、请求构成、结果与用量、推理拆分、缓存写入、单轮花费、内容跨度)。**准备耗时**是从请求开始到 HTTP 发出的本地耗时——它大是本地(目录冷启动/图片/历史转换)慢,它小但首字大是网关/模型侧慢。筛选有模型/结果/慢请求/会话,分组有会话/模型/结果;**点列头按该列排序**,再点一次反向。
|
|
147
147
|
- **tok/s** 是**首字到结束**这段解码窗口上的输出速率,口径与 Harness 自己的每轮读数一致(含推理 token)。没有 token 的请求没有速率——不编数。**突发推送同样没有速率**:网关把一整轮攒到最后一次性推过来时(推理模型常见:8 秒思考在 wire 上不可见,然后上百个 token 几毫秒内到达),内容跨度不足 100ms,算出来的"每秒几万 token"不是生成速度——显示为 —,也不计入平均。
|
|
148
148
|
- 会话列显示**会话标题**(标题来自会话目录;拿不到时回退显示 id,悬停可见完整 id)。会话筛选同时匹配标题与 id;按会话分组的组头同样显示标题。**点标题直接打开那个会话**(归档的打不开,会明说)。
|
|
149
149
|
- **缓存命中**是输入 token 里走缓存的占比(缓存读取/(未缓存输入+缓存读取))。网关没报输入就没有占比——同样不编数。
|
|
150
150
|
- **输入列是未缓存的新增 token**:网关把整段 prompt 拆成"未缓存+缓存命中"两部分上报,输入列只记新增的——同一会话前缀缓存全中时输入很小、缓存命中 100%,不是 bug。悬停列头有口径说明。
|
|
151
151
|
- **内容拆分**:明细里"请求体"下面跟一行"文本 X /图片 Y"——图片是各去重后附件按 base64 估的占比(单张按路由的编码上限封顶),文本是总数减图片的剩余,两者恒等于请求体总数。图片 dominant 的请求一眼就能看出来。
|
|
152
152
|
- 会话 id **始终完整记录**(它是会话身份,不是凭据;截断后两个会话会读成一个)。
|
|
153
|
-
-
|
|
154
|
-
- 凭据永远不进记录:记录器根本不接收请求头参数,只收白名单里的会话 id 与 UA
|
|
153
|
+
- 记录的是环形缓冲(超限丢最旧),进程重启即清;页面平时只在打开与手动刷新时读快照,有请求中时每秒自动刷新一次直到结束,没人打开的页面零流量。
|
|
154
|
+
- 凭据永远不进记录:记录器根本不接收请求头参数,只收白名单里的会话 id 与 UA;请求一开始就有一条"请求中"占位行(状态列读秒),结束时原位补全为完整记录。失败的请求同样记一条(含错误码);消费者提前终止的流记一条 `ABORTED`,不再无声丢失。
|
|
155
155
|
|
|
156
156
|
**图片**:三个字段各管一层,超限的图片会被明确拒绝,而不是静默降质或丢弃:
|
|
157
157
|
|
package/lib/client.js
CHANGED
|
@@ -252,12 +252,16 @@ function parseEntry(value) {
|
|
|
252
252
|
...row["reasoningTokens"] === void 0 ? {} : { reasoningTokens: count("reasoningTokens", row) },
|
|
253
253
|
...row["costUsd"] === void 0 ? {} : { costUsd: rate("costUsd", row) },
|
|
254
254
|
...row["requestBytes"] === void 0 ? {} : { requestBytes: count("requestBytes", row) },
|
|
255
|
+
...row["prepMs"] === void 0 ? {} : { prepMs: count("prepMs", row) },
|
|
255
256
|
...row["imageBytes"] === void 0 ? {} : { imageBytes: count("imageBytes", row) },
|
|
256
257
|
...row["durationMs"] === void 0 ? {} : { durationMs: count("durationMs", row) },
|
|
257
258
|
...row["firstTokenMs"] === void 0 ? {} : { firstTokenMs: count("firstTokenMs", row) },
|
|
258
259
|
...row["decodeMs"] === void 0 ? {} : { decodeMs: count("decodeMs", row) },
|
|
259
260
|
...row["streamMs"] === void 0 ? {} : { streamMs: count("streamMs", row) },
|
|
260
261
|
...row["tps"] === void 0 ? {} : { tps: rate("tps", row) },
|
|
262
|
+
// Running rows cross the wire too: a viewer opened mid-request must
|
|
263
|
+
// see them. Older snapshots predate the flag and read as finished.
|
|
264
|
+
...row["running"] === true ? { running: true } : {},
|
|
261
265
|
ok: row["ok"] === true,
|
|
262
266
|
...typeof row["code"] === "string" ? { code: row["code"] } : {},
|
|
263
267
|
...typeof row["poolKey"] === "string" ? { poolKey: row["poolKey"] } : {},
|
|
@@ -525,6 +529,7 @@ var en = {
|
|
|
525
529
|
requestLogOpenSessionFailed: "That session is archived and cannot be opened.",
|
|
526
530
|
requestLogClose: "Close",
|
|
527
531
|
requestLogRefresh: "Refresh",
|
|
532
|
+
requestLogRunning: "Requesting",
|
|
528
533
|
requestLogEmpty: "No requests recorded. Turn the log on, then make a request.",
|
|
529
534
|
requestLogDisabled: "The request log is off.",
|
|
530
535
|
requestLogFilterModel: "Model",
|
|
@@ -570,6 +575,7 @@ var en = {
|
|
|
570
575
|
requestLogDetailProtocol: "Protocol",
|
|
571
576
|
requestLogDetailUrl: "URL",
|
|
572
577
|
requestLogDetailRequestBytes: "Request body",
|
|
578
|
+
requestLogDetailPrep: "Preparation",
|
|
573
579
|
requestLogDetailContentSplit: "Content split",
|
|
574
580
|
requestLogDetailText: "Text",
|
|
575
581
|
requestLogDetailImages: "Images",
|
|
@@ -723,6 +729,7 @@ var zh = {
|
|
|
723
729
|
requestLogOpenSessionFailed: "\u8BE5\u4F1A\u8BDD\u5DF2\u5F52\u6863\uFF0C\u6253\u4E0D\u5F00\u3002",
|
|
724
730
|
requestLogClose: "\u5173\u95ED",
|
|
725
731
|
requestLogRefresh: "\u5237\u65B0",
|
|
732
|
+
requestLogRunning: "\u8BF7\u6C42\u4E2D",
|
|
726
733
|
requestLogEmpty: "\u6682\u65E0\u8BB0\u5F55\u3002\u5148\u6253\u5F00\u65E5\u5FD7\u5F00\u5173\uFF0C\u518D\u53D1\u8D77\u4E00\u6B21\u8BF7\u6C42\u3002",
|
|
727
734
|
requestLogDisabled: "\u8BF7\u6C42\u65E5\u5FD7\u672A\u5F00\u542F\u3002",
|
|
728
735
|
requestLogFilterModel: "\u6A21\u578B",
|
|
@@ -768,6 +775,7 @@ var zh = {
|
|
|
768
775
|
requestLogDetailProtocol: "\u534F\u8BAE",
|
|
769
776
|
requestLogDetailUrl: "\u5730\u5740",
|
|
770
777
|
requestLogDetailRequestBytes: "\u8BF7\u6C42\u4F53",
|
|
778
|
+
requestLogDetailPrep: "\u51C6\u5907\u8017\u65F6",
|
|
771
779
|
requestLogDetailContentSplit: "\u5185\u5BB9\u62C6\u5206",
|
|
772
780
|
requestLogDetailText: "\u6587\u672C",
|
|
773
781
|
requestLogDetailImages: "\u56FE\u7247",
|
|
@@ -2140,10 +2148,10 @@ if (!document.querySelector("style[data-plugin-css=" + JSON.stringify(id2) + "]"
|
|
|
2140
2148
|
const style = document.createElement("style");
|
|
2141
2149
|
style.dataset.plugin = "@dan-ai-studio/dshopencodego";
|
|
2142
2150
|
style.dataset.pluginCss = id2;
|
|
2143
|
-
style.textContent = ".Lx7MOq_page{box-sizing:border-box;background:var(--dsw-alias-bg-base);height:100%;min-height:0;color:var(--dsw-alias-label-primary);flex-direction:column;display:flex}.Lx7MOq_pageLoose{height:auto}.Lx7MOq_pageLoose .Lx7MOq_tableWrap{overflow:visible}.Lx7MOq_pageLoose .Lx7MOq_stuck{z-index:3;background:var(--dsw-alias-bg-base);position:sticky;top:0}.Lx7MOq_pageLoose .Lx7MOq_table th{top:var(--request-log-stuck,0px)}.Lx7MOq_pageLoose .Lx7MOq_foot{z-index:2;background:var(--dsw-alias-bg-base);position:sticky;bottom:0}.Lx7MOq_head{flex-wrap:wrap;flex:none;align-items:center;gap:12px;padding:20px clamp(16px,3vw,32px) 12px;display:flex}.Lx7MOq_headText{min-width:0}.Lx7MOq_title{margin:0;font-size:20px;font-weight:500;line-height:28px}.Lx7MOq_subtitle{color:var(--dsw-alias-label-secondary);margin:4px 0 0;font-size:13px;line-height:20px}.Lx7MOq_headActions{flex:none;align-items:center;gap:8px;margin-left:auto;display:flex}.Lx7MOq_button{border:1px solid var(--dsw-alias-border-l2);border-radius:var(--dsw-radius-sm);background:var(--dsw-alias-bg-layer-1);height:28px;color:var(--dsw-alias-label-primary);font:inherit;cursor:pointer;align-items:center;gap:6px;padding:0 12px;font-size:13px;display:inline-flex}.Lx7MOq_button:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}.Lx7MOq_button:disabled{cursor:default;opacity:.5}.Lx7MOq_summary{flex-wrap:wrap;flex:none;gap:8px;padding:0 clamp(16px,3vw,32px) 12px;display:flex}.Lx7MOq_card{border:1px solid var(--dsw-alias-border-l2);border-radius:var(--dsw-radius-md);background:var(--dsw-alias-bg-layer-1);flex-direction:column;gap:2px;min-width:104px;padding:8px 12px;display:flex}.Lx7MOq_cardLabel{color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:16px}.Lx7MOq_cardValue{font-variant-numeric:tabular-nums;font-size:16px;font-weight:500;line-height:22px}.Lx7MOq_cardError .Lx7MOq_cardValue{color:var(--dsw-alias-state-error-primary)}.Lx7MOq_cardWarn .Lx7MOq_cardValue{color:var(--dsw-alias-state-warn-primary)}.Lx7MOq_toolbar{flex-wrap:wrap;flex:none;align-items:center;gap:8px 12px;padding:0 clamp(16px,3vw,32px) 12px;display:flex}.Lx7MOq_field{color:var(--dsw-alias-label-secondary);align-items:center;gap:6px;font-size:13px;line-height:20px;display:inline-flex}.Lx7MOq_select,.Lx7MOq_input{border:1px solid var(--dsw-alias-border-l2);border-radius:var(--dsw-radius-sm);background:var(--dsw-alias-bg-layer-1);height:28px;color:var(--dsw-alias-label-primary);font:inherit;padding:0 8px;font-size:13px}.Lx7MOq_input{width:160px}.Lx7MOq_inputNumber{width:72px}.Lx7MOq_checkbox{accent-color:var(--dsw-alias-brand-primary)}.Lx7MOq_sessionLink{color:var(--dsw-alias-link);font:inherit;text-align:left;cursor:pointer;white-space:nowrap;background:0 0;border:none;padding:0}.Lx7MOq_sessionLink:hover{text-decoration:underline}.Lx7MOq_tableWrap{border:1px solid var(--dsw-alias-border-l2);border-radius:var(--dsw-radius-md);background:var(--dsw-alias-bg-layer-1);flex:1;min-height:0;margin:0 clamp(16px,3vw,32px);overflow:auto}.Lx7MOq_table{border-collapse:separate;border-spacing:0;width:100%;min-width:940px;font-size:13px;line-height:20px}.Lx7MOq_table th{z-index:1;border-bottom:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-tertiary);text-align:left;white-space:nowrap;padding:8px 10px;font-size:12px;font-weight:500;position:sticky;top:0}.Lx7MOq_thSortable{cursor:pointer;user-select:none}.Lx7MOq_thSortable:hover{color:var(--dsw-alias-label-primary)}.Lx7MOq_table td{border-bottom:1px solid var(--dsw-alias-border-l1);white-space:nowrap;font-variant-numeric:tabular-nums;padding:6px 10px}.Lx7MOq_table tbody tr:last-child td{border-bottom:none}.Lx7MOq_row{cursor:pointer}.Lx7MOq_row:hover td{background:var(--dsw-alias-interactive-bg-hover)}.Lx7MOq_rowOk td:first-child{box-shadow:inset 3px 0 0 var(--dsw-alias-border-l3)}.Lx7MOq_rowSlow td:first-child{box-shadow:inset 3px 0 0 var(--dsw-alias-state-warn-primary)}.Lx7MOq_rowFail td:first-child{box-shadow:inset 3px 0 0 var(--dsw-alias-state-error-primary)}.Lx7MOq_rowFail td:last-child,.Lx7MOq_rowSlow td:last-child{color:var(--dsw-alias-state-error-primary)}.Lx7MOq_group td{border-bottom:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-secondary);padding-top:12px;font-weight:500}.Lx7MOq_detail td{border-bottom:1px solid var(--dsw-alias-border-l1);white-space:normal;background:var(--dsw-alias-bg-layer-2);padding:12px 10px 16px}.Lx7MOq_detailGrid{grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:6px 24px;display:grid}.Lx7MOq_detailItem{gap:8px;min-width:0;font-size:13px;line-height:20px;display:flex}.Lx7MOq_detailLabel{color:var(--dsw-alias-label-tertiary);flex:none}.Lx7MOq_detailValue{overflow-wrap:anywhere;min-width:0;color:var(--dsw-alias-label-primary)}.Lx7MOq_mono{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:12px}.Lx7MOq_empty{text-align:center;color:var(--dsw-alias-label-tertiary);padding:48px 16px}.Lx7MOq_foot{color:var(--dsw-alias-label-tertiary);flex-wrap:wrap;flex:none;align-items:center;gap:12px;padding:12px clamp(16px,3vw,32px) 20px;font-size:12px;display:flex}.Lx7MOq_pager{align-items:center;gap:8px;margin-left:auto;display:flex}.Lx7MOq_notice{color:var(--dsw-alias-label-secondary);padding:0 clamp(16px,3vw,32px) 12px;font-size:13px;line-height:20px}.Lx7MOq_noticeError{color:var(--dsw-alias-state-error-primary)}";
|
|
2151
|
+
style.textContent = ".Lx7MOq_page{box-sizing:border-box;background:var(--dsw-alias-bg-base);height:100%;min-height:0;color:var(--dsw-alias-label-primary);flex-direction:column;display:flex}.Lx7MOq_pageLoose{height:auto}.Lx7MOq_pageLoose .Lx7MOq_tableWrap{overflow:visible}.Lx7MOq_pageLoose .Lx7MOq_stuck{z-index:3;background:var(--dsw-alias-bg-base);position:sticky;top:0}.Lx7MOq_pageLoose .Lx7MOq_table th{top:var(--request-log-stuck,0px)}.Lx7MOq_pageLoose .Lx7MOq_foot{z-index:2;background:var(--dsw-alias-bg-base);position:sticky;bottom:0}.Lx7MOq_head{flex-wrap:wrap;flex:none;align-items:center;gap:12px;padding:20px clamp(16px,3vw,32px) 12px;display:flex}.Lx7MOq_headText{min-width:0}.Lx7MOq_title{margin:0;font-size:20px;font-weight:500;line-height:28px}.Lx7MOq_subtitle{color:var(--dsw-alias-label-secondary);margin:4px 0 0;font-size:13px;line-height:20px}.Lx7MOq_headActions{flex:none;align-items:center;gap:8px;margin-left:auto;display:flex}.Lx7MOq_button{border:1px solid var(--dsw-alias-border-l2);border-radius:var(--dsw-radius-sm);background:var(--dsw-alias-bg-layer-1);height:28px;color:var(--dsw-alias-label-primary);font:inherit;cursor:pointer;align-items:center;gap:6px;padding:0 12px;font-size:13px;display:inline-flex}.Lx7MOq_button:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}.Lx7MOq_button:disabled{cursor:default;opacity:.5}.Lx7MOq_summary{flex-wrap:wrap;flex:none;gap:8px;padding:0 clamp(16px,3vw,32px) 12px;display:flex}.Lx7MOq_card{border:1px solid var(--dsw-alias-border-l2);border-radius:var(--dsw-radius-md);background:var(--dsw-alias-bg-layer-1);flex-direction:column;gap:2px;min-width:104px;padding:8px 12px;display:flex}.Lx7MOq_cardLabel{color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:16px}.Lx7MOq_cardValue{font-variant-numeric:tabular-nums;font-size:16px;font-weight:500;line-height:22px}.Lx7MOq_cardError .Lx7MOq_cardValue{color:var(--dsw-alias-state-error-primary)}.Lx7MOq_cardWarn .Lx7MOq_cardValue{color:var(--dsw-alias-state-warn-primary)}.Lx7MOq_toolbar{flex-wrap:wrap;flex:none;align-items:center;gap:8px 12px;padding:0 clamp(16px,3vw,32px) 12px;display:flex}.Lx7MOq_field{color:var(--dsw-alias-label-secondary);align-items:center;gap:6px;font-size:13px;line-height:20px;display:inline-flex}.Lx7MOq_select,.Lx7MOq_input{border:1px solid var(--dsw-alias-border-l2);border-radius:var(--dsw-radius-sm);background:var(--dsw-alias-bg-layer-1);height:28px;color:var(--dsw-alias-label-primary);font:inherit;padding:0 8px;font-size:13px}.Lx7MOq_input{width:160px}.Lx7MOq_inputNumber{width:72px}.Lx7MOq_checkbox{accent-color:var(--dsw-alias-brand-primary)}.Lx7MOq_sessionLink{color:var(--dsw-alias-link);font:inherit;text-align:left;cursor:pointer;white-space:nowrap;background:0 0;border:none;padding:0}.Lx7MOq_sessionLink:hover{text-decoration:underline}.Lx7MOq_tableWrap{border:1px solid var(--dsw-alias-border-l2);border-radius:var(--dsw-radius-md);background:var(--dsw-alias-bg-layer-1);flex:1;min-height:0;margin:0 clamp(16px,3vw,32px);overflow:auto}.Lx7MOq_table{border-collapse:separate;border-spacing:0;width:100%;min-width:940px;font-size:13px;line-height:20px}.Lx7MOq_table th{z-index:1;border-bottom:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-tertiary);text-align:left;white-space:nowrap;padding:8px 10px;font-size:12px;font-weight:500;position:sticky;top:0}.Lx7MOq_thSortable{cursor:pointer;user-select:none}.Lx7MOq_thSortable:hover{color:var(--dsw-alias-label-primary)}.Lx7MOq_table td{border-bottom:1px solid var(--dsw-alias-border-l1);white-space:nowrap;font-variant-numeric:tabular-nums;padding:6px 10px}.Lx7MOq_table tbody tr:last-child td{border-bottom:none}.Lx7MOq_row{cursor:pointer}.Lx7MOq_row:hover td{background:var(--dsw-alias-interactive-bg-hover)}.Lx7MOq_rowOk td:first-child{box-shadow:inset 3px 0 0 var(--dsw-alias-border-l3)}.Lx7MOq_rowSlow td:first-child{box-shadow:inset 3px 0 0 var(--dsw-alias-state-warn-primary)}.Lx7MOq_rowFail td:first-child{box-shadow:inset 3px 0 0 var(--dsw-alias-state-error-primary)}.Lx7MOq_rowRunning td:first-child{box-shadow:inset 3px 0 0 var(--dsw-alias-brand-primary)}.Lx7MOq_rowFail td:last-child,.Lx7MOq_rowSlow td:last-child{color:var(--dsw-alias-state-error-primary)}.Lx7MOq_group td{border-bottom:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-secondary);padding-top:12px;font-weight:500}.Lx7MOq_detail td{border-bottom:1px solid var(--dsw-alias-border-l1);white-space:normal;background:var(--dsw-alias-bg-layer-2);padding:12px 10px 16px}.Lx7MOq_detailGrid{grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:6px 24px;display:grid}.Lx7MOq_detailItem{gap:8px;min-width:0;font-size:13px;line-height:20px;display:flex}.Lx7MOq_detailLabel{color:var(--dsw-alias-label-tertiary);flex:none}.Lx7MOq_detailValue{overflow-wrap:anywhere;min-width:0;color:var(--dsw-alias-label-primary)}.Lx7MOq_mono{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:12px}.Lx7MOq_empty{text-align:center;color:var(--dsw-alias-label-tertiary);padding:48px 16px}.Lx7MOq_foot{color:var(--dsw-alias-label-tertiary);flex-wrap:wrap;flex:none;align-items:center;gap:12px;padding:12px clamp(16px,3vw,32px) 20px;font-size:12px;display:flex}.Lx7MOq_pager{align-items:center;gap:8px;margin-left:auto;display:flex}.Lx7MOq_notice{color:var(--dsw-alias-label-secondary);padding:0 clamp(16px,3vw,32px) 12px;font-size:13px;line-height:20px}.Lx7MOq_noticeError{color:var(--dsw-alias-state-error-primary)}";
|
|
2144
2152
|
document.head.appendChild(style);
|
|
2145
2153
|
}
|
|
2146
|
-
var request_log_default = { "button": "Lx7MOq_button", "card": "Lx7MOq_card", "cardError": "Lx7MOq_cardError", "cardLabel": "Lx7MOq_cardLabel", "cardValue": "Lx7MOq_cardValue", "cardWarn": "Lx7MOq_cardWarn", "checkbox": "Lx7MOq_checkbox", "detail": "Lx7MOq_detail", "detailGrid": "Lx7MOq_detailGrid", "detailItem": "Lx7MOq_detailItem", "detailLabel": "Lx7MOq_detailLabel", "detailValue": "Lx7MOq_detailValue", "empty": "Lx7MOq_empty", "field": "Lx7MOq_field", "foot": "Lx7MOq_foot", "group": "Lx7MOq_group", "head": "Lx7MOq_head", "headActions": "Lx7MOq_headActions", "headText": "Lx7MOq_headText", "input": "Lx7MOq_input", "inputNumber": "Lx7MOq_inputNumber", "mono": "Lx7MOq_mono", "notice": "Lx7MOq_notice", "noticeError": "Lx7MOq_noticeError", "page": "Lx7MOq_page", "pageLoose": "Lx7MOq_pageLoose", "pager": "Lx7MOq_pager", "row": "Lx7MOq_row", "rowFail": "Lx7MOq_rowFail", "rowOk": "Lx7MOq_rowOk", "rowSlow": "Lx7MOq_rowSlow", "select": "Lx7MOq_select", "sessionLink": "Lx7MOq_sessionLink", "stuck": "Lx7MOq_stuck", "subtitle": "Lx7MOq_subtitle", "summary": "Lx7MOq_summary", "table": "Lx7MOq_table", "tableWrap": "Lx7MOq_tableWrap", "thSortable": "Lx7MOq_thSortable", "title": "Lx7MOq_title", "toolbar": "Lx7MOq_toolbar" };
|
|
2154
|
+
var request_log_default = { "button": "Lx7MOq_button", "card": "Lx7MOq_card", "cardError": "Lx7MOq_cardError", "cardLabel": "Lx7MOq_cardLabel", "cardValue": "Lx7MOq_cardValue", "cardWarn": "Lx7MOq_cardWarn", "checkbox": "Lx7MOq_checkbox", "detail": "Lx7MOq_detail", "detailGrid": "Lx7MOq_detailGrid", "detailItem": "Lx7MOq_detailItem", "detailLabel": "Lx7MOq_detailLabel", "detailValue": "Lx7MOq_detailValue", "empty": "Lx7MOq_empty", "field": "Lx7MOq_field", "foot": "Lx7MOq_foot", "group": "Lx7MOq_group", "head": "Lx7MOq_head", "headActions": "Lx7MOq_headActions", "headText": "Lx7MOq_headText", "input": "Lx7MOq_input", "inputNumber": "Lx7MOq_inputNumber", "mono": "Lx7MOq_mono", "notice": "Lx7MOq_notice", "noticeError": "Lx7MOq_noticeError", "page": "Lx7MOq_page", "pageLoose": "Lx7MOq_pageLoose", "pager": "Lx7MOq_pager", "row": "Lx7MOq_row", "rowFail": "Lx7MOq_rowFail", "rowOk": "Lx7MOq_rowOk", "rowRunning": "Lx7MOq_rowRunning", "rowSlow": "Lx7MOq_rowSlow", "select": "Lx7MOq_select", "sessionLink": "Lx7MOq_sessionLink", "stuck": "Lx7MOq_stuck", "subtitle": "Lx7MOq_subtitle", "summary": "Lx7MOq_summary", "table": "Lx7MOq_table", "tableWrap": "Lx7MOq_tableWrap", "thSortable": "Lx7MOq_thSortable", "title": "Lx7MOq_title", "toolbar": "Lx7MOq_toolbar" };
|
|
2147
2155
|
|
|
2148
2156
|
// src/client/RequestLogPage.tsx
|
|
2149
2157
|
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
@@ -2183,7 +2191,7 @@ function sortValue(entry, key) {
|
|
|
2183
2191
|
case "durationMs":
|
|
2184
2192
|
return entry.durationMs ?? -1;
|
|
2185
2193
|
case "status":
|
|
2186
|
-
return entry.ok ? `ok-${entry.status ?? ""}` : `fail-${entry.code ?? ""}`;
|
|
2194
|
+
return entry.running === true ? "running" : entry.ok ? `ok-${entry.status ?? ""}` : `fail-${entry.code ?? ""}`;
|
|
2187
2195
|
}
|
|
2188
2196
|
}
|
|
2189
2197
|
var PAGE_SIZE = 50;
|
|
@@ -2233,8 +2241,10 @@ function groupKey(entry, groupBy) {
|
|
|
2233
2241
|
return entry.session === "" ? "\u2014" : entry.session;
|
|
2234
2242
|
case "model":
|
|
2235
2243
|
return entry.model;
|
|
2244
|
+
// A running row is neither a success nor a failure yet: it groups on
|
|
2245
|
+
// its own instead of inflating either outcome.
|
|
2236
2246
|
case "result":
|
|
2237
|
-
return entry.ok ? "ok" : entry.code ?? "fail";
|
|
2247
|
+
return entry.running === true ? "running" : entry.ok ? "ok" : entry.code ?? "fail";
|
|
2238
2248
|
case "none":
|
|
2239
2249
|
return void 0;
|
|
2240
2250
|
}
|
|
@@ -2276,6 +2286,7 @@ function RequestLogPage({ readLog, enabled, canOpenSession, openSession, t, getL
|
|
|
2276
2286
|
const title = catalog?.byId[id4]?.title?.trim();
|
|
2277
2287
|
return title === void 0 || title === "" ? { text: id4 === "" ? "\u2014" : id4, titled: false } : { text: title, titled: true };
|
|
2278
2288
|
}, [catalog]);
|
|
2289
|
+
const resultGroupLabel = (group) => group === "ok" ? t("requestLogResultOk") : group === "fail" ? t("requestLogResultFail") : group === "running" ? t("requestLogRunning") : group;
|
|
2279
2290
|
const toggleSort = (key) => {
|
|
2280
2291
|
if (key === sortKey) {
|
|
2281
2292
|
setSortDir(sortDir === "asc" ? "desc" : "asc");
|
|
@@ -2301,6 +2312,16 @@ function RequestLogPage({ readLog, enabled, canOpenSession, openSession, t, getL
|
|
|
2301
2312
|
(0, import_react2.useEffect)(() => {
|
|
2302
2313
|
void load();
|
|
2303
2314
|
}, [load]);
|
|
2315
|
+
const hasRunning = (snapshot?.entries ?? []).some((entry) => entry.running === true);
|
|
2316
|
+
(0, import_react2.useEffect)(() => {
|
|
2317
|
+
if (!hasRunning) return;
|
|
2318
|
+
const timer = setInterval(() => {
|
|
2319
|
+
void load();
|
|
2320
|
+
}, 1e3);
|
|
2321
|
+
return () => {
|
|
2322
|
+
clearInterval(timer);
|
|
2323
|
+
};
|
|
2324
|
+
}, [hasRunning, load]);
|
|
2304
2325
|
const models = (0, import_react2.useMemo)(() => {
|
|
2305
2326
|
const ids = /* @__PURE__ */ new Set();
|
|
2306
2327
|
for (const entry of snapshot?.entries ?? []) ids.add(entry.model);
|
|
@@ -2311,7 +2332,7 @@ function RequestLogPage({ readLog, enabled, canOpenSession, openSession, t, getL
|
|
|
2311
2332
|
return (snapshot?.entries ?? []).filter((entry) => {
|
|
2312
2333
|
if (model !== "all" && entry.model !== model) return false;
|
|
2313
2334
|
if (result === "ok" && !entry.ok) return false;
|
|
2314
|
-
if (result === "fail" && entry.ok) return false;
|
|
2335
|
+
if (result === "fail" && (entry.ok || entry.running === true)) return false;
|
|
2315
2336
|
if (slowOnly && (entry.durationMs ?? 0) < Math.max(0, slowSeconds) * 1e3) return false;
|
|
2316
2337
|
if (onlySession && sessionId !== void 0 && entry.session !== sessionId) return false;
|
|
2317
2338
|
if (query !== "") {
|
|
@@ -2334,7 +2355,7 @@ function RequestLogPage({ readLog, enabled, canOpenSession, openSession, t, getL
|
|
|
2334
2355
|
return next;
|
|
2335
2356
|
}, [filtered, sortKey, sortDir]);
|
|
2336
2357
|
const pageEntries = sorted.slice(safePage * PAGE_SIZE, (safePage + 1) * PAGE_SIZE);
|
|
2337
|
-
const failures = filtered.filter((entry) => !entry.ok).length;
|
|
2358
|
+
const failures = filtered.filter((entry) => !entry.ok && entry.running !== true).length;
|
|
2338
2359
|
const meanLatency = average(filtered.flatMap((entry) => entry.durationMs === void 0 ? [] : [entry.durationMs]));
|
|
2339
2360
|
const meanFirstToken = average(filtered.flatMap((entry) => entry.firstTokenMs === void 0 ? [] : [entry.firstTokenMs]));
|
|
2340
2361
|
const meanTps = average(filtered.flatMap((entry) => entry.tps === void 0 ? [] : [entry.tps]));
|
|
@@ -2357,10 +2378,10 @@ function RequestLogPage({ readLog, enabled, canOpenSession, openSession, t, getL
|
|
|
2357
2378
|
if (group !== void 0 && group !== lastGroup) {
|
|
2358
2379
|
lastGroup = group;
|
|
2359
2380
|
out.push(
|
|
2360
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("tr", { className: request_log_default.group, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("td", { colSpan: 11, children: groupBy === "session" ? sessionLabel(group).text : group }) }, `group-${group}-${entry.seq}`)
|
|
2381
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("tr", { className: request_log_default.group, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("td", { colSpan: 11, children: groupBy === "session" ? sessionLabel(group).text : groupBy === "result" ? resultGroupLabel(group) : group }) }, `group-${group}-${entry.seq}`)
|
|
2361
2382
|
);
|
|
2362
2383
|
}
|
|
2363
|
-
const state = !entry.ok ? "rowFail" : entry.tps !== void 0 && entry.tps < 5 ? "rowSlow" : "rowOk";
|
|
2384
|
+
const state = entry.running === true ? "rowRunning" : !entry.ok ? "rowFail" : entry.tps !== void 0 && entry.tps < 5 ? "rowSlow" : "rowOk";
|
|
2364
2385
|
const session = sessionLabel(entry.session);
|
|
2365
2386
|
out.push(
|
|
2366
2387
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
@@ -2398,8 +2419,8 @@ function RequestLogPage({ readLog, enabled, canOpenSession, openSession, t, getL
|
|
|
2398
2419
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("td", { children: percent(cacheRate(entry)) }),
|
|
2399
2420
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("td", { children: rate2(entry.tps) }),
|
|
2400
2421
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("td", { children: duration(entry.firstTokenMs) }),
|
|
2401
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("td", { children: duration(entry.durationMs) }),
|
|
2402
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("td", { children: entry.ok ? entry.status === void 0 ? "\u2713" : String(entry.status) : entry.code ?? "\u2717" })
|
|
2422
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("td", { children: entry.running === true ? duration(Math.max(0, Date.now() - entry.atMs)) : duration(entry.durationMs) }),
|
|
2423
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("td", { children: entry.running === true ? `${t("requestLogRunning")} \xB7 ${duration(Math.max(0, Date.now() - entry.atMs))}` : entry.ok ? entry.status === void 0 ? "\u2713" : String(entry.status) : entry.code ?? "\u2717" })
|
|
2403
2424
|
]
|
|
2404
2425
|
},
|
|
2405
2426
|
entry.seq
|
|
@@ -2428,6 +2449,10 @@ function RequestLogPage({ readLog, enabled, canOpenSession, openSession, t, getL
|
|
|
2428
2449
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: request_log_default.detailLabel, children: t("requestLogDetailContentSplit") }),
|
|
2429
2450
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: request_log_default.detailValue, children: contentSplit(entry, t("requestLogDetailText"), t("requestLogDetailImages")) })
|
|
2430
2451
|
] }),
|
|
2452
|
+
entry.prepMs !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: request_log_default.detailItem, children: [
|
|
2453
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: request_log_default.detailLabel, children: t("requestLogDetailPrep") }),
|
|
2454
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: request_log_default.detailValue, children: duration(entry.prepMs) })
|
|
2455
|
+
] }),
|
|
2431
2456
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: request_log_default.detailItem, children: [
|
|
2432
2457
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: request_log_default.detailLabel, children: t("requestLogDetailSession") }),
|
|
2433
2458
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
package/lib/index.js
CHANGED
|
@@ -77068,6 +77068,7 @@ var OpencodeGoAdapter = class extends LlmAdapter {
|
|
|
77068
77068
|
} finally {
|
|
77069
77069
|
if (!exhausted) {
|
|
77070
77070
|
timedOut = timeoutOf(watchdog.signal, "LLM_STREAM_IDLE_TIMEOUT") !== void 0;
|
|
77071
|
+
span?.finish(false, "ABORTED");
|
|
77071
77072
|
consumer.abort("opencode-go stream consumer stopped");
|
|
77072
77073
|
try {
|
|
77073
77074
|
void Promise.resolve(iterator.return(void 0)).catch(() => {
|
|
@@ -77315,12 +77316,16 @@ function parseEntry(value) {
|
|
|
77315
77316
|
...row["reasoningTokens"] === void 0 ? {} : { reasoningTokens: count("reasoningTokens", row) },
|
|
77316
77317
|
...row["costUsd"] === void 0 ? {} : { costUsd: rate("costUsd", row) },
|
|
77317
77318
|
...row["requestBytes"] === void 0 ? {} : { requestBytes: count("requestBytes", row) },
|
|
77319
|
+
...row["prepMs"] === void 0 ? {} : { prepMs: count("prepMs", row) },
|
|
77318
77320
|
...row["imageBytes"] === void 0 ? {} : { imageBytes: count("imageBytes", row) },
|
|
77319
77321
|
...row["durationMs"] === void 0 ? {} : { durationMs: count("durationMs", row) },
|
|
77320
77322
|
...row["firstTokenMs"] === void 0 ? {} : { firstTokenMs: count("firstTokenMs", row) },
|
|
77321
77323
|
...row["decodeMs"] === void 0 ? {} : { decodeMs: count("decodeMs", row) },
|
|
77322
77324
|
...row["streamMs"] === void 0 ? {} : { streamMs: count("streamMs", row) },
|
|
77323
77325
|
...row["tps"] === void 0 ? {} : { tps: rate("tps", row) },
|
|
77326
|
+
// Running rows cross the wire too: a viewer opened mid-request must
|
|
77327
|
+
// see them. Older snapshots predate the flag and read as finished.
|
|
77328
|
+
...row["running"] === true ? { running: true } : {},
|
|
77324
77329
|
ok: row["ok"] === true,
|
|
77325
77330
|
...typeof row["code"] === "string" ? { code: row["code"] } : {},
|
|
77326
77331
|
...typeof row["poolKey"] === "string" ? { poolKey: row["poolKey"] } : {},
|
|
@@ -77897,52 +77902,79 @@ var RequestLog = class {
|
|
|
77897
77902
|
* Begin one request's span, or nothing when the log is disabled.
|
|
77898
77903
|
*
|
|
77899
77904
|
* This is the single gate on the request path: a disabled log costs one
|
|
77900
|
-
* method call and one boolean read, and constructs nothing.
|
|
77905
|
+
* method call and one boolean read, and constructs nothing. When enabled,
|
|
77906
|
+
* the running row is appended before the span is returned, so the viewer
|
|
77907
|
+
* sees the request while it runs; `request` and `status` mutate that same
|
|
77908
|
+
* visible row as the wire facts arrive.
|
|
77901
77909
|
*/
|
|
77902
77910
|
start(detail) {
|
|
77903
77911
|
if (!this.gates.enabled()) return void 0;
|
|
77904
77912
|
const debug = this.gates.debug();
|
|
77905
77913
|
const startedAt = Date.now();
|
|
77906
77914
|
const session = detail.sessionId ?? "";
|
|
77915
|
+
const record2 = {
|
|
77916
|
+
seq: ++this.seq,
|
|
77917
|
+
atMs: startedAt,
|
|
77918
|
+
model: detail.model,
|
|
77919
|
+
...detail.protocol === void 0 ? {} : { protocol: detail.protocol },
|
|
77920
|
+
session,
|
|
77921
|
+
method: detail.method ?? "POST",
|
|
77922
|
+
url: detail.url ?? "",
|
|
77923
|
+
running: true,
|
|
77924
|
+
ok: false,
|
|
77925
|
+
shape: { messages: detail.messageCount, hasImage: detail.hasImage, tools: detail.toolCount },
|
|
77926
|
+
...detail.imageBytes === void 0 ? {} : { imageBytes: detail.imageBytes },
|
|
77927
|
+
...!debug ? {} : {
|
|
77928
|
+
headers: {
|
|
77929
|
+
...detail.headers.session === void 0 ? {} : { session: detail.headers.session },
|
|
77930
|
+
...detail.headers.userAgent === void 0 ? {} : { userAgent: detail.headers.userAgent }
|
|
77931
|
+
}
|
|
77932
|
+
}
|
|
77933
|
+
};
|
|
77934
|
+
this.insert(record2, true);
|
|
77907
77935
|
let firstTokenAt;
|
|
77908
77936
|
let lastTokenAt;
|
|
77909
|
-
let
|
|
77910
|
-
let url;
|
|
77911
|
-
let requestBytes;
|
|
77912
|
-
let status;
|
|
77937
|
+
let reported = false;
|
|
77913
77938
|
let finished = false;
|
|
77914
77939
|
return {
|
|
77915
77940
|
token: () => {
|
|
77916
77941
|
const now = Date.now();
|
|
77917
|
-
if (firstTokenAt === void 0)
|
|
77942
|
+
if (firstTokenAt === void 0) {
|
|
77943
|
+
firstTokenAt = now;
|
|
77944
|
+
record2.firstTokenMs = Math.max(0, now - startedAt);
|
|
77945
|
+
}
|
|
77918
77946
|
lastTokenAt = now;
|
|
77919
77947
|
},
|
|
77920
77948
|
request: (nextMethod, nextUrl, nextRequestBytes) => {
|
|
77921
|
-
if (
|
|
77922
|
-
|
|
77923
|
-
|
|
77924
|
-
|
|
77949
|
+
if (!reported) {
|
|
77950
|
+
reported = true;
|
|
77951
|
+
record2.method = nextMethod;
|
|
77952
|
+
record2.url = nextUrl;
|
|
77953
|
+
if (nextRequestBytes !== void 0) record2.requestBytes = nextRequestBytes;
|
|
77954
|
+
record2.prepMs = Math.max(0, Date.now() - startedAt);
|
|
77925
77955
|
}
|
|
77926
77956
|
},
|
|
77927
77957
|
status: (value) => {
|
|
77928
|
-
status = value;
|
|
77958
|
+
record2.status = value;
|
|
77929
77959
|
},
|
|
77930
77960
|
finish: (ok, code, usage, costUsd) => {
|
|
77931
77961
|
if (finished) return;
|
|
77932
77962
|
finished = true;
|
|
77963
|
+
const seq = record2.seq;
|
|
77933
77964
|
const endedAt = Date.now();
|
|
77934
77965
|
const decodeMs = firstTokenAt === void 0 ? void 0 : Math.max(0, endedAt - firstTokenAt);
|
|
77935
77966
|
const streamMs = firstTokenAt === void 0 || lastTokenAt === void 0 ? void 0 : Math.max(0, lastTokenAt - firstTokenAt);
|
|
77936
77967
|
const tps = decodeMs === void 0 || decodeMs <= 0 || usage === void 0 || streamMs === void 0 || streamMs < MIN_STREAM_MS_FOR_RATE ? void 0 : usage.outputTokens / (decodeMs / 1e3);
|
|
77937
77968
|
const entry = {
|
|
77938
|
-
seq
|
|
77969
|
+
seq,
|
|
77939
77970
|
atMs: startedAt,
|
|
77940
77971
|
model: detail.model,
|
|
77941
77972
|
...detail.protocol === void 0 ? {} : { protocol: detail.protocol },
|
|
77942
77973
|
session,
|
|
77943
|
-
method:
|
|
77944
|
-
url:
|
|
77945
|
-
...status === void 0 ? {} : { status },
|
|
77974
|
+
method: record2.method,
|
|
77975
|
+
url: record2.url,
|
|
77976
|
+
...record2.status === void 0 ? {} : { status: record2.status },
|
|
77977
|
+
...record2.prepMs === void 0 ? {} : { prepMs: record2.prepMs },
|
|
77946
77978
|
...usage === void 0 ? {} : {
|
|
77947
77979
|
inputTokens: usage.inputTokens,
|
|
77948
77980
|
outputTokens: usage.outputTokens,
|
|
@@ -77951,7 +77983,7 @@ var RequestLog = class {
|
|
|
77951
77983
|
...usage.reasoningTokens === void 0 || usage.reasoningTokens <= 0 ? {} : { reasoningTokens: usage.reasoningTokens }
|
|
77952
77984
|
},
|
|
77953
77985
|
...costUsd === void 0 ? {} : { costUsd },
|
|
77954
|
-
...requestBytes === void 0 ? {} : { requestBytes },
|
|
77986
|
+
...record2.requestBytes === void 0 ? {} : { requestBytes: record2.requestBytes },
|
|
77955
77987
|
...detail.imageBytes === void 0 ? {} : { imageBytes: detail.imageBytes },
|
|
77956
77988
|
durationMs: Math.max(0, endedAt - startedAt),
|
|
77957
77989
|
...firstTokenAt === void 0 ? {} : { firstTokenMs: Math.max(0, firstTokenAt - startedAt) },
|
|
@@ -77972,12 +78004,17 @@ var RequestLog = class {
|
|
|
77972
78004
|
}
|
|
77973
78005
|
}
|
|
77974
78006
|
};
|
|
77975
|
-
this.
|
|
78007
|
+
const at = this.entries.findIndex((candidate) => candidate.seq === seq);
|
|
78008
|
+
if (at === -1) {
|
|
78009
|
+
this.insert(entry, false);
|
|
78010
|
+
} else {
|
|
78011
|
+
this.entries[at] = entry;
|
|
78012
|
+
}
|
|
77976
78013
|
}
|
|
77977
78014
|
};
|
|
77978
78015
|
}
|
|
77979
|
-
|
|
77980
|
-
this.totalRecords += 1;
|
|
78016
|
+
insert(entry, count2) {
|
|
78017
|
+
if (count2) this.totalRecords += 1;
|
|
77981
78018
|
const bound = Math.max(1, Math.floor(this.gates.maxEntries()));
|
|
77982
78019
|
const next = [entry, ...this.entries].slice(0, bound);
|
|
77983
78020
|
this.droppedRecords += this.entries.length + 1 - next.length;
|
|
@@ -78097,7 +78134,7 @@ function createUpdateChecker(deps) {
|
|
|
78097
78134
|
}
|
|
78098
78135
|
|
|
78099
78136
|
// src/update/version.ts
|
|
78100
|
-
var PLUGIN_VERSION = true ? "0.1.
|
|
78137
|
+
var PLUGIN_VERSION = true ? "0.1.79" : "0.0.0-dev";
|
|
78101
78138
|
|
|
78102
78139
|
// src/update/service.ts
|
|
78103
78140
|
import { TypertRemoteService as TypertRemoteService4 } from "@deepseek-ai/dsh-typert-protocol";
|
|
@@ -4,9 +4,10 @@
|
|
|
4
4
|
* Everything here is inert unless the profile enables it. The recorder is
|
|
5
5
|
* constructed only when `requestLogEnabled` reads true, so a disabled log
|
|
6
6
|
* changes nothing on the request path — not one extra allocation. When
|
|
7
|
-
* enabled,
|
|
8
|
-
*
|
|
9
|
-
*
|
|
7
|
+
* enabled, `start` appends a running placeholder immediately — so a request
|
|
8
|
+
* is visible from the moment it begins, not only after it ends — and
|
|
9
|
+
* `finish` completes that same row in place. The oldest entries are dropped
|
|
10
|
+
* past the configured bound, so the log can never grow with the session.
|
|
10
11
|
*
|
|
11
12
|
* Two facts are structural rather than conventional:
|
|
12
13
|
*
|
|
@@ -81,6 +82,13 @@ export interface RequestLogEntry {
|
|
|
81
82
|
* whole reply still counts in the totals above.
|
|
82
83
|
*/
|
|
83
84
|
readonly streamMs?: number;
|
|
85
|
+
/**
|
|
86
|
+
* Wall time from request start to the moment the fetch actually went out,
|
|
87
|
+
* in milliseconds — the local preparation (catalog, conversion, images).
|
|
88
|
+
* The rest of the first-token wait belongs to the wire and the gateway.
|
|
89
|
+
* Absent when the request never reached the wire (rejected up front).
|
|
90
|
+
*/
|
|
91
|
+
readonly prepMs?: number;
|
|
84
92
|
/** Wall time from request start to terminal event, in milliseconds. */
|
|
85
93
|
readonly durationMs?: number;
|
|
86
94
|
/** Wall time from request start to the first token delta, in milliseconds. */
|
|
@@ -101,7 +109,16 @@ export interface RequestLogEntry {
|
|
|
101
109
|
* misreport a buffered reply as infinitely quick.
|
|
102
110
|
*/
|
|
103
111
|
readonly tps?: number;
|
|
104
|
-
/**
|
|
112
|
+
/**
|
|
113
|
+
* Whether the request is still in flight. Present only while it runs:
|
|
114
|
+
* `finish` removes it. A running row always reads `ok: false` with no
|
|
115
|
+
* `code`, so readers must check this flag before counting a failure.
|
|
116
|
+
*/
|
|
117
|
+
readonly running?: boolean;
|
|
118
|
+
/**
|
|
119
|
+
* Whether the request completed successfully. Always false while
|
|
120
|
+
* `running` is true.
|
|
121
|
+
*/
|
|
105
122
|
readonly ok: boolean;
|
|
106
123
|
/** Failure code (`TIMEOUT`, `TRANSPORT`, …) for a request that did not complete. */
|
|
107
124
|
readonly code?: string;
|
|
@@ -136,7 +153,7 @@ export interface PoolLogEvent {
|
|
|
136
153
|
}
|
|
137
154
|
/** What the viewer reads: the entries plus the counters a rate needs. */
|
|
138
155
|
export interface RequestLogSnapshot {
|
|
139
|
-
/** Newest first, at most the configured bound. */
|
|
156
|
+
/** Newest first, at most the configured bound — finished rows plus running ones. */
|
|
140
157
|
readonly entries: readonly RequestLogEntry[];
|
|
141
158
|
/** Recent pool lifecycle, newest first, capped. */
|
|
142
159
|
readonly poolEvents: readonly PoolLogEvent[];
|
|
@@ -161,11 +178,12 @@ export interface RequestLogGates {
|
|
|
161
178
|
/**
|
|
162
179
|
* An in-flight request, handed back only when the log is enabled.
|
|
163
180
|
*
|
|
164
|
-
*
|
|
165
|
-
*
|
|
166
|
-
*
|
|
167
|
-
*
|
|
168
|
-
* already
|
|
181
|
+
* `start` already appended its running row, so every method mutates the
|
|
182
|
+
* visible entry in place. Every method stays cheap by construction: one
|
|
183
|
+
* `Date.now` read per content delta, field writes for the wire line, and
|
|
184
|
+
* small integer arithmetic at close. No serialization, no hashing, no body
|
|
185
|
+
* handling — the counts ride the usage chunk the adapter already holds,
|
|
186
|
+
* and the timings bracket work that already happened.
|
|
169
187
|
*/
|
|
170
188
|
export interface RequestSpan {
|
|
171
189
|
/**
|
|
@@ -188,7 +206,7 @@ export interface RequestSpan {
|
|
|
188
206
|
/** Mark the response status line's arrival. */
|
|
189
207
|
status(status: number): void;
|
|
190
208
|
/**
|
|
191
|
-
* Close the span and
|
|
209
|
+
* Close the span and complete its running row in place.
|
|
192
210
|
* @param ok - whether the request completed.
|
|
193
211
|
* @param code - failure code for a request that did not complete.
|
|
194
212
|
* @param usage - provider-reported counts, when the terminal usage arrived.
|
|
@@ -254,10 +272,13 @@ export declare class RequestLog {
|
|
|
254
272
|
* Begin one request's span, or nothing when the log is disabled.
|
|
255
273
|
*
|
|
256
274
|
* This is the single gate on the request path: a disabled log costs one
|
|
257
|
-
* method call and one boolean read, and constructs nothing.
|
|
275
|
+
* method call and one boolean read, and constructs nothing. When enabled,
|
|
276
|
+
* the running row is appended before the span is returned, so the viewer
|
|
277
|
+
* sees the request while it runs; `request` and `status` mutate that same
|
|
278
|
+
* visible row as the wire facts arrive.
|
|
258
279
|
*/
|
|
259
280
|
start(detail: RequestStart): RequestSpan | undefined;
|
|
260
|
-
private
|
|
281
|
+
private insert;
|
|
261
282
|
/** Shrink the ring to a new bound immediately; growing backfills nothing. */
|
|
262
283
|
setMaxEntries(bound: number): void;
|
|
263
284
|
/** The current reading, newest first. Computed lazily: nothing on the hot path. */
|
package/package.json
CHANGED