@diegopetrucci/pi-oracle 0.1.14 → 0.1.16
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/.pi-fleet-tested-version +1 -1
- package/index.ts +126 -33
- package/package.json +1 -1
package/.pi-fleet-tested-version
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
0.
|
|
1
|
+
0.79.10
|
package/index.ts
CHANGED
|
@@ -612,6 +612,19 @@ function isTransientErrorMessage(message: string): boolean {
|
|
|
612
612
|
return TRANSIENT_ERROR_PATTERN.test(message);
|
|
613
613
|
}
|
|
614
614
|
|
|
615
|
+
// A model the catalog advertises but the active provider/subscription cannot
|
|
616
|
+
// serve surfaces as a not-found/404-style error (legacy snapshots or
|
|
617
|
+
// access-gated tiers). These are NOT transient: the same model keeps failing,
|
|
618
|
+
// so callers should fall back to a different model instead of retrying it.
|
|
619
|
+
const MODEL_AVAILABILITY_ERROR_PATTERN =
|
|
620
|
+
/\b(?:404|403|not[_ ]?found(?:[_ ]?error)?|model[_ ]?not[_ ]?found(?:[_ ]?error)?|no such model|unknown model|does not exist|is not available|not available|model[_ ]?not[_ ]?available|unsupported model|invalid model|forbidden|access[ _-]?denied|permission[ _-]?denied|not[ _-]?entitled|do(?:es)? not have access)\b/i;
|
|
621
|
+
|
|
622
|
+
function isModelAvailabilityError(message: string | undefined): boolean {
|
|
623
|
+
if (!message) return false;
|
|
624
|
+
if (isTransientErrorMessage(message)) return false;
|
|
625
|
+
return MODEL_AVAILABILITY_ERROR_PATTERN.test(message);
|
|
626
|
+
}
|
|
627
|
+
|
|
615
628
|
function formatOracleModelError(stopReason: "error" | "aborted", errorMessage: string | undefined): string {
|
|
616
629
|
const trimmed = errorMessage?.trim();
|
|
617
630
|
if (stopReason === "aborted") {
|
|
@@ -776,10 +789,48 @@ async function findAvailableModel(
|
|
|
776
789
|
return undefined;
|
|
777
790
|
}
|
|
778
791
|
|
|
792
|
+
function toOracleSelection(
|
|
793
|
+
model: PiModel,
|
|
794
|
+
thinkingLevelOverride: ThinkingLevel | undefined,
|
|
795
|
+
reason: string,
|
|
796
|
+
): OracleSelection {
|
|
797
|
+
const thinking = resolveThinkingLevel(model, thinkingLevelOverride);
|
|
798
|
+
return {
|
|
799
|
+
modelRef: `${model.provider}/${model.id}`,
|
|
800
|
+
provider: model.provider,
|
|
801
|
+
modelId: model.id,
|
|
802
|
+
modelName: model.name,
|
|
803
|
+
thinkingLevel: thinking.effective,
|
|
804
|
+
...(thinking.clamped ? { requestedThinkingLevel: thinking.requested, thinkingLevelClamped: true } : {}),
|
|
805
|
+
autoSelected: true,
|
|
806
|
+
selectionReason: appendThinkingLevelClampReason(reason, thinking),
|
|
807
|
+
};
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
function buildSessionFallbackSelection(
|
|
811
|
+
ctx: { model?: PiModel },
|
|
812
|
+
thinkingLevelOverride: ThinkingLevel | undefined,
|
|
813
|
+
): OracleSelection | undefined {
|
|
814
|
+
const model = ctx.model;
|
|
815
|
+
if (!model) return undefined;
|
|
816
|
+
return toOracleSelection(
|
|
817
|
+
model,
|
|
818
|
+
thinkingLevelOverride,
|
|
819
|
+
`Used the current session model ${model.provider}/${model.id} as a final fallback.`,
|
|
820
|
+
);
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
function withFallbackReason(selection: OracleSelection, previous: OracleSelection): OracleSelection {
|
|
824
|
+
return {
|
|
825
|
+
...selection,
|
|
826
|
+
selectionReason: `${selection.selectionReason} (Fell back from ${previous.modelRef} after a model-availability error.)`,
|
|
827
|
+
};
|
|
828
|
+
}
|
|
829
|
+
|
|
779
830
|
async function selectOracleModel(
|
|
780
831
|
ctx: { model?: PiModel; modelRegistry: { getAvailable(): PiModel[] | Promise<PiModel[]> } },
|
|
781
832
|
thinkingLevelOverride?: ThinkingLevel,
|
|
782
|
-
): Promise<{ ok: true; selection: OracleSelection } | { ok: false; error: string }> {
|
|
833
|
+
): Promise<{ ok: true; selection: OracleSelection; ordered: OracleSelection[] } | { ok: false; error: string }> {
|
|
783
834
|
const available = await ctx.modelRegistry.getAvailable();
|
|
784
835
|
if (available.length === 0) {
|
|
785
836
|
return {
|
|
@@ -810,27 +861,23 @@ async function selectOracleModel(
|
|
|
810
861
|
reason = "No reasoning models were available, so the top-ranked model across all available providers was used.";
|
|
811
862
|
}
|
|
812
863
|
|
|
864
|
+
const sorted = [...candidates].sort((a, b) => rankModel(b) - rankModel(a));
|
|
813
865
|
const preferred = selectPreferredModel(candidates, providerForPreferences);
|
|
814
|
-
const
|
|
815
|
-
|
|
816
|
-
const
|
|
817
|
-
|
|
818
|
-
|
|
866
|
+
const orderedModels = preferred ? [preferred, ...sorted.filter((model) => model !== preferred)] : sorted;
|
|
867
|
+
|
|
868
|
+
const ordered = orderedModels.map((model, index) =>
|
|
869
|
+
toOracleSelection(
|
|
870
|
+
model,
|
|
871
|
+
thinkingLevelOverride,
|
|
872
|
+
index === 0
|
|
873
|
+
? preferred
|
|
874
|
+
? `Selected ${model.id} via the hardcoded preference list for ${model.provider}.`
|
|
875
|
+
: reason
|
|
876
|
+
: `Auto-selected ${model.provider}/${model.id} as a lower-ranked fallback.`,
|
|
877
|
+
),
|
|
819
878
|
);
|
|
820
879
|
|
|
821
|
-
return {
|
|
822
|
-
ok: true,
|
|
823
|
-
selection: {
|
|
824
|
-
modelRef: `${winner.provider}/${winner.id}`,
|
|
825
|
-
provider: winner.provider,
|
|
826
|
-
modelId: winner.id,
|
|
827
|
-
modelName: winner.name,
|
|
828
|
-
thinkingLevel: thinking.effective,
|
|
829
|
-
...(thinking.clamped ? { requestedThinkingLevel: thinking.requested, thinkingLevelClamped: true } : {}),
|
|
830
|
-
autoSelected: true,
|
|
831
|
-
selectionReason,
|
|
832
|
-
},
|
|
833
|
-
};
|
|
880
|
+
return { ok: true, selection: ordered[0], ordered };
|
|
834
881
|
}
|
|
835
882
|
|
|
836
883
|
function updateOracleUi(ctx: ExtensionContext, activeRuns: Map<string, OracleUiRun>): void {
|
|
@@ -1237,7 +1284,16 @@ export default function oracleExtension(pi: ExtensionAPI) {
|
|
|
1237
1284
|
updateOracleUi(ctx, activeRuns);
|
|
1238
1285
|
|
|
1239
1286
|
try {
|
|
1240
|
-
|
|
1287
|
+
const attempts: OracleSelection[] = [];
|
|
1288
|
+
const seen = new Set<string>();
|
|
1289
|
+
const pushAttempt = (candidate: OracleSelection | undefined) => {
|
|
1290
|
+
if (!candidate) return;
|
|
1291
|
+
const key = `${candidate.provider}/${candidate.modelId}`.toLowerCase();
|
|
1292
|
+
if (seen.has(key)) return;
|
|
1293
|
+
seen.add(key);
|
|
1294
|
+
attempts.push(candidate);
|
|
1295
|
+
};
|
|
1296
|
+
|
|
1241
1297
|
if (configuredModel) {
|
|
1242
1298
|
const modelRef = configuredModel;
|
|
1243
1299
|
const matched = await findAvailableModel(ctx, modelRef);
|
|
@@ -1256,7 +1312,7 @@ export default function oracleExtension(pi: ExtensionAPI) {
|
|
|
1256
1312
|
: usedToolOverride
|
|
1257
1313
|
? "Used the explicit model override provided in the tool call. The model was not matched against the authenticated model list, so the reasoning level fallback was applied."
|
|
1258
1314
|
: "Used the configured default oracle model. The model was not matched against the authenticated model list, so the reasoning level fallback was applied.";
|
|
1259
|
-
|
|
1315
|
+
pushAttempt({
|
|
1260
1316
|
modelRef: matched ? `${matched.provider}/${matched.id}` : modelRef,
|
|
1261
1317
|
provider,
|
|
1262
1318
|
modelId,
|
|
@@ -1267,7 +1323,7 @@ export default function oracleExtension(pi: ExtensionAPI) {
|
|
|
1267
1323
|
: {}),
|
|
1268
1324
|
autoSelected: false,
|
|
1269
1325
|
selectionReason,
|
|
1270
|
-
};
|
|
1326
|
+
});
|
|
1271
1327
|
} else {
|
|
1272
1328
|
const selectionResult = await selectOracleModel(ctx, configuredThinkingLevel);
|
|
1273
1329
|
if (!selectionResult.ok) {
|
|
@@ -1290,11 +1346,14 @@ export default function oracleExtension(pi: ExtensionAPI) {
|
|
|
1290
1346
|
},
|
|
1291
1347
|
};
|
|
1292
1348
|
}
|
|
1293
|
-
|
|
1349
|
+
for (const candidate of selectionResult.ordered) pushAttempt(candidate);
|
|
1294
1350
|
}
|
|
1295
1351
|
|
|
1296
|
-
|
|
1297
|
-
|
|
1352
|
+
// Keep the known-good session model as a final fallback: the catalog can
|
|
1353
|
+
// advertise models the active provider/subscription cannot actually serve
|
|
1354
|
+
// (legacy snapshots, access-gated tiers), which fail with a not-found/404
|
|
1355
|
+
// error. Falling back lets the run degrade gracefully instead of hard-failing.
|
|
1356
|
+
pushAttempt(buildSessionFallbackSelection(ctx, configuredThinkingLevel));
|
|
1298
1357
|
|
|
1299
1358
|
const handleUpdate = (partial: { content: Array<{ type: "text"; text: string }>; details: OracleDetails }) => {
|
|
1300
1359
|
uiRun.preview = partial.content[0]?.text ?? uiRun.preview;
|
|
@@ -1302,17 +1361,51 @@ export default function oracleExtension(pi: ExtensionAPI) {
|
|
|
1302
1361
|
onUpdate?.(partial);
|
|
1303
1362
|
};
|
|
1304
1363
|
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1364
|
+
let lastErrorResult: { ok: false; error: string; details: OracleDetails } | undefined;
|
|
1365
|
+
for (let index = 0; index < attempts.length; index++) {
|
|
1366
|
+
const previous = index > 0 ? attempts[index - 1] : undefined;
|
|
1367
|
+
const attempt = previous ? withFallbackReason(attempts[index], previous) : attempts[index];
|
|
1368
|
+
uiRun.selection = attempt;
|
|
1369
|
+
updateOracleUi(ctx, activeRuns);
|
|
1370
|
+
|
|
1371
|
+
const result = await runOracle(attempt, params, signal, handleUpdate, ctx.cwd);
|
|
1372
|
+
if (result.ok) {
|
|
1373
|
+
return {
|
|
1374
|
+
content: [{ type: "text", text: result.output }],
|
|
1375
|
+
details: result.details,
|
|
1376
|
+
};
|
|
1377
|
+
}
|
|
1378
|
+
|
|
1379
|
+
lastErrorResult = result;
|
|
1380
|
+
const canFallBack = index < attempts.length - 1 && isModelAvailabilityError(result.error);
|
|
1381
|
+
if (!canFallBack) {
|
|
1382
|
+
return {
|
|
1383
|
+
content: [{ type: "text", text: result.error }],
|
|
1384
|
+
details: result.details,
|
|
1385
|
+
};
|
|
1386
|
+
}
|
|
1311
1387
|
}
|
|
1312
1388
|
|
|
1313
1389
|
return {
|
|
1314
|
-
content: [
|
|
1315
|
-
|
|
1390
|
+
content: [
|
|
1391
|
+
{ type: "text", text: lastErrorResult?.error ?? "Oracle could not select an available model." },
|
|
1392
|
+
],
|
|
1393
|
+
details:
|
|
1394
|
+
lastErrorResult?.details ?? {
|
|
1395
|
+
modelRef: "",
|
|
1396
|
+
provider: ctx.model?.provider ?? "unknown",
|
|
1397
|
+
modelId: "",
|
|
1398
|
+
modelName: undefined,
|
|
1399
|
+
thinkingLevel: configuredThinkingLevel ?? DEFAULT_THINKING_LEVEL,
|
|
1400
|
+
autoSelected: true,
|
|
1401
|
+
selectionReason: "No authenticated models were available to run the oracle.",
|
|
1402
|
+
includeBash: params.includeBash ?? false,
|
|
1403
|
+
usage: createEmptyUsage(),
|
|
1404
|
+
stderr: "",
|
|
1405
|
+
exitCode: 1,
|
|
1406
|
+
durationMs: 0,
|
|
1407
|
+
cwd: params.cwd ?? ctx.cwd,
|
|
1408
|
+
},
|
|
1316
1409
|
};
|
|
1317
1410
|
} finally {
|
|
1318
1411
|
activeRuns.delete(toolCallId);
|
package/package.json
CHANGED