@aliou/pi-synthetic 0.9.0 → 0.10.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/package.json
CHANGED
|
@@ -20,16 +20,77 @@ interface QuotaWindow {
|
|
|
20
20
|
windowSeconds: number;
|
|
21
21
|
usedValue: number;
|
|
22
22
|
limitValue: number;
|
|
23
|
+
isCredits?: boolean;
|
|
24
|
+
isLimited?: boolean;
|
|
25
|
+
tickPercent?: number;
|
|
26
|
+
nextRegenCredits?: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** Safely compute percentage, guarding against division by zero */
|
|
30
|
+
function safePercent(used: number, limit: number): number {
|
|
31
|
+
if (!Number.isFinite(used) || !Number.isFinite(limit) || limit <= 0) return 0;
|
|
32
|
+
return Math.max(0, Math.min(100, (used / limit) * 100));
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** Parse currency string like "$1,234.56" to number */
|
|
36
|
+
function parseCurrency(value: string): number {
|
|
37
|
+
const n = Number(value.replace(/[^0-9.-]/g, ""));
|
|
38
|
+
return Number.isFinite(n) ? n : 0;
|
|
23
39
|
}
|
|
24
40
|
|
|
25
41
|
function toWindows(quotas: QuotasResponse): QuotaWindow[] {
|
|
26
42
|
const windows: QuotaWindow[] = [];
|
|
27
43
|
|
|
28
|
-
|
|
44
|
+
// Weekly token limit (credits-based)
|
|
45
|
+
if (quotas.weeklyTokenLimit) {
|
|
46
|
+
const { weeklyTokenLimit } = quotas;
|
|
47
|
+
const limitValue = parseCurrency(weeklyTokenLimit.maxCredits);
|
|
48
|
+
const remainingValue = parseCurrency(weeklyTokenLimit.remainingCredits);
|
|
49
|
+
windows.push({
|
|
50
|
+
label: "Credits",
|
|
51
|
+
usedPercent: Math.max(
|
|
52
|
+
0,
|
|
53
|
+
Math.min(100, 100 - weeklyTokenLimit.percentRemaining),
|
|
54
|
+
),
|
|
55
|
+
resetsAt: new Date(weeklyTokenLimit.nextRegenAt),
|
|
56
|
+
windowSeconds: 7 * 24 * 60 * 60,
|
|
57
|
+
usedValue: limitValue - remainingValue,
|
|
58
|
+
limitValue,
|
|
59
|
+
isCredits: true,
|
|
60
|
+
nextRegenCredits: weeklyTokenLimit.nextRegenCredits,
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Rolling 5-hour limit (request-based)
|
|
65
|
+
if (quotas.rollingFiveHourLimit && quotas.rollingFiveHourLimit.max > 0) {
|
|
66
|
+
const { rollingFiveHourLimit } = quotas;
|
|
67
|
+
windows.push({
|
|
68
|
+
label: "5h",
|
|
69
|
+
usedPercent: safePercent(
|
|
70
|
+
rollingFiveHourLimit.max - rollingFiveHourLimit.remaining,
|
|
71
|
+
rollingFiveHourLimit.max,
|
|
72
|
+
),
|
|
73
|
+
resetsAt: new Date(rollingFiveHourLimit.nextTickAt),
|
|
74
|
+
windowSeconds: 5 * 60 * 60,
|
|
75
|
+
usedValue: rollingFiveHourLimit.max - rollingFiveHourLimit.remaining,
|
|
76
|
+
limitValue: rollingFiveHourLimit.max,
|
|
77
|
+
isLimited: rollingFiveHourLimit.limited,
|
|
78
|
+
tickPercent: rollingFiveHourLimit.tickPercent,
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Legacy subscription (fallback if rollingFiveHourLimit not available)
|
|
83
|
+
if (
|
|
84
|
+
!quotas.rollingFiveHourLimit &&
|
|
85
|
+
quotas.subscription?.limit &&
|
|
86
|
+
quotas.subscription.limit > 0
|
|
87
|
+
) {
|
|
29
88
|
windows.push({
|
|
30
89
|
label: "Completions",
|
|
31
|
-
usedPercent:
|
|
32
|
-
|
|
90
|
+
usedPercent: safePercent(
|
|
91
|
+
quotas.subscription.requests,
|
|
92
|
+
quotas.subscription.limit,
|
|
93
|
+
),
|
|
33
94
|
resetsAt: new Date(quotas.subscription.renewsAt),
|
|
34
95
|
windowSeconds: 5 * 60 * 60,
|
|
35
96
|
usedValue: quotas.subscription.requests,
|
|
@@ -37,11 +98,13 @@ function toWindows(quotas: QuotasResponse): QuotaWindow[] {
|
|
|
37
98
|
});
|
|
38
99
|
}
|
|
39
100
|
|
|
40
|
-
if (quotas.search.hourly.limit > 0) {
|
|
101
|
+
if (quotas.search?.hourly?.limit && quotas.search.hourly.limit > 0) {
|
|
41
102
|
windows.push({
|
|
42
103
|
label: "Search",
|
|
43
|
-
usedPercent:
|
|
44
|
-
|
|
104
|
+
usedPercent: safePercent(
|
|
105
|
+
quotas.search.hourly.requests,
|
|
106
|
+
quotas.search.hourly.limit,
|
|
107
|
+
),
|
|
45
108
|
resetsAt: new Date(quotas.search.hourly.renewsAt),
|
|
46
109
|
windowSeconds: 60 * 60,
|
|
47
110
|
usedValue: quotas.search.hourly.requests,
|
|
@@ -49,11 +112,13 @@ function toWindows(quotas: QuotasResponse): QuotaWindow[] {
|
|
|
49
112
|
});
|
|
50
113
|
}
|
|
51
114
|
|
|
52
|
-
if (quotas.freeToolCalls.limit > 0) {
|
|
115
|
+
if (quotas.freeToolCalls?.limit && quotas.freeToolCalls.limit > 0) {
|
|
53
116
|
windows.push({
|
|
54
117
|
label: "Free Tool Calls",
|
|
55
|
-
usedPercent:
|
|
56
|
-
|
|
118
|
+
usedPercent: safePercent(
|
|
119
|
+
quotas.freeToolCalls.requests,
|
|
120
|
+
quotas.freeToolCalls.limit,
|
|
121
|
+
),
|
|
57
122
|
resetsAt: new Date(quotas.freeToolCalls.renewsAt),
|
|
58
123
|
windowSeconds: 24 * 60 * 60,
|
|
59
124
|
usedValue: quotas.freeToolCalls.requests,
|
|
@@ -155,6 +220,34 @@ function renderProgressBar(
|
|
|
155
220
|
return parts.join("");
|
|
156
221
|
}
|
|
157
222
|
|
|
223
|
+
function renderSimpleIndicatorBar(
|
|
224
|
+
usedPercent: number,
|
|
225
|
+
width: number,
|
|
226
|
+
theme: Theme,
|
|
227
|
+
severity: "success" | "warning" | "error",
|
|
228
|
+
): string {
|
|
229
|
+
const clampedPercent = Math.max(0, Math.min(100, usedPercent));
|
|
230
|
+
// Clamp to width - 1 to avoid off-by-one when usedPercent === 100
|
|
231
|
+
const usedIndex = Math.min(
|
|
232
|
+
Math.round((clampedPercent / 100) * width),
|
|
233
|
+
width - 1,
|
|
234
|
+
);
|
|
235
|
+
const parts: string[] = [];
|
|
236
|
+
|
|
237
|
+
// Hide marker when within 5% of edges
|
|
238
|
+
const showMarker = clampedPercent >= 5 && clampedPercent <= 95;
|
|
239
|
+
|
|
240
|
+
for (let idx = 0; idx < width; idx++) {
|
|
241
|
+
if (showMarker && idx === usedIndex) {
|
|
242
|
+
parts.push(theme.fg(severity, "|"));
|
|
243
|
+
} else {
|
|
244
|
+
parts.push(theme.fg("dim", "░"));
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
return parts.join("");
|
|
249
|
+
}
|
|
250
|
+
|
|
158
251
|
export class QuotasComponent implements Component {
|
|
159
252
|
private state: QuotasState = { type: "loading" };
|
|
160
253
|
private theme: Theme;
|
|
@@ -254,22 +347,83 @@ export class QuotasComponent implements Component {
|
|
|
254
347
|
truncateToWidth(` ${theme.fg("accent", window.label)}`, maxWidth),
|
|
255
348
|
);
|
|
256
349
|
|
|
257
|
-
// Progress bar + usage
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
350
|
+
// Progress bar + usage (or indicator for new quota types)
|
|
351
|
+
if (window.isCredits || window.tickPercent !== undefined) {
|
|
352
|
+
// Show simple indicator bar for new quota types
|
|
353
|
+
const bar = renderSimpleIndicatorBar(
|
|
354
|
+
window.usedPercent,
|
|
355
|
+
barWidth,
|
|
356
|
+
theme,
|
|
357
|
+
severity,
|
|
358
|
+
);
|
|
359
|
+
const usedStr = window.isCredits
|
|
360
|
+
? `$${window.usedValue.toFixed(2)}/$${window.limitValue.toFixed(2)} (${Math.round(window.usedPercent)}%)`
|
|
361
|
+
: `${window.usedValue.toFixed(0)}/${window.limitValue.toFixed(0)} (${Math.round(window.usedPercent)}%)`;
|
|
362
|
+
const limitedBadge = window.isLimited
|
|
363
|
+
? theme.fg("error", " LIMITED")
|
|
364
|
+
: "";
|
|
365
|
+
lines.push(
|
|
366
|
+
truncateToWidth(
|
|
367
|
+
` ${bar} ${theme.fg(severity, usedStr)}${limitedBadge}`,
|
|
368
|
+
maxWidth,
|
|
369
|
+
),
|
|
370
|
+
);
|
|
371
|
+
} else {
|
|
372
|
+
// Traditional progress bar for legacy quota types
|
|
373
|
+
const bar = renderProgressBar(
|
|
374
|
+
window.usedPercent,
|
|
375
|
+
barWidth,
|
|
376
|
+
theme,
|
|
377
|
+
severity,
|
|
378
|
+
pacePercent,
|
|
379
|
+
);
|
|
380
|
+
const usedStr = `${window.usedValue.toLocaleString()}/${window.limitValue.toLocaleString()} (${Math.round(window.usedPercent)}%)`;
|
|
381
|
+
lines.push(
|
|
382
|
+
truncateToWidth(` ${bar} ${theme.fg(severity, usedStr)}`, maxWidth),
|
|
383
|
+
);
|
|
384
|
+
}
|
|
269
385
|
|
|
270
386
|
// Metadata: estimated + pace left, reset time right
|
|
271
387
|
const leftParts: string[] = [];
|
|
272
|
-
|
|
388
|
+
|
|
389
|
+
// Show tick info for rolling window
|
|
390
|
+
if (window.tickPercent !== undefined) {
|
|
391
|
+
const now = Date.now();
|
|
392
|
+
const remainingMs = window.resetsAt.getTime() - now;
|
|
393
|
+
const remainingMins = Math.ceil(remainingMs / (1000 * 60));
|
|
394
|
+
const remainingSecs = Math.ceil(remainingMs / 1000);
|
|
395
|
+
const timeStr =
|
|
396
|
+
remainingMs <= 0
|
|
397
|
+
? "now"
|
|
398
|
+
: remainingMins >= 1
|
|
399
|
+
? `${remainingMins}m`
|
|
400
|
+
: `${remainingSecs}s`;
|
|
401
|
+
const tickValue = (window.tickPercent / 100) * window.limitValue;
|
|
402
|
+
const tickStr = `+${tickValue.toFixed(1)} in ${timeStr}`;
|
|
403
|
+
leftParts.push(theme.fg("dim", tickStr));
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
// Show next regen credits for weekly token limit
|
|
407
|
+
if (window.nextRegenCredits !== undefined) {
|
|
408
|
+
const now = Date.now();
|
|
409
|
+
const remainingMs = window.resetsAt.getTime() - now;
|
|
410
|
+
const remainingHours = Math.ceil(remainingMs / (1000 * 60 * 60));
|
|
411
|
+
const remainingMins = Math.ceil(remainingMs / (1000 * 60));
|
|
412
|
+
const timeStr =
|
|
413
|
+
remainingMs <= 0
|
|
414
|
+
? "now"
|
|
415
|
+
: remainingHours >= 1
|
|
416
|
+
? `${remainingHours}h`
|
|
417
|
+
: `${remainingMins}m`;
|
|
418
|
+
const regenStr = `+${window.nextRegenCredits} in ${timeStr}`;
|
|
419
|
+
leftParts.push(theme.fg("dim", regenStr));
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
if (
|
|
423
|
+
projectedPercent > 0 &&
|
|
424
|
+
window.tickPercent === undefined &&
|
|
425
|
+
window.nextRegenCredits === undefined
|
|
426
|
+
) {
|
|
273
427
|
const estStr = `est ${Math.round(projectedPercent)}%`;
|
|
274
428
|
leftParts.push(
|
|
275
429
|
severity !== "success"
|
|
@@ -278,7 +432,11 @@ export class QuotasComponent implements Component {
|
|
|
278
432
|
);
|
|
279
433
|
}
|
|
280
434
|
|
|
281
|
-
if (
|
|
435
|
+
if (
|
|
436
|
+
pacePercent !== null &&
|
|
437
|
+
window.tickPercent === undefined &&
|
|
438
|
+
window.nextRegenCredits === undefined
|
|
439
|
+
) {
|
|
282
440
|
const paceDiff = window.usedPercent - pacePercent;
|
|
283
441
|
if (Math.abs(paceDiff) > 5) {
|
|
284
442
|
if (paceDiff > 0) {
|
|
@@ -28,34 +28,66 @@ interface SubCoreSettingsPayload {
|
|
|
28
28
|
function toUsageSnapshot(quotas: QuotasResponse): UsageSnapshot {
|
|
29
29
|
const windows: RateWindow[] = [];
|
|
30
30
|
|
|
31
|
-
|
|
31
|
+
// Weekly token limit (credits-based)
|
|
32
|
+
if (quotas.weeklyTokenLimit) {
|
|
33
|
+
const { weeklyTokenLimit } = quotas;
|
|
34
|
+
windows.push({
|
|
35
|
+
label: "Credits",
|
|
36
|
+
usedPercent: Math.round(
|
|
37
|
+
Math.max(0, Math.min(100, 100 - weeklyTokenLimit.percentRemaining)),
|
|
38
|
+
),
|
|
39
|
+
resetDescription: formatResetTime(weeklyTokenLimit.nextRegenAt),
|
|
40
|
+
resetAt: weeklyTokenLimit.nextRegenAt,
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Rolling 5-hour limit (request-based)
|
|
45
|
+
if (quotas.rollingFiveHourLimit && quotas.rollingFiveHourLimit.max > 0) {
|
|
46
|
+
const { rollingFiveHourLimit } = quotas;
|
|
47
|
+
const used = rollingFiveHourLimit.max - rollingFiveHourLimit.remaining;
|
|
48
|
+
windows.push({
|
|
49
|
+
label: "5h",
|
|
50
|
+
usedPercent: Math.round(
|
|
51
|
+
Math.max(0, Math.min(100, (used / rollingFiveHourLimit.max) * 100)),
|
|
52
|
+
),
|
|
53
|
+
resetDescription: formatResetTime(rollingFiveHourLimit.nextTickAt),
|
|
54
|
+
resetAt: rollingFiveHourLimit.nextTickAt,
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Legacy subscription (fallback if rollingFiveHourLimit not available)
|
|
59
|
+
if (
|
|
60
|
+
!quotas.rollingFiveHourLimit &&
|
|
61
|
+
quotas.subscription?.limit &&
|
|
62
|
+
quotas.subscription.limit > 0
|
|
63
|
+
) {
|
|
32
64
|
const pct =
|
|
33
65
|
(quotas.subscription.requests / quotas.subscription.limit) * 100;
|
|
34
66
|
windows.push({
|
|
35
67
|
label: "5h",
|
|
36
|
-
usedPercent: Math.round(pct),
|
|
68
|
+
usedPercent: Math.round(Math.max(0, Math.min(100, pct))),
|
|
37
69
|
resetDescription: formatResetTime(quotas.subscription.renewsAt),
|
|
38
70
|
resetAt: quotas.subscription.renewsAt,
|
|
39
71
|
});
|
|
40
72
|
}
|
|
41
73
|
|
|
42
|
-
if (quotas.search?.hourly) {
|
|
74
|
+
if (quotas.search?.hourly?.limit && quotas.search.hourly.limit > 0) {
|
|
43
75
|
const pct =
|
|
44
76
|
(quotas.search.hourly.requests / quotas.search.hourly.limit) * 100;
|
|
45
77
|
windows.push({
|
|
46
78
|
label: "Search",
|
|
47
|
-
usedPercent: Math.round(pct),
|
|
79
|
+
usedPercent: Math.round(Math.max(0, Math.min(100, pct))),
|
|
48
80
|
resetDescription: formatResetTime(quotas.search.hourly.renewsAt),
|
|
49
81
|
resetAt: quotas.search.hourly.renewsAt,
|
|
50
82
|
});
|
|
51
83
|
}
|
|
52
84
|
|
|
53
|
-
if (quotas.freeToolCalls) {
|
|
85
|
+
if (quotas.freeToolCalls?.limit && quotas.freeToolCalls.limit > 0) {
|
|
54
86
|
const pct =
|
|
55
87
|
(quotas.freeToolCalls.requests / quotas.freeToolCalls.limit) * 100;
|
|
56
88
|
windows.push({
|
|
57
89
|
label: "Tools",
|
|
58
|
-
usedPercent: Math.round(pct),
|
|
90
|
+
usedPercent: Math.round(Math.max(0, Math.min(100, pct))),
|
|
59
91
|
resetDescription: formatResetTime(quotas.freeToolCalls.renewsAt),
|
|
60
92
|
resetAt: quotas.freeToolCalls.renewsAt,
|
|
61
93
|
});
|
|
@@ -36,7 +36,7 @@ const SYNTHETIC_REASONING_EFFORT_MAP = {
|
|
|
36
36
|
} as const;
|
|
37
37
|
|
|
38
38
|
export const SYNTHETIC_MODELS: SyntheticModelConfig[] = [
|
|
39
|
-
// API: hf:zai-org/GLM-4.7 → ctx=202752,
|
|
39
|
+
// API: hf:zai-org/GLM-4.7 → ctx=202752, multimodal
|
|
40
40
|
{
|
|
41
41
|
id: "hf:zai-org/GLM-4.7",
|
|
42
42
|
name: "zai-org/GLM-4.7",
|
|
@@ -45,11 +45,11 @@ export const SYNTHETIC_MODELS: SyntheticModelConfig[] = [
|
|
|
45
45
|
supportsReasoningEffort: true,
|
|
46
46
|
reasoningEffortMap: SYNTHETIC_REASONING_EFFORT_MAP,
|
|
47
47
|
},
|
|
48
|
-
input: ["text"],
|
|
48
|
+
input: ["text", "image"],
|
|
49
49
|
cost: {
|
|
50
|
-
input:
|
|
50
|
+
input: 2.19,
|
|
51
51
|
output: 2.19,
|
|
52
|
-
cacheRead:
|
|
52
|
+
cacheRead: 2.19,
|
|
53
53
|
cacheWrite: 0,
|
|
54
54
|
},
|
|
55
55
|
contextWindow: 202752,
|
|
@@ -74,6 +74,26 @@ export const SYNTHETIC_MODELS: SyntheticModelConfig[] = [
|
|
|
74
74
|
contextWindow: 196608,
|
|
75
75
|
maxTokens: 65536,
|
|
76
76
|
},
|
|
77
|
+
// API: hf:zai-org/GLM-5.1 → ctx=196608, out=65536
|
|
78
|
+
{
|
|
79
|
+
id: "hf:zai-org/GLM-5.1",
|
|
80
|
+
name: "zai-org/GLM-5.1",
|
|
81
|
+
reasoning: true,
|
|
82
|
+
compat: {
|
|
83
|
+
supportsReasoningEffort: true,
|
|
84
|
+
reasoningEffortMap: SYNTHETIC_REASONING_EFFORT_MAP,
|
|
85
|
+
supportsDeveloperRole: false,
|
|
86
|
+
},
|
|
87
|
+
input: ["text"],
|
|
88
|
+
cost: {
|
|
89
|
+
input: 1,
|
|
90
|
+
output: 3,
|
|
91
|
+
cacheRead: 1,
|
|
92
|
+
cacheWrite: 0,
|
|
93
|
+
},
|
|
94
|
+
contextWindow: 196608,
|
|
95
|
+
maxTokens: 65536,
|
|
96
|
+
},
|
|
77
97
|
// API: hf:zai-org/GLM-4.7-Flash → ctx=196608
|
|
78
98
|
{
|
|
79
99
|
id: "hf:zai-org/GLM-4.7-Flash",
|
package/src/types/quotas.ts
CHANGED
|
@@ -1,19 +1,33 @@
|
|
|
1
1
|
export interface QuotasResponse {
|
|
2
|
-
subscription
|
|
2
|
+
subscription?: {
|
|
3
3
|
limit: number;
|
|
4
4
|
requests: number;
|
|
5
5
|
renewsAt: string;
|
|
6
6
|
};
|
|
7
|
-
search
|
|
8
|
-
hourly
|
|
7
|
+
search?: {
|
|
8
|
+
hourly?: {
|
|
9
9
|
limit: number;
|
|
10
10
|
requests: number;
|
|
11
11
|
renewsAt: string;
|
|
12
12
|
};
|
|
13
13
|
};
|
|
14
|
-
freeToolCalls
|
|
14
|
+
freeToolCalls?: {
|
|
15
15
|
limit: number;
|
|
16
16
|
requests: number;
|
|
17
17
|
renewsAt: string;
|
|
18
18
|
};
|
|
19
|
+
weeklyTokenLimit?: {
|
|
20
|
+
nextRegenAt: string;
|
|
21
|
+
percentRemaining: number;
|
|
22
|
+
maxCredits: string;
|
|
23
|
+
remainingCredits: string;
|
|
24
|
+
nextRegenCredits: string;
|
|
25
|
+
};
|
|
26
|
+
rollingFiveHourLimit?: {
|
|
27
|
+
nextTickAt: string;
|
|
28
|
+
tickPercent: number;
|
|
29
|
+
remaining: number;
|
|
30
|
+
max: number;
|
|
31
|
+
limited: boolean;
|
|
32
|
+
};
|
|
19
33
|
}
|