@aexol/spectral 0.2.22 → 0.3.0
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/server/pi-bridge.js
CHANGED
|
@@ -48,8 +48,8 @@
|
|
|
48
48
|
* a single pi session also work normally (the same AgentSession
|
|
49
49
|
* instance is reused across `prompt()` calls).
|
|
50
50
|
*/
|
|
51
|
-
import { AuthStorage, createAgentSession, DefaultResourceLoader, ModelRegistry, SessionManager, } from "@mariozechner/pi-coding-agent";
|
|
52
51
|
import { createJiti } from "@mariozechner/jiti";
|
|
52
|
+
import { AuthStorage, createAgentSession, DefaultResourceLoader, ModelRegistry, SessionManager, } from "@mariozechner/pi-coding-agent";
|
|
53
53
|
import { randomUUID } from "node:crypto";
|
|
54
54
|
import { existsSync } from "node:fs";
|
|
55
55
|
import { dirname, resolve } from "node:path";
|
|
@@ -249,6 +249,12 @@ export class PiBridge {
|
|
|
249
249
|
* `session.setModel()`. Phase 3 (Available Models whitelist).
|
|
250
250
|
*/
|
|
251
251
|
modelRegistry;
|
|
252
|
+
/**
|
|
253
|
+
* Raw allowed models list from the backend, preserved in sortOrder.
|
|
254
|
+
* Used by `getFirstAvailableModelId()` to return the backend-curated
|
|
255
|
+
* top pick when no explicit model selection is made.
|
|
256
|
+
*/
|
|
257
|
+
allowedModels;
|
|
252
258
|
/**
|
|
253
259
|
* Last `modelId` we successfully applied via `session.setModel()`, or
|
|
254
260
|
* `undefined` if we never applied one (pi falls back to its own settings
|
|
@@ -426,6 +432,7 @@ export class PiBridge {
|
|
|
426
432
|
throw new Error(`Failed to fetch allowed models from backend; check SPECTRAL_BACKEND_URL ` +
|
|
427
433
|
`and machine JWT. Underlying error: ${e.message}`);
|
|
428
434
|
}
|
|
435
|
+
this.allowedModels = allowedModels;
|
|
429
436
|
this.registerSyntheticProviders(allowedModels);
|
|
430
437
|
console.info(`✓ Inference routed via backend proxy (${allowedModels.length} model(s) available)`);
|
|
431
438
|
const result = await createAgentSession({
|
|
@@ -579,6 +586,18 @@ export class PiBridge {
|
|
|
579
586
|
*
|
|
580
587
|
* Phase 3 (Available Models whitelist).
|
|
581
588
|
*/
|
|
589
|
+
/**
|
|
590
|
+
* Return the modelId of the first available model from the backend
|
|
591
|
+
* whitelist (preserving the backend's sortOrder, which is the same
|
|
592
|
+
* ordering the frontend uses in its model picker). Returns `undefined`
|
|
593
|
+
* when no models are available (e.g. backend unreachable at startup).
|
|
594
|
+
*
|
|
595
|
+
* Used by `SessionStreamManager.prompt()` as a defense-in-depth
|
|
596
|
+
* default when neither the envelope nor SQLite supply a modelId.
|
|
597
|
+
*/
|
|
598
|
+
getFirstAvailableModelId() {
|
|
599
|
+
return this.allowedModels?.[0]?.modelId;
|
|
600
|
+
}
|
|
582
601
|
async setModel(modelId) {
|
|
583
602
|
if (!modelId)
|
|
584
603
|
return true; // nothing to apply — pi keeps its current model
|
|
@@ -232,7 +232,9 @@ export class SessionStreamManager {
|
|
|
232
232
|
// b) Else, look up the per-session persisted modelId in SQLite
|
|
233
233
|
// (cross-restart recovery — server restart wipes pi's in-memory
|
|
234
234
|
// session model state, but our durable store has the last value).
|
|
235
|
-
// c) Else,
|
|
235
|
+
// c) Else, ask the bridge for the first available model from the
|
|
236
|
+
// backend whitelist (same sortOrder the frontend uses for its
|
|
237
|
+
// default picker display).
|
|
236
238
|
//
|
|
237
239
|
// We apply BEFORE persisting the user message: if the bridge can't
|
|
238
240
|
// resolve the model (unknown id, registry unavailable), it has already
|
|
@@ -242,7 +244,9 @@ export class SessionStreamManager {
|
|
|
242
244
|
// Persistence: only the envelope-supplied value is written back. A
|
|
243
245
|
// recovery-only application (case b) doesn't update the row — the
|
|
244
246
|
// value is already there.
|
|
245
|
-
const effectiveModelId = modelId ??
|
|
247
|
+
const effectiveModelId = modelId ??
|
|
248
|
+
this.store.getSessionModel(sessionId) ??
|
|
249
|
+
stream.bridge.getFirstAvailableModelId?.();
|
|
246
250
|
if (effectiveModelId && stream.bridge.setModel) {
|
|
247
251
|
const ok = await stream.bridge.setModel(effectiveModelId);
|
|
248
252
|
if (!ok) {
|
|
@@ -252,9 +256,9 @@ export class SessionStreamManager {
|
|
|
252
256
|
return;
|
|
253
257
|
}
|
|
254
258
|
}
|
|
255
|
-
if (
|
|
259
|
+
if (effectiveModelId) {
|
|
256
260
|
try {
|
|
257
|
-
this.store.setSessionModel(sessionId,
|
|
261
|
+
this.store.setSessionModel(sessionId, effectiveModelId);
|
|
258
262
|
}
|
|
259
263
|
catch (err) {
|
|
260
264
|
// Persisting the sticky model is best-effort: the live turn will
|