@paean-ai/harness-model-pricing 0.1.1-rc.17
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/LICENSE +21 -0
- package/README.i18n.yaml +46 -0
- package/README.md +127 -0
- package/README.zh.md +127 -0
- package/lib/client.js +436 -0
- package/lib/index.js +280 -0
- package/lib/types/calculate.d.ts +52 -0
- package/lib/types/client/ModelPricing.d.ts +26 -0
- package/lib/types/client/index.d.ts +11 -0
- package/lib/types/client/locales.d.ts +68 -0
- package/lib/types/defaults.d.ts +5 -0
- package/lib/types/index.d.ts +263 -0
- package/lib/types/projection.d.ts +158 -0
- package/lib/types/types.d.ts +69 -0
- package/locale/en.json +6 -0
- package/locale/zh.json +6 -0
- package/package.json +86 -0
package/lib/client.js
ADDED
|
@@ -0,0 +1,436 @@
|
|
|
1
|
+
window.__ModuleLoader__.load({
|
|
2
|
+
id: "@paean-ai/harness-model-pricing",
|
|
3
|
+
factory: (require) => {
|
|
4
|
+
var module = { exports: {} };
|
|
5
|
+
var exports = module.exports;
|
|
6
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
7
|
+
let react_jsx_runtime = require("react/jsx-runtime");
|
|
8
|
+
let react = require("react");
|
|
9
|
+
let react_dom = require("react-dom");
|
|
10
|
+
let _deepseek_ai_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
|
|
11
|
+
//#region lib/types/calculate.js
|
|
12
|
+
/**
|
|
13
|
+
* Resolve an alias without querying account identity or changing its route.
|
|
14
|
+
* @param sheet - current tariff configuration.
|
|
15
|
+
* @param provider - recorded provider identity.
|
|
16
|
+
* @param model - recorded alias.
|
|
17
|
+
* @returns its tariff, or absence when no unambiguous mapping exists.
|
|
18
|
+
*/
|
|
19
|
+
function resolveCard(sheet, provider, model) {
|
|
20
|
+
const routes = sheet.routes.filter((route) => route.provider === provider && route.model === model);
|
|
21
|
+
if (routes.length !== 1) return void 0;
|
|
22
|
+
const cards = sheet.cards.filter((card) => card.id === routes[0]?.card);
|
|
23
|
+
return cards.length === 1 ? cards[0] : void 0;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Price disjoint usage buckets; reasoning is already part of output.
|
|
27
|
+
* @param sheet - current public rates, including any long-context band.
|
|
28
|
+
* @param request - one settled request's measured usage.
|
|
29
|
+
* @returns its estimate, or absence for unavailable usage or an unmapped route.
|
|
30
|
+
*/
|
|
31
|
+
function estimateRequest(sheet, request) {
|
|
32
|
+
const card = resolveCard(sheet, request.provider, request.model);
|
|
33
|
+
const usage = request.usage;
|
|
34
|
+
if (card === void 0 || usage === null) return void 0;
|
|
35
|
+
const prompt = usage.inputTokens + usage.cacheReadTokens + usage.cacheWriteTokens;
|
|
36
|
+
const rates = card.longContext !== void 0 && prompt > card.longContext.above ? card.longContext.rates : card;
|
|
37
|
+
const input = usage.inputTokens * rates.input / 1e6;
|
|
38
|
+
const output = usage.outputTokens * rates.output / 1e6;
|
|
39
|
+
const cacheRead = usage.cacheReadTokens * rates.cacheRead / 1e6;
|
|
40
|
+
const cacheWrite = usage.cacheWriteTokens * rates.cacheWrite / 1e6;
|
|
41
|
+
return {
|
|
42
|
+
card,
|
|
43
|
+
rates,
|
|
44
|
+
input,
|
|
45
|
+
output,
|
|
46
|
+
cacheRead,
|
|
47
|
+
cacheWrite,
|
|
48
|
+
total: input + output + cacheRead + cacheWrite
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Revalue the complete session against the current sheet, preserving missing coverage.
|
|
53
|
+
* @param sheet - current market prices.
|
|
54
|
+
* @param usage - whole-log request facts from the projection.
|
|
55
|
+
* @returns a subtotal and per-model breakdown; unpriced requests never become zero-cost claims.
|
|
56
|
+
*/
|
|
57
|
+
function estimateUsage(sheet, usage) {
|
|
58
|
+
const result = {
|
|
59
|
+
total: 0,
|
|
60
|
+
priced: 0,
|
|
61
|
+
unpriced: 0,
|
|
62
|
+
byCard: []
|
|
63
|
+
};
|
|
64
|
+
for (const request of usage.requests) {
|
|
65
|
+
const estimate = estimateRequest(sheet, request);
|
|
66
|
+
if (estimate === void 0) {
|
|
67
|
+
result.unpriced++;
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
result.total += estimate.total;
|
|
71
|
+
result.priced++;
|
|
72
|
+
const row = result.byCard.find((item) => item.card.id === estimate.card.id);
|
|
73
|
+
if (row === void 0) result.byCard.push({
|
|
74
|
+
card: estimate.card,
|
|
75
|
+
total: estimate.total,
|
|
76
|
+
requests: 1
|
|
77
|
+
});
|
|
78
|
+
else {
|
|
79
|
+
row.total += estimate.total;
|
|
80
|
+
row.requests++;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return result;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Format USD with enough precision for a tiny test; positive amounts never display as zero.
|
|
87
|
+
* @param amount - nonnegative estimated dollars.
|
|
88
|
+
* @returns a compact USD amount, with an upper bound for sub-micro-dollar values.
|
|
89
|
+
*/
|
|
90
|
+
function formatUsd(amount) {
|
|
91
|
+
if (amount > 0 && amount < 1e-6) return "<$0.000001";
|
|
92
|
+
return new Intl.NumberFormat("en-US", {
|
|
93
|
+
style: "currency",
|
|
94
|
+
currency: "USD",
|
|
95
|
+
minimumFractionDigits: 2,
|
|
96
|
+
maximumFractionDigits: 6
|
|
97
|
+
}).format(amount);
|
|
98
|
+
}
|
|
99
|
+
//#endregion
|
|
100
|
+
//#region \0dsh-css:/Users/ryan/Zero/gui/paean-harness/packages/paean/paean-model-pricing/src/client/ModelPricing.module.css.mjs
|
|
101
|
+
const css = ".nNFR6G_anchor{flex:none;display:inline-flex}.nNFR6G_pill{corner-shape:round;color:var(--dsw-alias-label-tertiary);font:inherit;font-size:calc(var(--dsh-content-font-size-secondary,13px) - 1px);line-height:calc(20px + var(--dsh-content-font-delta-secondary,0px));font-variant-numeric:tabular-nums;white-space:nowrap;cursor:pointer;background:0 0;border:0;border-radius:999px;align-items:center;gap:2px;padding:1px 8px;display:inline-flex}.nNFR6G_pill:hover,.nNFR6G_pill[aria-expanded=true]{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-secondary)}.nNFR6G_panel{z-index:1100;box-sizing:border-box;width:min(320px,100vw - 24px);max-height:calc(100dvh - var(--dsh-frame-top-clearance,0px) - 44px);border-radius:var(--dsw-radius-lg);background:var(--dsw-specific-menu);backdrop-filter:var(--dsw-menu-backdrop-filter);--dsw-elevation-stroke-color:var(--dsw-alias-border-l1);box-shadow:var(--dsw-elevation-prominent);color:var(--dsw-alias-label-secondary);border:0;outline:none;padding:16px;font-size:12px;line-height:18px;position:fixed;overflow:auto}.nNFR6G_heading{color:var(--dsw-alias-label-primary);justify-content:space-between;align-items:center;gap:12px;font-weight:500;display:flex}.nNFR6G_amount{font-variant-numeric:tabular-nums;color:var(--dsw-alias-label-primary);margin:8px 0}.nNFR6G_panel p{margin:8px 0}.nNFR6G_breakdown{border-block:.5px solid var(--dsw-alias-border-l2);margin:12px 0;padding:10px 0}.nNFR6G_breakdown>div{justify-content:space-between;align-items:baseline;gap:12px;margin:6px 0;display:flex}.nNFR6G_breakdown dt{overflow-wrap:anywhere;min-width:0}.nNFR6G_breakdown dd{white-space:nowrap;font-variant-numeric:tabular-nums;margin:0}.nNFR6G_breakdown small{color:var(--dsw-alias-label-tertiary);font:inherit;display:block}.nNFR6G_settingsHint{color:var(--dsw-alias-label-tertiary)}.nNFR6G_settings{color:var(--dsw-alias-label-secondary);font-size:13px;line-height:20px}.nNFR6G_settings h3{font-size:inherit;color:var(--dsw-alias-label-primary);margin:0 0 12px;font-weight:500}.nNFR6G_settings p{margin:8px 0}.nNFR6G_scroll{overflow-x:auto}.nNFR6G_settings table{border-collapse:collapse;white-space:nowrap;text-align:left;font-variant-numeric:tabular-nums;width:100%}.nNFR6G_settings th,.nNFR6G_settings td{padding:4px 8px}";
|
|
102
|
+
const tagId = "@paean-ai/harness-model-pricing/ModelPricing.module.css";
|
|
103
|
+
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
|
|
104
|
+
const tag = document.createElement("style");
|
|
105
|
+
tag.dataset.plugin = "@paean-ai/harness-model-pricing";
|
|
106
|
+
tag.dataset.pluginCss = tagId;
|
|
107
|
+
tag.textContent = css;
|
|
108
|
+
document.head.appendChild(tag);
|
|
109
|
+
}
|
|
110
|
+
var ModelPricing_module_css_default = {
|
|
111
|
+
"amount": "nNFR6G_amount",
|
|
112
|
+
"anchor": "nNFR6G_anchor",
|
|
113
|
+
"breakdown": "nNFR6G_breakdown",
|
|
114
|
+
"heading": "nNFR6G_heading",
|
|
115
|
+
"panel": "nNFR6G_panel",
|
|
116
|
+
"pill": "nNFR6G_pill",
|
|
117
|
+
"scroll": "nNFR6G_scroll",
|
|
118
|
+
"settings": "nNFR6G_settings",
|
|
119
|
+
"settingsHint": "nNFR6G_settingsHint"
|
|
120
|
+
};
|
|
121
|
+
//#endregion
|
|
122
|
+
//#region lib/types/client/ModelPricing.js
|
|
123
|
+
/** Compact session-cost pill and a separate Settings price list. */
|
|
124
|
+
/**
|
|
125
|
+
* Render a compact estimate beside token statistics; details never enlarge the composer.
|
|
126
|
+
* @param props - standard read-only form, session projection hook and locale seat.
|
|
127
|
+
* @returns the composer price row; unavailable configuration shows no monetary claim.
|
|
128
|
+
*/
|
|
129
|
+
function ModelPricing({ form, useProjection, t }) {
|
|
130
|
+
const usage = useProjection("paeanPricingUsage");
|
|
131
|
+
const sheet = (0, react.useSyncExternalStore)((listener) => form.subscribe(listener), () => form.getSnapshot()).value?.sheet;
|
|
132
|
+
if (sheet === void 0 || usage === void 0 || usage.requests.length === 0) return null;
|
|
133
|
+
return (0, react_jsx_runtime.jsx)(EstimatePill, {
|
|
134
|
+
sheet,
|
|
135
|
+
usage,
|
|
136
|
+
t
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Show the public price list in Settings before any session or credential exists.
|
|
141
|
+
* @param props - the same live price form and localized copy used by the composer.
|
|
142
|
+
* @returns the complete price list without a session cost claim.
|
|
143
|
+
*/
|
|
144
|
+
function ModelPricingSettings({ form, t }) {
|
|
145
|
+
const sheet = (0, react.useSyncExternalStore)((listener) => form.subscribe(listener), () => form.getSnapshot()).value?.sheet;
|
|
146
|
+
if (sheet === void 0) return (0, react_jsx_runtime.jsx)("div", {
|
|
147
|
+
className: ModelPricing_module_css_default.settings,
|
|
148
|
+
role: "status",
|
|
149
|
+
children: t("loading")
|
|
150
|
+
});
|
|
151
|
+
return (0, react_jsx_runtime.jsxs)("section", {
|
|
152
|
+
className: ModelPricing_module_css_default.settings,
|
|
153
|
+
children: [
|
|
154
|
+
(0, react_jsx_runtime.jsx)("h3", { children: t("title") }),
|
|
155
|
+
(0, react_jsx_runtime.jsx)("p", { children: t("assumptions") }),
|
|
156
|
+
(0, react_jsx_runtime.jsx)("p", { children: t("verified", { date: sheet.verifiedOn }) }),
|
|
157
|
+
(0, react_jsx_runtime.jsx)("p", { children: t("unit") }),
|
|
158
|
+
(0, react_jsx_runtime.jsx)("div", {
|
|
159
|
+
className: ModelPricing_module_css_default.scroll,
|
|
160
|
+
children: (0, react_jsx_runtime.jsxs)("table", { children: [(0, react_jsx_runtime.jsx)("thead", { children: (0, react_jsx_runtime.jsx)("tr", { children: [
|
|
161
|
+
"protocol",
|
|
162
|
+
"alias",
|
|
163
|
+
"input",
|
|
164
|
+
"cacheRead",
|
|
165
|
+
"cacheWrite",
|
|
166
|
+
"output"
|
|
167
|
+
].map((key) => (0, react_jsx_runtime.jsx)("th", {
|
|
168
|
+
scope: "col",
|
|
169
|
+
children: t(key)
|
|
170
|
+
}, key)) }) }), (0, react_jsx_runtime.jsx)("tbody", { children: sheet.routes.map((route, index) => {
|
|
171
|
+
const card = resolveCard(sheet, route.provider, route.model);
|
|
172
|
+
return (0, react_jsx_runtime.jsxs)("tr", { children: [
|
|
173
|
+
(0, react_jsx_runtime.jsx)("td", { children: protocolLabel(route.provider, t) }),
|
|
174
|
+
(0, react_jsx_runtime.jsx)("td", { children: route.model }),
|
|
175
|
+
[
|
|
176
|
+
"input",
|
|
177
|
+
"cacheRead",
|
|
178
|
+
"cacheWrite",
|
|
179
|
+
"output"
|
|
180
|
+
].map((key) => (0, react_jsx_runtime.jsx)("td", { children: card === void 0 ? "—" : formatUsd(card[key]) }, key))
|
|
181
|
+
] }, `${route.provider}/${route.model}/${index}`);
|
|
182
|
+
}) })] })
|
|
183
|
+
})
|
|
184
|
+
]
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
function protocolLabel(provider, t) {
|
|
188
|
+
return provider === "paean" ? t("openai") : provider === "paean-anthropic" ? t("anthropic") : provider;
|
|
189
|
+
}
|
|
190
|
+
/** Group existing request estimates by public protocol and alias, not underlying model identities. */
|
|
191
|
+
function routeTotals(sheet, usage) {
|
|
192
|
+
const rows = /* @__PURE__ */ new Map();
|
|
193
|
+
for (const request of usage.requests) {
|
|
194
|
+
const price = estimateRequest(sheet, request);
|
|
195
|
+
if (price === void 0) continue;
|
|
196
|
+
const key = JSON.stringify([request.provider, request.model]);
|
|
197
|
+
const row = rows.get(key);
|
|
198
|
+
if (row === void 0) rows.set(key, {
|
|
199
|
+
provider: request.provider,
|
|
200
|
+
model: request.model,
|
|
201
|
+
total: price.total,
|
|
202
|
+
requests: 1
|
|
203
|
+
});
|
|
204
|
+
else {
|
|
205
|
+
row.total += price.total;
|
|
206
|
+
row.requests++;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
return [...rows.values()];
|
|
210
|
+
}
|
|
211
|
+
function EstimatePill({ sheet, usage, t }) {
|
|
212
|
+
const estimate = (0, react.useMemo)(() => estimateUsage(sheet, usage), [sheet, usage]);
|
|
213
|
+
const [open, setOpen] = (0, react.useState)(false);
|
|
214
|
+
const anchor = (0, react.useRef)(null);
|
|
215
|
+
const panel = (0, react.useRef)(null);
|
|
216
|
+
const panelId = (0, react.useId)();
|
|
217
|
+
const rows = (0, react.useMemo)(() => routeTotals(sheet, usage), [sheet, usage]);
|
|
218
|
+
const position = (0, _deepseek_ai_dsh_client_ui_primitives.useAnchoredPosition)({
|
|
219
|
+
open,
|
|
220
|
+
anchorRef: anchor,
|
|
221
|
+
panelRef: panel,
|
|
222
|
+
side: "top",
|
|
223
|
+
align: "end",
|
|
224
|
+
gap: 8,
|
|
225
|
+
margin: 12
|
|
226
|
+
});
|
|
227
|
+
(0, _deepseek_ai_dsh_client_ui_primitives.useDismissOnOutsidePointer)(anchor, open, setOpen, panel);
|
|
228
|
+
(0, react.useEffect)(() => {
|
|
229
|
+
if (!open) return;
|
|
230
|
+
(0, _deepseek_ai_dsh_client_ui_primitives.focusWithoutRing)(panel.current);
|
|
231
|
+
const escape = (event) => {
|
|
232
|
+
if (event.key !== "Escape") return;
|
|
233
|
+
event.preventDefault();
|
|
234
|
+
event.stopPropagation();
|
|
235
|
+
setOpen(false);
|
|
236
|
+
(0, _deepseek_ai_dsh_client_ui_primitives.focusWithoutRing)(anchor.current);
|
|
237
|
+
};
|
|
238
|
+
document.addEventListener("keydown", escape, true);
|
|
239
|
+
return () => {
|
|
240
|
+
document.removeEventListener("keydown", escape, true);
|
|
241
|
+
};
|
|
242
|
+
}, [open]);
|
|
243
|
+
const amount = formatUsd(estimate.total);
|
|
244
|
+
const label = estimate.priced === 0 ? t("empty") : t(estimate.unpriced > 0 ? "partialTotal" : "total", { amount });
|
|
245
|
+
return (0, react_jsx_runtime.jsxs)("span", {
|
|
246
|
+
className: ModelPricing_module_css_default.anchor,
|
|
247
|
+
"data-pricing-pill": true,
|
|
248
|
+
children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
|
|
249
|
+
label: t("hint"),
|
|
250
|
+
side: "top",
|
|
251
|
+
delayMs: 300,
|
|
252
|
+
portal: true,
|
|
253
|
+
disabled: open,
|
|
254
|
+
maxWidth: 280,
|
|
255
|
+
children: (0, react_jsx_runtime.jsxs)("button", {
|
|
256
|
+
ref: anchor,
|
|
257
|
+
type: "button",
|
|
258
|
+
className: ModelPricing_module_css_default.pill,
|
|
259
|
+
"data-estimated": "true",
|
|
260
|
+
"aria-label": label,
|
|
261
|
+
"aria-haspopup": "dialog",
|
|
262
|
+
"aria-expanded": open,
|
|
263
|
+
"aria-controls": open ? panelId : void 0,
|
|
264
|
+
onClick: () => {
|
|
265
|
+
setOpen((value) => !value);
|
|
266
|
+
},
|
|
267
|
+
children: [estimate.priced === 0 ? t("unavailable") : t("compactTotal", { amount }), estimate.priced > 0 && estimate.unpriced > 0 && (0, react_jsx_runtime.jsx)("span", {
|
|
268
|
+
"aria-hidden": "true",
|
|
269
|
+
children: "*"
|
|
270
|
+
})]
|
|
271
|
+
})
|
|
272
|
+
}), open && (0, react_dom.createPortal)((0, react_jsx_runtime.jsxs)("div", {
|
|
273
|
+
ref: panel,
|
|
274
|
+
id: panelId,
|
|
275
|
+
role: "dialog",
|
|
276
|
+
"aria-label": t("details"),
|
|
277
|
+
tabIndex: -1,
|
|
278
|
+
className: ModelPricing_module_css_default.panel,
|
|
279
|
+
style: position ?? {
|
|
280
|
+
visibility: "hidden",
|
|
281
|
+
left: 0,
|
|
282
|
+
top: 0
|
|
283
|
+
},
|
|
284
|
+
children: [
|
|
285
|
+
(0, react_jsx_runtime.jsxs)("div", {
|
|
286
|
+
className: ModelPricing_module_css_default.heading,
|
|
287
|
+
children: [(0, react_jsx_runtime.jsx)("span", { children: t("details") }), (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
288
|
+
variant: "ghost",
|
|
289
|
+
size: "sm",
|
|
290
|
+
onClick: () => {
|
|
291
|
+
setOpen(false);
|
|
292
|
+
(0, _deepseek_ai_dsh_client_ui_primitives.focusWithoutRing)(anchor.current);
|
|
293
|
+
},
|
|
294
|
+
children: t("close")
|
|
295
|
+
})]
|
|
296
|
+
}),
|
|
297
|
+
(0, react_jsx_runtime.jsx)("div", {
|
|
298
|
+
className: ModelPricing_module_css_default.amount,
|
|
299
|
+
children: estimate.priced === 0 ? t("empty") : label
|
|
300
|
+
}),
|
|
301
|
+
(0, react_jsx_runtime.jsx)("p", { children: t("disclaimer") }),
|
|
302
|
+
(0, react_jsx_runtime.jsx)("dl", {
|
|
303
|
+
className: ModelPricing_module_css_default.breakdown,
|
|
304
|
+
children: rows.map((row) => (0, react_jsx_runtime.jsxs)("div", { children: [(0, react_jsx_runtime.jsxs)("dt", { children: [
|
|
305
|
+
protocolLabel(row.provider, t),
|
|
306
|
+
" · ",
|
|
307
|
+
row.model,
|
|
308
|
+
(0, react_jsx_runtime.jsx)("small", { children: t("routeCount", { count: row.requests }) })
|
|
309
|
+
] }), (0, react_jsx_runtime.jsx)("dd", { children: t("compactTotal", { amount: formatUsd(row.total) }) })] }, JSON.stringify([row.provider, row.model])))
|
|
310
|
+
}),
|
|
311
|
+
(0, react_jsx_runtime.jsx)("p", { children: t("coverage", { count: estimate.priced }) }),
|
|
312
|
+
estimate.unpriced > 0 && (0, react_jsx_runtime.jsx)("p", {
|
|
313
|
+
role: "status",
|
|
314
|
+
children: t("partial", { count: estimate.unpriced })
|
|
315
|
+
}),
|
|
316
|
+
(0, react_jsx_runtime.jsx)("p", { children: t("verified", { date: sheet.verifiedOn }) }),
|
|
317
|
+
(0, react_jsx_runtime.jsx)("p", {
|
|
318
|
+
className: ModelPricing_module_css_default.settingsHint,
|
|
319
|
+
children: t("settingsHint")
|
|
320
|
+
})
|
|
321
|
+
]
|
|
322
|
+
}), document.body)]
|
|
323
|
+
});
|
|
324
|
+
}
|
|
325
|
+
//#endregion
|
|
326
|
+
//#region lib/types/client/locales.js
|
|
327
|
+
/** Typed English and Chinese copy for estimated USD and public tariff assumptions. */
|
|
328
|
+
const NS = "paeanModelPricing";
|
|
329
|
+
/** English price-estimation copy. */
|
|
330
|
+
const en = {
|
|
331
|
+
title: "Model prices",
|
|
332
|
+
total: "Session estimate ≈{amount} USD",
|
|
333
|
+
partialTotal: "Session partial estimate ≈{amount} USD",
|
|
334
|
+
empty: "No priced requests yet",
|
|
335
|
+
loading: "Loading model prices…",
|
|
336
|
+
coverage: "{count} measured requests",
|
|
337
|
+
partial: "Partial estimate: {count} requests lack usage or a price mapping.",
|
|
338
|
+
assumptions: "Public market-price estimate, not your Paean bill. All accounts use the same price sheet. Discounts, off-peak rates, taxes and tool fees are excluded. Only this session’s recorded requests are included; child sessions and unreported failed calls are excluded. Price updates revalue previous usage.",
|
|
339
|
+
verified: "Prices checked {date}",
|
|
340
|
+
protocol: "Protocol",
|
|
341
|
+
alias: "Model",
|
|
342
|
+
input: "Input",
|
|
343
|
+
output: "Output",
|
|
344
|
+
cacheRead: "Cache read",
|
|
345
|
+
cacheWrite: "Cache write",
|
|
346
|
+
unit: "USD per 1M tokens · long-context rates apply automatically",
|
|
347
|
+
openai: "OpenAI",
|
|
348
|
+
anthropic: "Anthropic",
|
|
349
|
+
compactTotal: "≈{amount}",
|
|
350
|
+
unavailable: "Estimate —",
|
|
351
|
+
hint: "Public market-price estimate in USD, not your Paean bill. Click for details.",
|
|
352
|
+
disclaimer: "Public market-price estimate in USD, not your Paean bill.",
|
|
353
|
+
details: "Session cost estimate",
|
|
354
|
+
close: "Close",
|
|
355
|
+
routeCount: "{count} requests",
|
|
356
|
+
settingsHint: "Full price sheet: Settings → Model prices"
|
|
357
|
+
};
|
|
358
|
+
/** Simplified Chinese price-estimation copy. */
|
|
359
|
+
const zh = {
|
|
360
|
+
title: "模型价格",
|
|
361
|
+
total: "本会话估算 ≈{amount} USD",
|
|
362
|
+
partialTotal: "本会话部分估算 ≈{amount} USD",
|
|
363
|
+
empty: "暂无可估价请求",
|
|
364
|
+
loading: "正在加载模型价格…",
|
|
365
|
+
coverage: "{count} 次已计量请求",
|
|
366
|
+
partial: "部分估算:{count} 次请求缺少用量或价格映射。",
|
|
367
|
+
assumptions: "按公开市场价格估算,并非 Paean 账单。所有账号采用同一套估算价目表。不含折扣、低峰优惠、税费和工具费用。仅统计本会话已记录的请求,不含子会话及未报告用量的失败调用。更新价格后会重新估算历史用量。",
|
|
368
|
+
verified: "价格核对日期:{date}",
|
|
369
|
+
protocol: "协议",
|
|
370
|
+
alias: "模型",
|
|
371
|
+
input: "输入",
|
|
372
|
+
output: "输出",
|
|
373
|
+
cacheRead: "缓存读取",
|
|
374
|
+
cacheWrite: "缓存写入",
|
|
375
|
+
unit: "USD / 百万 token · 自动应用长上下文价格",
|
|
376
|
+
openai: "OpenAI",
|
|
377
|
+
anthropic: "Anthropic",
|
|
378
|
+
compactTotal: "≈{amount}",
|
|
379
|
+
unavailable: "估算 —",
|
|
380
|
+
hint: "按公开市场价格估算美元成本,并非 Paean 账单。点击查看明细",
|
|
381
|
+
disclaimer: "按公开市场价格估算美元成本,并非 Paean 账单",
|
|
382
|
+
details: "会话成本估算",
|
|
383
|
+
close: "关闭",
|
|
384
|
+
routeCount: "{count} 次请求",
|
|
385
|
+
settingsHint: "完整价目表:设置 → 模型价格"
|
|
386
|
+
};
|
|
387
|
+
//#endregion
|
|
388
|
+
//#region lib/types/client/index.js
|
|
389
|
+
/** Services supplied by the browser composition. */
|
|
390
|
+
const inject = [
|
|
391
|
+
"slots",
|
|
392
|
+
"locale",
|
|
393
|
+
"configForms"
|
|
394
|
+
];
|
|
395
|
+
/**
|
|
396
|
+
* Contribute localized prices while the matching Host settings namespace is served.
|
|
397
|
+
* @param ctx - browser composition services; all registrations withdraw with the fiber.
|
|
398
|
+
*/
|
|
399
|
+
function apply(ctx) {
|
|
400
|
+
ctx.locale.register(NS, {
|
|
401
|
+
en,
|
|
402
|
+
zh
|
|
403
|
+
});
|
|
404
|
+
const t = ctx.locale.bind(NS);
|
|
405
|
+
const form = ctx.configForms.get("paean-model-pricing");
|
|
406
|
+
ctx.effect(() => ctx.configForms.whileServed(["paean-model-pricing"], () => {
|
|
407
|
+
const dock = ctx.slots.inject("conversation.composer.dock", () => ctx.slots.register({
|
|
408
|
+
name: "conversation.composer.dock",
|
|
409
|
+
id: "paean-model-pricing",
|
|
410
|
+
order: 10,
|
|
411
|
+
locale: NS,
|
|
412
|
+
label: () => t("title"),
|
|
413
|
+
inject: () => ({ form })
|
|
414
|
+
}, ModelPricing));
|
|
415
|
+
const settings = ctx.slots.inject("settings.section", () => ctx.slots.register({
|
|
416
|
+
name: "settings.section",
|
|
417
|
+
id: "paean-model-pricing",
|
|
418
|
+
order: 32,
|
|
419
|
+
locale: NS,
|
|
420
|
+
label: () => t("title"),
|
|
421
|
+
inject: () => ({ form })
|
|
422
|
+
}, ModelPricingSettings));
|
|
423
|
+
return () => {
|
|
424
|
+
dock();
|
|
425
|
+
settings();
|
|
426
|
+
};
|
|
427
|
+
}));
|
|
428
|
+
}
|
|
429
|
+
//#endregion
|
|
430
|
+
exports.apply = apply;
|
|
431
|
+
exports.inject = inject;
|
|
432
|
+
return module.exports;
|
|
433
|
+
}
|
|
434
|
+
});
|
|
435
|
+
|
|
436
|
+
//# sourceMappingURL=client.js.map
|