@dan-ai-studio/dshopencodego 0.1.67 → 0.1.69
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 -2
- package/README.md +3 -2
- package/lib/client.js +120 -10
- package/lib/index.js +42 -10
- package/lib/types/request-log/store.d.ts +41 -2
- package/lib/types/usage/meter.d.ts +16 -0
- package/package.json +1 -1
package/README.en.md
CHANGED
|
@@ -146,10 +146,11 @@ The API key comes from the Harness credential store (reference name `OPENCODE_GO
|
|
|
146
146
|
- **Two entries, one page**:
|
|
147
147
|
- **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.
|
|
148
148
|
- **A "Request log" View inside the conversation**: this session's requests, in place — answering "why was that turn slow" without leaving the chat.
|
|
149
|
-
- 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, URL, redacted headers, request shape, result and usage). Filters: model, result, slow requests, session; groups: session, model, result; **click a column head to sort by it**, click again to flip.
|
|
149
|
+
- 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.
|
|
150
150
|
- **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.
|
|
151
|
-
- 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.
|
|
151
|
+
- 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).
|
|
152
152
|
- **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.
|
|
153
|
+
- **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.
|
|
153
154
|
- 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
155
|
- The log is a ring buffer (oldest dropped first) that dies with the process; snapshots are read on demand, never polled, so a page nobody opens costs no traffic.
|
|
155
156
|
- 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 request cut off before any terminal event is not recorded — no total reported, none invented.
|
package/README.md
CHANGED
|
@@ -145,10 +145,11 @@ API Key 通过 Harness 凭证库提供(引用名 `OPENCODE_GO_API_KEY`),
|
|
|
145
145
|
- **两个入口,同一个页面**:
|
|
146
146
|
- **侧边栏图标 → 主区域整页**:跨会话看全部请求。日志是进程级的数据,放在侧边栏最合理——位置固定、常驻可见,和 DSH 自己的插件管理器同一个范式。
|
|
147
147
|
- **会话里的「请求日志」标签页**:就地看**本会话**的请求,回答"刚才那轮为什么慢"不用离开对话。
|
|
148
|
-
- 页面是一整页而不是浮层:标题、统计(请求数/失败数/平均耗时/平均首字/平均 tok/s/平均缓存命中/连接复用率)、筛选栏固定在上方,只有表格自己滚动。每行含时间、模型、会话、输入/输出/缓存、**缓存命中**、**tok/s**、首字、耗时与状态;点一行展开 HTTP
|
|
148
|
+
- 页面是一整页而不是浮层:标题、统计(请求数/失败数/平均耗时/平均首字/平均 tok/s/平均缓存命中/连接复用率)、筛选栏固定在上方,只有表格自己滚动。每行含时间、模型、会话、输入/输出/缓存、**缓存命中**、**tok/s**、首字、耗时与状态;点一行展开 HTTP 明细(方法、协议、地址、请求体大小、脱敏后的请求头、请求构成、结果与用量、推理拆分、缓存写入、单轮花费、内容跨度)。筛选有模型/结果/慢请求/会话,分组有会话/模型/结果;**点列头按该列排序**,再点一次反向。
|
|
149
149
|
- **tok/s** 是**首字到结束**这段解码窗口上的输出速率,口径与 Harness 自己的每轮读数一致(含推理 token)。没有 token 的请求没有速率——不编数。**突发推送同样没有速率**:网关把一整轮攒到最后一次性推过来时(推理模型常见:8 秒思考在 wire 上不可见,然后上百个 token 几毫秒内到达),内容跨度不足 100ms,算出来的"每秒几万 token"不是生成速度——显示为 —,也不计入平均。
|
|
150
|
-
- 会话列显示**会话标题**(标题来自会话目录;拿不到时回退显示 id,悬停可见完整 id)。会话筛选同时匹配标题与 id
|
|
150
|
+
- 会话列显示**会话标题**(标题来自会话目录;拿不到时回退显示 id,悬停可见完整 id)。会话筛选同时匹配标题与 id;按会话分组的组头同样显示标题。**点标题直接打开那个会话**(归档的打不开,会明说)。
|
|
151
151
|
- **缓存命中**是输入 token 里走缓存的占比(缓存读取/(未缓存输入+缓存读取))。网关没报输入就没有占比——同样不编数。
|
|
152
|
+
- **输入列是未缓存的新增 token**:网关把整段 prompt 拆成"未缓存+缓存命中"两部分上报,输入列只记新增的——同一会话前缀缓存全中时输入很小、缓存命中 100%,不是 bug。悬停列头有口径说明。
|
|
152
153
|
- 会话 id **始终完整记录**(它是会话身份,不是凭据;截断后两个会话会读成一个)。
|
|
153
154
|
- 记录的是环形缓冲(超限丢最旧),进程重启即清;只读快照,不轮询,没人打开的页面零流量。
|
|
154
155
|
- 凭据永远不进记录:记录器根本不接收请求头参数,只收白名单里的会话 id 与 UA;失败的请求同样记一条(含错误码),中断到没有终端事件的不记(网关没报总数就不编数)。
|
package/lib/client.js
CHANGED
|
@@ -233,10 +233,14 @@ function parseEntry(value) {
|
|
|
233
233
|
const row = value;
|
|
234
234
|
const shape = row["shape"];
|
|
235
235
|
const headers = row["headers"];
|
|
236
|
+
const protocol = row["protocol"];
|
|
236
237
|
return {
|
|
237
238
|
seq: count("seq", row),
|
|
238
239
|
atMs: count("atMs", row),
|
|
239
240
|
model: text("model", row),
|
|
241
|
+
// Older rows predate the protocol recording; they read without one
|
|
242
|
+
// rather than failing the whole snapshot.
|
|
243
|
+
...protocol === "anthropic-messages" || protocol === "openai-completions" || protocol === "openai-responses" ? { protocol } : {},
|
|
240
244
|
session: text("session", row),
|
|
241
245
|
method: text("method", row),
|
|
242
246
|
url: text("url", row),
|
|
@@ -244,9 +248,14 @@ function parseEntry(value) {
|
|
|
244
248
|
...row["inputTokens"] === void 0 ? {} : { inputTokens: count("inputTokens", row) },
|
|
245
249
|
...row["outputTokens"] === void 0 ? {} : { outputTokens: count("outputTokens", row) },
|
|
246
250
|
...row["cacheReadTokens"] === void 0 ? {} : { cacheReadTokens: count("cacheReadTokens", row) },
|
|
251
|
+
...row["cacheWriteTokens"] === void 0 ? {} : { cacheWriteTokens: count("cacheWriteTokens", row) },
|
|
252
|
+
...row["reasoningTokens"] === void 0 ? {} : { reasoningTokens: count("reasoningTokens", row) },
|
|
253
|
+
...row["costUsd"] === void 0 ? {} : { costUsd: rate("costUsd", row) },
|
|
254
|
+
...row["requestBytes"] === void 0 ? {} : { requestBytes: count("requestBytes", row) },
|
|
247
255
|
...row["durationMs"] === void 0 ? {} : { durationMs: count("durationMs", row) },
|
|
248
256
|
...row["firstTokenMs"] === void 0 ? {} : { firstTokenMs: count("firstTokenMs", row) },
|
|
249
257
|
...row["decodeMs"] === void 0 ? {} : { decodeMs: count("decodeMs", row) },
|
|
258
|
+
...row["streamMs"] === void 0 ? {} : { streamMs: count("streamMs", row) },
|
|
250
259
|
...row["tps"] === void 0 ? {} : { tps: rate("tps", row) },
|
|
251
260
|
ok: row["ok"] === true,
|
|
252
261
|
...typeof row["code"] === "string" ? { code: row["code"] } : {},
|
|
@@ -507,6 +516,7 @@ var en = {
|
|
|
507
516
|
requestLogMaxEntriesHint: "50 to 500; oldest entries are dropped first. Default 200.",
|
|
508
517
|
requestLogOpen: "Open request log",
|
|
509
518
|
requestLogOpenFailed: "The request-log page could not be opened. Use the sidebar entry.",
|
|
519
|
+
requestLogOpenSessionFailed: "That session is archived and cannot be opened.",
|
|
510
520
|
requestLogClose: "Close",
|
|
511
521
|
requestLogRefresh: "Refresh",
|
|
512
522
|
requestLogEmpty: "No requests recorded. Turn the log on, then make a request.",
|
|
@@ -536,22 +546,31 @@ var en = {
|
|
|
536
546
|
requestLogColModel: "Model",
|
|
537
547
|
requestLogColSession: "Session",
|
|
538
548
|
requestLogColIn: "In",
|
|
549
|
+
requestLogColInHint: "Uncached input tokens: what this turn added beyond the cache",
|
|
539
550
|
requestLogColOut: "Out",
|
|
551
|
+
requestLogColOutHint: "Generated output tokens",
|
|
540
552
|
requestLogColCache: "Cached",
|
|
553
|
+
requestLogColCacheHint: "Input tokens served from the cache",
|
|
541
554
|
requestLogColCacheRate: "Cache hit",
|
|
542
555
|
requestLogColLatency: "Latency",
|
|
543
556
|
requestLogColTps: "tok/s",
|
|
557
|
+
requestLogColStream: "Content span",
|
|
544
558
|
requestLogColFirstToken: "First token",
|
|
545
559
|
requestLogColStatus: "Status",
|
|
546
560
|
requestLogDetailRequest: "Request",
|
|
547
561
|
requestLogDetailTiming: "Timing",
|
|
548
562
|
requestLogDetailResult: "Result",
|
|
549
563
|
requestLogDetailMethod: "Method",
|
|
564
|
+
requestLogDetailProtocol: "Protocol",
|
|
550
565
|
requestLogDetailUrl: "URL",
|
|
566
|
+
requestLogDetailRequestBytes: "Request body",
|
|
551
567
|
requestLogDetailSession: "Session",
|
|
552
568
|
requestLogDetailPool: "Pool",
|
|
553
569
|
requestLogDetailError: "Error",
|
|
554
570
|
requestLogDetailTokens: "Tokens",
|
|
571
|
+
requestLogDetailReasoning: "Reasoning",
|
|
572
|
+
requestLogDetailCacheWrite: "Cache write",
|
|
573
|
+
requestLogDetailCost: "Cost",
|
|
555
574
|
requestLogDetailHeaders: "Headers",
|
|
556
575
|
requestLogDetailUserAgent: "User-Agent",
|
|
557
576
|
requestLogDetailShape: "Shape",
|
|
@@ -687,6 +706,7 @@ var zh = {
|
|
|
687
706
|
requestLogMaxEntriesHint: "50\uFF5E500 \u6761\uFF0C\u8D85\u9650\u4E22\u5F03\u6700\u65E7\u7684\u3002\u9ED8\u8BA4 200 \u6761\u3002",
|
|
688
707
|
requestLogOpen: "\u6253\u5F00\u8BF7\u6C42\u65E5\u5FD7",
|
|
689
708
|
requestLogOpenFailed: "\u6253\u4E0D\u5F00\u8BF7\u6C42\u65E5\u5FD7\u9875\u9762\uFF0C\u8BF7\u4ECE\u4FA7\u8FB9\u680F\u7684\u5165\u53E3\u6253\u5F00\u3002",
|
|
709
|
+
requestLogOpenSessionFailed: "\u8BE5\u4F1A\u8BDD\u5DF2\u5F52\u6863\uFF0C\u6253\u4E0D\u5F00\u3002",
|
|
690
710
|
requestLogClose: "\u5173\u95ED",
|
|
691
711
|
requestLogRefresh: "\u5237\u65B0",
|
|
692
712
|
requestLogEmpty: "\u6682\u65E0\u8BB0\u5F55\u3002\u5148\u6253\u5F00\u65E5\u5FD7\u5F00\u5173\uFF0C\u518D\u53D1\u8D77\u4E00\u6B21\u8BF7\u6C42\u3002",
|
|
@@ -716,22 +736,31 @@ var zh = {
|
|
|
716
736
|
requestLogColModel: "\u6A21\u578B",
|
|
717
737
|
requestLogColSession: "\u4F1A\u8BDD",
|
|
718
738
|
requestLogColIn: "\u8F93\u5165",
|
|
739
|
+
requestLogColInHint: "\u672A\u7F13\u5B58\u7684\u8F93\u5165 token\uFF1A\u672C\u8F6E\u65B0\u589E\u7684\uFF0C\u4E0D\u542B\u7F13\u5B58\u547D\u4E2D\u7684\u90E8\u5206",
|
|
719
740
|
requestLogColOut: "\u8F93\u51FA",
|
|
741
|
+
requestLogColOutHint: "\u751F\u6210\u7684\u8F93\u51FA token",
|
|
720
742
|
requestLogColCache: "\u7F13\u5B58",
|
|
743
|
+
requestLogColCacheHint: "\u547D\u4E2D\u7F13\u5B58\u7684\u8F93\u5165 token",
|
|
721
744
|
requestLogColCacheRate: "\u7F13\u5B58\u547D\u4E2D",
|
|
722
745
|
requestLogColLatency: "\u8017\u65F6",
|
|
723
746
|
requestLogColTps: "tok/s",
|
|
747
|
+
requestLogColStream: "\u5185\u5BB9\u8DE8\u5EA6",
|
|
724
748
|
requestLogColFirstToken: "\u9996\u5B57",
|
|
725
749
|
requestLogColStatus: "\u72B6\u6001",
|
|
726
750
|
requestLogDetailRequest: "\u8BF7\u6C42",
|
|
727
751
|
requestLogDetailTiming: "\u8017\u65F6",
|
|
728
752
|
requestLogDetailResult: "\u7ED3\u679C",
|
|
729
753
|
requestLogDetailMethod: "\u65B9\u6CD5",
|
|
754
|
+
requestLogDetailProtocol: "\u534F\u8BAE",
|
|
730
755
|
requestLogDetailUrl: "\u5730\u5740",
|
|
756
|
+
requestLogDetailRequestBytes: "\u8BF7\u6C42\u4F53",
|
|
731
757
|
requestLogDetailSession: "\u4F1A\u8BDD",
|
|
732
758
|
requestLogDetailPool: "\u8FDE\u63A5\u6C60",
|
|
733
759
|
requestLogDetailError: "\u9519\u8BEF",
|
|
734
760
|
requestLogDetailTokens: "Token",
|
|
761
|
+
requestLogDetailReasoning: "\u63A8\u7406",
|
|
762
|
+
requestLogDetailCacheWrite: "\u7F13\u5B58\u5199\u5165",
|
|
763
|
+
requestLogDetailCost: "\u82B1\u8D39",
|
|
735
764
|
requestLogDetailHeaders: "\u8BF7\u6C42\u5934",
|
|
736
765
|
requestLogDetailUserAgent: "User-Agent",
|
|
737
766
|
requestLogDetailShape: "\u8BF7\u6C42\u6784\u6210",
|
|
@@ -2044,10 +2073,10 @@ if (!document.querySelector("style[data-plugin-css=" + JSON.stringify(id2) + "]"
|
|
|
2044
2073
|
const style = document.createElement("style");
|
|
2045
2074
|
style.dataset.plugin = "@dan-ai-studio/dshopencodego";
|
|
2046
2075
|
style.dataset.pluginCss = id2;
|
|
2047
|
-
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_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)}";
|
|
2076
|
+
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)}";
|
|
2048
2077
|
document.head.appendChild(style);
|
|
2049
2078
|
}
|
|
2050
|
-
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", "stuck": "Lx7MOq_stuck", "subtitle": "Lx7MOq_subtitle", "summary": "Lx7MOq_summary", "table": "Lx7MOq_table", "tableWrap": "Lx7MOq_tableWrap", "thSortable": "Lx7MOq_thSortable", "title": "Lx7MOq_title", "toolbar": "Lx7MOq_toolbar" };
|
|
2079
|
+
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" };
|
|
2051
2080
|
|
|
2052
2081
|
// src/client/RequestLogPage.tsx
|
|
2053
2082
|
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
@@ -2101,6 +2130,16 @@ function duration(value) {
|
|
|
2101
2130
|
function rate2(value) {
|
|
2102
2131
|
return value === void 0 ? "\u2014" : value.toFixed(1);
|
|
2103
2132
|
}
|
|
2133
|
+
function cost(value) {
|
|
2134
|
+
if (value === void 0) return "\u2014";
|
|
2135
|
+
return `$${value.toFixed(value < 0.01 ? 6 : 4)}`;
|
|
2136
|
+
}
|
|
2137
|
+
function bytes(value) {
|
|
2138
|
+
if (value === void 0) return "\u2014";
|
|
2139
|
+
if (value < 1024) return `${value} B`;
|
|
2140
|
+
if (value < 1024 * 1024) return `${(value / 1024).toFixed(1)} KB`;
|
|
2141
|
+
return `${(value / (1024 * 1024)).toFixed(2)} MB`;
|
|
2142
|
+
}
|
|
2104
2143
|
function cacheRate(entry) {
|
|
2105
2144
|
if (entry.inputTokens === void 0) return void 0;
|
|
2106
2145
|
const cached = entry.cacheReadTokens ?? 0;
|
|
@@ -2128,7 +2167,7 @@ function groupKey(entry, groupBy) {
|
|
|
2128
2167
|
return void 0;
|
|
2129
2168
|
}
|
|
2130
2169
|
}
|
|
2131
|
-
function RequestLogPage({ readLog, enabled, t, getLocale, sessionId, useSessions }) {
|
|
2170
|
+
function RequestLogPage({ readLog, enabled, canOpenSession, openSession, t, getLocale, sessionId, useSessions }) {
|
|
2132
2171
|
const [snapshot, setSnapshot] = (0, import_react2.useState)(void 0);
|
|
2133
2172
|
const [failure, setFailure] = (0, import_react2.useState)(void 0);
|
|
2134
2173
|
const [refreshing, setRefreshing] = (0, import_react2.useState)(false);
|
|
@@ -2140,6 +2179,7 @@ function RequestLogPage({ readLog, enabled, t, getLocale, sessionId, useSessions
|
|
|
2140
2179
|
const [groupBy, setGroupBy] = (0, import_react2.useState)("none");
|
|
2141
2180
|
const [page, setPage] = (0, import_react2.useState)(0);
|
|
2142
2181
|
const [expanded, setExpanded] = (0, import_react2.useState)(null);
|
|
2182
|
+
const [sessionOpenFailed, setSessionOpenFailed] = (0, import_react2.useState)(false);
|
|
2143
2183
|
const [sortKey, setSortKey] = (0, import_react2.useState)("atMs");
|
|
2144
2184
|
const [sortDir, setSortDir] = (0, import_react2.useState)("desc");
|
|
2145
2185
|
const [onlySession, setOnlySession] = (0, import_react2.useState)(sessionId !== void 0);
|
|
@@ -2231,9 +2271,9 @@ function RequestLogPage({ readLog, enabled, t, getLocale, sessionId, useSessions
|
|
|
2231
2271
|
return value === void 0 ? [] : [value];
|
|
2232
2272
|
}));
|
|
2233
2273
|
const reuse = snapshot !== void 0 && snapshot.totalRecords > 0 ? Math.max(0, Math.min(1, 1 - snapshot.newConnections / snapshot.totalRecords)) : void 0;
|
|
2234
|
-
const head = (key, label) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("th", { className: request_log_default.thSortable, onClick: () => {
|
|
2274
|
+
const head = (key, label, hint) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("th", { className: request_log_default.thSortable, onClick: () => {
|
|
2235
2275
|
toggleSort(key);
|
|
2236
|
-
}, children: [
|
|
2276
|
+
}, title: hint, children: [
|
|
2237
2277
|
label,
|
|
2238
2278
|
sortKey === key ? sortDir === "asc" ? " \u25B2" : " \u25BC" : ""
|
|
2239
2279
|
] });
|
|
@@ -2261,7 +2301,25 @@ function RequestLogPage({ readLog, enabled, t, getLocale, sessionId, useSessions
|
|
|
2261
2301
|
children: [
|
|
2262
2302
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("td", { children: new Date(entry.atMs).toLocaleTimeString(getLocale(), { hour12: false }) }),
|
|
2263
2303
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("td", { className: request_log_default.mono, children: entry.model }),
|
|
2264
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
2304
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
2305
|
+
"td",
|
|
2306
|
+
{
|
|
2307
|
+
title: session.titled ? entry.session : void 0,
|
|
2308
|
+
className: session.titled ? void 0 : request_log_default.mono,
|
|
2309
|
+
children: session.titled && canOpenSession() ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
2310
|
+
"button",
|
|
2311
|
+
{
|
|
2312
|
+
type: "button",
|
|
2313
|
+
className: request_log_default.sessionLink,
|
|
2314
|
+
title: `${session.text} \xB7 ${entry.session}`,
|
|
2315
|
+
onClick: () => {
|
|
2316
|
+
setSessionOpenFailed(!openSession(entry.session));
|
|
2317
|
+
},
|
|
2318
|
+
children: session.text
|
|
2319
|
+
}
|
|
2320
|
+
) : session.text
|
|
2321
|
+
}
|
|
2322
|
+
),
|
|
2265
2323
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("td", { children: count2(entry.inputTokens) }),
|
|
2266
2324
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("td", { children: count2(entry.outputTokens) }),
|
|
2267
2325
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("td", { children: count2(entry.cacheReadTokens) }),
|
|
@@ -2282,10 +2340,18 @@ function RequestLogPage({ readLog, enabled, t, getLocale, sessionId, useSessions
|
|
|
2282
2340
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: request_log_default.detailLabel, children: t("requestLogDetailMethod") }),
|
|
2283
2341
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: `${request_log_default.detailValue} ${request_log_default.mono}`, children: entry.method })
|
|
2284
2342
|
] }),
|
|
2343
|
+
entry.protocol !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: request_log_default.detailItem, children: [
|
|
2344
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: request_log_default.detailLabel, children: t("requestLogDetailProtocol") }),
|
|
2345
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: `${request_log_default.detailValue} ${request_log_default.mono}`, children: entry.protocol })
|
|
2346
|
+
] }),
|
|
2285
2347
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: request_log_default.detailItem, children: [
|
|
2286
2348
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: request_log_default.detailLabel, children: t("requestLogDetailUrl") }),
|
|
2287
2349
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: `${request_log_default.detailValue} ${request_log_default.mono}`, children: entry.url })
|
|
2288
2350
|
] }),
|
|
2351
|
+
entry.requestBytes !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: request_log_default.detailItem, children: [
|
|
2352
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: request_log_default.detailLabel, children: t("requestLogDetailRequestBytes") }),
|
|
2353
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: request_log_default.detailValue, children: bytes(entry.requestBytes) })
|
|
2354
|
+
] }),
|
|
2289
2355
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: request_log_default.detailItem, children: [
|
|
2290
2356
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: request_log_default.detailLabel, children: t("requestLogDetailSession") }),
|
|
2291
2357
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
@@ -2315,7 +2381,15 @@ function RequestLogPage({ readLog, enabled, t, getLocale, sessionId, useSessions
|
|
|
2315
2381
|
] }),
|
|
2316
2382
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: request_log_default.detailItem, children: [
|
|
2317
2383
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: request_log_default.detailLabel, children: t("requestLogDetailTokens") }),
|
|
2318
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.
|
|
2384
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: request_log_default.detailValue, children: [
|
|
2385
|
+
`${count2(entry.inputTokens)} / ${count2(entry.outputTokens)} / ${count2(entry.cacheReadTokens)}`,
|
|
2386
|
+
entry.cacheWriteTokens !== void 0 && ` \xB7 ${t("requestLogDetailCacheWrite")} ${count2(entry.cacheWriteTokens)}`,
|
|
2387
|
+
entry.reasoningTokens !== void 0 && ` \xB7 ${t("requestLogDetailReasoning")} ${count2(entry.reasoningTokens)}`
|
|
2388
|
+
] })
|
|
2389
|
+
] }),
|
|
2390
|
+
entry.costUsd !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: request_log_default.detailItem, children: [
|
|
2391
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: request_log_default.detailLabel, children: t("requestLogDetailCost") }),
|
|
2392
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: request_log_default.detailValue, children: cost(entry.costUsd) })
|
|
2319
2393
|
] }),
|
|
2320
2394
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: request_log_default.detailItem, children: [
|
|
2321
2395
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: request_log_default.detailLabel, children: t("requestLogColCacheRate") }),
|
|
@@ -2325,6 +2399,10 @@ function RequestLogPage({ readLog, enabled, t, getLocale, sessionId, useSessions
|
|
|
2325
2399
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: request_log_default.detailLabel, children: t("requestLogColTps") }),
|
|
2326
2400
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: request_log_default.detailValue, children: rate2(entry.tps) })
|
|
2327
2401
|
] }),
|
|
2402
|
+
entry.streamMs !== void 0 && entry.tps === void 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: request_log_default.detailItem, children: [
|
|
2403
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: request_log_default.detailLabel, children: t("requestLogColStream") }),
|
|
2404
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: request_log_default.detailValue, children: duration(entry.streamMs) })
|
|
2405
|
+
] }),
|
|
2328
2406
|
!entry.ok && entry.code !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: request_log_default.detailItem, children: [
|
|
2329
2407
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: request_log_default.detailLabel, children: t("requestLogDetailError") }),
|
|
2330
2408
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: request_log_default.detailValue, children: entry.code })
|
|
@@ -2356,6 +2434,7 @@ function RequestLogPage({ readLog, enabled, t, getLocale, sessionId, useSessions
|
|
|
2356
2434
|
] }),
|
|
2357
2435
|
!enabled() && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: request_log_default.notice, children: t("requestLogDisabled") }),
|
|
2358
2436
|
failure !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: `${request_log_default.notice} ${request_log_default.noticeError}`, children: failure }),
|
|
2437
|
+
sessionOpenFailed && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: `${request_log_default.notice} ${request_log_default.noticeError}`, children: t("requestLogOpenSessionFailed") }),
|
|
2359
2438
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: request_log_default.summary, children: [
|
|
2360
2439
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: request_log_default.card, children: [
|
|
2361
2440
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: request_log_default.cardLabel, children: t("requestLogStatRequests") }),
|
|
@@ -2513,9 +2592,9 @@ function RequestLogPage({ readLog, enabled, t, getLocale, sessionId, useSessions
|
|
|
2513
2592
|
head("atMs", t("requestLogColTime")),
|
|
2514
2593
|
head("model", t("requestLogColModel")),
|
|
2515
2594
|
head("session", t("requestLogColSession")),
|
|
2516
|
-
head("inputTokens", t("requestLogColIn")),
|
|
2517
|
-
head("outputTokens", t("requestLogColOut")),
|
|
2518
|
-
head("cacheReadTokens", t("requestLogColCache")),
|
|
2595
|
+
head("inputTokens", t("requestLogColIn"), t("requestLogColInHint")),
|
|
2596
|
+
head("outputTokens", t("requestLogColOut"), t("requestLogColOutHint")),
|
|
2597
|
+
head("cacheReadTokens", t("requestLogColCache"), t("requestLogColCacheHint")),
|
|
2519
2598
|
head("cacheRate", t("requestLogColCacheRate")),
|
|
2520
2599
|
head("tps", t("requestLogColTps")),
|
|
2521
2600
|
head("firstTokenMs", t("requestLogColFirstToken")),
|
|
@@ -2754,6 +2833,20 @@ function apply(ctx) {
|
|
|
2754
2833
|
layout.current = void 0;
|
|
2755
2834
|
};
|
|
2756
2835
|
});
|
|
2836
|
+
const sessionOpener = { current: void 0 };
|
|
2837
|
+
ctx.inject(["uiWorkspace"], (workspaceScope) => {
|
|
2838
|
+
sessionOpener.current = workspaceScope.uiWorkspace;
|
|
2839
|
+
return () => {
|
|
2840
|
+
sessionOpener.current = void 0;
|
|
2841
|
+
};
|
|
2842
|
+
});
|
|
2843
|
+
const sessionArchive = { current: void 0 };
|
|
2844
|
+
ctx.inject(["workspaces"], (archiveScope) => {
|
|
2845
|
+
sessionArchive.current = archiveScope.workspaces;
|
|
2846
|
+
return () => {
|
|
2847
|
+
sessionArchive.current = void 0;
|
|
2848
|
+
};
|
|
2849
|
+
});
|
|
2757
2850
|
const mounted = ctx.remote.$mount({
|
|
2758
2851
|
package: usageRemote.package,
|
|
2759
2852
|
descriptors: [
|
|
@@ -2806,6 +2899,23 @@ function apply(ctx) {
|
|
|
2806
2899
|
if (value === null || typeof value !== "object") return false;
|
|
2807
2900
|
return value.requestLogEnabled === true;
|
|
2808
2901
|
},
|
|
2902
|
+
// Whether a title click can open its session. The navigation service
|
|
2903
|
+
// is a separate composition member; without it titles stay text.
|
|
2904
|
+
canOpenSession: () => sessionOpener.current !== void 0,
|
|
2905
|
+
// Select a session and show its conversation: the same call the
|
|
2906
|
+
// sidebar's own rows make. Archived sessions refuse, and a throw
|
|
2907
|
+
// leaves the selection where it was — both read as false.
|
|
2908
|
+
openSession: (sessionId) => {
|
|
2909
|
+
const opener = sessionOpener.current;
|
|
2910
|
+
if (opener === void 0) return false;
|
|
2911
|
+
if (sessionArchive.current?.list.getSnapshot().archivedSessionIds.includes(sessionId) === true) return false;
|
|
2912
|
+
try {
|
|
2913
|
+
opener.openSession(sessionId);
|
|
2914
|
+
return true;
|
|
2915
|
+
} catch {
|
|
2916
|
+
return false;
|
|
2917
|
+
}
|
|
2918
|
+
},
|
|
2809
2919
|
t: (key) => translate(key),
|
|
2810
2920
|
getLocale: () => scope.locale.getLocale().active
|
|
2811
2921
|
};
|
package/lib/index.js
CHANGED
|
@@ -76522,6 +76522,10 @@ function spendOf(usage, rates2) {
|
|
|
76522
76522
|
const cacheWrite = usage.cacheWriteTokens ?? 0;
|
|
76523
76523
|
return (usage.inputTokens * rates2.input + usage.outputTokens * rates2.output + cacheRead * (rates2.cacheRead ?? fallback) + cacheWrite * (rates2.cacheWrite ?? fallback)) / 1e6;
|
|
76524
76524
|
}
|
|
76525
|
+
function costOf(usage, rates2) {
|
|
76526
|
+
if (rates2 === void 0) return void 0;
|
|
76527
|
+
return spendOf(usage, rates2);
|
|
76528
|
+
}
|
|
76525
76529
|
function add(totals, usage) {
|
|
76526
76530
|
const inputTokens = totals.inputTokens + usage.inputTokens;
|
|
76527
76531
|
const outputTokens = totals.outputTokens + usage.outputTokens;
|
|
@@ -76600,6 +76604,12 @@ function requestUrl(input) {
|
|
|
76600
76604
|
if (input instanceof URL) return input.toString();
|
|
76601
76605
|
return input.url;
|
|
76602
76606
|
}
|
|
76607
|
+
function bodyBytes(body) {
|
|
76608
|
+
if (typeof body === "string") return Buffer.byteLength(body);
|
|
76609
|
+
if (body instanceof ArrayBuffer) return body.byteLength;
|
|
76610
|
+
if (ArrayBuffer.isView(body)) return body.byteLength;
|
|
76611
|
+
return void 0;
|
|
76612
|
+
}
|
|
76603
76613
|
async function* observingTerminalUsage(events, observe) {
|
|
76604
76614
|
for await (const event of events) {
|
|
76605
76615
|
if (event.type === "done") observe(event.message.usage);
|
|
@@ -76773,6 +76783,7 @@ var OpencodeGoAdapter = class extends LlmAdapter {
|
|
|
76773
76783
|
const span = this.options.requestLog?.start({
|
|
76774
76784
|
model: options.model,
|
|
76775
76785
|
sessionId: options.sessionId === void 0 ? void 0 : String(options.sessionId),
|
|
76786
|
+
protocol: facts.api,
|
|
76776
76787
|
method: "POST",
|
|
76777
76788
|
url: config.baseURL,
|
|
76778
76789
|
messageCount: options.messages.length,
|
|
@@ -76788,7 +76799,7 @@ var OpencodeGoAdapter = class extends LlmAdapter {
|
|
|
76788
76799
|
const base = fetchFactory();
|
|
76789
76800
|
return (async (input, init) => {
|
|
76790
76801
|
const response = await base(input, init);
|
|
76791
|
-
span.request(typeof init?.method === "string" ? init.method : "POST", requestUrl(input));
|
|
76802
|
+
span.request(typeof init?.method === "string" ? init.method : "POST", requestUrl(input), bodyBytes(init?.body));
|
|
76792
76803
|
span.status(response.status);
|
|
76793
76804
|
return response;
|
|
76794
76805
|
});
|
|
@@ -76868,14 +76879,18 @@ var OpencodeGoAdapter = class extends LlmAdapter {
|
|
|
76868
76879
|
} else if (result.value.type === "finish") {
|
|
76869
76880
|
const reason = result.value.reason;
|
|
76870
76881
|
const ok = reason.kind === "stop" || reason.kind === "tool-calls" || reason.kind === "max-tokens";
|
|
76882
|
+
const usage = pendingUsage === void 0 ? void 0 : {
|
|
76883
|
+
inputTokens: pendingUsage.inputTokens,
|
|
76884
|
+
outputTokens: pendingUsage.outputTokens,
|
|
76885
|
+
...pendingUsage.cacheReadTokens === void 0 ? {} : { cacheReadTokens: pendingUsage.cacheReadTokens },
|
|
76886
|
+
...pendingUsage.cacheWriteTokens === void 0 ? {} : { cacheWriteTokens: pendingUsage.cacheWriteTokens },
|
|
76887
|
+
...reportedReasoning === void 0 || reportedReasoning <= 0 ? {} : { reasoningTokens: reportedReasoning }
|
|
76888
|
+
};
|
|
76871
76889
|
span?.finish(
|
|
76872
76890
|
ok,
|
|
76873
76891
|
ok ? void 0 : reason.failure?.code,
|
|
76874
|
-
|
|
76875
|
-
|
|
76876
|
-
outputTokens: pendingUsage.outputTokens,
|
|
76877
|
-
...pendingUsage.cacheReadTokens === void 0 ? {} : { cacheReadTokens: pendingUsage.cacheReadTokens }
|
|
76878
|
-
}
|
|
76892
|
+
usage,
|
|
76893
|
+
usage === void 0 ? void 0 : costOf(usage, rates2)
|
|
76879
76894
|
);
|
|
76880
76895
|
}
|
|
76881
76896
|
yield result.value;
|
|
@@ -77111,10 +77126,14 @@ function parseEntry(value) {
|
|
|
77111
77126
|
const row = value;
|
|
77112
77127
|
const shape = row["shape"];
|
|
77113
77128
|
const headers = row["headers"];
|
|
77129
|
+
const protocol = row["protocol"];
|
|
77114
77130
|
return {
|
|
77115
77131
|
seq: count("seq", row),
|
|
77116
77132
|
atMs: count("atMs", row),
|
|
77117
77133
|
model: text("model", row),
|
|
77134
|
+
// Older rows predate the protocol recording; they read without one
|
|
77135
|
+
// rather than failing the whole snapshot.
|
|
77136
|
+
...protocol === "anthropic-messages" || protocol === "openai-completions" || protocol === "openai-responses" ? { protocol } : {},
|
|
77118
77137
|
session: text("session", row),
|
|
77119
77138
|
method: text("method", row),
|
|
77120
77139
|
url: text("url", row),
|
|
@@ -77122,9 +77141,14 @@ function parseEntry(value) {
|
|
|
77122
77141
|
...row["inputTokens"] === void 0 ? {} : { inputTokens: count("inputTokens", row) },
|
|
77123
77142
|
...row["outputTokens"] === void 0 ? {} : { outputTokens: count("outputTokens", row) },
|
|
77124
77143
|
...row["cacheReadTokens"] === void 0 ? {} : { cacheReadTokens: count("cacheReadTokens", row) },
|
|
77144
|
+
...row["cacheWriteTokens"] === void 0 ? {} : { cacheWriteTokens: count("cacheWriteTokens", row) },
|
|
77145
|
+
...row["reasoningTokens"] === void 0 ? {} : { reasoningTokens: count("reasoningTokens", row) },
|
|
77146
|
+
...row["costUsd"] === void 0 ? {} : { costUsd: rate("costUsd", row) },
|
|
77147
|
+
...row["requestBytes"] === void 0 ? {} : { requestBytes: count("requestBytes", row) },
|
|
77125
77148
|
...row["durationMs"] === void 0 ? {} : { durationMs: count("durationMs", row) },
|
|
77126
77149
|
...row["firstTokenMs"] === void 0 ? {} : { firstTokenMs: count("firstTokenMs", row) },
|
|
77127
77150
|
...row["decodeMs"] === void 0 ? {} : { decodeMs: count("decodeMs", row) },
|
|
77151
|
+
...row["streamMs"] === void 0 ? {} : { streamMs: count("streamMs", row) },
|
|
77128
77152
|
...row["tps"] === void 0 ? {} : { tps: rate("tps", row) },
|
|
77129
77153
|
ok: row["ok"] === true,
|
|
77130
77154
|
...typeof row["code"] === "string" ? { code: row["code"] } : {},
|
|
@@ -77713,6 +77737,7 @@ var RequestLog = class {
|
|
|
77713
77737
|
let lastTokenAt;
|
|
77714
77738
|
let method;
|
|
77715
77739
|
let url;
|
|
77740
|
+
let requestBytes;
|
|
77716
77741
|
let status;
|
|
77717
77742
|
let finished = false;
|
|
77718
77743
|
return {
|
|
@@ -77721,16 +77746,17 @@ var RequestLog = class {
|
|
|
77721
77746
|
if (firstTokenAt === void 0) firstTokenAt = now;
|
|
77722
77747
|
lastTokenAt = now;
|
|
77723
77748
|
},
|
|
77724
|
-
request: (nextMethod, nextUrl) => {
|
|
77749
|
+
request: (nextMethod, nextUrl, nextRequestBytes) => {
|
|
77725
77750
|
if (method === void 0) {
|
|
77726
77751
|
method = nextMethod;
|
|
77727
77752
|
url = nextUrl;
|
|
77753
|
+
requestBytes = nextRequestBytes;
|
|
77728
77754
|
}
|
|
77729
77755
|
},
|
|
77730
77756
|
status: (value) => {
|
|
77731
77757
|
status = value;
|
|
77732
77758
|
},
|
|
77733
|
-
finish: (ok, code, usage) => {
|
|
77759
|
+
finish: (ok, code, usage, costUsd) => {
|
|
77734
77760
|
if (finished) return;
|
|
77735
77761
|
finished = true;
|
|
77736
77762
|
const endedAt = Date.now();
|
|
@@ -77741,6 +77767,7 @@ var RequestLog = class {
|
|
|
77741
77767
|
seq: ++this.seq,
|
|
77742
77768
|
atMs: startedAt,
|
|
77743
77769
|
model: detail.model,
|
|
77770
|
+
...detail.protocol === void 0 ? {} : { protocol: detail.protocol },
|
|
77744
77771
|
session,
|
|
77745
77772
|
method: method ?? detail.method ?? "POST",
|
|
77746
77773
|
url: url ?? detail.url ?? "",
|
|
@@ -77748,11 +77775,16 @@ var RequestLog = class {
|
|
|
77748
77775
|
...usage === void 0 ? {} : {
|
|
77749
77776
|
inputTokens: usage.inputTokens,
|
|
77750
77777
|
outputTokens: usage.outputTokens,
|
|
77751
|
-
...usage.cacheReadTokens === void 0 || usage.cacheReadTokens <= 0 ? {} : { cacheReadTokens: usage.cacheReadTokens }
|
|
77778
|
+
...usage.cacheReadTokens === void 0 || usage.cacheReadTokens <= 0 ? {} : { cacheReadTokens: usage.cacheReadTokens },
|
|
77779
|
+
...usage.cacheWriteTokens === void 0 || usage.cacheWriteTokens <= 0 ? {} : { cacheWriteTokens: usage.cacheWriteTokens },
|
|
77780
|
+
...usage.reasoningTokens === void 0 || usage.reasoningTokens <= 0 ? {} : { reasoningTokens: usage.reasoningTokens }
|
|
77752
77781
|
},
|
|
77782
|
+
...costUsd === void 0 ? {} : { costUsd },
|
|
77783
|
+
...requestBytes === void 0 ? {} : { requestBytes },
|
|
77753
77784
|
durationMs: Math.max(0, endedAt - startedAt),
|
|
77754
77785
|
...firstTokenAt === void 0 ? {} : { firstTokenMs: Math.max(0, firstTokenAt - startedAt) },
|
|
77755
77786
|
...decodeMs === void 0 ? {} : { decodeMs },
|
|
77787
|
+
...streamMs === void 0 ? {} : { streamMs },
|
|
77756
77788
|
...tps === void 0 ? {} : { tps },
|
|
77757
77789
|
ok,
|
|
77758
77790
|
...code === void 0 ? {} : { code },
|
|
@@ -77893,7 +77925,7 @@ function createUpdateChecker(deps) {
|
|
|
77893
77925
|
}
|
|
77894
77926
|
|
|
77895
77927
|
// src/update/version.ts
|
|
77896
|
-
var PLUGIN_VERSION = true ? "0.1.
|
|
77928
|
+
var PLUGIN_VERSION = true ? "0.1.69" : "0.0.0-dev";
|
|
77897
77929
|
|
|
77898
77930
|
// src/update/service.ts
|
|
77899
77931
|
import { TypertRemoteService as TypertRemoteService4 } from "@deepseek-ai/dsh-typert-protocol";
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
*
|
|
20
20
|
* @module @dan-ai-studio/dshopencodego/request-log/store
|
|
21
21
|
*/
|
|
22
|
+
import type { WireProtocol } from '../catalog/protocol.ts';
|
|
22
23
|
/** One recorded request. Counts are provider-reported; timings are Host-measured. */
|
|
23
24
|
export interface RequestLogEntry {
|
|
24
25
|
/** Monotonic sequence within this process; gaps mean nothing was recorded. */
|
|
@@ -27,6 +28,11 @@ export interface RequestLogEntry {
|
|
|
27
28
|
readonly atMs: number;
|
|
28
29
|
/** The model id the request was made with. */
|
|
29
30
|
readonly model: string;
|
|
31
|
+
/**
|
|
32
|
+
* The wire protocol the request ran on, as the catalog resolved it.
|
|
33
|
+
* Present from the build that records it; older rows simply lack it.
|
|
34
|
+
*/
|
|
35
|
+
readonly protocol?: WireProtocol;
|
|
30
36
|
/** The session id the request belonged to, whole. */
|
|
31
37
|
readonly session: string;
|
|
32
38
|
/** HTTP method used. */
|
|
@@ -41,6 +47,32 @@ export interface RequestLogEntry {
|
|
|
41
47
|
readonly outputTokens?: number;
|
|
42
48
|
/** Provider-reported cache-read tokens, when positive. */
|
|
43
49
|
readonly cacheReadTokens?: number;
|
|
50
|
+
/** Provider-reported cache-write tokens, when positive. */
|
|
51
|
+
readonly cacheWriteTokens?: number;
|
|
52
|
+
/**
|
|
53
|
+
* Provider-reported reasoning tokens, when positive. A subset of
|
|
54
|
+
* `outputTokens` — never added to a total — recorded so a thinking turn's
|
|
55
|
+
* shape stays visible.
|
|
56
|
+
*/
|
|
57
|
+
readonly reasoningTokens?: number;
|
|
58
|
+
/**
|
|
59
|
+
* Estimated USD for this request at the rates in force when it ran, by the
|
|
60
|
+
* same function that prices the since-boot meter. Absent for unpriced
|
|
61
|
+
* models: no zero standing in for "unknown".
|
|
62
|
+
*/
|
|
63
|
+
readonly costUsd?: number;
|
|
64
|
+
/**
|
|
65
|
+
* Request body size in bytes, as handed to fetch. Explanatory, not
|
|
66
|
+
* sensitive: a multi-megabyte turn uploads slowly no matter the model.
|
|
67
|
+
*/
|
|
68
|
+
readonly requestBytes?: number;
|
|
69
|
+
/**
|
|
70
|
+
* Wall time from the first to the last content token, in milliseconds —
|
|
71
|
+
* the window the output actually streamed in. Absent when fewer than two
|
|
72
|
+
* content tokens arrived; a burst delivery reads here as ~zero while the
|
|
73
|
+
* whole reply still counts in the totals above.
|
|
74
|
+
*/
|
|
75
|
+
readonly streamMs?: number;
|
|
44
76
|
/** Wall time from request start to terminal event, in milliseconds. */
|
|
45
77
|
readonly durationMs?: number;
|
|
46
78
|
/** Wall time from request start to the first token delta, in milliseconds. */
|
|
@@ -141,8 +173,10 @@ export interface RequestSpan {
|
|
|
141
173
|
* Record the wire request line, from the fetch wrapper. Called at most
|
|
142
174
|
* once per span; a second call keeps the first, so retries never rewrite
|
|
143
175
|
* the original line.
|
|
176
|
+
* @param requestBytes - request body size in bytes, when the body was a
|
|
177
|
+
* measurable in-memory value rather than a stream.
|
|
144
178
|
*/
|
|
145
|
-
request(method: string, url: string): void;
|
|
179
|
+
request(method: string, url: string, requestBytes?: number): void;
|
|
146
180
|
/** Mark the response status line's arrival. */
|
|
147
181
|
status(status: number): void;
|
|
148
182
|
/**
|
|
@@ -150,17 +184,22 @@ export interface RequestSpan {
|
|
|
150
184
|
* @param ok - whether the request completed.
|
|
151
185
|
* @param code - failure code for a request that did not complete.
|
|
152
186
|
* @param usage - provider-reported counts, when the terminal usage arrived.
|
|
187
|
+
* @param costUsd - estimated USD at the call's rates, when priced.
|
|
153
188
|
*/
|
|
154
189
|
finish(ok: boolean, code?: string, usage?: {
|
|
155
190
|
readonly inputTokens: number;
|
|
156
191
|
readonly outputTokens: number;
|
|
157
192
|
readonly cacheReadTokens?: number;
|
|
158
|
-
|
|
193
|
+
readonly cacheWriteTokens?: number;
|
|
194
|
+
readonly reasoningTokens?: number;
|
|
195
|
+
}, costUsd?: number): void;
|
|
159
196
|
}
|
|
160
197
|
/** Detail the adapter hands the recorder for one request. */
|
|
161
198
|
export interface RequestStart {
|
|
162
199
|
readonly model: string;
|
|
163
200
|
readonly sessionId: string | undefined;
|
|
201
|
+
/** The wire protocol, as the catalog resolved it for this model. */
|
|
202
|
+
readonly protocol?: WireProtocol;
|
|
164
203
|
/** Fallback line, used only when the fetch wrapper never reported the real one. */
|
|
165
204
|
readonly method?: string;
|
|
166
205
|
readonly url?: string;
|
|
@@ -123,6 +123,22 @@ export interface GoMeter {
|
|
|
123
123
|
/** Per-model entries, busiest first. */
|
|
124
124
|
readonly models: readonly MeterModelEntry[];
|
|
125
125
|
}
|
|
126
|
+
/**
|
|
127
|
+
* What one call's tokens cost at the rates in force for it, without touching
|
|
128
|
+
* any accumulator.
|
|
129
|
+
*
|
|
130
|
+
* The request log prices one row through this while the meter keeps the
|
|
131
|
+
* since-boot totals: same function, same rates, so the two can never
|
|
132
|
+
* disagree. Unpriced calls have no estimate rather than a zero one.
|
|
133
|
+
* @param usage - the call's counts; reasoning tokens ride inside `output`
|
|
134
|
+
* and are never billed twice.
|
|
135
|
+
* @param rates - the rates published for the model at call time.
|
|
136
|
+
* @returns estimated USD, or undefined when the model published no rates.
|
|
137
|
+
*/
|
|
138
|
+
export declare function costOf(usage: Pick<TokenUsage, 'inputTokens' | 'outputTokens'> & {
|
|
139
|
+
readonly cacheReadTokens?: number;
|
|
140
|
+
readonly cacheWriteTokens?: number;
|
|
141
|
+
}, rates: MeterRates | undefined): number | undefined;
|
|
126
142
|
/** Accumulates provider-reported usage for the lifetime of one Host process. */
|
|
127
143
|
export declare class UsageMeter {
|
|
128
144
|
private readonly sinceMs;
|
package/package.json
CHANGED