@gamegeek-saikel/dsh-cost-meter 0.2.1 → 0.3.0-alpha.1
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/lib/client.js +33 -67
- package/lib/client.js.map +1 -1
- package/lib/index.js +13 -22
- package/lib/types/client/AssistantCostChip.d.ts +8 -19
- package/lib/types/client/AssistantCostChip.d.ts.map +1 -1
- package/lib/types/client/AssistantCostChip.js +32 -38
- package/lib/types/client/CostPluginCard.d.ts +1 -1
- package/lib/types/client/CostPluginCard.d.ts.map +1 -1
- package/lib/types/client/CostView.d.ts +1 -1
- package/lib/types/client/CostView.d.ts.map +1 -1
- package/lib/types/client/SessionCostLine.d.ts +1 -4
- package/lib/types/client/SessionCostLine.d.ts.map +1 -1
- package/lib/types/client/SessionCostPill.d.ts +3 -3
- package/lib/types/client/SessionCostPill.d.ts.map +1 -1
- package/lib/types/client/SessionCostPill.js +8 -4
- package/lib/types/client/index.d.ts +8 -7
- package/lib/types/client/index.d.ts.map +1 -1
- package/lib/types/client/index.js +8 -5
- package/lib/types/index.d.ts +2 -1
- package/lib/types/index.d.ts.map +1 -1
- package/lib/types/index.js +14 -12
- package/lib/types/session-cost-projection.d.ts +9 -8
- package/lib/types/session-cost-projection.d.ts.map +1 -1
- package/lib/types/session-cost-projection.js +11 -17
- package/package.json +33 -29
- package/src/client/AssistantCostChip.tsx +37 -48
- package/src/client/CostPluginCard.tsx +1 -1
- package/src/client/CostView.tsx +1 -1
- package/src/client/SessionCostLine.tsx +1 -3
- package/src/client/SessionCostPill.tsx +12 -7
- package/src/client/index.ts +23 -12
- package/src/index.ts +15 -12
- package/src/session-cost-projection.ts +12 -17
package/lib/client.js
CHANGED
|
@@ -323,28 +323,6 @@ window.__ModuleLoader__.load({
|
|
|
323
323
|
if (offPeakTotal === 0) return null;
|
|
324
324
|
return (peak.cacheReadPerMillion + peak.inputPerMillion + peak.outputPerMillion) / offPeakTotal;
|
|
325
325
|
}
|
|
326
|
-
/** CJK characters (the /1.5 density class). */
|
|
327
|
-
const CJK_RE = /[\u3400-\u4dbf\u4e00-\u9fff]/g;
|
|
328
|
-
/**
|
|
329
|
-
* Heuristic character→token estimate: CJK ÷ 1.5, everything else ÷ 4
|
|
330
|
-
* (the streaming estimate's output side).
|
|
331
|
-
* @param text - the accumulated text.
|
|
332
|
-
* @returns the estimated token count.
|
|
333
|
-
*/
|
|
334
|
-
function estimateTokens(text) {
|
|
335
|
-
const cjk = (text.match(CJK_RE) ?? []).length;
|
|
336
|
-
return Math.ceil(cjk / 1.5 + (text.length - cjk) / 4);
|
|
337
|
-
}
|
|
338
|
-
/**
|
|
339
|
-
* Tokens of the streamed text/reasoning blocks of a partial assistant.
|
|
340
|
-
* @param blocks - the partial's content blocks (text/reasoning carry `text`).
|
|
341
|
-
* @returns the estimated token count.
|
|
342
|
-
*/
|
|
343
|
-
function blocksOutputTokens(blocks) {
|
|
344
|
-
let tokens = 0;
|
|
345
|
-
for (const block of blocks ?? []) if ((block.kind === "text" || block.kind === "reasoning") && typeof block.text === "string" && block.text.length > 0) tokens += estimateTokens(block.text);
|
|
346
|
-
return tokens;
|
|
347
|
-
}
|
|
348
326
|
/**
|
|
349
327
|
* The cache-read ratio of the newest completed step (input side of the
|
|
350
328
|
* estimate). Absent history conservatively prices all input as uncached
|
|
@@ -819,44 +797,28 @@ window.__ModuleLoader__.load({
|
|
|
819
797
|
//#endregion
|
|
820
798
|
//#region src/client/AssistantCostChip.tsx
|
|
821
799
|
/**
|
|
822
|
-
* Per-reply cost
|
|
823
|
-
* through the
|
|
824
|
-
*
|
|
825
|
-
* after the
|
|
826
|
-
*
|
|
827
|
-
*
|
|
828
|
-
*
|
|
829
|
-
*
|
|
800
|
+
* Per-reply cost at the end of the completed Turn's timing row. Rendered
|
|
801
|
+
* through the `conversation.chat.turnTail` chain slot — the selector only
|
|
802
|
+
* accepts closed Turns, so the extension appears once the reply settled and
|
|
803
|
+
* sits directly after the timing facts (用时 · 首 token · tok/s), with a dot
|
|
804
|
+
* separator and the same label-tertiary type as the clock text.
|
|
805
|
+
*
|
|
806
|
+
* DSH 0.1.5 removed the per-message assistant-actions slot and replaced the
|
|
807
|
+
* legacy chat-node window with turn/step locations; the cost is therefore
|
|
808
|
+
* addressed per Turn: the anchored step ledger (`sessionCost` projection) is
|
|
809
|
+
* filtered to the Turn's own steps and summed. Every price is the anchored
|
|
810
|
+
* snapshot value, never a current-price recompute; an unpriced Turn renders
|
|
830
811
|
* `—` (the Cost tab explains why).
|
|
831
812
|
*/
|
|
832
|
-
/**
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
* @param snapshot - the conversation snapshot.
|
|
836
|
-
* @param messageId - the addressed message identity.
|
|
837
|
-
* @returns the step coordinates, or null.
|
|
838
|
-
*/
|
|
839
|
-
function stepOfMessage(snapshot, messageId) {
|
|
840
|
-
const nodes = snapshot.chat.legacy.nodes;
|
|
841
|
-
for (let i = nodes.length - 1; i >= 0; i -= 1) {
|
|
842
|
-
const node = nodes[i];
|
|
843
|
-
if (node?.kind === "assistant" && node.messageId === messageId && node.turn !== void 0) return {
|
|
844
|
-
turn: node.turn,
|
|
845
|
-
step: node.step
|
|
846
|
-
};
|
|
847
|
-
}
|
|
848
|
-
return null;
|
|
849
|
-
}
|
|
850
|
-
/** The projection step for one (turn, step) coordinate, if the ledger has it. */
|
|
851
|
-
function stepOf(steps, turn, step) {
|
|
852
|
-
return steps?.find((entry) => entry.turn === turn && entry.step === step);
|
|
813
|
+
/** The Turn's own ledger steps, in fold order. */
|
|
814
|
+
function stepsOfTurn(steps, turn) {
|
|
815
|
+
return steps?.filter((entry) => entry.turn === turn) ?? [];
|
|
853
816
|
}
|
|
854
|
-
const AssistantCostChip = (0, react.memo)(function AssistantCostChip({
|
|
817
|
+
const AssistantCostChip = (0, react.memo)(function AssistantCostChip({ matched, useProjection, t }) {
|
|
855
818
|
const currency = displayCurrency(t);
|
|
856
|
-
const step = useSession((snapshot) => stepOfMessage(snapshot, messageId));
|
|
857
819
|
const costCny = useProjection("sessionCost");
|
|
858
820
|
const costUsd = useProjection("sessionCostUsd");
|
|
859
|
-
const
|
|
821
|
+
const cost = currency === "USD" ? costUsd : costCny;
|
|
860
822
|
const [response, setResponse] = (0, react.useState)(null);
|
|
861
823
|
(0, react.useEffect)(() => {
|
|
862
824
|
let alive = true;
|
|
@@ -868,9 +830,11 @@ window.__ModuleLoader__.load({
|
|
|
868
830
|
alive = false;
|
|
869
831
|
};
|
|
870
832
|
}, [currency]);
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
833
|
+
const steps = stepsOfTurn(cost?.steps, matched.turn);
|
|
834
|
+
const priced = steps.filter((entry) => entry.cost !== null);
|
|
835
|
+
if (steps.length === 0) return null;
|
|
836
|
+
if (priced.length === 0) {
|
|
837
|
+
const reason = steps[0]?.unpricedReason === "NO_MODEL" ? t("reason.NO_MODEL") : t("reason.NO_PRICE");
|
|
874
838
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
875
839
|
className: AssistantCostChip_module_css_default.unpriced,
|
|
876
840
|
"data-cost-chip": true,
|
|
@@ -879,9 +843,12 @@ window.__ModuleLoader__.load({
|
|
|
879
843
|
children: t("chip.unpriced")
|
|
880
844
|
});
|
|
881
845
|
}
|
|
882
|
-
const
|
|
883
|
-
const
|
|
884
|
-
const
|
|
846
|
+
const total = priced.reduce((sum, entry) => sum + (entry.cost ?? 0), 0);
|
|
847
|
+
const amount = `${currencySymbol(currency)}${formatMoney(total)}`;
|
|
848
|
+
const last = priced[priced.length - 1];
|
|
849
|
+
if (last === void 0) return null;
|
|
850
|
+
const band = last.band ?? bandForTime(last.time);
|
|
851
|
+
const ratio = peakOffPeakMultiplier(response?.pricebook?.current ?? null, last.provider, last.model);
|
|
885
852
|
const bandLabel = band === "peak" ? ratio === null ? t("band.peak") : t("price.peakRatio", { multiplier: formatMultiplier(ratio) }) : band === "offPeak" ? ratio === null ? t("band.offPeak") : t("price.offPeakRatio", { multiplier: formatMultiplier(1 / ratio) }) : null;
|
|
886
853
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
887
854
|
className: AssistantCostChip_module_css_default.root,
|
|
@@ -934,7 +901,7 @@ window.__ModuleLoader__.load({
|
|
|
934
901
|
* ratio (conservative all-uncached without history), output at the
|
|
935
902
|
* character heuristic — both at CURRENT snapshot prices.
|
|
936
903
|
*/
|
|
937
|
-
/** Read the
|
|
904
|
+
/** Read the session's running flag (the pill's estimate gate). */
|
|
938
905
|
function runningOf(snapshot) {
|
|
939
906
|
return snapshot.running;
|
|
940
907
|
}
|
|
@@ -970,7 +937,7 @@ window.__ModuleLoader__.load({
|
|
|
970
937
|
const cost = currency === "USD" ? costUsd : costCny;
|
|
971
938
|
const pressure = useProjection("contextPressure");
|
|
972
939
|
const running = useSession(runningOf);
|
|
973
|
-
const partial =
|
|
940
|
+
const partial = null;
|
|
974
941
|
const [open, setOpen] = (0, react.useState)(false);
|
|
975
942
|
const [response, setResponse] = (0, react.useState)(null);
|
|
976
943
|
const wrapRef = (0, react.useRef)(null);
|
|
@@ -1004,7 +971,7 @@ window.__ModuleLoader__.load({
|
|
|
1004
971
|
if (!running || projection === void 0) return null;
|
|
1005
972
|
const bucket = bucketAt(snapshot ?? void 0, model?.provider, model?.model ?? "unknown", Date.now(), schedule);
|
|
1006
973
|
if (bucket === void 0) return null;
|
|
1007
|
-
const stepEstimate = estimateCost(bucket, pressure?.projectedTokens ?? pressure?.pressureTokens ?? 0, cacheReadRatioOf(projection.steps),
|
|
974
|
+
const stepEstimate = estimateCost(bucket, pressure?.projectedTokens ?? pressure?.pressureTokens ?? 0, cacheReadRatioOf(projection.steps), 0);
|
|
1008
975
|
return (combined?.cost ?? 0) + stepEstimate;
|
|
1009
976
|
}, [
|
|
1010
977
|
running,
|
|
@@ -1485,10 +1452,9 @@ window.__ModuleLoader__.load({
|
|
|
1485
1452
|
locale: NS,
|
|
1486
1453
|
label: () => t("view.cost")
|
|
1487
1454
|
}, CostView));
|
|
1488
|
-
ctx.slots.inject("conversation.chat.
|
|
1489
|
-
name: "conversation.chat.
|
|
1490
|
-
|
|
1491
|
-
order: 20,
|
|
1455
|
+
ctx.slots.inject("conversation.chat.turnTail", () => ctx.slots.register({
|
|
1456
|
+
name: "conversation.chat.turnTail",
|
|
1457
|
+
select: (owner) => owner.turn.status === "closed" ? { turn: owner.turn.turn } : null,
|
|
1492
1458
|
locale: NS
|
|
1493
1459
|
}, AssistantCostChip));
|
|
1494
1460
|
ctx.slots.inject("conversation.session.header.utilities", () => ctx.slots.register({
|