pi-better-openai 0.1.7 → 0.1.8
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/index.ts +247 -76
- package/package.json +12 -5
- package/src/config.ts +62 -27
- package/src/format.ts +12 -4
- package/src/image.ts +242 -86
- package/src/usage.ts +54 -15
package/README.md
CHANGED
package/index.ts
CHANGED
|
@@ -15,7 +15,6 @@ import {
|
|
|
15
15
|
IMAGE_OUTPUT_FORMATS,
|
|
16
16
|
IMAGE_SAVE_MODES,
|
|
17
17
|
configPaths,
|
|
18
|
-
type FooterMode,
|
|
19
18
|
type ResolvedConfig,
|
|
20
19
|
type SupportedModel,
|
|
21
20
|
isRecord,
|
|
@@ -24,7 +23,7 @@ import {
|
|
|
24
23
|
parseModels,
|
|
25
24
|
readRawConfig,
|
|
26
25
|
resolveConfig,
|
|
27
|
-
writeConfig
|
|
26
|
+
writeConfig,
|
|
28
27
|
} from "./src/config.ts";
|
|
29
28
|
import {
|
|
30
29
|
AUTH_FILE,
|
|
@@ -34,7 +33,7 @@ import {
|
|
|
34
33
|
formatUsageSnapshot,
|
|
35
34
|
parseUsageSnapshot,
|
|
36
35
|
readCodexAuth,
|
|
37
|
-
requestCodexUsage
|
|
36
|
+
requestCodexUsage,
|
|
38
37
|
} from "./src/usage.ts";
|
|
39
38
|
import { registerOpenAIImage, _imageTest } from "./src/image.ts";
|
|
40
39
|
|
|
@@ -49,7 +48,10 @@ type SettingsPickerItem = {
|
|
|
49
48
|
description?: string;
|
|
50
49
|
currentValue: string;
|
|
51
50
|
values?: string[];
|
|
52
|
-
submenu?: (
|
|
51
|
+
submenu?: (
|
|
52
|
+
currentValue: string,
|
|
53
|
+
done: (selectedValue?: string) => void,
|
|
54
|
+
) => { render(width: number): string[]; invalidate(): void; handleInput?(data: string): void };
|
|
53
55
|
};
|
|
54
56
|
|
|
55
57
|
function currentModelKey(ctx: ExtensionContext): string {
|
|
@@ -59,7 +61,9 @@ function currentModelKey(ctx: ExtensionContext): string {
|
|
|
59
61
|
function supportsFast(ctx: ExtensionContext, supportedModels: SupportedModel[]): boolean {
|
|
60
62
|
const current = ctx.model;
|
|
61
63
|
if (!current) return false;
|
|
62
|
-
return supportedModels.some(
|
|
64
|
+
return supportedModels.some(
|
|
65
|
+
(model) => model.provider === current.provider && model.id === current.id,
|
|
66
|
+
);
|
|
63
67
|
}
|
|
64
68
|
|
|
65
69
|
function modelList(supportedModels: SupportedModel[]): string {
|
|
@@ -68,7 +72,12 @@ function modelList(supportedModels: SupportedModel[]): string {
|
|
|
68
72
|
: "none configured";
|
|
69
73
|
}
|
|
70
74
|
|
|
71
|
-
function stateText(
|
|
75
|
+
function stateText(
|
|
76
|
+
ctx: ExtensionContext,
|
|
77
|
+
desiredActive: boolean,
|
|
78
|
+
active: boolean,
|
|
79
|
+
supportedModels: SupportedModel[],
|
|
80
|
+
): string {
|
|
72
81
|
const model = currentModelKey(ctx);
|
|
73
82
|
if (active) return `Fast mode is on for ${model}.`;
|
|
74
83
|
if (desiredActive) {
|
|
@@ -78,7 +87,8 @@ function stateText(ctx: ExtensionContext, desiredActive: boolean, active: boolea
|
|
|
78
87
|
}
|
|
79
88
|
|
|
80
89
|
function isOpenAISubscriptionModel(ctx: ExtensionContext, cfg: ResolvedConfig): boolean {
|
|
81
|
-
if (!ctx.model || (ctx.model.provider !== "openai" && ctx.model.provider !== "openai-codex"))
|
|
90
|
+
if (!ctx.model || (ctx.model.provider !== "openai" && ctx.model.provider !== "openai-codex"))
|
|
91
|
+
return false;
|
|
82
92
|
return !cfg.usage.showOnlyOnSubscriptionModels || ctx.modelRegistry.isUsingOAuth(ctx.model);
|
|
83
93
|
}
|
|
84
94
|
|
|
@@ -114,7 +124,11 @@ export default function betterOpenAI(pi: ExtensionAPI): void {
|
|
|
114
124
|
function persist(nextConfig: ResolvedConfig): void {
|
|
115
125
|
cachedConfig = { ...nextConfig, active, desiredActive };
|
|
116
126
|
if (!nextConfig.persistState) return;
|
|
117
|
-
writeConfig(nextConfig.configPath, {
|
|
127
|
+
writeConfig(nextConfig.configPath, {
|
|
128
|
+
...readRawConfig(nextConfig.configPath),
|
|
129
|
+
active,
|
|
130
|
+
desiredActive,
|
|
131
|
+
});
|
|
118
132
|
}
|
|
119
133
|
|
|
120
134
|
function applyDesiredFastState(ctx: ExtensionContext, cfg = config(ctx)): void {
|
|
@@ -128,13 +142,20 @@ export default function betterOpenAI(pi: ExtensionAPI): void {
|
|
|
128
142
|
persist(nextConfig);
|
|
129
143
|
updateFooter(ctx);
|
|
130
144
|
if (next && !active) {
|
|
131
|
-
ctx.ui.notify(
|
|
145
|
+
ctx.ui.notify(
|
|
146
|
+
`Fast mode requested, but ${currentModelKey(ctx)} is unsupported. It will activate automatically when you switch to a supported model: ${modelList(nextConfig.supportedModels)}.`,
|
|
147
|
+
"warning",
|
|
148
|
+
);
|
|
132
149
|
return;
|
|
133
150
|
}
|
|
134
151
|
ctx.ui.notify(stateText(ctx, desiredActive, active, nextConfig.supportedModels), "info");
|
|
135
152
|
}
|
|
136
153
|
|
|
137
|
-
async function refreshUsage(
|
|
154
|
+
async function refreshUsage(
|
|
155
|
+
ctx: ExtensionContext,
|
|
156
|
+
modelId = ctx.model?.id,
|
|
157
|
+
options?: { notify?: boolean },
|
|
158
|
+
): Promise<void> {
|
|
138
159
|
if (shuttingDown || !ctx.hasUI) return;
|
|
139
160
|
if (usageRefreshInFlight) {
|
|
140
161
|
queuedUsageRefresh = { ctx, modelId, notify: queuedUsageRefresh?.notify || options?.notify };
|
|
@@ -164,7 +185,8 @@ export default function betterOpenAI(pi: ExtensionAPI): void {
|
|
|
164
185
|
usageUpdatedAt = usageSnapshot ? Date.now() : undefined;
|
|
165
186
|
usageError = data ? undefined : `Missing openai-codex OAuth credentials in ${AUTH_FILE}.`;
|
|
166
187
|
if (!shuttingDown) updateFooter(ctx);
|
|
167
|
-
if (!shuttingDown && options?.notify)
|
|
188
|
+
if (!shuttingDown && options?.notify)
|
|
189
|
+
ctx.ui.notify(formatUsageStatus(ctx), usageSnapshot ? "info" : "warning");
|
|
168
190
|
} catch (error) {
|
|
169
191
|
if (shuttingDown) return;
|
|
170
192
|
usageError = error instanceof Error ? error.message : String(error);
|
|
@@ -216,25 +238,27 @@ export default function betterOpenAI(pi: ExtensionAPI): void {
|
|
|
216
238
|
`Last successful update: ${usageUpdatedAt ? new Date(usageUpdatedAt).toLocaleTimeString() : "never"}`,
|
|
217
239
|
`Last error: ${usageError ?? "none"}`,
|
|
218
240
|
`Refresh interval: ${cfg.usage.refreshIntervalMs}ms`,
|
|
219
|
-
`Endpoint: https://chatgpt.com/backend-api/wham/usage
|
|
241
|
+
`Endpoint: https://chatgpt.com/backend-api/wham/usage`,
|
|
220
242
|
].join("\n");
|
|
221
243
|
}
|
|
222
244
|
|
|
223
245
|
function formatUsageStatus(ctx: ExtensionContext): string {
|
|
224
246
|
const cfg = config(ctx);
|
|
225
247
|
if (!cfg.usage.enabled) return "Usage display is disabled.";
|
|
226
|
-
if (!isOpenAISubscriptionModel(ctx, cfg))
|
|
248
|
+
if (!isOpenAISubscriptionModel(ctx, cfg))
|
|
249
|
+
return "Usage hidden: current model is not an OpenAI subscription model.";
|
|
227
250
|
if (!usageSnapshot) return `Usage unavailable${usageError ? `: ${usageError}` : "."}`;
|
|
228
|
-
const stale =
|
|
229
|
-
|
|
230
|
-
|
|
251
|
+
const stale =
|
|
252
|
+
usageUpdatedAt && Date.now() - usageUpdatedAt > cfg.usage.refreshIntervalMs * 2
|
|
253
|
+
? ` | stale ${formatResetCountdown((Date.now() - usageUpdatedAt) / 1000)}`
|
|
254
|
+
: "";
|
|
231
255
|
return `${formatUsageSnapshot(usageSnapshot, cfg.usage)}${stale}`;
|
|
232
256
|
}
|
|
233
257
|
|
|
234
258
|
pi.registerFlag(FLAG, {
|
|
235
259
|
description: "Start with OpenAI fast mode enabled (service_tier=priority)",
|
|
236
260
|
type: "boolean",
|
|
237
|
-
default: false
|
|
261
|
+
default: false,
|
|
238
262
|
});
|
|
239
263
|
|
|
240
264
|
function formatDebugStatus(ctx: ExtensionContext): string {
|
|
@@ -247,10 +271,12 @@ export default function betterOpenAI(pi: ExtensionAPI): void {
|
|
|
247
271
|
`Configured service_tier: ${SERVICE_TIER}`,
|
|
248
272
|
`Last injected: ${lastInjectedAt ? `${new Date(lastInjectedAt).toLocaleTimeString()} (${lastInjectedModel}, ${lastInjectedTier})` : "never"}`,
|
|
249
273
|
`Footer mode: ${cfg.footer.mode}`,
|
|
250
|
-
|
|
274
|
+
"",
|
|
275
|
+
formatUsageDebug(ctx),
|
|
276
|
+
"",
|
|
251
277
|
`Image enabled: ${cfg.image.enabled}`,
|
|
252
278
|
`Image default save: ${cfg.image.defaultSave}`,
|
|
253
|
-
`Config: ${cfg.configPath}
|
|
279
|
+
`Config: ${cfg.configPath}`,
|
|
254
280
|
].join("\n");
|
|
255
281
|
}
|
|
256
282
|
|
|
@@ -265,14 +291,14 @@ export default function betterOpenAI(pi: ExtensionAPI): void {
|
|
|
265
291
|
const arg = args.trim().toLowerCase();
|
|
266
292
|
if (!arg) return setActive(ctx, !desiredActive);
|
|
267
293
|
ctx.ui.notify("Usage: /fast", "error");
|
|
268
|
-
}
|
|
294
|
+
},
|
|
269
295
|
});
|
|
270
296
|
|
|
271
297
|
pi.registerCommand(OPENAI_STATUS_COMMAND, {
|
|
272
298
|
description: "Show OpenAI subscription usage status",
|
|
273
299
|
handler: async (_args, ctx) => {
|
|
274
300
|
ctx.ui.notify(formatOpenAIStatus(ctx), "info");
|
|
275
|
-
}
|
|
301
|
+
},
|
|
276
302
|
});
|
|
277
303
|
|
|
278
304
|
function textPanel(title: string, lines: string[], done: () => void) {
|
|
@@ -284,27 +310,124 @@ export default function betterOpenAI(pi: ExtensionAPI): void {
|
|
|
284
310
|
invalidate() {},
|
|
285
311
|
handleInput(data: string) {
|
|
286
312
|
if (data.includes("\x1b") || data === "escape" || data === "q" || data === "\x03") done();
|
|
287
|
-
}
|
|
313
|
+
},
|
|
288
314
|
};
|
|
289
315
|
}
|
|
290
316
|
|
|
291
317
|
function buildSettingsItems(ctx: ExtensionContext, cfg: ResolvedConfig): SettingsPickerItem[] {
|
|
292
318
|
return [
|
|
293
|
-
{
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
{
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
{
|
|
319
|
+
{
|
|
320
|
+
id: "fast.enabled",
|
|
321
|
+
label: "Fast mode",
|
|
322
|
+
currentValue: String(desiredActive),
|
|
323
|
+
values: ["true", "false"],
|
|
324
|
+
description: `Request OpenAI fast mode. Activates for supported models: ${modelList(cfg.supportedModels)}.`,
|
|
325
|
+
},
|
|
326
|
+
{
|
|
327
|
+
id: "persistState",
|
|
328
|
+
label: "Persist fast state",
|
|
329
|
+
currentValue: String(cfg.persistState),
|
|
330
|
+
values: ["true", "false"],
|
|
331
|
+
description: "Remember fast-mode state across sessions.",
|
|
332
|
+
},
|
|
333
|
+
{
|
|
334
|
+
id: "footer.mode",
|
|
335
|
+
label: "Footer mode",
|
|
336
|
+
currentValue: cfg.footer.mode,
|
|
337
|
+
values: [...FOOTER_MODES],
|
|
338
|
+
description:
|
|
339
|
+
"replace = custom footer, status = pi footer plus status line, off = no Better OpenAI footer/status.",
|
|
340
|
+
},
|
|
341
|
+
{
|
|
342
|
+
id: "usage.enabled",
|
|
343
|
+
label: "Usage display",
|
|
344
|
+
currentValue: String(cfg.usage.enabled),
|
|
345
|
+
values: ["true", "false"],
|
|
346
|
+
description: "Fetch and display OpenAI subscription usage windows.",
|
|
347
|
+
},
|
|
348
|
+
{
|
|
349
|
+
id: "usage.refreshIntervalMs",
|
|
350
|
+
label: "Usage refresh",
|
|
351
|
+
currentValue: String(cfg.usage.refreshIntervalMs),
|
|
352
|
+
values: ["15000", "30000", "60000", "120000", "300000", "600000"],
|
|
353
|
+
description: "Usage refresh interval in milliseconds.",
|
|
354
|
+
},
|
|
355
|
+
{
|
|
356
|
+
id: "usage.showOnlyOnSubscriptionModels",
|
|
357
|
+
label: "Usage only on OAuth",
|
|
358
|
+
currentValue: String(cfg.usage.showOnlyOnSubscriptionModels),
|
|
359
|
+
values: ["true", "false"],
|
|
360
|
+
description: "Only show usage when the current OpenAI model uses subscription/OAuth auth.",
|
|
361
|
+
},
|
|
362
|
+
{
|
|
363
|
+
id: "usage.showResetTimes",
|
|
364
|
+
label: "Usage reset times",
|
|
365
|
+
currentValue: String(cfg.usage.showResetTimes),
|
|
366
|
+
values: ["true", "false"],
|
|
367
|
+
description: "Include compact reset countdowns and local reset times.",
|
|
368
|
+
},
|
|
369
|
+
{
|
|
370
|
+
id: "image.enabled",
|
|
371
|
+
label: "Image tool",
|
|
372
|
+
currentValue: String(cfg.image.enabled),
|
|
373
|
+
values: ["true", "false"],
|
|
374
|
+
description: "Allow the openai_image tool to make image requests.",
|
|
375
|
+
},
|
|
376
|
+
{
|
|
377
|
+
id: "image.defaultModel",
|
|
378
|
+
label: "Image model",
|
|
379
|
+
currentValue: cfg.image.defaultModel,
|
|
380
|
+
values: ["gpt-5.5", "gpt-5.4", "gpt-5.2", "gpt-5"],
|
|
381
|
+
description:
|
|
382
|
+
"Mainline model used for image generation when current model is not openai-codex.",
|
|
383
|
+
},
|
|
384
|
+
{
|
|
385
|
+
id: "image.defaultSave",
|
|
386
|
+
label: "Image save",
|
|
387
|
+
currentValue: cfg.image.defaultSave,
|
|
388
|
+
values: [...IMAGE_SAVE_MODES],
|
|
389
|
+
description: "Where generated images are saved by default.",
|
|
390
|
+
},
|
|
391
|
+
{
|
|
392
|
+
id: "image.outputFormat",
|
|
393
|
+
label: "Image format",
|
|
394
|
+
currentValue: cfg.image.outputFormat,
|
|
395
|
+
values: [...IMAGE_OUTPUT_FORMATS],
|
|
396
|
+
description: "Generated image file format.",
|
|
397
|
+
},
|
|
398
|
+
{
|
|
399
|
+
id: "image.timeoutMs",
|
|
400
|
+
label: "Image timeout",
|
|
401
|
+
currentValue: String(cfg.image.timeoutMs),
|
|
402
|
+
values: ["30000", "60000", "120000", "180000", "300000"],
|
|
403
|
+
description: "Image request timeout in milliseconds.",
|
|
404
|
+
},
|
|
405
|
+
{
|
|
406
|
+
id: "debug",
|
|
407
|
+
label: "Debug info",
|
|
408
|
+
currentValue: "open",
|
|
409
|
+
description: "Show Better OpenAI diagnostics.",
|
|
410
|
+
submenu: (_value, done) =>
|
|
411
|
+
textPanel("Debug info", formatDebugStatus(ctx).split("\n"), () => done()),
|
|
412
|
+
},
|
|
413
|
+
{
|
|
414
|
+
id: "config.path",
|
|
415
|
+
label: "Config path",
|
|
416
|
+
currentValue: cfg.configPath,
|
|
417
|
+
description: `Project: ${cfg.projectConfigPath}\nGlobal: ${cfg.globalConfigPath}`,
|
|
418
|
+
},
|
|
419
|
+
{
|
|
420
|
+
id: "config.print",
|
|
421
|
+
label: "Print config",
|
|
422
|
+
currentValue: "open",
|
|
423
|
+
description: "Show the selected raw config JSON.",
|
|
424
|
+
submenu: (_value, done) =>
|
|
425
|
+
textPanel(
|
|
426
|
+
"Config",
|
|
427
|
+
JSON.stringify(readRawConfig(cfg.configPath), null, 2).split("\n"),
|
|
428
|
+
() => done(),
|
|
429
|
+
),
|
|
430
|
+
},
|
|
308
431
|
];
|
|
309
432
|
}
|
|
310
433
|
|
|
@@ -333,7 +456,14 @@ export default function betterOpenAI(pi: ExtensionAPI): void {
|
|
|
333
456
|
} else if (id.startsWith("image.")) {
|
|
334
457
|
const image = isRecord(current.image) ? current.image : {};
|
|
335
458
|
const key = id.slice("image.".length);
|
|
336
|
-
image[key] =
|
|
459
|
+
image[key] =
|
|
460
|
+
key === "timeoutMs"
|
|
461
|
+
? num
|
|
462
|
+
: rawValue === "true"
|
|
463
|
+
? true
|
|
464
|
+
: rawValue === "false"
|
|
465
|
+
? false
|
|
466
|
+
: rawValue;
|
|
337
467
|
current.image = image;
|
|
338
468
|
}
|
|
339
469
|
writeConfig(cfg.configPath, current);
|
|
@@ -353,37 +483,51 @@ export default function betterOpenAI(pi: ExtensionAPI): void {
|
|
|
353
483
|
async function showSettingsPicker(ctx: ExtensionContext): Promise<void> {
|
|
354
484
|
const [{ getSettingsListTheme }, { Container, SettingsList }] = await Promise.all([
|
|
355
485
|
import("@mariozechner/pi-coding-agent"),
|
|
356
|
-
import("@mariozechner/pi-tui")
|
|
486
|
+
import("@mariozechner/pi-tui"),
|
|
357
487
|
]);
|
|
358
488
|
await ctx.ui.custom((tui, theme, _kb, done) => {
|
|
359
489
|
const container = new Container();
|
|
360
|
-
container.addChild(
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
490
|
+
container.addChild(
|
|
491
|
+
new (class {
|
|
492
|
+
render(_width: number) {
|
|
493
|
+
const cfg = config(ctx);
|
|
494
|
+
return [
|
|
495
|
+
theme.fg("accent", theme.bold("Better OpenAI Settings")),
|
|
496
|
+
theme.fg("dim", cfg.configPath),
|
|
497
|
+
"",
|
|
498
|
+
];
|
|
499
|
+
}
|
|
500
|
+
invalidate() {}
|
|
501
|
+
})(),
|
|
502
|
+
);
|
|
367
503
|
const settingsList = new SettingsList(
|
|
368
504
|
buildSettingsItems(ctx, refresh(ctx)),
|
|
369
505
|
13,
|
|
370
506
|
getSettingsListTheme(),
|
|
371
507
|
(id, newValue) => {
|
|
372
508
|
writeSetting(ctx, id, newValue);
|
|
373
|
-
settingsList.updateValue(
|
|
509
|
+
settingsList.updateValue(
|
|
510
|
+
id,
|
|
511
|
+
buildSettingsItems(ctx, config(ctx)).find((item) => item.id === id)?.currentValue ??
|
|
512
|
+
newValue,
|
|
513
|
+
);
|
|
374
514
|
tui.requestRender();
|
|
375
515
|
},
|
|
376
516
|
() => done(undefined),
|
|
377
|
-
{ enableSearch: true }
|
|
517
|
+
{ enableSearch: true },
|
|
378
518
|
);
|
|
379
519
|
container.addChild(settingsList);
|
|
380
520
|
return {
|
|
381
|
-
render(width: number) {
|
|
382
|
-
|
|
521
|
+
render(width: number) {
|
|
522
|
+
return container.render(width);
|
|
523
|
+
},
|
|
524
|
+
invalidate() {
|
|
525
|
+
container.invalidate();
|
|
526
|
+
},
|
|
383
527
|
handleInput(data: string) {
|
|
384
528
|
settingsList.handleInput(data);
|
|
385
529
|
tui.requestRender();
|
|
386
|
-
}
|
|
530
|
+
},
|
|
387
531
|
};
|
|
388
532
|
});
|
|
389
533
|
}
|
|
@@ -392,10 +536,9 @@ export default function betterOpenAI(pi: ExtensionAPI): void {
|
|
|
392
536
|
description: "Open Better OpenAI settings picker",
|
|
393
537
|
handler: async (_args, ctx) => {
|
|
394
538
|
await showSettingsPicker(ctx);
|
|
395
|
-
}
|
|
539
|
+
},
|
|
396
540
|
});
|
|
397
541
|
|
|
398
|
-
|
|
399
542
|
registerOpenAIImage(pi, config);
|
|
400
543
|
|
|
401
544
|
function installFooter(ctx: ExtensionContext): void {
|
|
@@ -438,18 +581,24 @@ export default function betterOpenAI(pi: ExtensionAPI): void {
|
|
|
438
581
|
if (totalCacheWrite) parts.push(`W${formatTokens(totalCacheWrite)}`);
|
|
439
582
|
|
|
440
583
|
const usingSubscription = ctx.model ? ctx.modelRegistry.isUsingOAuth(ctx.model) : false;
|
|
441
|
-
if (totalCost || usingSubscription)
|
|
584
|
+
if (totalCost || usingSubscription)
|
|
585
|
+
parts.push(`$${totalCost.toFixed(3)}${usingSubscription ? " (sub)" : ""}`);
|
|
442
586
|
|
|
443
587
|
const contextUsage = ctx.getContextUsage();
|
|
444
588
|
const contextWindow = contextUsage?.contextWindow ?? ctx.model?.contextWindow ?? 0;
|
|
445
589
|
const contextPercentValue = contextUsage?.percent ?? 0;
|
|
446
|
-
const contextPercent =
|
|
447
|
-
|
|
448
|
-
const
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
590
|
+
const contextPercent =
|
|
591
|
+
contextUsage?.percent !== null ? contextPercentValue.toFixed(1) : "?";
|
|
592
|
+
const contextDisplay =
|
|
593
|
+
contextPercent === "?"
|
|
594
|
+
? `?/${formatTokens(contextWindow)} (auto)`
|
|
595
|
+
: `${contextPercent}%/${formatTokens(contextWindow)} (auto)`;
|
|
596
|
+
const contextText =
|
|
597
|
+
contextPercentValue > 90
|
|
598
|
+
? theme.fg("error", contextDisplay)
|
|
599
|
+
: contextPercentValue > 70
|
|
600
|
+
? theme.fg("warning", contextDisplay)
|
|
601
|
+
: contextDisplay;
|
|
453
602
|
parts.push(contextText);
|
|
454
603
|
|
|
455
604
|
let usageLine: string | undefined;
|
|
@@ -467,12 +616,14 @@ export default function betterOpenAI(pi: ExtensionAPI): void {
|
|
|
467
616
|
|
|
468
617
|
const modelName = ctx.model?.id || "no-model";
|
|
469
618
|
const thinkingLevel = pi.getThinkingLevel();
|
|
470
|
-
const fastSuffix =
|
|
619
|
+
const fastSuffix =
|
|
620
|
+
active && supportsFast(ctx, config(ctx).supportedModels) ? " fast" : "";
|
|
471
621
|
let rightWithoutProvider = modelName;
|
|
472
622
|
if (ctx.model?.reasoning) {
|
|
473
|
-
rightWithoutProvider =
|
|
474
|
-
|
|
475
|
-
|
|
623
|
+
rightWithoutProvider =
|
|
624
|
+
thinkingLevel === "off"
|
|
625
|
+
? `${modelName}${fastSuffix} • thinking off`
|
|
626
|
+
: `${modelName}${fastSuffix} • ${thinkingLevel}`;
|
|
476
627
|
} else if (fastSuffix) {
|
|
477
628
|
rightWithoutProvider = `${modelName}${fastSuffix}`;
|
|
478
629
|
}
|
|
@@ -492,7 +643,10 @@ export default function betterOpenAI(pi: ExtensionAPI): void {
|
|
|
492
643
|
const availableForRight = width - statsLeftWidth - 2;
|
|
493
644
|
if (availableForRight > 0) {
|
|
494
645
|
const truncatedRight = truncateToWidth(rightSide, availableForRight, "");
|
|
495
|
-
statsLine =
|
|
646
|
+
statsLine =
|
|
647
|
+
statsLeft +
|
|
648
|
+
" ".repeat(Math.max(0, width - statsLeftWidth - visibleWidth(truncatedRight))) +
|
|
649
|
+
truncatedRight;
|
|
496
650
|
} else {
|
|
497
651
|
statsLine = statsLeft;
|
|
498
652
|
}
|
|
@@ -500,7 +654,7 @@ export default function betterOpenAI(pi: ExtensionAPI): void {
|
|
|
500
654
|
|
|
501
655
|
const lines = [
|
|
502
656
|
truncateToWidth(theme.fg("dim", pwd), width, theme.fg("dim", "...")),
|
|
503
|
-
theme.fg("dim", statsLeft) + theme.fg("dim", statsLine.slice(statsLeft.length))
|
|
657
|
+
theme.fg("dim", statsLeft) + theme.fg("dim", statsLine.slice(statsLeft.length)),
|
|
504
658
|
];
|
|
505
659
|
|
|
506
660
|
if (usageLine) {
|
|
@@ -517,7 +671,7 @@ export default function betterOpenAI(pi: ExtensionAPI): void {
|
|
|
517
671
|
}
|
|
518
672
|
|
|
519
673
|
return lines;
|
|
520
|
-
}
|
|
674
|
+
},
|
|
521
675
|
};
|
|
522
676
|
});
|
|
523
677
|
}
|
|
@@ -536,8 +690,14 @@ export default function betterOpenAI(pi: ExtensionAPI): void {
|
|
|
536
690
|
ctx.ui.setStatus(STATUS_KEY, undefined);
|
|
537
691
|
return;
|
|
538
692
|
}
|
|
539
|
-
const fast =
|
|
540
|
-
|
|
693
|
+
const fast =
|
|
694
|
+
active && supportsFast(ctx, cfg.supportedModels)
|
|
695
|
+
? `${ctx.model?.id ?? "model"} fast`
|
|
696
|
+
: undefined;
|
|
697
|
+
const usage =
|
|
698
|
+
usageSnapshot && cfg.usage.enabled && isOpenAISubscriptionModel(ctx, cfg)
|
|
699
|
+
? formatUsageSnapshot(usageSnapshot, cfg.usage)
|
|
700
|
+
: undefined;
|
|
541
701
|
ctx.ui.setStatus(STATUS_KEY, [fast, usage].filter(Boolean).join(" | ") || undefined);
|
|
542
702
|
}
|
|
543
703
|
|
|
@@ -546,14 +706,19 @@ export default function betterOpenAI(pi: ExtensionAPI): void {
|
|
|
546
706
|
desiredActive = nextConfig.persistState ? nextConfig.desiredActive : false;
|
|
547
707
|
if (pi.getFlag(FLAG) === true) desiredActive = true;
|
|
548
708
|
applyDesiredFastState(ctx, nextConfig);
|
|
549
|
-
if (desiredActive !== nextConfig.desiredActive || active !== nextConfig.active)
|
|
709
|
+
if (desiredActive !== nextConfig.desiredActive || active !== nextConfig.active)
|
|
710
|
+
persist(nextConfig);
|
|
550
711
|
if (desiredActive && !active) {
|
|
551
|
-
ctx.ui.notify(
|
|
712
|
+
ctx.ui.notify(
|
|
713
|
+
`Fast mode requested, but ${currentModelKey(ctx)} is unsupported. It will activate automatically when you switch to a supported model: ${modelList(nextConfig.supportedModels)}.`,
|
|
714
|
+
"warning",
|
|
715
|
+
);
|
|
552
716
|
}
|
|
553
717
|
refreshFooterTotals(ctx);
|
|
554
718
|
updateFooter(ctx);
|
|
555
719
|
startUsageRefresh(ctx);
|
|
556
|
-
if (active)
|
|
720
|
+
if (active)
|
|
721
|
+
ctx.ui.notify(stateText(ctx, desiredActive, active, nextConfig.supportedModels), "info");
|
|
557
722
|
});
|
|
558
723
|
|
|
559
724
|
pi.on("turn_end", (_event, ctx) => {
|
|
@@ -578,7 +743,12 @@ export default function betterOpenAI(pi: ExtensionAPI): void {
|
|
|
578
743
|
applyDesiredFastState(ctx, cfg);
|
|
579
744
|
if (active !== wasActive) {
|
|
580
745
|
persist(cfg);
|
|
581
|
-
ctx.ui.notify(
|
|
746
|
+
ctx.ui.notify(
|
|
747
|
+
active
|
|
748
|
+
? stateText(ctx, desiredActive, active, cfg.supportedModels)
|
|
749
|
+
: `Fast mode inactive for unsupported model ${currentModelKey(ctx)}.`,
|
|
750
|
+
active ? "info" : "warning",
|
|
751
|
+
);
|
|
582
752
|
}
|
|
583
753
|
updateFooter(ctx);
|
|
584
754
|
void refreshUsage(ctx, event.model.id);
|
|
@@ -595,7 +765,8 @@ export default function betterOpenAI(pi: ExtensionAPI): void {
|
|
|
595
765
|
|
|
596
766
|
pi.on("before_provider_request", (event, ctx) => {
|
|
597
767
|
const nextConfig = config(ctx);
|
|
598
|
-
if (!active || !supportsFast(ctx, nextConfig.supportedModels) || !isRecord(event.payload))
|
|
768
|
+
if (!active || !supportsFast(ctx, nextConfig.supportedModels) || !isRecord(event.payload))
|
|
769
|
+
return;
|
|
599
770
|
lastInjectedAt = Date.now();
|
|
600
771
|
lastInjectedModel = currentModelKey(ctx);
|
|
601
772
|
lastInjectedTier = SERVICE_TIER;
|
|
@@ -620,5 +791,5 @@ export const _test = {
|
|
|
620
791
|
formatPercent,
|
|
621
792
|
formatUsageSnapshot,
|
|
622
793
|
readCodexAuth,
|
|
623
|
-
imageTest: _imageTest
|
|
794
|
+
imageTest: _imageTest,
|
|
624
795
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-better-openai",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.8",
|
|
4
4
|
"description": "Personal pi extension that improves OpenAI with fast mode, usage stats, and footer polish.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"fast",
|
|
@@ -29,9 +29,20 @@
|
|
|
29
29
|
"access": "public"
|
|
30
30
|
},
|
|
31
31
|
"scripts": {
|
|
32
|
+
"check": "npm run typecheck && npm run lint && npm run format:check && npm test",
|
|
33
|
+
"format": "oxfmt .",
|
|
34
|
+
"format:check": "oxfmt --check .",
|
|
35
|
+
"lint": "oxlint --deny-warnings .",
|
|
36
|
+
"lint:fix": "oxlint --fix .",
|
|
32
37
|
"test": "node tests/basic.mjs",
|
|
33
38
|
"typecheck": "tsc --noEmit"
|
|
34
39
|
},
|
|
40
|
+
"devDependencies": {
|
|
41
|
+
"@types/node": "^25.6.0",
|
|
42
|
+
"oxfmt": "^0.47.0",
|
|
43
|
+
"oxlint": "^1.62.0",
|
|
44
|
+
"typescript": "^6.0.3"
|
|
45
|
+
},
|
|
35
46
|
"peerDependencies": {
|
|
36
47
|
"@mariozechner/pi-coding-agent": ">=0.57.0",
|
|
37
48
|
"@mariozechner/pi-tui": "*"
|
|
@@ -40,9 +51,5 @@
|
|
|
40
51
|
"extensions": [
|
|
41
52
|
"./index.ts"
|
|
42
53
|
]
|
|
43
|
-
},
|
|
44
|
-
"devDependencies": {
|
|
45
|
-
"@types/node": "^25.6.0",
|
|
46
|
-
"typescript": "^6.0.3"
|
|
47
54
|
}
|
|
48
55
|
}
|