@edgehero/pi-dispatch 3.0.0 → 3.1.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/package.json +2 -2
- package/src/config.mjs +1 -1
- package/src/doctor.mjs +32 -5
- package/src/env-allowlist.mjs +23 -5
- package/src/host-pi.mjs +1 -1
- package/src/model-catalog.mjs +1 -1
- package/src/model-endpoints.mjs +22 -0
- package/src/models-json.mjs +10 -4
- package/src/output-cap.mjs +3 -3
- package/src/reserved-env.mjs +3 -2
- package/src/triggers.mjs +6 -4
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@edgehero/pi-dispatch",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.1.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "The pi-dispatch worker and CLI: runs the pi coding agent as a self-hosted service, one locked down Docker or Podman container per job, with spend caps checked before anything is spent, plus init, up, doctor and service install.",
|
|
6
6
|
"keywords": [
|
|
@@ -105,7 +105,7 @@
|
|
|
105
105
|
"start": "node src/cli.mjs worker"
|
|
106
106
|
},
|
|
107
107
|
"dependencies": {
|
|
108
|
-
"@earendil-works/pi-ai": "0.
|
|
108
|
+
"@earendil-works/pi-ai": "1.0.3",
|
|
109
109
|
"@octokit/auth-app": "8.2.0",
|
|
110
110
|
"@octokit/rest": "22.0.1",
|
|
111
111
|
"bullmq": "5.80.4",
|
package/src/config.mjs
CHANGED
|
@@ -195,7 +195,7 @@ function forwardEnvList(raw, egressArmed = false) {
|
|
|
195
195
|
if (owned.length > 0) {
|
|
196
196
|
throw configError(`PI_FORWARD_ENV must not forward ${owned.join(", ")} -- the worker writes ${owned.length === 1 ? "it" : "them"} into every job's container itself, and a forwarded host value would replace the per-job one (a forwarded PI_MODEL runs a model the pre-spend checks never saw)`);
|
|
197
197
|
}
|
|
198
|
-
// Issue #500: the
|
|
198
|
+
// Issue #500: the names the runner sets in its own environment for its descendants (RUNNER_ENV_NAMES). A forwarded
|
|
199
199
|
// host value would sit in the runner's environment before the runner sets its own.
|
|
200
200
|
const runnerOwned = names.filter((n) => RUNNER_ENV_NAMES.has(n));
|
|
201
201
|
if (runnerOwned.length > 0) {
|
package/src/doctor.mjs
CHANGED
|
@@ -66,7 +66,7 @@ import { isTransientOverlayRead, overlayProviderProblem } from "./model-catalog.
|
|
|
66
66
|
import { EMPTY_USD_FINGERPRINT, usdFingerprint } from "./dollar-fingerprint.mjs";
|
|
67
67
|
import { splitModelEntry } from "./model-ref.mjs";
|
|
68
68
|
import { ignoredOutputCapModels, outputCapView, outputUnboundable } from "./output-cap.mjs";
|
|
69
|
-
import { KEYLESS_API_KEY, KEYLESS_HOW, MODEL_ENDPOINTS_FILE_NAME, MODEL_ENDPOINTS_INCLUDE_NAME, MODEL_ENDPOINT_ID_RE, OVERLAY_LINK_FIX, OVERLAY_NOT_A_FILE_FIX, baseUrlTarget, keylessVerdict, loadModelEndpoints, readOverlayModels, renderEndpointsInclude, unreportedUsageModels } from "./model-endpoints.mjs";
|
|
69
|
+
import { KEYLESS_API_KEY, KEYLESS_HOW, MODEL_ENDPOINTS_FILE_NAME, MODEL_ENDPOINTS_INCLUDE_NAME, MODEL_ENDPOINT_ID_RE, OVERLAY_LINK_FIX, OVERLAY_NOT_A_FILE_FIX, RENAMED_PROVIDERS, baseUrlTarget, keylessVerdict, loadModelEndpoints, providerRenameHint, readOverlayModels, renderEndpointsInclude, unreportedUsageModels } from "./model-endpoints.mjs";
|
|
70
70
|
import { declaredEndpointsIn, endpointsDeclaredIn, reloadCommand, rulesFileIncludes, rulesPredateEndpointsLine } from "./egress-cli.mjs";
|
|
71
71
|
import { loadPauseWindows, parseScopeString } from "./pause-windows.mjs";
|
|
72
72
|
import { WAIT_AFTER_MAX_DEFAULT_MS, afterInstantMs, parseWaitProfiles } from "./wait-for.mjs";
|
|
@@ -2554,7 +2554,17 @@ export async function collectChecks(shellVars, seams) {
|
|
|
2554
2554
|
if (typeof error?.code === "string") keylessUnreadable = error.code;
|
|
2555
2555
|
}
|
|
2556
2556
|
}
|
|
2557
|
-
|
|
2557
|
+
// Issue #587's review: the rename hint is judged on the overlay itself, read whenever there is one, endpoint or not.
|
|
2558
|
+
let hintModels = keylessModels;
|
|
2559
|
+
let hintUnread = false;
|
|
2560
|
+
if (unknownToPi && hintModels === null && Object.hasOwn(RENAMED_PROVIDERS, provider) && typeof env.PI_GLOBAL_PI_DIR === "string" && isAbsolute(env.PI_GLOBAL_PI_DIR)) {
|
|
2561
|
+
try {
|
|
2562
|
+
hintModels = readOverlayModels(env.PI_GLOBAL_PI_DIR, keylessIo);
|
|
2563
|
+
} catch {
|
|
2564
|
+
hintUnread = true;
|
|
2565
|
+
}
|
|
2566
|
+
}
|
|
2567
|
+
const keyCheck = providerKeyCheck({ provider, env: { ...env, ...keyFromFile }, agentDir: keyAgentDir, oracle, nodeOk: checks[0]?.ok, keyless: { endpoints: keylessEndpoints, models: keylessModels, unreadable: keylessUnreadable, hintModels, hintUnread } });
|
|
2558
2568
|
const keyNamed = Object.keys(keyFromFile).filter((name) => keyCheck.label?.includes(`: ${name})`));
|
|
2559
2569
|
// Issue #481 (PR #485 review round 2): a key found nowhere doctor can look is one an env-setup script may export, which
|
|
2560
2570
|
// is the documented home for a provider key fetched from a secrets manager (docs/secrets.md).
|
|
@@ -2692,7 +2702,20 @@ export async function collectChecks(shellVars, seams) {
|
|
|
2692
2702
|
// `overlayProviderProblem`). pi drops that whole entry, so the worker refuses every job on the provider.
|
|
2693
2703
|
const providers = overlayModels?.providers;
|
|
2694
2704
|
for (const name of providers !== null && typeof providers === "object" && !Array.isArray(providers) ? Object.keys(providers) : []) {
|
|
2695
|
-
|
|
2705
|
+
// Issue #587: an entry under an id pi renamed that lacks what a provider of its own needs (an api, a baseUrl
|
|
2706
|
+
// and models). It was an override of the builtin provider; under the new pi it is a custom provider with no
|
|
2707
|
+
// models, so it overrides nothing, and the builtin models it meant run with their own (empty) baseUrl.
|
|
2708
|
+
const entry = providers[name];
|
|
2709
|
+
if (Object.hasOwn(RENAMED_PROVIDERS, name) && entry !== null && typeof entry === "object" && !Array.isArray(entry) && (entry.api === undefined || entry.baseUrl === undefined || !Array.isArray(entry.models))) {
|
|
2710
|
+
const renamedTo = RENAMED_PROVIDERS[name];
|
|
2711
|
+
checks.push({
|
|
2712
|
+
ok: false,
|
|
2713
|
+
warn: true,
|
|
2714
|
+
label: `Overlay models.json entry ${JSON.stringify(name)} no longer overrides anything: pi 1.0.3 renamed that provider to ${JSON.stringify(renamedTo)}, so this entry is now a provider of its own, holding only what it declares itself, and the ${renamedTo} models do not get its settings`,
|
|
2715
|
+
fix: `rename the entry to ${JSON.stringify(renamedTo)} in ${modelsPath} (and every ${name}/ model reference in the triggers to ${renamedTo}/), then re-run doctor`,
|
|
2716
|
+
});
|
|
2717
|
+
}
|
|
2718
|
+
const problem = overlayProviderProblem(entry, name);
|
|
2696
2719
|
if (problem === null) continue;
|
|
2697
2720
|
checks.push({
|
|
2698
2721
|
ok: false,
|
|
@@ -4241,10 +4264,14 @@ function noKeyVariableCheck(provider, oracle, keyless = null) {
|
|
|
4241
4264
|
const owner = oracle.piProviders().find((id) => oracle.providerKeyCandidates(id).includes(wanted));
|
|
4242
4265
|
// Why a custom provider the overlay defines is not keyless, when it is one: the one fact the operator has to change.
|
|
4243
4266
|
const why = verdict.why ? `; it is not keyless because ${verdict.why}` : "";
|
|
4267
|
+
// Issue #587: an id pi renamed (azure-openai-responses is `azure` since pi 1.0.3), named, unless the overlay declares it.
|
|
4268
|
+
const renamed = providerRenameHint(provider, { models: keyless?.hintModels ?? keyless?.models ?? null, overlayUnread: keyless?.hintUnread === true, piProviders: oracle.piProviders() });
|
|
4244
4269
|
return {
|
|
4245
4270
|
ok: false,
|
|
4246
|
-
label: `PI_PROVIDER is ${JSON.stringify(provider)}, which is not a provider pi has${why}`,
|
|
4247
|
-
fix:
|
|
4271
|
+
label: `PI_PROVIDER is ${JSON.stringify(provider)}, which is not a provider pi has${why}${renamed ? `;${renamed}` : ""}`,
|
|
4272
|
+
fix: renamed
|
|
4273
|
+
? `set PI_PROVIDER=${RENAMED_PROVIDERS[provider]}, and rename the provider in every trigger's model and allowed-models entries and in models.json the same way (docs/triggers.md)`
|
|
4274
|
+
: owner
|
|
4248
4275
|
? `set PI_PROVIDER=${owner}, the provider pi reads ${wanted} from -- or unset it for the default \`anthropic\`; or, ${KEYLESS_HOW}`
|
|
4249
4276
|
: `set PI_PROVIDER to a provider id pi has, or unset it for the default \`anthropic\`: ${oracle.piProviders().join(", ")}; or, ${KEYLESS_HOW}`,
|
|
4250
4277
|
};
|
package/src/env-allowlist.mjs
CHANGED
|
@@ -33,7 +33,7 @@ import { egressEnv } from "./egress.mjs";
|
|
|
33
33
|
import { forgeSpec } from "./forges.mjs";
|
|
34
34
|
import { apiKeyVariable } from "./provider-key.mjs";
|
|
35
35
|
import { isDeterminateFsCode } from "./transient.mjs";
|
|
36
|
-
import { KEYLESS_HOW, keylessVerdict } from "./model-endpoints.mjs";
|
|
36
|
+
import { KEYLESS_HOW, keylessVerdict, providerRenameHint, readOverlayModels, RENAMED_PROVIDERS } from "./model-endpoints.mjs";
|
|
37
37
|
import { KEYLESS_ENV_NAME } from "./reserved-env.mjs";
|
|
38
38
|
|
|
39
39
|
function configError(message) {
|
|
@@ -161,7 +161,7 @@ export function resolveProviderCredential({ provider, hostEnv, authFromPi = fals
|
|
|
161
161
|
error.code = unreadable.code;
|
|
162
162
|
throw error;
|
|
163
163
|
}
|
|
164
|
-
throw configError(unknownProviderMessage(provider));
|
|
164
|
+
throw configError(unknownProviderMessage(provider, hintOverlay(provider, modelEndpoints, hostEnv)));
|
|
165
165
|
}
|
|
166
166
|
const held = candidates
|
|
167
167
|
.map((name) => [name, hostEnv[name]])
|
|
@@ -209,8 +209,24 @@ export function keylessEndpointsFor(provider, modelEndpoints) {
|
|
|
209
209
|
return verdict.keyless ? verdict.endpoints : null;
|
|
210
210
|
}
|
|
211
211
|
|
|
212
|
-
function unknownProviderMessage(provider) {
|
|
213
|
-
return `pi has no provider "${provider}", so there is no key variable to give it a key
|
|
212
|
+
function unknownProviderMessage(provider, overlay = {}) {
|
|
213
|
+
return `pi has no provider "${provider}", so there is no key variable to give it a key.${providerRenameHint(provider, { ...overlay, piProviders: piProviders() })} Use one of pi's provider ids with its key in the worker environment or in pi's auth.json, or, ${KEYLESS_HOW}.`;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* The overlay models.json the rename hint is judged on (issue #587's review): the pickup's snapshot when it read one, else
|
|
218
|
+
* the overlay itself, read here, only for an id pi renamed. The snapshot reads the overlay only when an endpoint is
|
|
219
|
+
* declared, so a full custom provider under the old id, with no endpoint, was told "did you mean". A read that fails
|
|
220
|
+
* is `overlayUnread`: no guess.
|
|
221
|
+
*/
|
|
222
|
+
function hintOverlay(provider, modelEndpoints, hostEnv) {
|
|
223
|
+
if (modelEndpoints?.models) return { models: modelEndpoints.models };
|
|
224
|
+
if (!Object.hasOwn(RENAMED_PROVIDERS, provider)) return {};
|
|
225
|
+
try {
|
|
226
|
+
return { models: readOverlayModels(hostEnv?.PI_GLOBAL_PI_DIR) };
|
|
227
|
+
} catch {
|
|
228
|
+
return { overlayUnread: true };
|
|
229
|
+
}
|
|
214
230
|
}
|
|
215
231
|
|
|
216
232
|
function defaultAgentDir(hostEnv) {
|
|
@@ -524,13 +540,15 @@ export function buildContainerEnv({ provider, model, maxTurns, maxTokens, maxCos
|
|
|
524
540
|
if (exitAuth === true) env.PI_EXIT_AUTH = "stdin";
|
|
525
541
|
else delete env.PI_EXIT_AUTH;
|
|
526
542
|
|
|
527
|
-
// Issue #500: the runner sets these
|
|
543
|
+
// Issue #500: the runner sets these in its own environment, for its child processes, and no value from outside
|
|
528
544
|
// may arrive first. Both lists refuse them upstream (config.mjs for PI_FORWARD_ENV, the triggers loader for
|
|
529
545
|
// run.secrets, through RUNNER_ENV_NAMES); this is the line that holds if either refusal is ever bypassed.
|
|
530
546
|
// env-internal PI_DISPATCH_CHILD_LEDGER: set by the runner inside the container, never by the worker, so removed here.
|
|
531
547
|
delete env.PI_DISPATCH_CHILD_LEDGER;
|
|
532
548
|
// env-internal PI_DISPATCH_RUNNER_PID: set by the runner inside the container, never by the worker, so removed here.
|
|
533
549
|
delete env.PI_DISPATCH_RUNNER_PID;
|
|
550
|
+
// env-internal PI_DISPATCH_PRICE_TABLE: set by the runner inside the container, never by the worker, so removed here.
|
|
551
|
+
delete env.PI_DISPATCH_PRICE_TABLE;
|
|
534
552
|
|
|
535
553
|
// Forge-backed jobs, and local cron jobs that opted in via run.github. Other local-folder jobs have
|
|
536
554
|
// no token (CONST-TOKEN-SCOPED-PER-JOB). The mint goes into BOTH of its forge's variables because
|
package/src/host-pi.mjs
CHANGED
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
* What it must never become. Nothing on the worker's BOOT path may import this file. It reads host paths and
|
|
16
16
|
* may spawn a package manager, and neither belongs anywhere near `start.mjs`.
|
|
17
17
|
*
|
|
18
|
-
* Everything here MIRRORS a private detail of the pinned pi (0.
|
|
18
|
+
* Everything here MIRRORS a private detail of the pinned pi (1.0.3) rather than calling it: pi exports no
|
|
19
19
|
* public answer to "where is this package installed" or "is this resource enabled", and importing the whole
|
|
20
20
|
* coding-agent SDK to read two well-known paths is not worth the weight. That mirroring is a real risk --
|
|
21
21
|
* pi could change the grammar and we would silently start staging something the operator turned off -- so it
|
package/src/model-catalog.mjs
CHANGED
|
@@ -91,7 +91,7 @@ function overlayDeclares(overlay, provider, id) {
|
|
|
91
91
|
|
|
92
92
|
/**
|
|
93
93
|
* Would pi COMPOSE this overlay provider (PR #536's review)? A file that passes pi's schema can still lose a provider
|
|
94
|
-
* at the next step: pi
|
|
94
|
+
* at the next step: the pinned pi's `applyModelsJson` and `modelFromJson` (`pi-coding-agent/dist/core/provider-composer.js`)
|
|
95
95
|
* throw for `oauth` with no `baseUrl`, and per model no resolvable `api` or
|
|
96
96
|
* `baseUrl`, or a `contextWindow` or `maxTokens` at or below zero. pi then keeps the provider's BUILTIN models
|
|
97
97
|
* (`ModelRuntime.composeProvider` falls back to the base) and drops every model the overlay added, so such a model
|
package/src/model-endpoints.mjs
CHANGED
|
@@ -518,6 +518,28 @@ export function unreportedUsageModels(models, { builtinModel = () => null, built
|
|
|
518
518
|
/** The exact `apiKey` a keyless provider's models.json entry carries: pi resolves `$NAME` from the job's environment. */
|
|
519
519
|
export const KEYLESS_API_KEY = `$${KEYLESS_ENV_NAME}`;
|
|
520
520
|
|
|
521
|
+
/**
|
|
522
|
+
* Provider ids pi renamed, old to new (issue #587: pi 1.0.3 renamed `azure-openai-responses` to `azure`, and its catalog
|
|
523
|
+
* file with it; the api id `azure-openai-responses` is unchanged). Pinned against the catalog by
|
|
524
|
+
* worker/test/env-allowlist.test.mjs: every old id is gone from pi, every new id is there. A rename is not a refusal of
|
|
525
|
+
* its own: the old id is already refused before spend as a provider pi does not have, and this only lets that refusal
|
|
526
|
+
* and doctor say which id was meant.
|
|
527
|
+
*/
|
|
528
|
+
export const RENAMED_PROVIDERS = Object.freeze({ "azure-openai-responses": "azure" });
|
|
529
|
+
|
|
530
|
+
/**
|
|
531
|
+
* The "did you mean" for a provider pi renamed, or "" (issue #587). Only for an old id pi no longer has, whose new id pi
|
|
532
|
+
* does have, and that the overlay's models.json (`models`, parsed, or null when there is none) does not declare: a
|
|
533
|
+
* provider the file declares under the old id is the operator's own custom provider, and the hint would be wrong there.
|
|
534
|
+
* `overlayUnread`: the overlay exists but could not be read, so whether it declares the id is not known: no guess.
|
|
535
|
+
*/
|
|
536
|
+
export function providerRenameHint(provider, { models = null, piProviders = [], overlayUnread = false } = {}) {
|
|
537
|
+
if (typeof provider !== "string" || !Object.hasOwn(RENAMED_PROVIDERS, provider)) return "";
|
|
538
|
+
const renamed = RENAMED_PROVIDERS[provider];
|
|
539
|
+
if (overlayUnread || piProviders.includes(provider) || !piProviders.includes(renamed) || providerOf(models, provider) !== null) return "";
|
|
540
|
+
return ` pi renamed the provider "${provider}" to "${renamed}" in pi 1.0.3: did you mean "${renamed}"?`;
|
|
541
|
+
}
|
|
542
|
+
|
|
521
543
|
/**
|
|
522
544
|
* The second way into the credential gate, said once for every refusal and doctor line that names it (issue #503).
|
|
523
545
|
*/
|
package/src/models-json.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The overlay `models.json`, read the way pi reads it (issue #502, PR #536's review): pi
|
|
2
|
+
* The overlay `models.json`, read the way pi reads it (issue #502, PR #536's review): pi's
|
|
3
3
|
* `ModelConfig.load` (`pi-coding-agent/dist/core/model-config.js`) strips a leading BOM, strips `//` comments and
|
|
4
4
|
* trailing commas outside strings, parses, and then checks the whole document against its TypeBox schema. ANY
|
|
5
5
|
* schema error drops the WHOLE file: pi then knows none of its providers or models.
|
|
@@ -121,7 +121,8 @@ const T = {
|
|
|
121
121
|
const opt = (check) => [check, true];
|
|
122
122
|
const req = (check) => [check, false];
|
|
123
123
|
|
|
124
|
-
// The schema, transcribed from model-config.js at the 0.
|
|
124
|
+
// The schema, transcribed from model-config.js at the pin (1.0.3; worker/test/models-json.test.mjs holds the file's
|
|
125
|
+
// content hash), in its order.
|
|
125
126
|
const PercentileCutoffs = T.obj({ p50: opt(T.num()), p75: opt(T.num()), p90: opt(T.num()), p99: opt(T.num()) });
|
|
126
127
|
const numOrStr = T.union(T.num(), T.str());
|
|
127
128
|
const OpenRouterRouting = T.obj({
|
|
@@ -142,6 +143,9 @@ const OpenRouterRouting = T.obj({
|
|
|
142
143
|
const VercelGatewayRouting = T.obj({ only: opt(T.arr(T.str())), order: opt(T.arr(T.str())) });
|
|
143
144
|
const ThinkingValue = T.union(T.str(), T.nul());
|
|
144
145
|
const ThinkingLevelMap = T.obj({ off: opt(ThinkingValue), minimal: opt(ThinkingValue), low: opt(ThinkingValue), medium: opt(ThinkingValue), high: opt(ThinkingValue), xhigh: opt(ThinkingValue), max: opt(ThinkingValue) });
|
|
146
|
+
// pi 1.0.2 (issue #587): a model's sampling parameters, and the same per thinking level, merged per call over them.
|
|
147
|
+
const SamplingParams = T.rec(T.unknown());
|
|
148
|
+
const SamplingParamsByThinkingLevel = T.obj({ off: opt(SamplingParams), minimal: opt(SamplingParams), low: opt(SamplingParams), medium: opt(SamplingParams), high: opt(SamplingParams), xhigh: opt(SamplingParams), max: opt(SamplingParams) });
|
|
145
149
|
const KwargScalar = T.union(T.str(), T.num(), T.bool(), T.nul());
|
|
146
150
|
const KwargVariable = T.obj({ $var: req(T.union(T.lit("thinking.enabled"), T.lit("thinking.effort"))), omitWhenOff: opt(T.bool()) });
|
|
147
151
|
const Kwarg = T.union(KwargScalar, KwargVariable);
|
|
@@ -214,7 +218,8 @@ const ModelDefinition = T.obj({
|
|
|
214
218
|
promptCache: opt(ModelPromptCache),
|
|
215
219
|
contextWindow: opt(T.num()),
|
|
216
220
|
maxTokens: opt(T.num()),
|
|
217
|
-
samplingParams: opt(
|
|
221
|
+
samplingParams: opt(SamplingParams),
|
|
222
|
+
samplingParamsByThinkingLevel: opt(SamplingParamsByThinkingLevel),
|
|
218
223
|
headers: opt(headers),
|
|
219
224
|
compat: opt(ProviderCompat),
|
|
220
225
|
});
|
|
@@ -228,7 +233,8 @@ const ModelOverride = T.obj({
|
|
|
228
233
|
promptCache: opt(ModelPromptCache),
|
|
229
234
|
contextWindow: opt(T.num()),
|
|
230
235
|
maxTokens: opt(T.num()),
|
|
231
|
-
samplingParams: opt(
|
|
236
|
+
samplingParams: opt(SamplingParams),
|
|
237
|
+
samplingParamsByThinkingLevel: opt(SamplingParamsByThinkingLevel),
|
|
232
238
|
headers: opt(headers),
|
|
233
239
|
compat: opt(ProviderCompat),
|
|
234
240
|
});
|
package/src/output-cap.mjs
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
|
|
14
14
|
import { composedCost, endpointsForModel, isZeroCost, modelEntryOf } from "./model-endpoints.mjs";
|
|
15
15
|
|
|
16
|
-
/** The hosts pi's catalog serves openai-completions on: the runner's `COMPLETIONS_CATALOG_HOSTS`,
|
|
16
|
+
/** The hosts pi's catalog serves openai-completions on: the runner's `COMPLETIONS_CATALOG_HOSTS`, generated the same way. */
|
|
17
17
|
export const COMPLETIONS_CATALOG_HOSTS = Object.freeze([
|
|
18
18
|
"api.ant-ling.com",
|
|
19
19
|
"api.cerebras.ai",
|
|
@@ -56,7 +56,7 @@ export function completionsOwnServer(model) {
|
|
|
56
56
|
}
|
|
57
57
|
|
|
58
58
|
/**
|
|
59
|
-
* The parts of a model the rule reads, composed as pi
|
|
59
|
+
* The parts of a model the rule reads, composed as the pinned pi's provider-composer.js composes them from the overlay
|
|
60
60
|
* `models.json` (`models`, parsed, or null) and the builtin catalog (`builtinModel(provider, id)`, injected):
|
|
61
61
|
* - a model the overlay DEFINES: its own `api` and `baseUrl`, else its provider's, else those of pi's DEFAULTS
|
|
62
62
|
* model (`findModelDefaults`, mirrored in `definedDefaults`); its compat field from its `modelOverrides` entry,
|
|
@@ -94,7 +94,7 @@ export function outputCapView({ models, provider, modelId, builtinModel = () =>
|
|
|
94
94
|
const str = (v) => (typeof v === "string" ? v : undefined);
|
|
95
95
|
|
|
96
96
|
/**
|
|
97
|
-
* The `{ api, baseUrl }` pi
|
|
97
|
+
* The `{ api, baseUrl }` the pinned pi composes for the overlay-defined model `modelId` (provider-composer.js
|
|
98
98
|
* `applyModelsJson`): the provider's chat models start as the catalog's (each on the provider's `baseUrl` when it sets
|
|
99
99
|
* one), and each definition in the file's order is composed and then replaces the model of its id or joins the list.
|
|
100
100
|
* A definition's `api` is its own, else the provider's, else its DEFAULTS model's, and its `baseUrl` likewise, where the
|
package/src/reserved-env.mjs
CHANGED
|
@@ -64,11 +64,12 @@ export const CONTAINER_ENV_NAMES = new Set([
|
|
|
64
64
|
|
|
65
65
|
/**
|
|
66
66
|
* Issue #500: the names the RUNNER sets in its own environment inside the container, for its descendants: the child
|
|
67
|
-
* ledger directory and the runner's pid (image/runner/src/usage-meter.mjs, openChildLedger)
|
|
67
|
+
* ledger directory and the runner's pid (image/runner/src/usage-meter.mjs, openChildLedger), and the hash of the price
|
|
68
|
+
* table the runner hands its children (publishPriceTable, issue #587's review). The worker never writes
|
|
68
69
|
* them, so they are not in the set above (which a test pins to what buildContainerEnv emits). They are reserved all the
|
|
69
70
|
* same: a trigger binding one through `run.secrets`, or a host value forwarded through `PI_FORWARD_ENV`, would arrive in
|
|
70
71
|
* the runner's environment before the runner sets its own, and a value the runner then failed to replace would point
|
|
71
72
|
* every pi child's ledger at a directory the agent chose. Refused at load in both lists, and deleted by
|
|
72
73
|
* buildContainerEnv after both loops as the backstop.
|
|
73
74
|
*/
|
|
74
|
-
export const RUNNER_ENV_NAMES = new Set(["PI_DISPATCH_CHILD_LEDGER", "PI_DISPATCH_RUNNER_PID"]);
|
|
75
|
+
export const RUNNER_ENV_NAMES = new Set(["PI_DISPATCH_CHILD_LEDGER", "PI_DISPATCH_PRICE_TABLE", "PI_DISPATCH_RUNNER_PID"]);
|
package/src/triggers.mjs
CHANGED
|
@@ -173,12 +173,14 @@ export const REVIEW_STATES = new Set(["approved", "changes_requested", "commente
|
|
|
173
173
|
const REVIEW_ACTION = "review_submitted";
|
|
174
174
|
|
|
175
175
|
/**
|
|
176
|
-
* The pi tool names a trigger may exclude (issue #291) -- the built-in set of the PINNED pi
|
|
177
|
-
* own `allToolNames` order. `powershell` joined it at the 0.99.1 bump (issue #509): pi registers it as a
|
|
176
|
+
* The pi tool names a trigger may exclude (issue #291) -- the built-in set of the PINNED pi (the version
|
|
177
|
+
* image/runner/package.json pins), in pi's own `allToolNames` order. `powershell` joined it at the 0.99.1 bump (issue #509): pi registers it as a
|
|
178
178
|
* built-in but does not activate it by default (`DEFAULT_TOOL_NAMES` is read, bash, edit, write), so a
|
|
179
179
|
* trigger that means "no shell" names it beside `bash`.
|
|
180
|
-
*
|
|
181
|
-
* package)
|
|
180
|
+
* A literal because this validator is pure and pi-free (the worker does not depend on the agent
|
|
181
|
+
* package). The line is GENERATED from the pinned pi by .github/scripts/pi-derived.mjs (issue #587; a pi bump
|
|
182
|
+
* reruns it), held to that generation by worker/test/pi-derived.test.mjs, and BOLTED twice to the artifact it
|
|
183
|
+
* restates: `worker/test/exclude-tools.pinned.test.mjs`
|
|
182
184
|
* and `image/runner/test/pinned-api.test.mjs` both derive the set from the pinned package and fail with a
|
|
183
185
|
* grow-these-together message when a pin bump moves it. Extension and custom tool names are deliberately
|
|
184
186
|
* NOT here: they register at container start (a staged package, a serviced repo's own `.pi/extensions`),
|