dsh-api-dashboard 1.4.2 → 1.4.3
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.md +1 -1
- package/client/client.js +5 -3
- package/package.json +1 -1
- package/src/index.js +65 -51
package/README.md
CHANGED
|
@@ -125,7 +125,7 @@ dsh plugin --profile web add link:/root/dsha-api-dashboard
|
|
|
125
125
|
|
|
126
126
|
## 更新日志
|
|
127
127
|
|
|
128
|
-
最近一版 **v1.4.
|
|
128
|
+
最近一版 **v1.4.3** —— 子代理胶囊只显示**它自己新产生**的消耗(不再把父会话继承来的历史、兄弟或后代子代理的消耗算进去)。一版一行的完整历史见 [`CHANGELOG.md`](CHANGELOG.md)。
|
|
129
129
|
|
|
130
130
|
## License
|
|
131
131
|
|
package/client/client.js
CHANGED
|
@@ -858,13 +858,15 @@ window.__ModuleLoader__.load({
|
|
|
858
858
|
ref: (n) => { if (n) requestAnimationFrame(() => syncSubsOverflow(n)); },
|
|
859
859
|
onScroll: (e) => syncSubsOverflow(e.currentTarget) },
|
|
860
860
|
list.map((s, i) => {
|
|
861
|
-
const f = formatSessionCost({ cost: s.cost, currency: mainCur, costByCurrency: s.costByCurrency, waiting:
|
|
861
|
+
const f = formatSessionCost({ cost: s.cost, currency: mainCur, costByCurrency: s.costByCurrency, waiting: s.waiting === true });
|
|
862
|
+
// waiting(等自身数据) 时 formatSessionCost 直接返回 null → "~—";
|
|
863
|
+
// 真实零消耗时 f 非 null 但 hasValue=false, 也必须回落 "~—"(与 v1.4.x 一致), 不能显示 ~¥0.00
|
|
862
864
|
const amt = f !== null && f.hasValue ? "~" + f.text : "~—";
|
|
863
865
|
const kind = s.mode === "continuable" ? "可续聊子代理" : "一次性子代理";
|
|
864
866
|
const models = (s.models || []).join(", ");
|
|
865
867
|
const tip = [
|
|
866
868
|
kind + " " + (s.label || "子代理"),
|
|
867
|
-
"
|
|
869
|
+
"自身消耗 " + (f !== null ? f.title : "等待自身用量数据"),
|
|
868
870
|
models ? "模型 " + models : "",
|
|
869
871
|
s.id ? "ID " + String(s.id).slice(0, 8) : "",
|
|
870
872
|
].filter(Boolean).join("\n");
|
|
@@ -2645,7 +2647,7 @@ window.__ModuleLoader__.load({
|
|
|
2645
2647
|
|
|
2646
2648
|
// v1.4.0: 子代理消耗行 —— 有子代理时在状态条**下面换行**追加一行,
|
|
2647
2649
|
// 按父会话里的创建顺序从左到右排列 (顺序由服务端 subagentCatalog 决定, 这里不排序)。
|
|
2648
|
-
//
|
|
2650
|
+
// 只计该子代理自身新用量,不含继承历史、兄弟或后代。
|
|
2649
2651
|
const subRow = buildSubagentRow(cost);
|
|
2650
2652
|
|
|
2651
2653
|
return react.createElement("span", { className: "dshadb_barwrap" }, [
|
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -1726,7 +1726,8 @@ const readSessionCacheRecord = (sessionId) => {
|
|
|
1726
1726
|
|
|
1727
1727
|
/** 冷路径: 从缓存文件里取该会话的 queryBalanceCost **状态**(不是 wire 视图)。 */
|
|
1728
1728
|
const cachedCostState = (sessionId) => {
|
|
1729
|
-
const
|
|
1729
|
+
const row = readSessionCacheRecord(sessionId)?.queryBalanceCost
|
|
1730
|
+
const val = row?.ver === 3 ? row.val : null
|
|
1730
1731
|
return (val !== null && typeof val === 'object' && Array.isArray(val.modelOrder) && val.byModel !== null && typeof val.byModel === 'object') ? val : null
|
|
1731
1732
|
}
|
|
1732
1733
|
|
|
@@ -1745,7 +1746,7 @@ const cachedCatalog = (sessionId) => {
|
|
|
1745
1746
|
}))
|
|
1746
1747
|
}
|
|
1747
1748
|
|
|
1748
|
-
export function collectSubagentCosts(services, rootSessionId, summarize) {
|
|
1749
|
+
export function collectSubagentCosts(services, rootSessionId, summarize, ownChildIds) {
|
|
1749
1750
|
const out = []
|
|
1750
1751
|
if (typeof rootSessionId !== 'string' || rootSessionId === '') return out
|
|
1751
1752
|
let sessions = null, projections = null
|
|
@@ -1779,32 +1780,26 @@ export function collectSubagentCosts(services, rootSessionId, summarize) {
|
|
|
1779
1780
|
if (s !== null && projections !== null) {
|
|
1780
1781
|
try {
|
|
1781
1782
|
const st = projections.stateOf(s, 'queryBalanceCost')
|
|
1782
|
-
if (st !== null && st !== undefined
|
|
1783
|
+
if (st !== null && st !== undefined) return st
|
|
1783
1784
|
} catch { /* 落到冷路径 */ }
|
|
1784
1785
|
}
|
|
1785
1786
|
return cachedCostState(sessionId)
|
|
1786
1787
|
}
|
|
1787
1788
|
|
|
1788
|
-
/** 递归汇总: 自身 + 后代, 返回与 summarize 同形的汇总。 */
|
|
1789
|
-
const rollup = (sessionId, depth) => {
|
|
1790
|
-
const st = costStateOf(sessionId)
|
|
1791
|
-
let acc = (st !== null && subagentCostSummarize !== null) ? subagentCostSummarize(st) : null
|
|
1792
|
-
if (depth >= SUBAGENT_MAX_DEPTH) return acc
|
|
1793
|
-
for (const entry of childrenOf(sessionId)) {
|
|
1794
|
-
acc = mergeSummary(acc, rollup(entry.id, depth + 1))
|
|
1795
|
-
}
|
|
1796
|
-
return acc
|
|
1797
|
-
}
|
|
1798
|
-
|
|
1799
1789
|
for (const entry of childrenOf(rootSessionId)) {
|
|
1800
1790
|
if (out.length >= SUBAGENT_MAX_ROWS) break
|
|
1801
|
-
|
|
1791
|
+
if (Array.isArray(ownChildIds) && !ownChildIds.includes(entry.id)) continue
|
|
1792
|
+
const st = costStateOf(entry.id)
|
|
1793
|
+
const ready = st?.ownBoundaryKnown === true && st?.own && Array.isArray(st.own.modelOrder)
|
|
1794
|
+
const waiting = !ready || st.own.modelOrder.length === 0
|
|
1795
|
+
const s = ready ? summarize(st.own) : emptySummary()
|
|
1802
1796
|
out.push({
|
|
1803
1797
|
id: String(entry.id),
|
|
1804
1798
|
label: typeof entry.label === 'string' && entry.label !== '' ? entry.label : String(entry.id).slice(0, 12),
|
|
1805
1799
|
mode: entry.mode === 'continuable' ? 'continuable' : 'one-shot',
|
|
1806
1800
|
createdAt: typeof entry.createdAt === 'number' ? entry.createdAt : 0,
|
|
1807
|
-
cost: s.cost,
|
|
1801
|
+
cost: waiting ? -1 : s.cost,
|
|
1802
|
+
waiting,
|
|
1808
1803
|
costByCurrency: s.costByCurrency,
|
|
1809
1804
|
currencyByModel: s.currencyByModel,
|
|
1810
1805
|
mixedCurrency: s.mixedCurrency,
|
|
@@ -1952,39 +1947,7 @@ export function makeCostProjection(configOrGetter, services) {
|
|
|
1952
1947
|
}
|
|
1953
1948
|
}
|
|
1954
1949
|
|
|
1955
|
-
|
|
1956
|
-
key: 'queryBalanceCost',
|
|
1957
|
-
// 框架要求的投影定义 API: stateSchema(内部状态) + wire.{viewSchema,view}(客户端可见视图)。
|
|
1958
|
-
// 旧版误用顶层 schema+view, 导致 wire 缺失, 服务端 drive 永不通知、客户端永远拿不到值。
|
|
1959
|
-
stateSchema: z.object({
|
|
1960
|
-
currentModel: z.string().nullable(),
|
|
1961
|
-
currentProvider: z.string().nullable(),
|
|
1962
|
-
last: z.object({
|
|
1963
|
-
turn: z.number(),
|
|
1964
|
-
step: z.number(),
|
|
1965
|
-
model: z.string(),
|
|
1966
|
-
buckets: z.object({
|
|
1967
|
-
uncachedInputTokens: z.number(),
|
|
1968
|
-
cacheReadTokens: z.number(),
|
|
1969
|
-
cacheWriteTokens: z.number(),
|
|
1970
|
-
outputTokens: z.number(),
|
|
1971
|
-
}),
|
|
1972
|
-
}).nullable(),
|
|
1973
|
-
byModel: z.record(z.string(), z.object({
|
|
1974
|
-
uncachedInputTokens: z.number(),
|
|
1975
|
-
cacheReadTokens: z.number(),
|
|
1976
|
-
cacheWriteTokens: z.number(),
|
|
1977
|
-
outputTokens: z.number(),
|
|
1978
|
-
})),
|
|
1979
|
-
modelOrder: z.array(z.string()),
|
|
1980
|
-
/** v1.4.0: 本投影所属会话 id —— 子代理汇总要拿它去查 `subagentCatalog`。空串表示未知。 */
|
|
1981
|
-
sessionId: z.string(),
|
|
1982
|
-
}),
|
|
1983
|
-
init: (header) => ({
|
|
1984
|
-
currentModel: null, currentProvider: null, last: null, byModel: {}, modelOrder: [],
|
|
1985
|
-
sessionId: typeof header?.id === 'string' ? header.id : '',
|
|
1986
|
-
}),
|
|
1987
|
-
apply: (state, event) => {
|
|
1950
|
+
const foldUsage = (state, event) => {
|
|
1988
1951
|
// v1.4.0: `llm/retry-started` 关闭「替换槽位」—— 被重试的那次 attempt 的用量要**留在总量里**,
|
|
1989
1952
|
// 下一次 attempt 是**新增**而不是替换。与 dsh-token-meter 的 usage-projection 对齐。
|
|
1990
1953
|
if (event.type === 'llm/retry-started') {
|
|
@@ -2027,6 +1990,56 @@ export function makeCostProjection(configOrGetter, services) {
|
|
|
2027
1990
|
if (prev !== null) byModel = { ...byModel, [prev.model]: subBuckets(byModel[prev.model] ?? zero(), prev.buckets) }
|
|
2028
1991
|
byModel = { ...byModel, [model]: addBuckets(byModel[model] ?? zero(), buckets) }
|
|
2029
1992
|
return { ...state, currentModel: nextModel, currentProvider: nextProvider, last: { turn, step, model, buckets }, byModel, modelOrder: isNewModel ? [...state.modelOrder, model] : state.modelOrder }
|
|
1993
|
+
}
|
|
1994
|
+
|
|
1995
|
+
return {
|
|
1996
|
+
key: 'queryBalanceCost',
|
|
1997
|
+
// 框架要求的投影定义 API: stateSchema(内部状态) + wire.{viewSchema,view}(客户端可见视图)。
|
|
1998
|
+
// 旧版误用顶层 schema+view, 导致 wire 缺失, 服务端 drive 永不通知、客户端永远拿不到值。
|
|
1999
|
+
stateSchema: z.object({
|
|
2000
|
+
currentModel: z.string().nullable(),
|
|
2001
|
+
currentProvider: z.string().nullable(),
|
|
2002
|
+
last: z.object({
|
|
2003
|
+
turn: z.number(),
|
|
2004
|
+
step: z.number(),
|
|
2005
|
+
model: z.string(),
|
|
2006
|
+
buckets: z.object({
|
|
2007
|
+
uncachedInputTokens: z.number(),
|
|
2008
|
+
cacheReadTokens: z.number(),
|
|
2009
|
+
cacheWriteTokens: z.number(),
|
|
2010
|
+
outputTokens: z.number(),
|
|
2011
|
+
}),
|
|
2012
|
+
}).nullable(),
|
|
2013
|
+
byModel: z.record(z.string(), z.object({
|
|
2014
|
+
uncachedInputTokens: z.number(),
|
|
2015
|
+
cacheReadTokens: z.number(),
|
|
2016
|
+
cacheWriteTokens: z.number(),
|
|
2017
|
+
outputTokens: z.number(),
|
|
2018
|
+
})),
|
|
2019
|
+
modelOrder: z.array(z.string()),
|
|
2020
|
+
/** v1.4.0: 本投影所属会话 id —— 子代理汇总要拿它去查 `subagentCatalog`。空串表示未知。 */
|
|
2021
|
+
sessionId: z.string(),
|
|
2022
|
+
inheritedEventCount: z.number().int().nonnegative(),
|
|
2023
|
+
ownBoundaryKnown: z.boolean(),
|
|
2024
|
+
ownChildIds: z.array(z.string()),
|
|
2025
|
+
own: z.object({ currentModel: z.string().nullable(), currentProvider: z.string().nullable(), last: z.any().nullable(), byModel: z.record(z.string(), z.object({ uncachedInputTokens: z.number().nonnegative(), cacheReadTokens: z.number().nonnegative(), cacheWriteTokens: z.number().nonnegative(), outputTokens: z.number().nonnegative() })), modelOrder: z.array(z.string()) }),
|
|
2026
|
+
}),
|
|
2027
|
+
init: (header, inheritedEventCount) => ({
|
|
2028
|
+
currentModel: null, currentProvider: null, last: null, byModel: {}, modelOrder: [],
|
|
2029
|
+
sessionId: typeof header?.id === 'string' ? header.id : '',
|
|
2030
|
+
inheritedEventCount: Number.isSafeInteger(inheritedEventCount) && inheritedEventCount >= 0 ? inheritedEventCount : 0,
|
|
2031
|
+
ownBoundaryKnown: (Number.isSafeInteger(inheritedEventCount) && inheritedEventCount >= 0) || !header?.isSeeded,
|
|
2032
|
+
ownChildIds: [],
|
|
2033
|
+
own: { currentModel: null, currentProvider: null, last: null, byModel: {}, modelOrder: [] },
|
|
2034
|
+
}),
|
|
2035
|
+
apply: (state, event) => {
|
|
2036
|
+
const full = foldUsage(state, event)
|
|
2037
|
+
const isOwn = state.ownBoundaryKnown && (state.inheritedEventCount === 0 || (Number.isSafeInteger(event.seq) && event.seq >= state.inheritedEventCount))
|
|
2038
|
+
// Inherited model context is useful; inherited usage and retry slots are not.
|
|
2039
|
+
const own = isOwn ? foldUsage(state.own, event) : { ...state.own, currentModel: full.currentModel, currentProvider: full.currentProvider }
|
|
2040
|
+
const childId = isOwn && event.type === 'subagent/catalog' ? event.data?.childId : null
|
|
2041
|
+
const ownChildIds = typeof childId === 'string' && !state.ownChildIds.includes(childId) ? [...state.ownChildIds, childId] : state.ownChildIds
|
|
2042
|
+
return { ...full, own, ownChildIds }
|
|
2030
2043
|
},
|
|
2031
2044
|
wire: {
|
|
2032
2045
|
viewSchema: z.object({
|
|
@@ -2055,6 +2068,7 @@ export function makeCostProjection(configOrGetter, services) {
|
|
|
2055
2068
|
mode: z.enum(['one-shot', 'continuable']),
|
|
2056
2069
|
createdAt: z.number(),
|
|
2057
2070
|
cost: z.number(),
|
|
2071
|
+
waiting: z.boolean().optional(),
|
|
2058
2072
|
costByCurrency: z.record(z.string(), z.number().nonnegative()),
|
|
2059
2073
|
currencyByModel: z.record(z.string(), z.string()),
|
|
2060
2074
|
mixedCurrency: z.boolean(),
|
|
@@ -2071,7 +2085,7 @@ export function makeCostProjection(configOrGetter, services) {
|
|
|
2071
2085
|
const cfg = getConfig()
|
|
2072
2086
|
const mainCurrency = (cfg.currency ?? 'CNY').toUpperCase()
|
|
2073
2087
|
// v1.4.0: 子代理消耗 (换行单独展示)。取不到服务/没有子代理 → 空数组。
|
|
2074
|
-
const subagents = collectSubagentCosts(services, state.sessionId, summarize)
|
|
2088
|
+
const subagents = collectSubagentCosts(services, state.sessionId, summarize, state.ownChildIds)
|
|
2075
2089
|
// 无事件时返回 waiting 标记, 客户端据此显示 "~—" 而非 "~¥0"
|
|
2076
2090
|
if (state.modelOrder.length === 0) {
|
|
2077
2091
|
return { models: [], currentModel: state.currentModel ?? null, currentProvider: state.currentProvider ?? null, cost: -1, costByModel: {}, costByCurrency: {}, currencyByModel: {}, mixedCurrency: false, tokens: { uncachedInput: 0, cacheRead: 0, cacheWrite: 0, output: 0 }, tokensByModel: {}, currency: mainCurrency, isPeak: isPeakTime(), waiting: true, subagents }
|
|
@@ -2085,7 +2099,7 @@ export function makeCostProjection(configOrGetter, services) {
|
|
|
2085
2099
|
}
|
|
2086
2100
|
},
|
|
2087
2101
|
},
|
|
2088
|
-
stateVersion:
|
|
2102
|
+
stateVersion: 3,
|
|
2089
2103
|
}
|
|
2090
2104
|
}
|
|
2091
2105
|
|