modelrig 0.0.1 → 0.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/LICENSE +201 -0
- package/README.md +115 -8
- package/dist/adapters/anthropic.d.ts +46 -0
- package/dist/adapters/anthropic.d.ts.map +1 -0
- package/dist/adapters/anthropic.js +202 -0
- package/dist/adapters/deepseek.d.ts +41 -0
- package/dist/adapters/deepseek.d.ts.map +1 -0
- package/dist/adapters/deepseek.js +163 -0
- package/dist/adapters/gemini.d.ts +44 -0
- package/dist/adapters/gemini.d.ts.map +1 -0
- package/dist/adapters/gemini.js +222 -0
- package/dist/adapters/grok.d.ts +44 -0
- package/dist/adapters/grok.d.ts.map +1 -0
- package/dist/adapters/grok.js +168 -0
- package/dist/adapters/openai-compat.d.ts +74 -0
- package/dist/adapters/openai-compat.d.ts.map +1 -0
- package/dist/adapters/openai-compat.js +187 -0
- package/dist/adapters/openai.d.ts +49 -0
- package/dist/adapters/openai.d.ts.map +1 -0
- package/dist/adapters/openai.js +222 -0
- package/dist/bakeoff/engine.d.ts +52 -0
- package/dist/bakeoff/engine.d.ts.map +1 -0
- package/dist/bakeoff/engine.js +163 -0
- package/dist/bakeoff/replay.d.ts +52 -0
- package/dist/bakeoff/replay.d.ts.map +1 -0
- package/dist/bakeoff/replay.js +94 -0
- package/dist/bakeoff/samples.d.ts +62 -0
- package/dist/bakeoff/samples.d.ts.map +1 -0
- package/dist/bakeoff/samples.js +128 -0
- package/dist/bakeoff/stats.d.ts +13 -0
- package/dist/bakeoff/stats.d.ts.map +1 -0
- package/dist/bakeoff/stats.js +48 -0
- package/dist/bakeoff/types.d.ts +37 -0
- package/dist/bakeoff/types.d.ts.map +1 -0
- package/dist/bakeoff/types.js +10 -0
- package/dist/cli.d.ts +27 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +572 -0
- package/dist/config.d.ts +104 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +148 -0
- package/dist/feedback/coverage.d.ts +32 -0
- package/dist/feedback/coverage.d.ts.map +1 -0
- package/dist/feedback/coverage.js +95 -0
- package/dist/feedback/feedback.d.ts +35 -0
- package/dist/feedback/feedback.d.ts.map +1 -0
- package/dist/feedback/feedback.js +39 -0
- package/dist/index.d.ts +155 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +448 -0
- package/dist/init/finder.d.ts +79 -0
- package/dist/init/finder.d.ts.map +1 -0
- package/dist/init/finder.js +297 -0
- package/dist/init/run.d.ts +99 -0
- package/dist/init/run.d.ts.map +1 -0
- package/dist/init/run.js +281 -0
- package/dist/init/signatures.d.ts +39 -0
- package/dist/init/signatures.d.ts.map +1 -0
- package/dist/init/signatures.js +116 -0
- package/dist/matrix/aggregate.d.ts +89 -0
- package/dist/matrix/aggregate.d.ts.map +1 -0
- package/dist/matrix/aggregate.js +186 -0
- package/dist/query/grammar.d.ts +35 -0
- package/dist/query/grammar.d.ts.map +1 -0
- package/dist/query/grammar.js +279 -0
- package/dist/query/run.d.ts +8 -0
- package/dist/query/run.d.ts.map +1 -0
- package/dist/query/run.js +21 -0
- package/dist/registry/build.d.ts +126 -0
- package/dist/registry/build.d.ts.map +1 -0
- package/dist/registry/build.js +192 -0
- package/dist/registry/call-notes.d.ts +11 -0
- package/dist/registry/call-notes.d.ts.map +1 -0
- package/dist/registry/call-notes.js +72 -0
- package/dist/registry/claims.d.ts +63 -0
- package/dist/registry/claims.d.ts.map +1 -0
- package/dist/registry/claims.js +107 -0
- package/dist/registry/facts.d.ts +32 -0
- package/dist/registry/facts.d.ts.map +1 -0
- package/dist/registry/facts.js +120 -0
- package/dist/registry/fingerprint.d.ts +20 -0
- package/dist/registry/fingerprint.d.ts.map +1 -0
- package/dist/registry/fingerprint.js +150 -0
- package/dist/registry/parity.d.ts +26 -0
- package/dist/registry/parity.d.ts.map +1 -0
- package/dist/registry/parity.js +41 -0
- package/dist/registry/pricing.d.ts +19 -0
- package/dist/registry/pricing.d.ts.map +1 -0
- package/dist/registry/pricing.js +69 -0
- package/dist/registry/rows.d.ts +16 -0
- package/dist/registry/rows.d.ts.map +1 -0
- package/dist/registry/rows.js +57 -0
- package/dist/registry/seed.d.ts +43 -0
- package/dist/registry/seed.d.ts.map +1 -0
- package/dist/registry/seed.js +97 -0
- package/dist/registry/select.d.ts +68 -0
- package/dist/registry/select.d.ts.map +1 -0
- package/dist/registry/select.js +141 -0
- package/dist/registry/staleness.d.ts +18 -0
- package/dist/registry/staleness.d.ts.map +1 -0
- package/dist/registry/staleness.js +39 -0
- package/dist/routes/draft.d.ts +83 -0
- package/dist/routes/draft.d.ts.map +1 -0
- package/dist/routes/draft.js +135 -0
- package/dist/routes/loader.d.ts +26 -0
- package/dist/routes/loader.d.ts.map +1 -0
- package/dist/routes/loader.js +436 -0
- package/dist/routes/manifest.d.ts +24 -0
- package/dist/routes/manifest.d.ts.map +1 -0
- package/dist/routes/manifest.js +118 -0
- package/dist/routes/serveability.d.ts +52 -0
- package/dist/routes/serveability.d.ts.map +1 -0
- package/dist/routes/serveability.js +73 -0
- package/dist/routes/template.d.ts +18 -0
- package/dist/routes/template.d.ts.map +1 -0
- package/dist/routes/template.js +94 -0
- package/dist/run/cost.d.ts +26 -0
- package/dist/run/cost.d.ts.map +1 -0
- package/dist/run/cost.js +41 -0
- package/dist/run/envelope.d.ts +21 -0
- package/dist/run/envelope.d.ts.map +1 -0
- package/dist/run/envelope.js +45 -0
- package/dist/run/grounding.d.ts +56 -0
- package/dist/run/grounding.d.ts.map +1 -0
- package/dist/run/grounding.js +89 -0
- package/dist/run/repair.d.ts +36 -0
- package/dist/run/repair.d.ts.map +1 -0
- package/dist/run/repair.js +61 -0
- package/dist/run/resolve.d.ts +26 -0
- package/dist/run/resolve.d.ts.map +1 -0
- package/dist/run/resolve.js +81 -0
- package/dist/run/retry.d.ts +19 -0
- package/dist/run/retry.d.ts.map +1 -0
- package/dist/run/retry.js +50 -0
- package/dist/run/run.d.ts +41 -0
- package/dist/run/run.d.ts.map +1 -0
- package/dist/run/run.js +0 -0
- package/dist/run/validate.d.ts +15 -0
- package/dist/run/validate.d.ts.map +1 -0
- package/dist/run/validate.js +56 -0
- package/dist/savings/emit.d.ts +25 -0
- package/dist/savings/emit.d.ts.map +1 -0
- package/dist/savings/emit.js +75 -0
- package/dist/savings/odometer.d.ts +32 -0
- package/dist/savings/odometer.d.ts.map +1 -0
- package/dist/savings/odometer.js +70 -0
- package/dist/savings/swap-execute.d.ts +44 -0
- package/dist/savings/swap-execute.d.ts.map +1 -0
- package/dist/savings/swap-execute.js +87 -0
- package/dist/savings/swaps-client.d.ts +41 -0
- package/dist/savings/swaps-client.d.ts.map +1 -0
- package/dist/savings/swaps-client.js +81 -0
- package/dist/telemetry/capture.d.ts +28 -0
- package/dist/telemetry/capture.d.ts.map +1 -0
- package/dist/telemetry/capture.js +74 -0
- package/dist/telemetry/exporter.d.ts +62 -0
- package/dist/telemetry/exporter.d.ts.map +1 -0
- package/dist/telemetry/exporter.js +233 -0
- package/dist/telemetry/ingest-protocol.d.ts +161 -0
- package/dist/telemetry/ingest-protocol.d.ts.map +1 -0
- package/dist/telemetry/ingest-protocol.js +163 -0
- package/dist/telemetry/sinks.d.ts +54 -0
- package/dist/telemetry/sinks.d.ts.map +1 -0
- package/dist/telemetry/sinks.js +89 -0
- package/dist/telemetry/store.d.ts +38 -0
- package/dist/telemetry/store.d.ts.map +1 -0
- package/dist/telemetry/store.js +332 -0
- package/dist/telemetry/supabase-exporter.d.ts +64 -0
- package/dist/telemetry/supabase-exporter.d.ts.map +1 -0
- package/dist/telemetry/supabase-exporter.js +211 -0
- package/dist/tenancy/api-key-format.d.ts +67 -0
- package/dist/tenancy/api-key-format.d.ts.map +1 -0
- package/dist/tenancy/api-key-format.js +91 -0
- package/dist/tenancy/constants.d.ts +23 -0
- package/dist/tenancy/constants.d.ts.map +1 -0
- package/dist/tenancy/constants.js +25 -0
- package/dist/tenancy/scrub.d.ts +47 -0
- package/dist/tenancy/scrub.d.ts.map +1 -0
- package/dist/tenancy/scrub.js +85 -0
- package/dist/types/failures-teaching.d.ts +56 -0
- package/dist/types/failures-teaching.d.ts.map +1 -0
- package/dist/types/failures-teaching.js +177 -0
- package/dist/types/failures.d.ts +27 -0
- package/dist/types/failures.d.ts.map +1 -0
- package/dist/types/failures.js +41 -0
- package/dist/types/provider.d.ts +79 -0
- package/dist/types/provider.d.ts.map +1 -0
- package/dist/types/provider.js +20 -0
- package/dist/types/route.d.ts +127 -0
- package/dist/types/route.d.ts.map +1 -0
- package/dist/types/route.js +6 -0
- package/dist/types/telemetry.d.ts +89 -0
- package/dist/types/telemetry.d.ts.map +1 -0
- package/dist/types/telemetry.js +6 -0
- package/dist/watch/budget.d.ts +19 -0
- package/dist/watch/budget.d.ts.map +1 -0
- package/dist/watch/budget.js +35 -0
- package/dist/watch/evaluate.d.ts +66 -0
- package/dist/watch/evaluate.d.ts.map +1 -0
- package/dist/watch/evaluate.js +94 -0
- package/dist/watch/proposals.d.ts +73 -0
- package/dist/watch/proposals.d.ts.map +1 -0
- package/dist/watch/proposals.js +169 -0
- package/dist/watch/run-record.d.ts +64 -0
- package/dist/watch/run-record.d.ts.map +1 -0
- package/dist/watch/run-record.js +86 -0
- package/dist/watch/run-reported.d.ts +20 -0
- package/dist/watch/run-reported.d.ts.map +1 -0
- package/dist/watch/run-reported.js +53 -0
- package/dist/watch/run.d.ts +40 -0
- package/dist/watch/run.d.ts.map +1 -0
- package/dist/watch/run.js +44 -0
- package/dist/watch/state-store.d.ts +48 -0
- package/dist/watch/state-store.d.ts.map +1 -0
- package/dist/watch/state-store.js +86 -0
- package/dist/watch/state.d.ts +21 -0
- package/dist/watch/state.d.ts.map +1 -0
- package/dist/watch/state.js +93 -0
- package/package.json +50 -14
- package/index.js +0 -3
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Load-time route serveability — Phase 4 resilience (architect-flagged
|
|
4
|
+
* change to the Phase-1 fail-fast guard).
|
|
5
|
+
*
|
|
6
|
+
* A declared candidate whose provider has no registered adapter (no API key
|
|
7
|
+
* in THIS environment) is INERT, not fatal: the gateway must not crash at
|
|
8
|
+
* boot because a bake-off challenger arm lacks a key here. This is the
|
|
9
|
+
* "safe but inert" behaviour the route-v3 commit assumed — true on the
|
|
10
|
+
* InferWealth cutover wrapper (it catches + falls back to legacy), but the
|
|
11
|
+
* standalone modelrig-server builds the rig eagerly at boot with no catch,
|
|
12
|
+
* so a keyless arm downed staging.
|
|
13
|
+
*
|
|
14
|
+
* Rule: warn per inert candidate and keep serving on the keyed ones; a route
|
|
15
|
+
* with ZERO serveable candidates is still a hard error (genuinely dead). The
|
|
16
|
+
* candidate-set UPPER bound is untouched — resolveCandidates still only
|
|
17
|
+
* yields declared candidates, and the run loop skips inert ones at dispatch.
|
|
18
|
+
*/
|
|
19
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
20
|
+
exports.inertCandidateFailure = inertCandidateFailure;
|
|
21
|
+
exports.serveabilityWarnings = serveabilityWarnings;
|
|
22
|
+
exports.checkServeability = checkServeability;
|
|
23
|
+
const config_1 = require("../config");
|
|
24
|
+
const loader_1 = require("./loader");
|
|
25
|
+
function envVarFor(provider) {
|
|
26
|
+
return config_1.PROVIDER_KEY_ENV[provider] ?? `${provider.toUpperCase()}_API_KEY`;
|
|
27
|
+
}
|
|
28
|
+
/** Typed failure for an inert (keyless) candidate the run loop skips at
|
|
29
|
+
* dispatch — non-terminal config_auth so the loop advances to the next
|
|
30
|
+
* declared candidate; surfaced as the run's error only if EVERY candidate
|
|
31
|
+
* is inert. */
|
|
32
|
+
function inertCandidateFailure(candidate, route) {
|
|
33
|
+
return {
|
|
34
|
+
class: "config_auth",
|
|
35
|
+
message: `provider "${candidate.provider}" has no adapter (declared by route "${route}") — ` +
|
|
36
|
+
`its API key is not configured; skipping this candidate`,
|
|
37
|
+
provider: candidate.provider,
|
|
38
|
+
model: candidate.model,
|
|
39
|
+
fixHint: `configure the ${candidate.provider} API key to enable this candidate`,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
function serveabilityWarnings(bundles, hasAdapter) {
|
|
43
|
+
const warnings = [];
|
|
44
|
+
for (const bundle of bundles) {
|
|
45
|
+
const serveable = bundle.candidates.filter((c) => hasAdapter(c.provider));
|
|
46
|
+
if (serveable.length === 0) {
|
|
47
|
+
const providers = [...new Set(bundle.candidates.map((c) => c.provider))];
|
|
48
|
+
throw new loader_1.RouteConfigError(`route "${bundle.route}" has no serveable candidate — none of its declared providers ` +
|
|
49
|
+
`[${providers.join(", ")}] has an API key configured ` +
|
|
50
|
+
`(set ${providers.map(envVarFor).join(" or ")})`);
|
|
51
|
+
}
|
|
52
|
+
const inert = bundle.candidates.filter((c) => !hasAdapter(c.provider));
|
|
53
|
+
if (inert.length === 0)
|
|
54
|
+
continue;
|
|
55
|
+
const missingProviders = [...new Set(inert.map((c) => c.provider))];
|
|
56
|
+
warnings.push({
|
|
57
|
+
route: bundle.route,
|
|
58
|
+
inert,
|
|
59
|
+
missingProviders,
|
|
60
|
+
serveableCount: serveable.length,
|
|
61
|
+
message: `route "${bundle.route}": ${inert.length} candidate(s) inert (no API key) — ` +
|
|
62
|
+
`${inert.map((c) => `${c.provider}/${c.model}`).join(", ")}; serving on ` +
|
|
63
|
+
`${serveable.length} keyed candidate(s) until their keys are configured.`,
|
|
64
|
+
fixHint: `set ${missingProviders.map(envVarFor).join(", ")} to enable these candidates`,
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
return warnings;
|
|
68
|
+
}
|
|
69
|
+
/** String façade over {@link serveabilityWarnings} — one traversal, one
|
|
70
|
+
* source of wording. Throws identically on a zero-serveable route. */
|
|
71
|
+
function checkServeability(bundles, hasAdapter) {
|
|
72
|
+
return serveabilityWarnings(bundles, hasAdapter).map((w) => w.message);
|
|
73
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Template renderer — spec §3, plan §4.
|
|
3
|
+
* Supports {{var}} substitution and capability-conditional blocks:
|
|
4
|
+
* {{#if capability.grounded_native}}...{{/if}}
|
|
5
|
+
* {{#unless capability.structured_native}}...{{/unless}}
|
|
6
|
+
* Deliberately hand-rolled and tiny — NO external template engine (jinja-class
|
|
7
|
+
* engines are a documented CVE surface; see litellm-vendoring-strategy.md §3).
|
|
8
|
+
*/
|
|
9
|
+
import type { CapabilityFlag } from "../types/provider";
|
|
10
|
+
export interface RenderContext {
|
|
11
|
+
readonly variables: Record<string, unknown>;
|
|
12
|
+
readonly capabilities: ReadonlySet<CapabilityFlag>;
|
|
13
|
+
}
|
|
14
|
+
export declare function renderTemplate(template: string, ctx: RenderContext): string;
|
|
15
|
+
/** Distinct {{var}} names referenced anywhere in the template (loader
|
|
16
|
+
* validation: every reference must be a declared variable). */
|
|
17
|
+
export declare function scanTemplateVariables(template: string): readonly string[];
|
|
18
|
+
//# sourceMappingURL=template.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"template.d.ts","sourceRoot":"","sources":["../../src/routes/template.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAExD,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC5C,QAAQ,CAAC,YAAY,EAAE,WAAW,CAAC,cAAc,CAAC,CAAC;CACpD;AAuFD,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,aAAa,GAAG,MAAM,CAE3E;AAED;+DAC+D;AAC/D,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,CAIzE"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Template renderer — spec §3, plan §4.
|
|
4
|
+
* Supports {{var}} substitution and capability-conditional blocks:
|
|
5
|
+
* {{#if capability.grounded_native}}...{{/if}}
|
|
6
|
+
* {{#unless capability.structured_native}}...{{/unless}}
|
|
7
|
+
* Deliberately hand-rolled and tiny — NO external template engine (jinja-class
|
|
8
|
+
* engines are a documented CVE surface; see litellm-vendoring-strategy.md §3).
|
|
9
|
+
*/
|
|
10
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
+
exports.renderTemplate = renderTemplate;
|
|
12
|
+
exports.scanTemplateVariables = scanTemplateVariables;
|
|
13
|
+
const provider_1 = require("../types/provider");
|
|
14
|
+
const CAPABILITY_FLAGS = new Set(provider_1.ALL_CAPABILITY_FLAGS);
|
|
15
|
+
const OPEN_TAG = /\{\{#(if|unless)\s+capability\.([A-Za-z_][A-Za-z0-9_]*)\s*\}\}/g;
|
|
16
|
+
const CLOSE_TAG = /\{\{\/(if|unless)\}\}/g;
|
|
17
|
+
const VAR_TAG = /\{\{\s*([A-Za-z_][A-Za-z0-9_]*)\s*\}\}/g;
|
|
18
|
+
function scanTags(template) {
|
|
19
|
+
const tags = [];
|
|
20
|
+
for (const m of template.matchAll(OPEN_TAG)) {
|
|
21
|
+
tags.push({
|
|
22
|
+
kind: "open",
|
|
23
|
+
block: m[1],
|
|
24
|
+
flag: m[2],
|
|
25
|
+
start: m.index,
|
|
26
|
+
end: m.index + m[0].length,
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
for (const m of template.matchAll(CLOSE_TAG)) {
|
|
30
|
+
tags.push({ kind: "close", block: m[1], start: m.index, end: m.index + m[0].length });
|
|
31
|
+
}
|
|
32
|
+
return tags.sort((a, b) => a.start - b.start);
|
|
33
|
+
}
|
|
34
|
+
/** Pass 1: resolve conditional blocks (flat only — nesting is rejected). */
|
|
35
|
+
function resolveConditionals(template, capabilities) {
|
|
36
|
+
const tags = scanTags(template);
|
|
37
|
+
let out = "";
|
|
38
|
+
let cursor = 0;
|
|
39
|
+
let open = null;
|
|
40
|
+
for (const tag of tags) {
|
|
41
|
+
if (tag.kind === "open") {
|
|
42
|
+
if (open) {
|
|
43
|
+
throw new Error(`template error: nested {{#${tag.block}}} block at index ${tag.start} — nesting is not supported in Phase 1`);
|
|
44
|
+
}
|
|
45
|
+
if (tag.flag === undefined || !CAPABILITY_FLAGS.has(tag.flag)) {
|
|
46
|
+
throw new Error(`template error: unknown capability flag "${tag.flag ?? ""}" at index ${tag.start}`);
|
|
47
|
+
}
|
|
48
|
+
out += template.slice(cursor, tag.start);
|
|
49
|
+
open = tag;
|
|
50
|
+
cursor = tag.end;
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
if (!open) {
|
|
54
|
+
throw new Error(`template error: unmatched {{/${tag.block}}} at index ${tag.start}`);
|
|
55
|
+
}
|
|
56
|
+
if (open.block !== tag.block) {
|
|
57
|
+
throw new Error(`template error: {{#${open.block}}} at index ${open.start} closed by {{/${tag.block}}} at index ${tag.start}`);
|
|
58
|
+
}
|
|
59
|
+
const present = capabilities.has(open.flag);
|
|
60
|
+
const keep = open.block === "if" ? present : !present;
|
|
61
|
+
if (keep)
|
|
62
|
+
out += template.slice(open.end, tag.start);
|
|
63
|
+
open = null;
|
|
64
|
+
cursor = tag.end;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
if (open) {
|
|
68
|
+
throw new Error(`template error: unclosed {{#${open.block}}} block at index ${open.start}`);
|
|
69
|
+
}
|
|
70
|
+
return out + template.slice(cursor);
|
|
71
|
+
}
|
|
72
|
+
/** Pass 2: substitute {{var}} from ctx.variables; missing variables throw. */
|
|
73
|
+
function substituteVariables(template, variables) {
|
|
74
|
+
return template.replace(VAR_TAG, (_match, name) => {
|
|
75
|
+
if (!(name in variables)) {
|
|
76
|
+
throw new Error(`template error: missing variable "${name}"`);
|
|
77
|
+
}
|
|
78
|
+
const value = variables[name];
|
|
79
|
+
if (typeof value === "string")
|
|
80
|
+
return value;
|
|
81
|
+
return JSON.stringify(value);
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
function renderTemplate(template, ctx) {
|
|
85
|
+
return substituteVariables(resolveConditionals(template, ctx.capabilities), ctx.variables);
|
|
86
|
+
}
|
|
87
|
+
/** Distinct {{var}} names referenced anywhere in the template (loader
|
|
88
|
+
* validation: every reference must be a declared variable). */
|
|
89
|
+
function scanTemplateVariables(template) {
|
|
90
|
+
const names = new Set();
|
|
91
|
+
for (const m of template.matchAll(VAR_TAG))
|
|
92
|
+
names.add(m[1]);
|
|
93
|
+
return [...names];
|
|
94
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Attempt cost accounting — extracted from run.ts (addendum A1 pushed it
|
|
3
|
+
* past the 700-LOC cap). One rule, applied at every dispatch/settlement
|
|
4
|
+
* site: a model with no pricing entry NEVER costs $0 against an active
|
|
5
|
+
* envelope — the envelope is charged a conservative per-token rate instead,
|
|
6
|
+
* while telemetry keeps the honest $0 with `pricing_missing` flagged.
|
|
7
|
+
*/
|
|
8
|
+
import type { AdapterUsage, ProviderId } from "../types/provider";
|
|
9
|
+
export declare const ESTIMATE_CHARS_PER_TOKEN = 4;
|
|
10
|
+
export declare const ESTIMATE_MAX_OUTPUT_TOKENS = 64000;
|
|
11
|
+
/** Addendum A1 conservative default — above the priciest frontier output
|
|
12
|
+
* rate, so an unpriced model can only OVER-charge an envelope, never ride
|
|
13
|
+
* free. Override via MODELRIG_UNPRICED_RATE_USD_PER_MTOK / RigConfig. */
|
|
14
|
+
export declare const DEFAULT_UNPRICED_RATE_USD_PER_MTOK = 100;
|
|
15
|
+
export interface AttemptCost {
|
|
16
|
+
/** What telemetry records — honest $0 when pricing is missing. */
|
|
17
|
+
readonly costUsd: number;
|
|
18
|
+
/** What an active envelope is charged — conservative when unpriced. */
|
|
19
|
+
readonly envelopeChargeUsd: number;
|
|
20
|
+
readonly pricingMissing: boolean;
|
|
21
|
+
}
|
|
22
|
+
export declare function attemptCost(provider: ProviderId, model: string, usage: AdapterUsage, unpricedRatePerTok: number): AttemptCost;
|
|
23
|
+
/** Pre-dispatch upper-bound estimate for the envelope precheck: prompt
|
|
24
|
+
* length + the full output bound, at real pricing or the conservative rate. */
|
|
25
|
+
export declare function dispatchEstimateUsd(provider: ProviderId, model: string, promptChars: number, unpricedRatePerTok: number): number;
|
|
26
|
+
//# sourceMappingURL=cost.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cost.d.ts","sourceRoot":"","sources":["../../src/run/cost.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAElE,eAAO,MAAM,wBAAwB,IAAI,CAAC;AAC1C,eAAO,MAAM,0BAA0B,QAAS,CAAC;AAEjD;;yEAEyE;AACzE,eAAO,MAAM,kCAAkC,MAAM,CAAC;AAEtD,MAAM,WAAW,WAAW;IAC1B,kEAAkE;IAClE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,uEAAuE;IACvE,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAC;IACnC,QAAQ,CAAC,cAAc,EAAE,OAAO,CAAC;CAClC;AAED,wBAAgB,WAAW,CACzB,QAAQ,EAAE,UAAU,EACpB,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,YAAY,EACnB,kBAAkB,EAAE,MAAM,GACzB,WAAW,CAWb;AAED;+EAC+E;AAC/E,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,UAAU,EACpB,KAAK,EAAE,MAAM,EACb,WAAW,EAAE,MAAM,EACnB,kBAAkB,EAAE,MAAM,GACzB,MAAM,CAOR"}
|
package/dist/run/cost.js
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Attempt cost accounting — extracted from run.ts (addendum A1 pushed it
|
|
4
|
+
* past the 700-LOC cap). One rule, applied at every dispatch/settlement
|
|
5
|
+
* site: a model with no pricing entry NEVER costs $0 against an active
|
|
6
|
+
* envelope — the envelope is charged a conservative per-token rate instead,
|
|
7
|
+
* while telemetry keeps the honest $0 with `pricing_missing` flagged.
|
|
8
|
+
*/
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.DEFAULT_UNPRICED_RATE_USD_PER_MTOK = exports.ESTIMATE_MAX_OUTPUT_TOKENS = exports.ESTIMATE_CHARS_PER_TOKEN = void 0;
|
|
11
|
+
exports.attemptCost = attemptCost;
|
|
12
|
+
exports.dispatchEstimateUsd = dispatchEstimateUsd;
|
|
13
|
+
const pricing_1 = require("../registry/pricing");
|
|
14
|
+
exports.ESTIMATE_CHARS_PER_TOKEN = 4;
|
|
15
|
+
exports.ESTIMATE_MAX_OUTPUT_TOKENS = 64000;
|
|
16
|
+
/** Addendum A1 conservative default — above the priciest frontier output
|
|
17
|
+
* rate, so an unpriced model can only OVER-charge an envelope, never ride
|
|
18
|
+
* free. Override via MODELRIG_UNPRICED_RATE_USD_PER_MTOK / RigConfig. */
|
|
19
|
+
exports.DEFAULT_UNPRICED_RATE_USD_PER_MTOK = 100;
|
|
20
|
+
function attemptCost(provider, model, usage, unpricedRatePerTok) {
|
|
21
|
+
const pricing = (0, pricing_1.getPricing)(provider, model);
|
|
22
|
+
if (pricing === null) {
|
|
23
|
+
return {
|
|
24
|
+
costUsd: 0,
|
|
25
|
+
envelopeChargeUsd: (usage.tokensIn + usage.tokensOut) * unpricedRatePerTok,
|
|
26
|
+
pricingMissing: true,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
const costUsd = (0, pricing_1.estimateCostUsd)(pricing, usage.tokensIn, usage.tokensOut, usage.tokensCached);
|
|
30
|
+
return { costUsd, envelopeChargeUsd: costUsd, pricingMissing: false };
|
|
31
|
+
}
|
|
32
|
+
/** Pre-dispatch upper-bound estimate for the envelope precheck: prompt
|
|
33
|
+
* length + the full output bound, at real pricing or the conservative rate. */
|
|
34
|
+
function dispatchEstimateUsd(provider, model, promptChars, unpricedRatePerTok) {
|
|
35
|
+
const promptTokens = Math.ceil(promptChars / exports.ESTIMATE_CHARS_PER_TOKEN);
|
|
36
|
+
const pricing = (0, pricing_1.getPricing)(provider, model);
|
|
37
|
+
if (pricing === null) {
|
|
38
|
+
return (promptTokens + exports.ESTIMATE_MAX_OUTPUT_TOKENS) * unpricedRatePerTok;
|
|
39
|
+
}
|
|
40
|
+
return (0, pricing_1.estimateCostUsd)(pricing, promptTokens, exports.ESTIMATE_MAX_OUTPUT_TOKENS, 0);
|
|
41
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-run budget envelopes v0 — spec §2. Hard stop only in Phase 1
|
|
3
|
+
* (degrade-before-die routes arrive in Phase 3). Backed by the telemetry
|
|
4
|
+
* store's envelopes table so envelopes survive process restarts within a run.
|
|
5
|
+
*/
|
|
6
|
+
import type { TelemetryStore } from "../types/telemetry";
|
|
7
|
+
import type { RigFailure } from "../types/failures";
|
|
8
|
+
export interface EnvelopeGuard {
|
|
9
|
+
/**
|
|
10
|
+
* Check + reserve before a dispatch. Returns null when spend may proceed,
|
|
11
|
+
* or a budget_exhausted RigFailure when the envelope is breached.
|
|
12
|
+
*/
|
|
13
|
+
precheck(estimatedNextCallUsd: number): RigFailure | null;
|
|
14
|
+
/** Record actual cost after a dispatch completes. */
|
|
15
|
+
settle(actualUsd: number): void;
|
|
16
|
+
}
|
|
17
|
+
/** Default per-envelope budget when the caller doesn't specify one (small on
|
|
18
|
+
* purpose — a hard stop that surprises is safer than silent unbounded spend). */
|
|
19
|
+
export declare const DEFAULT_ENVELOPE_BUDGET_USD = 25;
|
|
20
|
+
export declare function createEnvelopeGuard(store: TelemetryStore, envelopeKey: string, budgetUsd?: number): EnvelopeGuard;
|
|
21
|
+
//# sourceMappingURL=envelope.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"envelope.d.ts","sourceRoot":"","sources":["../../src/run/envelope.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAEpD,MAAM,WAAW,aAAa;IAC5B;;;OAGG;IACH,QAAQ,CAAC,oBAAoB,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI,CAAC;IAC1D,qDAAqD;IACrD,MAAM,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CACjC;AAED;iFACiF;AACjF,eAAO,MAAM,2BAA2B,KAAK,CAAC;AAE9C,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,cAAc,EACrB,WAAW,EAAE,MAAM,EACnB,SAAS,CAAC,EAAE,MAAM,GACjB,aAAa,CAoCf"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Per-run budget envelopes v0 — spec §2. Hard stop only in Phase 1
|
|
4
|
+
* (degrade-before-die routes arrive in Phase 3). Backed by the telemetry
|
|
5
|
+
* store's envelopes table so envelopes survive process restarts within a run.
|
|
6
|
+
*/
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.DEFAULT_ENVELOPE_BUDGET_USD = void 0;
|
|
9
|
+
exports.createEnvelopeGuard = createEnvelopeGuard;
|
|
10
|
+
/** Default per-envelope budget when the caller doesn't specify one (small on
|
|
11
|
+
* purpose — a hard stop that surprises is safer than silent unbounded spend). */
|
|
12
|
+
exports.DEFAULT_ENVELOPE_BUDGET_USD = 25;
|
|
13
|
+
function createEnvelopeGuard(store, envelopeKey, budgetUsd) {
|
|
14
|
+
// Open-or-get: the first opener's budget wins; a pre-existing row (earlier
|
|
15
|
+
// step of the same run, or an operator-seeded envelope) is never resized.
|
|
16
|
+
store.openEnvelope(envelopeKey, budgetUsd ?? exports.DEFAULT_ENVELOPE_BUDGET_USD);
|
|
17
|
+
function exhaustedFailure(row) {
|
|
18
|
+
return {
|
|
19
|
+
class: "budget_exhausted",
|
|
20
|
+
message: `envelope "${envelopeKey}" exhausted: spent $${row.spentUsd.toFixed(4)} ` +
|
|
21
|
+
`of $${row.budgetUsd} budget`,
|
|
22
|
+
fixHint: "raise the envelope budget or investigate runaway retries for this run",
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
return {
|
|
26
|
+
precheck(estimatedNextCallUsd) {
|
|
27
|
+
const row = store.getEnvelope(envelopeKey);
|
|
28
|
+
if (row === null) {
|
|
29
|
+
// openEnvelope above guarantees a row; a vanished row means the store
|
|
30
|
+
// was externally mutated — fail closed rather than spend blind.
|
|
31
|
+
return {
|
|
32
|
+
class: "budget_exhausted",
|
|
33
|
+
message: `envelope "${envelopeKey}" row missing at precheck — failing closed`,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
if (row.status === "exhausted" || row.spentUsd + estimatedNextCallUsd > row.budgetUsd) {
|
|
37
|
+
return exhaustedFailure(row);
|
|
38
|
+
}
|
|
39
|
+
return null;
|
|
40
|
+
},
|
|
41
|
+
settle(actualUsd) {
|
|
42
|
+
store.accrue(envelopeKey, actualUsd);
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Grounding-inject rung — Phase 3 spec WS3, plan §4. For candidates WITHOUT
|
|
3
|
+
* grounded_native on a `require: grounded` route: pre-dispatch search via a
|
|
4
|
+
* SearchProvider → results injected into the rendered system prompt →
|
|
5
|
+
* citations normalized to url_citation annotations in RunMeta. Search costs
|
|
6
|
+
* accrue to the envelope and telemetry.
|
|
7
|
+
*
|
|
8
|
+
* The Tavily-style provider ports the PATTERN from InferWealth's production
|
|
9
|
+
* websearch provider (query-length cap, basic/advanced depth, result shape)
|
|
10
|
+
* — logic only, no cross-package imports; key arrives via config.
|
|
11
|
+
*/
|
|
12
|
+
export interface SearchResultItem {
|
|
13
|
+
readonly title: string;
|
|
14
|
+
readonly url: string;
|
|
15
|
+
readonly snippet: string;
|
|
16
|
+
readonly publishedDate?: string;
|
|
17
|
+
}
|
|
18
|
+
export interface SearchResponse {
|
|
19
|
+
readonly results: readonly SearchResultItem[];
|
|
20
|
+
/** What this search cost — accrued to the envelope + telemetry. */
|
|
21
|
+
readonly costUsd: number;
|
|
22
|
+
}
|
|
23
|
+
export interface SearchProvider {
|
|
24
|
+
readonly id: string;
|
|
25
|
+
search(query: string, opts?: {
|
|
26
|
+
readonly maxResults?: number;
|
|
27
|
+
}): Promise<SearchResponse>;
|
|
28
|
+
}
|
|
29
|
+
/** Ported cap: the Tavily API hard-rejects queries over 400 chars. */
|
|
30
|
+
export declare const SEARCH_QUERY_MAX_CHARS = 400;
|
|
31
|
+
export declare function capSearchQuery(query: string): string;
|
|
32
|
+
export interface TavilySearchConfig {
|
|
33
|
+
readonly apiKey: string;
|
|
34
|
+
/** Flat per-search cost (Tavily bills credits, not tokens) — default one
|
|
35
|
+
* basic-depth credit at the pay-as-you-go rate. */
|
|
36
|
+
readonly costPerSearchUsd?: number;
|
|
37
|
+
readonly searchDepth?: "basic" | "advanced";
|
|
38
|
+
readonly baseUrl?: string;
|
|
39
|
+
/** Injectable for tests; defaults to global fetch. */
|
|
40
|
+
readonly fetchFn?: typeof fetch;
|
|
41
|
+
}
|
|
42
|
+
export declare function createTavilySearchProvider(config: TavilySearchConfig): SearchProvider;
|
|
43
|
+
/** The injected block — appended to the rendered system prompt, before json
|
|
44
|
+
* coaching. Sources are numbered so outputs can cite by URL. */
|
|
45
|
+
export declare function searchInjectionBlock(results: readonly SearchResultItem[], retrievedAt: string): string;
|
|
46
|
+
/** Normalized citation annotation (spec WS3) — RunMeta carries these. */
|
|
47
|
+
export interface UrlCitation {
|
|
48
|
+
readonly type: "url_citation";
|
|
49
|
+
readonly url: string;
|
|
50
|
+
readonly title?: string;
|
|
51
|
+
}
|
|
52
|
+
/** Extract URLs cited in the model's output and normalize to url_citation
|
|
53
|
+
* annotations, enriching with titles from the injected sources. Deduped,
|
|
54
|
+
* trailing punctuation stripped (same normalization as the probe suite). */
|
|
55
|
+
export declare function extractCitations(outputText: string, sources: readonly SearchResultItem[]): UrlCitation[];
|
|
56
|
+
//# sourceMappingURL=grounding.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"grounding.d.ts","sourceRoot":"","sources":["../../src/run/grounding.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;CACjC;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,OAAO,EAAE,SAAS,gBAAgB,EAAE,CAAC;IAC9C,mEAAmE;IACnE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE;QAAE,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,cAAc,CAAC,CAAC;CACzF;AAED,sEAAsE;AACtE,eAAO,MAAM,sBAAsB,MAAM,CAAC;AAE1C,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEpD;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB;uDACmD;IACnD,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IACnC,QAAQ,CAAC,WAAW,CAAC,EAAE,OAAO,GAAG,UAAU,CAAC;IAC5C,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,sDAAsD;IACtD,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,KAAK,CAAC;CACjC;AAaD,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,kBAAkB,GAAG,cAAc,CAoCrF;AAED;gEACgE;AAChE,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,SAAS,gBAAgB,EAAE,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM,CAYtG;AAED,yEAAyE;AACzE,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,IAAI,EAAE,cAAc,CAAC;IAC9B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;CACzB;AAID;;4EAE4E;AAC5E,wBAAgB,gBAAgB,CAC9B,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,SAAS,gBAAgB,EAAE,GACnC,WAAW,EAAE,CAYf"}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Grounding-inject rung — Phase 3 spec WS3, plan §4. For candidates WITHOUT
|
|
4
|
+
* grounded_native on a `require: grounded` route: pre-dispatch search via a
|
|
5
|
+
* SearchProvider → results injected into the rendered system prompt →
|
|
6
|
+
* citations normalized to url_citation annotations in RunMeta. Search costs
|
|
7
|
+
* accrue to the envelope and telemetry.
|
|
8
|
+
*
|
|
9
|
+
* The Tavily-style provider ports the PATTERN from InferWealth's production
|
|
10
|
+
* websearch provider (query-length cap, basic/advanced depth, result shape)
|
|
11
|
+
* — logic only, no cross-package imports; key arrives via config.
|
|
12
|
+
*/
|
|
13
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
|
+
exports.SEARCH_QUERY_MAX_CHARS = void 0;
|
|
15
|
+
exports.capSearchQuery = capSearchQuery;
|
|
16
|
+
exports.createTavilySearchProvider = createTavilySearchProvider;
|
|
17
|
+
exports.searchInjectionBlock = searchInjectionBlock;
|
|
18
|
+
exports.extractCitations = extractCitations;
|
|
19
|
+
/** Ported cap: the Tavily API hard-rejects queries over 400 chars. */
|
|
20
|
+
exports.SEARCH_QUERY_MAX_CHARS = 400;
|
|
21
|
+
function capSearchQuery(query) {
|
|
22
|
+
return query.length <= exports.SEARCH_QUERY_MAX_CHARS ? query : query.slice(0, exports.SEARCH_QUERY_MAX_CHARS);
|
|
23
|
+
}
|
|
24
|
+
const TAVILY_BASE_URL = "https://api.tavily.com";
|
|
25
|
+
const DEFAULT_COST_PER_SEARCH_USD = 0.008;
|
|
26
|
+
const DEFAULT_MAX_RESULTS = 5;
|
|
27
|
+
function createTavilySearchProvider(config) {
|
|
28
|
+
const fetchFn = config.fetchFn ?? fetch;
|
|
29
|
+
const base = (config.baseUrl ?? TAVILY_BASE_URL).replace(/\/$/, "");
|
|
30
|
+
const costUsd = config.costPerSearchUsd ?? DEFAULT_COST_PER_SEARCH_USD;
|
|
31
|
+
return {
|
|
32
|
+
id: "tavily",
|
|
33
|
+
async search(query, opts) {
|
|
34
|
+
const response = await fetchFn(`${base}/search`, {
|
|
35
|
+
method: "POST",
|
|
36
|
+
headers: {
|
|
37
|
+
"content-type": "application/json",
|
|
38
|
+
authorization: `Bearer ${config.apiKey}`,
|
|
39
|
+
},
|
|
40
|
+
body: JSON.stringify({
|
|
41
|
+
query: capSearchQuery(query),
|
|
42
|
+
search_depth: config.searchDepth ?? "basic",
|
|
43
|
+
max_results: opts?.maxResults ?? DEFAULT_MAX_RESULTS,
|
|
44
|
+
include_answer: false,
|
|
45
|
+
}),
|
|
46
|
+
});
|
|
47
|
+
if (!response.ok) {
|
|
48
|
+
throw new Error(`tavily search → ${response.status} ${await response.text().catch(() => "")}`);
|
|
49
|
+
}
|
|
50
|
+
const body = (await response.json());
|
|
51
|
+
const results = (body.results ?? [])
|
|
52
|
+
.filter((r) => typeof r.url === "string" && r.url !== "")
|
|
53
|
+
.map((r) => ({
|
|
54
|
+
title: r.title ?? "",
|
|
55
|
+
url: r.url,
|
|
56
|
+
snippet: r.content ?? "",
|
|
57
|
+
...(r.published_date !== undefined ? { publishedDate: r.published_date } : {}),
|
|
58
|
+
}));
|
|
59
|
+
return { results, costUsd };
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
/** The injected block — appended to the rendered system prompt, before json
|
|
64
|
+
* coaching. Sources are numbered so outputs can cite by URL. */
|
|
65
|
+
function searchInjectionBlock(results, retrievedAt) {
|
|
66
|
+
const lines = results.map((r, i) => `${i + 1}. ${r.title || "(untitled)"} — ${r.url}` +
|
|
67
|
+
(r.publishedDate !== undefined ? ` (published ${r.publishedDate})` : "") +
|
|
68
|
+
(r.snippet !== "" ? `\n ${r.snippet}` : ""));
|
|
69
|
+
return (`\n\n---\nWEB SEARCH RESULTS (retrieved ${retrievedAt}). Ground your answer in these ` +
|
|
70
|
+
"sources and cite the URLs of the sources you actually rely on:\n" +
|
|
71
|
+
lines.join("\n"));
|
|
72
|
+
}
|
|
73
|
+
const URL_PATTERN = /https?:\/\/[^\s"'<>)\]\\]+/g;
|
|
74
|
+
/** Extract URLs cited in the model's output and normalize to url_citation
|
|
75
|
+
* annotations, enriching with titles from the injected sources. Deduped,
|
|
76
|
+
* trailing punctuation stripped (same normalization as the probe suite). */
|
|
77
|
+
function extractCitations(outputText, sources) {
|
|
78
|
+
const raw = outputText.match(URL_PATTERN) ?? [];
|
|
79
|
+
const urls = [...new Set(raw.map((u) => u.replace(/[.,;:]+$/, "")))];
|
|
80
|
+
const titleByUrl = new Map(sources.map((s) => [s.url, s.title]));
|
|
81
|
+
return urls.map((url) => {
|
|
82
|
+
const title = titleByUrl.get(url);
|
|
83
|
+
return {
|
|
84
|
+
type: "url_citation",
|
|
85
|
+
url,
|
|
86
|
+
...(title !== undefined && title !== "" ? { title } : {}),
|
|
87
|
+
};
|
|
88
|
+
});
|
|
89
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Repair rung (ladder rung 4) — Phase 3 spec WS2, plan §4. Pure pieces only:
|
|
3
|
+
* the repair-attempt state machine and the two repair prompts. Dispatch,
|
|
4
|
+
* cost, and telemetry stay in run.ts (repair calls are ordinary attempts with
|
|
5
|
+
* `repaired_by` stamped).
|
|
6
|
+
*
|
|
7
|
+
* Budget model (plan §4): repair draws from its OWN budget (RepairPolicy.
|
|
8
|
+
* maxRepairs) — a policy key, NOT a failure class. It never consumes the
|
|
9
|
+
* content_invalid budget and vice versa (non-fungibility extends to repair).
|
|
10
|
+
*/
|
|
11
|
+
import type { RepairPolicy, CandidateSpec } from "../types/route";
|
|
12
|
+
export type RepairStep = {
|
|
13
|
+
readonly kind: "same_model";
|
|
14
|
+
} | {
|
|
15
|
+
readonly kind: "repair_model";
|
|
16
|
+
readonly model: CandidateSpec;
|
|
17
|
+
};
|
|
18
|
+
export interface RepairLedger {
|
|
19
|
+
/** The next repair step to take, or null when the budget is spent (or
|
|
20
|
+
* repair is not configured). Each call consumes one unit. */
|
|
21
|
+
next(): RepairStep | null;
|
|
22
|
+
/** Repair attempts consumed so far. */
|
|
23
|
+
used(): number;
|
|
24
|
+
}
|
|
25
|
+
/** Attempt 1 = same model with error feedback; attempt 2 = the designated
|
|
26
|
+
* repair model (only when configured — loader guarantees maxRepairs=2 has
|
|
27
|
+
* repairModel). */
|
|
28
|
+
export declare function createRepairLedger(policy: RepairPolicy | undefined): RepairLedger;
|
|
29
|
+
/** Appended to the ORIGINAL system prompt for the same-model repair retry:
|
|
30
|
+
* the model sees exactly what failed validation and why. */
|
|
31
|
+
export declare function repairFeedbackBlock(errors: readonly string[]): string;
|
|
32
|
+
/** Full system prompt for the designated repair model: it never sees the
|
|
33
|
+
* original task — only {invalid output, errors, schema}. Fixed by design
|
|
34
|
+
* (plan §4); the repair model's job is mechanical correction, not re-answering. */
|
|
35
|
+
export declare function repairModelPrompt(invalidOutput: string, errors: readonly string[], schema: object): string;
|
|
36
|
+
//# sourceMappingURL=repair.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"repair.d.ts","sourceRoot":"","sources":["../../src/run/repair.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAElE,MAAM,MAAM,UAAU,GAClB;IAAE,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAA;CAAE,GAC/B;IAAE,QAAQ,CAAC,IAAI,EAAE,cAAc,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAA;CAAE,CAAC;AAErE,MAAM,WAAW,YAAY;IAC3B;iEAC6D;IAC7D,IAAI,IAAI,UAAU,GAAG,IAAI,CAAC;IAC1B,uCAAuC;IACvC,IAAI,IAAI,MAAM,CAAC;CAChB;AAED;;mBAEmB;AACnB,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,YAAY,GAAG,SAAS,GAAG,YAAY,CAejF;AAED;4DAC4D;AAC5D,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,CAUrE;AAED;;mFAEmF;AACnF,wBAAgB,iBAAiB,CAC/B,aAAa,EAAE,MAAM,EACrB,MAAM,EAAE,SAAS,MAAM,EAAE,EACzB,MAAM,EAAE,MAAM,GACb,MAAM,CAYR"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Repair rung (ladder rung 4) — Phase 3 spec WS2, plan §4. Pure pieces only:
|
|
4
|
+
* the repair-attempt state machine and the two repair prompts. Dispatch,
|
|
5
|
+
* cost, and telemetry stay in run.ts (repair calls are ordinary attempts with
|
|
6
|
+
* `repaired_by` stamped).
|
|
7
|
+
*
|
|
8
|
+
* Budget model (plan §4): repair draws from its OWN budget (RepairPolicy.
|
|
9
|
+
* maxRepairs) — a policy key, NOT a failure class. It never consumes the
|
|
10
|
+
* content_invalid budget and vice versa (non-fungibility extends to repair).
|
|
11
|
+
*/
|
|
12
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
+
exports.createRepairLedger = createRepairLedger;
|
|
14
|
+
exports.repairFeedbackBlock = repairFeedbackBlock;
|
|
15
|
+
exports.repairModelPrompt = repairModelPrompt;
|
|
16
|
+
/** Attempt 1 = same model with error feedback; attempt 2 = the designated
|
|
17
|
+
* repair model (only when configured — loader guarantees maxRepairs=2 has
|
|
18
|
+
* repairModel). */
|
|
19
|
+
function createRepairLedger(policy) {
|
|
20
|
+
let used = 0;
|
|
21
|
+
return {
|
|
22
|
+
next() {
|
|
23
|
+
if (policy === undefined || used >= policy.maxRepairs)
|
|
24
|
+
return null;
|
|
25
|
+
used += 1;
|
|
26
|
+
if (used === 1)
|
|
27
|
+
return { kind: "same_model" };
|
|
28
|
+
return policy.repairModel !== undefined
|
|
29
|
+
? { kind: "repair_model", model: policy.repairModel }
|
|
30
|
+
: null;
|
|
31
|
+
},
|
|
32
|
+
used() {
|
|
33
|
+
return used;
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
/** Appended to the ORIGINAL system prompt for the same-model repair retry:
|
|
38
|
+
* the model sees exactly what failed validation and why. */
|
|
39
|
+
function repairFeedbackBlock(errors) {
|
|
40
|
+
return ("\n\n---\nREPAIR PASS: Your previous response FAILED JSON Schema validation with these " +
|
|
41
|
+
`errors:\n${errors.map((e) => `- ${e}`).join("\n")}\n` +
|
|
42
|
+
"Produce the COMPLETE corrected JSON object now. Fix every listed error, keep all fields " +
|
|
43
|
+
"that were already valid, and output ONLY the JSON object — no commentary, no fences. " +
|
|
44
|
+
"CHARACTER LIMITS ARE EXACT: if an error says a string has too many characters, rewrite " +
|
|
45
|
+
"that string DRASTICALLY shorter — aim near the midpoint of the schema's minLength and " +
|
|
46
|
+
"maxLength for that field; your length estimates run long, so when unsure, cut more.");
|
|
47
|
+
}
|
|
48
|
+
/** Full system prompt for the designated repair model: it never sees the
|
|
49
|
+
* original task — only {invalid output, errors, schema}. Fixed by design
|
|
50
|
+
* (plan §4); the repair model's job is mechanical correction, not re-answering. */
|
|
51
|
+
function repairModelPrompt(invalidOutput, errors, schema) {
|
|
52
|
+
return ("You are a JSON repair specialist. You receive a JSON document that FAILED validation " +
|
|
53
|
+
"against a JSON Schema, plus the validation errors. Emit the corrected document.\n\n" +
|
|
54
|
+
"RULES: Output ONLY a single valid JSON object — no markdown fences, no commentary. " +
|
|
55
|
+
"Preserve every value that does not violate the schema; change ONLY what the errors " +
|
|
56
|
+
"require. Do not invent facts: where a required field is missing and its value cannot be " +
|
|
57
|
+
"derived from the invalid document, use the most conservative schema-valid completion.\n\n" +
|
|
58
|
+
`THE JSON SCHEMA:\n${JSON.stringify(schema)}\n\n` +
|
|
59
|
+
`THE VALIDATION ERRORS:\n${errors.map((e) => `- ${e}`).join("\n")}\n\n` +
|
|
60
|
+
`THE INVALID DOCUMENT:\n${invalidOutput}`);
|
|
61
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Candidate resolution — plan §4. THE ONLY constructor of CandidateRef in the
|
|
3
|
+
* codebase (the candidate-set invariant is enforced here structurally).
|
|
4
|
+
* Phase 1 selection: first candidate whose static capability flags satisfy
|
|
5
|
+
* route.require, ordered by prefer:[cost] via the pricing snapshot; on a
|
|
6
|
+
* retryable failure the run loop asks for the next candidate.
|
|
7
|
+
*/
|
|
8
|
+
import type { RouteBundle } from "../types/route";
|
|
9
|
+
import type { CandidateRef, CapabilityFlag, ProviderId } from "../types/provider";
|
|
10
|
+
/** Static capability stub — replaced by the probed registry in Phase 2. */
|
|
11
|
+
export type CapabilityLookup = (provider: ProviderId, model: string) => ReadonlySet<CapabilityFlag>;
|
|
12
|
+
export interface CandidateIterator {
|
|
13
|
+
/** Next eligible candidate, or null when the set is exhausted. */
|
|
14
|
+
next(): CandidateRef | null;
|
|
15
|
+
}
|
|
16
|
+
export declare function resolveCandidates(route: RouteBundle, capabilities: CapabilityLookup, costUsdPerMTok: (provider: ProviderId, model: string) => number | null, searchAvailable?: boolean): CandidateIterator;
|
|
17
|
+
/**
|
|
18
|
+
* Mint a CandidateRef for a SPECIFIC declared candidate (the repair rung's
|
|
19
|
+
* designated repair model — loader-validated to be in the route's candidate
|
|
20
|
+
* list; re-checked here). Same brand site, same sanctioned-cast scope.
|
|
21
|
+
*/
|
|
22
|
+
export declare function resolveDeclaredCandidate(route: RouteBundle, spec: {
|
|
23
|
+
readonly provider: ProviderId;
|
|
24
|
+
readonly model: string;
|
|
25
|
+
}): CandidateRef | null;
|
|
26
|
+
//# sourceMappingURL=resolve.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolve.d.ts","sourceRoot":"","sources":["../../src/run/resolve.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAClD,OAAO,KAAK,EAAE,YAAY,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAElF,2EAA2E;AAC3E,MAAM,MAAM,gBAAgB,GAAG,CAC7B,QAAQ,EAAE,UAAU,EACpB,KAAK,EAAE,MAAM,KACV,WAAW,CAAC,cAAc,CAAC,CAAC;AAEjC,MAAM,WAAW,iBAAiB;IAChC,kEAAkE;IAClE,IAAI,IAAI,YAAY,GAAG,IAAI,CAAC;CAC7B;AAuCD,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,WAAW,EAClB,YAAY,EAAE,gBAAgB,EAC9B,cAAc,EAAE,CAAC,QAAQ,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,EACtE,eAAe,UAAQ,GACtB,iBAAiB,CA6BnB;AAED;;;;GAIG;AACH,wBAAgB,wBAAwB,CACtC,KAAK,EAAE,WAAW,EAClB,IAAI,EAAE;IAAE,QAAQ,CAAC,QAAQ,EAAE,UAAU,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAC9D,YAAY,GAAG,IAAI,CAMrB"}
|