@evandu/super-pi 0.1.125 → 0.1.126
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/.super-pi-check-inputs.json +2 -2
- package/dist/client/.super-pi-client-sources.json +6 -6
- package/dist/client/assets/{CodeMirrorEditor-CK5tZxIo.js → CodeMirrorEditor-B0lBmhx0.js} +2 -2
- package/dist/client/assets/{dist-D87R6Wol.js → dist-BCUV3ivx.js} +1 -1
- package/dist/client/assets/{dist-BDAJ6COK.js → dist-BFm82vnX.js} +1 -1
- package/dist/client/assets/{dist-BbK-QOz1.js → dist-BNQkX4KY.js} +1 -1
- package/dist/client/assets/{dist-Om17wCc8.js → dist-BSZCQuEt.js} +1 -1
- package/dist/client/assets/{dist-fEUHAgwX.js → dist-BW_rg1Bj.js} +1 -1
- package/dist/client/assets/{dist-DsObObmI.js → dist-BYtrk8af.js} +1 -1
- package/dist/client/assets/{dist-nSf0tcKa.js → dist-BZ7PyaBp.js} +1 -1
- package/dist/client/assets/{dist-5O-klFrP.js → dist-Bi1EIIwj.js} +1 -1
- package/dist/client/assets/{dist-DsY1TPps.js → dist-BzqfS4cF.js} +1 -1
- package/dist/client/assets/{dist-CrnIVt5K.js → dist-CL6353ch.js} +1 -1
- package/dist/client/assets/{dist-BC3_yB3K.js → dist-CjS579f2.js} +1 -1
- package/dist/client/assets/{dist-D9IZ5pHN.js → dist-CoiRxusA.js} +1 -1
- package/dist/client/assets/{dist-B8X48E9G.js → dist-CuTxPpgb.js} +1 -1
- package/dist/client/assets/{dist-BR5dccfj.js → dist-DAujCIzu.js} +1 -1
- package/dist/client/assets/{dist-MYmiV3U3.js → dist-DawI143o.js} +1 -1
- package/dist/client/assets/{dist-DR8q7BI-.js → dist-GF4SWnU-.js} +1 -1
- package/dist/client/assets/{dist-r_bnlg1k.js → dist-SzL6z89P.js} +1 -1
- package/dist/client/assets/{dist-Did2mkbm.js → dist-XPppcWa2.js} +1 -1
- package/dist/client/assets/{dist-DIa1uLNH.js → dist-eerDlQWM.js} +1 -1
- package/dist/client/assets/{dist-BDULvJIt.js → dist-pUnfiqui.js} +1 -1
- package/dist/client/assets/{dist-D90mwiSm.js → dist-rONoVhdP.js} +1 -1
- package/dist/client/assets/{dist-Ck1wVFJ1.js → dist-x58QSOcp.js} +1 -1
- package/dist/client/assets/{index-CBPWeTvl.css → index-09I_ABgJ.css} +1 -1
- package/dist/client/assets/{index-qTPZ6eno.js → index-Dp0610g1.js} +5 -4
- package/dist/client/index.html +2 -2
- package/dist/server/application/provider-service.d.ts +14 -0
- package/dist/server/application/provider-service.js +16 -0
- package/dist/server/command-handler.js +6 -0
- package/dist/server/pi-session.d.ts +9 -0
- package/dist/server/pi-session.js +1 -0
- package/dist/server/providers.d.ts +27 -0
- package/dist/server/providers.js +83 -1
- package/dist/server/runtime.js +5 -0
- package/dist/server/types.d.ts +22 -0
- package/package.json +1 -1
package/dist/client/index.html
CHANGED
|
@@ -8,8 +8,8 @@
|
|
|
8
8
|
<link rel="alternate icon" href="/favicon.ico" />
|
|
9
9
|
<link rel="apple-touch-icon" href="/favicon.svg" />
|
|
10
10
|
<title>Super Pi</title>
|
|
11
|
-
<script type="module" crossorigin src="/assets/index-
|
|
12
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
11
|
+
<script type="module" crossorigin src="/assets/index-Dp0610g1.js"></script>
|
|
12
|
+
<link rel="stylesheet" crossorigin href="/assets/index-09I_ABgJ.css">
|
|
13
13
|
</head>
|
|
14
14
|
<body>
|
|
15
15
|
<div id="root"></div>
|
|
@@ -56,6 +56,20 @@ export declare class ProviderService {
|
|
|
56
56
|
*/
|
|
57
57
|
disable(providerId: string): Promise<PublicProvider[]>;
|
|
58
58
|
enable(providerId: string): Promise<PublicProvider[]>;
|
|
59
|
+
/**
|
|
60
|
+
* Repairs Pi's model configuration: the empty fields Pi's schema refuses are dropped, and nothing
|
|
61
|
+
* else in the file is touched.
|
|
62
|
+
*
|
|
63
|
+
* WHY IT IS WORTH A BUTTON. Pi validates the WHOLE `models.json`, so one entry with an empty
|
|
64
|
+
* `baseUrl` takes every custom provider out of every picker - and nothing on screen says so,
|
|
65
|
+
* because the settings panel reads the file leniently and lists them as if they worked. The repair
|
|
66
|
+
* restores the file Pi will accept, which is the difference between "my provider is gone" and
|
|
67
|
+
* "your file had an empty field".
|
|
68
|
+
*/
|
|
69
|
+
repair(): Promise<{
|
|
70
|
+
fixed: string[];
|
|
71
|
+
providers: PublicProvider[];
|
|
72
|
+
}>;
|
|
59
73
|
/**
|
|
60
74
|
* Adds a provider, or adds a model to one, or updates one.
|
|
61
75
|
*
|
|
@@ -135,6 +135,22 @@ export class ProviderService {
|
|
|
135
135
|
await this.afterChange();
|
|
136
136
|
return this.list();
|
|
137
137
|
}
|
|
138
|
+
/**
|
|
139
|
+
* Repairs Pi's model configuration: the empty fields Pi's schema refuses are dropped, and nothing
|
|
140
|
+
* else in the file is touched.
|
|
141
|
+
*
|
|
142
|
+
* WHY IT IS WORTH A BUTTON. Pi validates the WHOLE `models.json`, so one entry with an empty
|
|
143
|
+
* `baseUrl` takes every custom provider out of every picker - and nothing on screen says so,
|
|
144
|
+
* because the settings panel reads the file leniently and lists them as if they worked. The repair
|
|
145
|
+
* restores the file Pi will accept, which is the difference between "my provider is gone" and
|
|
146
|
+
* "your file had an empty field".
|
|
147
|
+
*/
|
|
148
|
+
async repair() {
|
|
149
|
+
const result = await this.registry.repairModelsConfig();
|
|
150
|
+
if (result.changed)
|
|
151
|
+
await this.afterChange();
|
|
152
|
+
return { fixed: result.fixed, providers: await this.list() };
|
|
153
|
+
}
|
|
138
154
|
/**
|
|
139
155
|
* Adds a provider, or adds a model to one, or updates one.
|
|
140
156
|
*
|
|
@@ -122,6 +122,12 @@ export class CommandHandler {
|
|
|
122
122
|
await this.providerService.enable(command.providerId);
|
|
123
123
|
return;
|
|
124
124
|
}
|
|
125
|
+
if (command.type === "providers.repair") {
|
|
126
|
+
if (!this.providerService)
|
|
127
|
+
throw new Error("供应商功能不可用");
|
|
128
|
+
await this.providerService.repair();
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
125
131
|
if (command.type === "providers.fetch") {
|
|
126
132
|
if (!this.providerService)
|
|
127
133
|
throw new Error("供应商功能不可用");
|
|
@@ -77,6 +77,15 @@ export interface SdkCatalog {
|
|
|
77
77
|
provider: string;
|
|
78
78
|
modelId: string;
|
|
79
79
|
}): Promise<string[] | undefined>;
|
|
80
|
+
/**
|
|
81
|
+
* WHAT PI SAYS IS WRONG WITH ITS OWN MODEL CONFIGURATION, verbatim.
|
|
82
|
+
*
|
|
83
|
+
* Pi validates the WHOLE `models.json` against a schema, and one entry it refuses takes every
|
|
84
|
+
* custom provider out of every picker - while a lenient reader of the same file still lists them
|
|
85
|
+
* all. Pi's own words are the only ones that name the offending field, so they are what the
|
|
86
|
+
* settings panel shows instead of a guess.
|
|
87
|
+
*/
|
|
88
|
+
configError(): string | undefined;
|
|
80
89
|
}
|
|
81
90
|
/**
|
|
82
91
|
* Builds a zero-process catalog reader.
|
|
@@ -89,6 +89,7 @@ export async function createSdkCatalog(cwd) {
|
|
|
89
89
|
const levelsByModel = new Map();
|
|
90
90
|
return {
|
|
91
91
|
models,
|
|
92
|
+
configError: () => modelRuntime.getError?.(),
|
|
92
93
|
...(settingsManager.getDefaultProvider?.() && settingsManager.getDefaultModel?.()
|
|
93
94
|
? {
|
|
94
95
|
defaultModel: {
|
|
@@ -64,6 +64,16 @@ export type ProviderDefinition = {
|
|
|
64
64
|
apiKey?: string;
|
|
65
65
|
hasAuthKey?: boolean;
|
|
66
66
|
models: ProviderModelDefinition[];
|
|
67
|
+
/**
|
|
68
|
+
* The fields in this provider's `models.json` entry that Pi's own schema refuses - today, an
|
|
69
|
+
* empty string where Pi asks for at least one character.
|
|
70
|
+
*
|
|
71
|
+
* WHY THE PANEL CARES. Pi validates the WHOLE file, so one bad entry takes every custom
|
|
72
|
+
* provider out of every picker, while this panel - which reads the file leniently - still lists
|
|
73
|
+
* them all. Naming the field is what turns "my provider is gone" into "your file has an empty
|
|
74
|
+
* baseUrl".
|
|
75
|
+
*/
|
|
76
|
+
invalidFields?: string[];
|
|
67
77
|
/**
|
|
68
78
|
* The key is written into `models.json` itself rather than into `auth.json`.
|
|
69
79
|
*
|
|
@@ -227,6 +237,23 @@ export declare class ProviderRegistry {
|
|
|
227
237
|
removeCredential(providerId: string): Promise<void>;
|
|
228
238
|
/** Forgets a disabled provider without restoring anything: the permanent form of "off". */
|
|
229
239
|
forgetDisabled(providerId: string): Promise<void>;
|
|
240
|
+
/**
|
|
241
|
+
* Removes the empty fields Pi's schema refuses, so the file is accepted again.
|
|
242
|
+
*
|
|
243
|
+
* WHY THIS IS A REPAIR AND NOT A DELETE. An empty `baseUrl` is not a provider with no address -
|
|
244
|
+
* it is a value Pi refuses, and Pi refuses the WHOLE FILE when it sees one. The fix is to drop
|
|
245
|
+
* the empty field (leaving the provider exactly as it was otherwise), NOT to remove the provider:
|
|
246
|
+
* the person wrote it, and an override-only entry - one that only adds models to a built-in - has
|
|
247
|
+
* no address at all, which is legal as long as the field is ABSENT rather than empty.
|
|
248
|
+
*
|
|
249
|
+
* NOTHING ELSE IN THE FILE IS TOUCHED. The entries are read as raw JSON and written back with
|
|
250
|
+
* only the offending fields removed, so a comment-free copy of a hand-written file stays a
|
|
251
|
+
* hand-written file.
|
|
252
|
+
*/
|
|
253
|
+
repairModelsConfig(): Promise<{
|
|
254
|
+
fixed: string[];
|
|
255
|
+
changed: boolean;
|
|
256
|
+
}>;
|
|
230
257
|
/** Writes the registry. The key lives here, so the file is 0600. */
|
|
231
258
|
save(providers: ProviderDefinition[]): Promise<void>;
|
|
232
259
|
/**
|
package/dist/server/providers.js
CHANGED
|
@@ -167,6 +167,21 @@ async function writeJsonAtomic(path, value) {
|
|
|
167
167
|
throw error;
|
|
168
168
|
}
|
|
169
169
|
}
|
|
170
|
+
/**
|
|
171
|
+
* The fields of a `models.json` provider entry that Pi's schema refuses.
|
|
172
|
+
*
|
|
173
|
+
* PI'S RULE, NOT OURS: `baseUrl`, `name` and `api` are optional, but when they are present they
|
|
174
|
+
* need at least one character. An empty string is not "absent" - it is a value Pi rejects, and Pi
|
|
175
|
+
* rejects the WHOLE FILE when it finds one, which is why one bad entry can empty every picker.
|
|
176
|
+
*/
|
|
177
|
+
function invalidModelFields(entry) {
|
|
178
|
+
const fields = [];
|
|
179
|
+
for (const field of ["name", "baseUrl", "api"]) {
|
|
180
|
+
if (entry[field] === "")
|
|
181
|
+
fields.push(field);
|
|
182
|
+
}
|
|
183
|
+
return fields;
|
|
184
|
+
}
|
|
170
185
|
/**
|
|
171
186
|
* The provider entry Super Pi writes into Pi's `models.json`, MERGED into what was already there.
|
|
172
187
|
*
|
|
@@ -200,7 +215,10 @@ function toPiProvider(provider, existing) {
|
|
|
200
215
|
return {
|
|
201
216
|
...priorModel,
|
|
202
217
|
id: model.id,
|
|
203
|
-
|
|
218
|
+
// NEVER AN EMPTY STRING. Pi's schema asks for at least one character, and one empty name
|
|
219
|
+
// invalidates the WHOLE models.json - which takes every custom provider out of every picker
|
|
220
|
+
// with nothing but "must not have fewer than 1 characters" to explain it.
|
|
221
|
+
name: model.name || model.id,
|
|
204
222
|
reasoning: model.reasoning ?? false,
|
|
205
223
|
input: model.imageInput ? ["text", "image"] : ["text"],
|
|
206
224
|
contextWindow: model.contextWindow ?? DEFAULT_CONTEXT_WINDOW,
|
|
@@ -325,6 +343,12 @@ export class ProviderRegistry {
|
|
|
325
343
|
// credential - and the panel then says "Pi will not offer it", which is wrong exactly when
|
|
326
344
|
// the environment variable is set and Pi is offering it right now.
|
|
327
345
|
...(typeof rec.apiKey === "string" && rec.apiKey ? { keyInModelsJson: true } : {}),
|
|
346
|
+
// THE FIELDS PI WILL REFUSE. Pi validates the WHOLE models.json against a schema where an
|
|
347
|
+
// empty string is not a value ("must not have fewer than 1 characters"), and one bad entry
|
|
348
|
+
// invalidates the file - which takes EVERY custom provider out of every picker while this
|
|
349
|
+
// panel, which reads the file leniently, still lists them all. Naming the offending fields
|
|
350
|
+
// is what turns "my provider vanished" into "your file has an empty baseUrl".
|
|
351
|
+
...(invalidModelFields(rec).length ? { invalidFields: invalidModelFields(rec) } : {}),
|
|
328
352
|
models,
|
|
329
353
|
});
|
|
330
354
|
}
|
|
@@ -485,6 +509,63 @@ export class ProviderRegistry {
|
|
|
485
509
|
throw new Error(`供应商「${providerId}」没有被停用`);
|
|
486
510
|
await this.writeRegistry({ disabled: next });
|
|
487
511
|
}
|
|
512
|
+
/**
|
|
513
|
+
* Removes the empty fields Pi's schema refuses, so the file is accepted again.
|
|
514
|
+
*
|
|
515
|
+
* WHY THIS IS A REPAIR AND NOT A DELETE. An empty `baseUrl` is not a provider with no address -
|
|
516
|
+
* it is a value Pi refuses, and Pi refuses the WHOLE FILE when it sees one. The fix is to drop
|
|
517
|
+
* the empty field (leaving the provider exactly as it was otherwise), NOT to remove the provider:
|
|
518
|
+
* the person wrote it, and an override-only entry - one that only adds models to a built-in - has
|
|
519
|
+
* no address at all, which is legal as long as the field is ABSENT rather than empty.
|
|
520
|
+
*
|
|
521
|
+
* NOTHING ELSE IN THE FILE IS TOUCHED. The entries are read as raw JSON and written back with
|
|
522
|
+
* only the offending fields removed, so a comment-free copy of a hand-written file stays a
|
|
523
|
+
* hand-written file.
|
|
524
|
+
*/
|
|
525
|
+
async repairModelsConfig() {
|
|
526
|
+
const modelsPath = piModelsPath(this.piDir);
|
|
527
|
+
const raw = (await readJsonFile(modelsPath));
|
|
528
|
+
if (!raw || !raw.providers || typeof raw.providers !== "object")
|
|
529
|
+
return { fixed: [], changed: false };
|
|
530
|
+
const fixed = [];
|
|
531
|
+
let changed = false;
|
|
532
|
+
const providers = { ...raw.providers };
|
|
533
|
+
for (const [providerId, value] of Object.entries(providers)) {
|
|
534
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
535
|
+
continue;
|
|
536
|
+
const entry = { ...value };
|
|
537
|
+
let touched = false;
|
|
538
|
+
for (const field of invalidModelFields(entry)) {
|
|
539
|
+
delete entry[field];
|
|
540
|
+
fixed.push(`${providerId}.${field}`);
|
|
541
|
+
touched = true;
|
|
542
|
+
}
|
|
543
|
+
if (Array.isArray(entry.models)) {
|
|
544
|
+
let modelsTouched = false;
|
|
545
|
+
const models = entry.models.map((model) => {
|
|
546
|
+
if (!model || typeof model !== "object" || Array.isArray(model))
|
|
547
|
+
return model;
|
|
548
|
+
const record = { ...model };
|
|
549
|
+
if (record.name === "") {
|
|
550
|
+
delete record.name;
|
|
551
|
+
fixed.push(`${providerId}.models.name`);
|
|
552
|
+
modelsTouched = true;
|
|
553
|
+
}
|
|
554
|
+
return record;
|
|
555
|
+
});
|
|
556
|
+
if (modelsTouched)
|
|
557
|
+
entry.models = models;
|
|
558
|
+
touched = touched || modelsTouched;
|
|
559
|
+
}
|
|
560
|
+
if (touched) {
|
|
561
|
+
providers[providerId] = entry;
|
|
562
|
+
changed = true;
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
if (changed)
|
|
566
|
+
await writeJsonAtomic(modelsPath, { ...raw, providers });
|
|
567
|
+
return { fixed, changed };
|
|
568
|
+
}
|
|
488
569
|
/** Writes the registry. The key lives here, so the file is 0600. */
|
|
489
570
|
async save(providers) {
|
|
490
571
|
await this.writeRegistry({ providers });
|
|
@@ -541,6 +622,7 @@ export function publicProviders(providers) {
|
|
|
541
622
|
api: provider.api,
|
|
542
623
|
hasKey: Boolean(provider.apiKey || provider.hasAuthKey),
|
|
543
624
|
...(provider.keyInModelsJson ? { keyInModelsJson: true } : {}),
|
|
625
|
+
...(provider.invalidFields?.length ? { invalidFields: [...provider.invalidFields] } : {}),
|
|
544
626
|
...(provider.builtin ? { builtin: true } : {}),
|
|
545
627
|
...(provider.disabled ? { disabled: true } : {}),
|
|
546
628
|
models: provider.models.map(model => ({
|
package/dist/server/runtime.js
CHANGED
|
@@ -2445,6 +2445,11 @@ export class PiRpcRuntime {
|
|
|
2445
2445
|
source: "pi",
|
|
2446
2446
|
models,
|
|
2447
2447
|
levels: levelNames,
|
|
2448
|
+
// PI'S OWN VERDICT ON ITS OWN CONFIGURATION. It belongs on the catalog because the catalog is
|
|
2449
|
+
// what the picker shows, and a picker that is missing a provider without saying why is the
|
|
2450
|
+
// hardest kind of missing: Pi rejects a whole models.json over one bad field, so EVERY
|
|
2451
|
+
// custom provider disappears at once and the only explanation is somewhere else entirely.
|
|
2452
|
+
...(reader.configError?.() ? { configError: reader.configError() } : {}),
|
|
2448
2453
|
defaultModel: pick(reader.defaultModel ? describeModel({ provider: reader.defaultModel.provider, id: reader.defaultModel.modelId, name: reader.defaultModel.modelId }) : undefined),
|
|
2449
2454
|
defaultThinkingLevel: reader.defaultThinkingLevel ?? levelNames[0],
|
|
2450
2455
|
fetchedAt: new Date().toISOString(),
|
package/dist/server/types.d.ts
CHANGED
|
@@ -410,6 +410,12 @@ export type PublicProvider = {
|
|
|
410
410
|
* in `auth.json`. It is a credential all the same, so the panel must not report it as missing.
|
|
411
411
|
*/
|
|
412
412
|
keyInModelsJson?: boolean;
|
|
413
|
+
/**
|
|
414
|
+
* The fields of this provider's `models.json` entry that Pi's schema refuses (an empty string
|
|
415
|
+
* where Pi asks for at least one character). Pi rejects the WHOLE file over one of them, so every
|
|
416
|
+
* custom provider leaves the picker at once.
|
|
417
|
+
*/
|
|
418
|
+
invalidFields?: string[];
|
|
413
419
|
/**
|
|
414
420
|
* TRUE when the provider is one of Pi's own, with no entry in `models.json`.
|
|
415
421
|
*
|
|
@@ -441,6 +447,13 @@ export type ModelCatalog = {
|
|
|
441
447
|
defaultThinkingLevel?: string;
|
|
442
448
|
fetchedAt: string;
|
|
443
449
|
error?: string;
|
|
450
|
+
/**
|
|
451
|
+
* What Pi says is wrong with its own model configuration, in Pi's own words.
|
|
452
|
+
*
|
|
453
|
+
* Pi validates the whole `models.json`, so one entry it refuses empties every picker of custom
|
|
454
|
+
* providers. This is the only place that says which field is at fault.
|
|
455
|
+
*/
|
|
456
|
+
configError?: string;
|
|
444
457
|
};
|
|
445
458
|
export type Conversation = {
|
|
446
459
|
id: string;
|
|
@@ -1000,7 +1013,16 @@ export type ClientCommandBody =
|
|
|
1000
1013
|
modelId: string;
|
|
1001
1014
|
}
|
|
1002
1015
|
/** Asks for the providers Super Pi manages. The answer carries no key. */
|
|
1016
|
+
/**
|
|
1017
|
+
* Repairs Pi's model configuration: the empty fields Pi's schema refuses are dropped from
|
|
1018
|
+
* `models.json`, and nothing else in the file is touched.
|
|
1019
|
+
*
|
|
1020
|
+
* Pi validates the whole file, so one refused entry takes every custom provider out of every
|
|
1021
|
+
* picker. This is the way back.
|
|
1022
|
+
*/
|
|
1003
1023
|
| {
|
|
1024
|
+
type: "providers.repair";
|
|
1025
|
+
} | {
|
|
1004
1026
|
type: "providers.fetch";
|
|
1005
1027
|
}
|
|
1006
1028
|
/**
|