@aexol/spectral 0.3.2 → 0.3.5
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/dist/relay/models-fetch.js +12 -2
- package/dist/server/pi-bridge.js +43 -2
- package/package.json +1 -1
|
@@ -28,7 +28,7 @@ const cache = new Map();
|
|
|
28
28
|
export function clearAllowedModelsCache() {
|
|
29
29
|
cache.clear();
|
|
30
30
|
}
|
|
31
|
-
const QUERY = `query AvailableBaseModels { availableBaseModels { name provider userModelId agentEnabled } }`;
|
|
31
|
+
const QUERY = `query AvailableBaseModels { availableBaseModels { name provider userModelId agentEnabled creditInputPer1M creditOutputPer1M creditCachedInputPer1M creditCacheReadPer1M creditCacheWritePer1M } }`;
|
|
32
32
|
/**
|
|
33
33
|
* Fetch the whitelist of allowed base models. Throws on any failure with a
|
|
34
34
|
* message tailored for an operator running `spectral serve` — the caller
|
|
@@ -97,7 +97,17 @@ export async function fetchAllowedModels(opts) {
|
|
|
97
97
|
const provider = typeof row?.provider === "string" ? row.provider : null;
|
|
98
98
|
if (!name || !provider)
|
|
99
99
|
continue; // skip malformed rows defensively
|
|
100
|
-
const
|
|
100
|
+
const asOptionalNumber = (v) => typeof v === "number" ? v : v == null ? null : undefined;
|
|
101
|
+
const model = {
|
|
102
|
+
modelId: name,
|
|
103
|
+
displayName: name,
|
|
104
|
+
provider,
|
|
105
|
+
creditInputPer1M: asOptionalNumber(row?.creditInputPer1M),
|
|
106
|
+
creditOutputPer1M: asOptionalNumber(row?.creditOutputPer1M),
|
|
107
|
+
creditCachedInputPer1M: asOptionalNumber(row?.creditCachedInputPer1M),
|
|
108
|
+
creditCacheReadPer1M: asOptionalNumber(row?.creditCacheReadPer1M),
|
|
109
|
+
creditCacheWritePer1M: asOptionalNumber(row?.creditCacheWritePer1M),
|
|
110
|
+
};
|
|
101
111
|
if (typeof row?.userModelId === "string") {
|
|
102
112
|
model.userModelId = row.userModelId;
|
|
103
113
|
}
|
package/dist/server/pi-bridge.js
CHANGED
|
@@ -190,6 +190,31 @@ function supportsReasoning(modelId) {
|
|
|
190
190
|
const bare = bareModelId(modelId);
|
|
191
191
|
return REASONING_SUPPORT_PREFIXES.some((p) => modelId.startsWith(p) || bare.startsWith(p));
|
|
192
192
|
}
|
|
193
|
+
/**
|
|
194
|
+
* Calculate credits from token usage using per-model credit rates.
|
|
195
|
+
*
|
|
196
|
+
* NOTE: We intentionally keep higher precision here (no 2-decimal rounding)
|
|
197
|
+
* so small turns don't collapse to 0.00 in live UI aggregation. Rendering
|
|
198
|
+
* layers decide final display precision.
|
|
199
|
+
*/
|
|
200
|
+
function calculateCredits(inputTokens, outputTokens, cacheReadTokens = 0, cacheWriteTokens = 0, creditInputPer1M, creditOutputPer1M, creditCachedInputPer1M, creditCacheReadPer1M, creditCacheWritePer1M) {
|
|
201
|
+
const inputRate = creditInputPer1M ?? 0;
|
|
202
|
+
const outputRate = creditOutputPer1M ?? 0;
|
|
203
|
+
const cachedInputRate = creditCachedInputPer1M ?? 0;
|
|
204
|
+
const cacheReadRate = creditCacheReadPer1M ?? 0;
|
|
205
|
+
const cacheWriteRate = creditCacheWritePer1M ?? 0;
|
|
206
|
+
const credits = (inputTokens / 1_000_000) * inputRate +
|
|
207
|
+
(outputTokens / 1_000_000) * outputRate +
|
|
208
|
+
(cacheReadTokens / 1_000_000) * cacheReadRate +
|
|
209
|
+
(cacheWriteTokens / 1_000_000) * cacheWriteRate;
|
|
210
|
+
if (inputRate === 0 && outputRate === 0 && cacheReadRate === 0 && cacheWriteRate === 0) {
|
|
211
|
+
if (cachedInputRate > 0) {
|
|
212
|
+
return ((inputTokens + cacheReadTokens + cacheWriteTokens) / 1_000_000) * cachedInputRate;
|
|
213
|
+
}
|
|
214
|
+
return 0;
|
|
215
|
+
}
|
|
216
|
+
return credits;
|
|
217
|
+
}
|
|
193
218
|
/**
|
|
194
219
|
* Parse the newline-delimited JSON of wire events persisted alongside an
|
|
195
220
|
* assistant message and extract all tool_call / tool_result events.
|
|
@@ -294,6 +319,8 @@ export class PiBridge {
|
|
|
294
319
|
* envelopes carrying the same modelId don't churn pi's internal state.
|
|
295
320
|
*/
|
|
296
321
|
lastAppliedModelId;
|
|
322
|
+
/** Current model's credit rates (from availableBaseModels), used for token_usage. */
|
|
323
|
+
activeCreditRates = null;
|
|
297
324
|
constructor(opts) {
|
|
298
325
|
this.opts = opts;
|
|
299
326
|
}
|
|
@@ -660,6 +687,16 @@ export class PiBridge {
|
|
|
660
687
|
try {
|
|
661
688
|
await this.session.setModel(model);
|
|
662
689
|
this.lastAppliedModelId = modelId;
|
|
690
|
+
const selected = this.allowedModels?.find((m) => m.modelId === modelId);
|
|
691
|
+
this.activeCreditRates = selected
|
|
692
|
+
? {
|
|
693
|
+
creditInputPer1M: selected.creditInputPer1M ?? null,
|
|
694
|
+
creditOutputPer1M: selected.creditOutputPer1M ?? null,
|
|
695
|
+
creditCachedInputPer1M: selected.creditCachedInputPer1M ?? null,
|
|
696
|
+
creditCacheReadPer1M: selected.creditCacheReadPer1M ?? null,
|
|
697
|
+
creditCacheWritePer1M: selected.creditCacheWritePer1M ?? null,
|
|
698
|
+
}
|
|
699
|
+
: null;
|
|
663
700
|
return true;
|
|
664
701
|
}
|
|
665
702
|
catch (err) {
|
|
@@ -889,8 +926,11 @@ export class PiBridge {
|
|
|
889
926
|
this.pending.wireEvents.push(endEvent);
|
|
890
927
|
this.opts.emit(endEvent);
|
|
891
928
|
// Emit token usage for this assistant message. pi provides token
|
|
892
|
-
// counts via ev.message.usage;
|
|
893
|
-
// configured
|
|
929
|
+
// counts via ev.message.usage; credits are computed from the active
|
|
930
|
+
// model's configured credit rates.
|
|
931
|
+
//
|
|
932
|
+
// NOTE: `usage.cost` is still forwarded as a legacy wire field for
|
|
933
|
+
// backward compatibility, but the UI is credits-first.
|
|
894
934
|
//
|
|
895
935
|
// Skip zero-total-usage events — they happen when a turn is
|
|
896
936
|
// cancelled before the provider starts streaming, and emitting
|
|
@@ -912,6 +952,7 @@ export class PiBridge {
|
|
|
912
952
|
cacheWriteTokens: usage.cacheWrite ?? 0,
|
|
913
953
|
totalTokens: usage.totalTokens ?? totalTokens,
|
|
914
954
|
cost: usage.cost?.total ?? null,
|
|
955
|
+
creditsUsed: calculateCredits(usage.input ?? 0, usage.output ?? 0, usage.cacheRead ?? 0, usage.cacheWrite ?? 0, this.activeCreditRates?.creditInputPer1M, this.activeCreditRates?.creditOutputPer1M, this.activeCreditRates?.creditCachedInputPer1M, this.activeCreditRates?.creditCacheReadPer1M, this.activeCreditRates?.creditCacheWritePer1M),
|
|
915
956
|
},
|
|
916
957
|
};
|
|
917
958
|
this.pending.wireEvents.push(usageEvent);
|