modelrig 0.11.0 → 0.12.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/CHANGELOG.md +82 -0
- package/dist/adapters/anthropic.d.ts +4 -4
- package/dist/adapters/anthropic.d.ts.map +1 -1
- package/dist/adapters/anthropic.js +8 -1
- package/dist/adapters/deepseek.d.ts +2 -4
- package/dist/adapters/deepseek.d.ts.map +1 -1
- package/dist/adapters/deepseek.js +9 -5
- package/dist/adapters/gemini.d.ts +4 -1
- package/dist/adapters/gemini.d.ts.map +1 -1
- package/dist/adapters/gemini.js +16 -1
- package/dist/adapters/grok.d.ts +2 -4
- package/dist/adapters/grok.d.ts.map +1 -1
- package/dist/adapters/grok.js +8 -4
- package/dist/adapters/history-mapping.d.ts +126 -0
- package/dist/adapters/history-mapping.d.ts.map +1 -0
- package/dist/adapters/history-mapping.js +230 -0
- package/dist/adapters/openai-compat.d.ts +2 -4
- package/dist/adapters/openai-compat.d.ts.map +1 -1
- package/dist/adapters/openai-compat.js +9 -5
- package/dist/adapters/openai.d.ts +2 -4
- package/dist/adapters/openai.d.ts.map +1 -1
- package/dist/adapters/openai.js +8 -4
- package/dist/adapters/responses-api.d.ts.map +1 -1
- package/dist/adapters/responses-api.js +9 -4
- package/dist/artifacts/artifact.d.ts.map +1 -1
- package/dist/artifacts/artifact.js +79 -0
- package/dist/artifacts/conversation.d.ts +128 -0
- package/dist/artifacts/conversation.d.ts.map +1 -0
- package/dist/artifacts/conversation.js +172 -0
- package/dist/artifacts/index.d.ts +1 -1
- package/dist/artifacts/index.d.ts.map +1 -1
- package/dist/artifacts/media.d.ts +46 -0
- package/dist/artifacts/media.d.ts.map +1 -0
- package/dist/artifacts/media.js +88 -0
- package/dist/artifacts/run-context.d.ts +61 -2
- package/dist/artifacts/run-context.d.ts.map +1 -1
- package/dist/artifacts/run-context.js +183 -12
- package/dist/artifacts/scrub.d.ts +3 -0
- package/dist/artifacts/scrub.d.ts.map +1 -1
- package/dist/bakeoff/engine.d.ts.map +1 -1
- package/dist/bakeoff/engine.js +34 -7
- package/dist/bakeoff/eval-cases.d.ts +5 -0
- package/dist/bakeoff/eval-cases.d.ts.map +1 -1
- package/dist/bakeoff/eval-cases.js +10 -1
- package/dist/bakeoff/replay.d.ts +15 -0
- package/dist/bakeoff/replay.d.ts.map +1 -1
- package/dist/bakeoff/replay.js +19 -1
- package/dist/bakeoff/report.d.ts.map +1 -1
- package/dist/bakeoff/report.js +9 -2
- package/dist/bakeoff/turn-replay.d.ts +93 -0
- package/dist/bakeoff/turn-replay.d.ts.map +1 -0
- package/dist/bakeoff/turn-replay.js +150 -0
- package/dist/bakeoff/types.d.ts +12 -0
- package/dist/bakeoff/types.d.ts.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +1 -0
- package/dist/config.d.ts +18 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +23 -3
- package/dist/doctor-preflight.d.ts.map +1 -1
- package/dist/doctor-preflight.js +6 -1
- package/dist/feedback/grade.d.ts +7 -2
- package/dist/feedback/grade.d.ts.map +1 -1
- package/dist/feedback/grade.js +14 -0
- package/dist/feedback/outcome.d.ts +103 -0
- package/dist/feedback/outcome.d.ts.map +1 -0
- package/dist/feedback/outcome.js +175 -0
- package/dist/gateway/history-bridge.d.ts +32 -0
- package/dist/gateway/history-bridge.d.ts.map +1 -0
- package/dist/gateway/history-bridge.js +64 -0
- package/dist/gateway/serve-client.d.ts.map +1 -1
- package/dist/gateway/serve-client.js +6 -0
- package/dist/index.d.ts +44 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +110 -9
- package/dist/init/init-cli.d.ts.map +1 -1
- package/dist/init/init-cli.js +21 -4
- package/dist/init/observe-cli.js +1 -1
- package/dist/init/observe.d.ts +7 -1
- package/dist/init/observe.d.ts.map +1 -1
- package/dist/init/observe.js +74 -22
- package/dist/init/starter.d.ts +36 -0
- package/dist/init/starter.d.ts.map +1 -0
- package/dist/init/starter.js +85 -0
- package/dist/registry/build.d.ts +4 -0
- package/dist/registry/build.d.ts.map +1 -1
- package/dist/registry/generation-models.d.ts +68 -0
- package/dist/registry/generation-models.d.ts.map +1 -0
- package/dist/registry/generation-models.js +168 -0
- package/dist/registry/model-kind.d.ts +34 -0
- package/dist/registry/model-kind.d.ts.map +1 -0
- package/dist/registry/model-kind.js +40 -0
- package/dist/registry/pricing-shape.d.ts +29 -1
- package/dist/registry/pricing-shape.d.ts.map +1 -1
- package/dist/registry/pricing-shape.js +9 -0
- package/dist/registry/pricing.d.ts +26 -0
- package/dist/registry/pricing.d.ts.map +1 -1
- package/dist/registry/pricing.js +29 -0
- package/dist/registry/rows.d.ts.map +1 -1
- package/dist/registry/rows.js +3 -1
- package/dist/registry/unit-pricing.d.ts +32 -0
- package/dist/registry/unit-pricing.d.ts.map +1 -0
- package/dist/registry/unit-pricing.js +62 -0
- package/dist/routes/input-loader.d.ts +38 -0
- package/dist/routes/input-loader.d.ts.map +1 -0
- package/dist/routes/input-loader.js +80 -0
- package/dist/routes/loader.d.ts.map +1 -1
- package/dist/routes/loader.js +3 -0
- package/dist/routes/resolved-bundle.d.ts +2 -0
- package/dist/routes/resolved-bundle.d.ts.map +1 -1
- package/dist/routes/resolved-bundle.js +3 -0
- package/dist/run/cost.d.ts +28 -0
- package/dist/run/cost.d.ts.map +1 -1
- package/dist/run/cost.js +74 -0
- package/dist/run/dispatch.d.ts +4 -1
- package/dist/run/dispatch.d.ts.map +1 -1
- package/dist/run/dispatch.js +3 -0
- package/dist/run/limits.d.ts +88 -0
- package/dist/run/limits.d.ts.map +1 -0
- package/dist/run/limits.js +155 -0
- package/dist/run/messages.d.ts +199 -0
- package/dist/run/messages.d.ts.map +1 -0
- package/dist/run/messages.js +430 -0
- package/dist/run/raw-finalize.d.ts +5 -2
- package/dist/run/raw-finalize.d.ts.map +1 -1
- package/dist/run/raw-finalize.js +8 -5
- package/dist/run/raw-request.d.ts.map +1 -1
- package/dist/run/raw-request.js +2 -1
- package/dist/run/raw.d.ts +10 -4
- package/dist/run/raw.d.ts.map +1 -1
- package/dist/run/raw.js +11 -3
- package/dist/run/rawonly.d.ts +29 -5
- package/dist/run/rawonly.d.ts.map +1 -1
- package/dist/run/rawonly.js +54 -19
- package/dist/run/record-attempt.d.ts.map +1 -1
- package/dist/run/record-attempt.js +16 -6
- package/dist/run/record-call.d.ts +13 -2
- package/dist/run/record-call.d.ts.map +1 -1
- package/dist/run/record-call.js +50 -7
- package/dist/run/run.d.ts.map +1 -1
- package/dist/run/run.js +12 -8
- package/dist/telemetry/attempt-meta.d.ts +49 -1
- package/dist/telemetry/attempt-meta.d.ts.map +1 -1
- package/dist/telemetry/attempt-meta.js +45 -1
- package/dist/telemetry/exporter.d.ts.map +1 -1
- package/dist/telemetry/exporter.js +2 -1
- package/dist/telemetry/grade-mappers.d.ts +1 -1
- package/dist/telemetry/grade-mappers.d.ts.map +1 -1
- package/dist/telemetry/grade-mappers.js +7 -1
- package/dist/telemetry/grade-task.d.ts +49 -0
- package/dist/telemetry/grade-task.d.ts.map +1 -0
- package/dist/telemetry/grade-task.js +85 -0
- package/dist/telemetry/grades-sql.d.ts +16 -1
- package/dist/telemetry/grades-sql.d.ts.map +1 -1
- package/dist/telemetry/grades-sql.js +64 -4
- package/dist/telemetry/ingest-protocol.d.ts +24 -1
- package/dist/telemetry/ingest-protocol.d.ts.map +1 -1
- package/dist/telemetry/ingest-protocol.js +72 -43
- package/dist/telemetry/otel-content.d.ts +31 -0
- package/dist/telemetry/otel-content.d.ts.map +1 -0
- package/dist/telemetry/otel-content.js +106 -0
- package/dist/telemetry/otel-hierarchy.d.ts +57 -0
- package/dist/telemetry/otel-hierarchy.d.ts.map +1 -0
- package/dist/telemetry/otel-hierarchy.js +155 -0
- package/dist/telemetry/otel-normalize.d.ts +15 -1
- package/dist/telemetry/otel-normalize.d.ts.map +1 -1
- package/dist/telemetry/otel-normalize.js +24 -39
- package/dist/telemetry/sinks.d.ts +4 -1
- package/dist/telemetry/sinks.d.ts.map +1 -1
- package/dist/telemetry/sinks.js +53 -34
- package/dist/telemetry/store-migrations.d.ts.map +1 -1
- package/dist/telemetry/store-migrations.js +8 -0
- package/dist/telemetry/store.d.ts +9 -0
- package/dist/telemetry/store.d.ts.map +1 -1
- package/dist/telemetry/store.js +10 -0
- package/dist/telemetry/uniform-rows.d.ts +43 -0
- package/dist/telemetry/uniform-rows.d.ts.map +1 -0
- package/dist/telemetry/uniform-rows.js +139 -0
- package/dist/types/failures-teaching.d.ts.map +1 -1
- package/dist/types/failures-teaching.js +11 -0
- package/dist/types/failures.d.ts +1 -1
- package/dist/types/failures.d.ts.map +1 -1
- package/dist/types/failures.js +2 -0
- package/dist/types/provider.d.ts +12 -0
- package/dist/types/provider.d.ts.map +1 -1
- package/dist/types/route.d.ts +13 -0
- package/dist/types/route.d.ts.map +1 -1
- package/dist/types/telemetry.d.ts +14 -1
- package/dist/types/telemetry.d.ts.map +1 -1
- package/docs/agent-skill/SKILL.md +122 -12
- package/docs/how-it-fits.md +4 -4
- package/docs/instrumentation.md +148 -44
- package/docs/llms.txt +143 -13
- package/docs/mcp-oracle.md +15 -4
- package/docs/quickstart.md +129 -16
- package/package.json +4 -2
- package/registry/registry.json +456 -0
- package/templates/creative-approval.json +81 -0
- package/templates/support-conversation.json +82 -0
|
@@ -0,0 +1,430 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* The messages-native lane — ARC-β (agent-archetypes spec §4, builder-prompt-02
|
|
4
|
+
* §1.1). A provider-neutral conversation HISTORY that every adapter maps to its
|
|
5
|
+
* native shape (adapters/history-mapping.ts), so a chat turn or a tool loop can
|
|
6
|
+
* be routed, captured and replayed on a cheaper model.
|
|
7
|
+
*
|
|
8
|
+
* Design rules (binding):
|
|
9
|
+
* - NO `system` role in history. The system prompt stays
|
|
10
|
+
* `AdapterRequest.systemPrompt`, rendered by the route template exactly as
|
|
11
|
+
* today. A `system` message in caller input is rejected with a teaching error.
|
|
12
|
+
* - Tool calls reuse the existing normalized {@link ToolCall} (OpenAI
|
|
13
|
+
* `tool_calls` shape) — there is no second tool-call vocabulary.
|
|
14
|
+
* - `history` absent ⇒ every adapter takes today's code path, byte-identical
|
|
15
|
+
* (pinned by adapters/one-turn-parity.test.ts).
|
|
16
|
+
*
|
|
17
|
+
* NAMING NOTE: the spec calls the type `ChatMessage`, but `ChatMessage` is
|
|
18
|
+
* already a public export (the gateway's OpenAI wire message,
|
|
19
|
+
* gateway/http-protocol.ts). To keep that export stable the lane's type is
|
|
20
|
+
* exported as {@link HistoryMessage} (it is what `AdapterRequest.history` holds).
|
|
21
|
+
*
|
|
22
|
+
* Pure module: types, validation, canonicalization and the capture codec. No I/O.
|
|
23
|
+
*/
|
|
24
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
25
|
+
exports.HISTORY_INCOMPATIBLE_PREFIX = exports.HISTORY_SKIPPED_TAG = exports.MESSAGES_CAPTURE_KIND = exports.MessagesInputError = exports.MESSAGES_DOCS_ANCHOR = void 0;
|
|
26
|
+
exports.historyProblems = historyProblems;
|
|
27
|
+
exports.assertValidHistory = assertValidHistory;
|
|
28
|
+
exports.contentText = contentText;
|
|
29
|
+
exports.historyChars = historyChars;
|
|
30
|
+
exports.canonicalize = canonicalize;
|
|
31
|
+
exports.renderForCapture = renderForCapture;
|
|
32
|
+
exports.parseCaptureInput = parseCaptureInput;
|
|
33
|
+
exports.captureInputFor = captureInputFor;
|
|
34
|
+
exports.routedHistory = routedHistory;
|
|
35
|
+
exports.withSkipTag = withSkipTag;
|
|
36
|
+
exports.messagesUserTurn = messagesUserTurn;
|
|
37
|
+
exports.historyIncompatibility = historyIncompatibility;
|
|
38
|
+
exports.isGemini3Model = isGemini3Model;
|
|
39
|
+
exports.historyIncompatibilityCode = historyIncompatibilityCode;
|
|
40
|
+
exports.rawHistory = rawHistory;
|
|
41
|
+
exports.userSideForHash = userSideForHash;
|
|
42
|
+
const loader_1 = require("../routes/loader");
|
|
43
|
+
const failures_1 = require("../types/failures");
|
|
44
|
+
const input_loader_1 = require("../routes/input-loader");
|
|
45
|
+
const template_1 = require("../routes/template");
|
|
46
|
+
/** Where every messages-lane teaching message points. */
|
|
47
|
+
exports.MESSAGES_DOCS_ANCHOR = "messages-lane.md";
|
|
48
|
+
/** A caller-contract breach in a `messages` input. Subclasses RouteConfigError
|
|
49
|
+
* so every surface that already maps config errors (the gateway's 422, the CLI)
|
|
50
|
+
* treats it the same way: thrown pre-dispatch, nothing billed, no telemetry. */
|
|
51
|
+
class MessagesInputError extends loader_1.RouteConfigError {
|
|
52
|
+
constructor(problems) {
|
|
53
|
+
super("invalid messages", { problems });
|
|
54
|
+
this.name = "MessagesInputError";
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
exports.MessagesInputError = MessagesInputError;
|
|
58
|
+
function isRecord(v) {
|
|
59
|
+
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
60
|
+
}
|
|
61
|
+
function contentProblems(content, at, problems) {
|
|
62
|
+
if (typeof content === "string")
|
|
63
|
+
return;
|
|
64
|
+
if (!Array.isArray(content)) {
|
|
65
|
+
problems.push(`${at}.content must be a string or an array of {type: "text", text} parts`);
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
content.forEach((p, j) => {
|
|
69
|
+
if (!isRecord(p) || p.type !== "text" || typeof p.text !== "string") {
|
|
70
|
+
const kind = isRecord(p) && typeof p.type === "string" ? `"${p.type}" ` : "";
|
|
71
|
+
problems.push(`${at}.content[${j}] ${kind}is not a text part — only {type: "text", text: string} parts are supported ` +
|
|
72
|
+
"(image parts arrive with the image-input lane)");
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
function isJsonObjectString(s) {
|
|
77
|
+
try {
|
|
78
|
+
return isRecord(JSON.parse(s));
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
function toolCallProblems(calls, at, problems) {
|
|
85
|
+
if (!Array.isArray(calls)) {
|
|
86
|
+
problems.push(`${at}.toolCalls must be an array of tool calls (the shape meta.toolCalls returned)`);
|
|
87
|
+
return [];
|
|
88
|
+
}
|
|
89
|
+
const ids = [];
|
|
90
|
+
calls.forEach((c, j) => {
|
|
91
|
+
const fn = isRecord(c) ? c.function : undefined;
|
|
92
|
+
if (!isRecord(c) ||
|
|
93
|
+
typeof c.id !== "string" ||
|
|
94
|
+
c.id === "" ||
|
|
95
|
+
c.type !== "function" ||
|
|
96
|
+
!isRecord(fn) ||
|
|
97
|
+
typeof fn.name !== "string" ||
|
|
98
|
+
typeof fn.arguments !== "string") {
|
|
99
|
+
problems.push(`${at}.toolCalls[${j}] must be {id, type: "function", function: {name, arguments: <JSON string>}}`);
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
if (!isJsonObjectString(fn.arguments)) {
|
|
103
|
+
problems.push(`${at}.toolCalls[${j}].function.arguments must be a JSON object string (e.g. "{\\"q\\":\\"acme\\"}")`);
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
ids.push(c.id);
|
|
107
|
+
});
|
|
108
|
+
return ids;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Validate a caller-supplied history. Returns the list of teaching problems
|
|
112
|
+
* (empty ⇒ valid). Rules: a non-empty array; roles user | assistant | tool only
|
|
113
|
+
* (a `system` message is rejected, pointing at the route template /
|
|
114
|
+
* `systemPrompt`); `toolCalls` only on assistant messages; every tool message
|
|
115
|
+
* answers a PRIOR assistant tool call by id; the last message is `user` or
|
|
116
|
+
* `tool` (the model answers it).
|
|
117
|
+
*/
|
|
118
|
+
function historyProblems(messages) {
|
|
119
|
+
const problems = [];
|
|
120
|
+
if (!Array.isArray(messages))
|
|
121
|
+
return ["messages must be an array of {role, content} messages"];
|
|
122
|
+
if (messages.length === 0) {
|
|
123
|
+
return ["messages is empty — a conversation needs at least one user message for the model to answer"];
|
|
124
|
+
}
|
|
125
|
+
// Tool-call PAIRING (QA M2 — every provider enforces it): the tool messages that
|
|
126
|
+
// immediately follow an assistant turn with toolCalls answer EXACTLY that turn's
|
|
127
|
+
// ids — none missing, none extra, none twice — before any other turn. Ids are
|
|
128
|
+
// scoped to ONE assistant turn and its results (R2-3): hosts that emit `call_0`
|
|
129
|
+
// on every turn are valid. One problem per real fault (no overlapping echoes).
|
|
130
|
+
const everIssued = new Set();
|
|
131
|
+
const reportedUnanswered = new Set();
|
|
132
|
+
let windowAnswered = new Set();
|
|
133
|
+
let pending = null;
|
|
134
|
+
const closePending = (before) => {
|
|
135
|
+
if (pending !== null && pending.ids.size > 0) {
|
|
136
|
+
problems.push(`${pending.at} issued tool call(s) ${[...pending.ids].map((id) => `"${id}"`).join(", ")} that are not answered ` +
|
|
137
|
+
`before ${before} — every tool call needs its tool result immediately after the assistant turn`);
|
|
138
|
+
for (const id of pending.ids)
|
|
139
|
+
reportedUnanswered.add(id);
|
|
140
|
+
}
|
|
141
|
+
pending = null;
|
|
142
|
+
};
|
|
143
|
+
messages.forEach((m, i) => {
|
|
144
|
+
const at = `messages[${i}]`;
|
|
145
|
+
if (!isRecord(m)) {
|
|
146
|
+
problems.push(`${at} must be an object {role, content}`);
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
if (m.role === "system") {
|
|
150
|
+
problems.push(`send your system prompt via the route template, not as a system message — ${at} has role "system". ` +
|
|
151
|
+
"With rig.run it belongs in the route's system template; with runRaw pass it as `systemPrompt`; " +
|
|
152
|
+
"history holds only user, assistant and tool turns");
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
if (m.role === "user" || m.role === "assistant") {
|
|
156
|
+
closePending(at);
|
|
157
|
+
contentProblems(m.content, at, problems);
|
|
158
|
+
if (m.toolCalls !== undefined) {
|
|
159
|
+
if (m.role === "user") {
|
|
160
|
+
problems.push(`${at}.toolCalls is only allowed on an assistant message`);
|
|
161
|
+
}
|
|
162
|
+
else {
|
|
163
|
+
const ids = new Set();
|
|
164
|
+
for (const id of toolCallProblems(m.toolCalls, at, problems)) {
|
|
165
|
+
if (ids.has(id))
|
|
166
|
+
problems.push(`${at} issues tool call id "${id}" more than once in the same turn`);
|
|
167
|
+
ids.add(id);
|
|
168
|
+
everIssued.add(id);
|
|
169
|
+
reportedUnanswered.delete(id);
|
|
170
|
+
}
|
|
171
|
+
windowAnswered = new Set();
|
|
172
|
+
if (ids.size > 0)
|
|
173
|
+
pending = { at, ids };
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
if (m.role === "tool") {
|
|
179
|
+
if (typeof m.content !== "string")
|
|
180
|
+
problems.push(`${at}.content must be a string (the tool's result)`);
|
|
181
|
+
const id = m.toolCallId;
|
|
182
|
+
if (typeof id !== "string" || id === "") {
|
|
183
|
+
problems.push(`${at}.toolCallId must name the tool call this result answers`);
|
|
184
|
+
}
|
|
185
|
+
else if (pending !== null && pending.ids.has(id)) {
|
|
186
|
+
pending.ids.delete(id);
|
|
187
|
+
windowAnswered.add(id);
|
|
188
|
+
}
|
|
189
|
+
else if (windowAnswered.has(id)) {
|
|
190
|
+
problems.push(`${at} answers tool call "${id}" a second time — each tool call takes exactly one tool result`);
|
|
191
|
+
}
|
|
192
|
+
else if (reportedUnanswered.has(id)) {
|
|
193
|
+
// already reported once, as the unanswered call — no second message
|
|
194
|
+
}
|
|
195
|
+
else if (everIssued.has(id)) {
|
|
196
|
+
problems.push(`${at} answers tool call "${id}" out of place — a tool result must immediately follow the assistant turn ` +
|
|
197
|
+
"that issued it (no user or assistant turn in between)");
|
|
198
|
+
}
|
|
199
|
+
else {
|
|
200
|
+
problems.push(`${at} answers tool call "${id}", but no earlier assistant message issued it — ` +
|
|
201
|
+
"include the assistant turn with its toolCalls before the tool result");
|
|
202
|
+
}
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
problems.push(`${at}.role ${JSON.stringify(m.role)} is not one of user | assistant | tool`);
|
|
206
|
+
});
|
|
207
|
+
closePending("the end of the conversation");
|
|
208
|
+
const last = messages[messages.length - 1];
|
|
209
|
+
if (isRecord(last) && last.role !== "user" && last.role !== "tool" && last.role !== "system") {
|
|
210
|
+
problems.push(`the last message is "${String(last.role)}" — a conversation must end with a user or tool message for the model to answer`);
|
|
211
|
+
}
|
|
212
|
+
return problems;
|
|
213
|
+
}
|
|
214
|
+
/** Throw a {@link MessagesInputError} when `messages` is not a valid history;
|
|
215
|
+
* otherwise return it typed. */
|
|
216
|
+
function assertValidHistory(messages) {
|
|
217
|
+
const problems = historyProblems(messages);
|
|
218
|
+
if (problems.length > 0)
|
|
219
|
+
throw new MessagesInputError(problems);
|
|
220
|
+
return messages;
|
|
221
|
+
}
|
|
222
|
+
/** The text of a content value (parts concatenated in order, no separator —
|
|
223
|
+
* the same joining OpenAI applies to multi-part text content). */
|
|
224
|
+
function contentText(content) {
|
|
225
|
+
return typeof content === "string" ? content : content.map((p) => p.text).join("");
|
|
226
|
+
}
|
|
227
|
+
/** Total characters across every message's text and tool-call arguments — the
|
|
228
|
+
* history's contribution to the envelope's pre-dispatch input estimate. */
|
|
229
|
+
function historyChars(history) {
|
|
230
|
+
if (history === undefined)
|
|
231
|
+
return 0;
|
|
232
|
+
let n = 0;
|
|
233
|
+
for (const m of history) {
|
|
234
|
+
n += m.role === "tool" ? m.content.length : contentText(m.content).length;
|
|
235
|
+
if (m.role === "assistant")
|
|
236
|
+
for (const c of m.toolCalls ?? [])
|
|
237
|
+
n += c.function.name.length + c.function.arguments.length;
|
|
238
|
+
}
|
|
239
|
+
return n;
|
|
240
|
+
}
|
|
241
|
+
/** Deterministic JSON (keys sorted, arrays in order). */
|
|
242
|
+
function stable(value) {
|
|
243
|
+
if (value === null || typeof value !== "object")
|
|
244
|
+
return JSON.stringify(value) ?? "null";
|
|
245
|
+
if (Array.isArray(value))
|
|
246
|
+
return `[${value.map(stable).join(",")}]`;
|
|
247
|
+
const rec = value;
|
|
248
|
+
return `{${Object.keys(rec)
|
|
249
|
+
.filter((k) => rec[k] !== undefined)
|
|
250
|
+
.sort()
|
|
251
|
+
.map((k) => `${JSON.stringify(k)}:${stable(rec[k])}`)
|
|
252
|
+
.join(",")}}`;
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* Canonical text of a history, for hashing and cache-prefix reasoning: one
|
|
256
|
+
* sorted-key JSON line per message. PREFIX-STABLE by construction —
|
|
257
|
+
* `canonicalize(h.slice(0, n))` is a string prefix of `canonicalize(h)` — so a
|
|
258
|
+
* conversation that grows by appending turns keeps every earlier turn's bytes
|
|
259
|
+
* (the property provider prefix caches key on).
|
|
260
|
+
*/
|
|
261
|
+
function canonicalize(history) {
|
|
262
|
+
return history.map((m) => `${stable(m)}\n`).join("");
|
|
263
|
+
}
|
|
264
|
+
// ── Capture codec (spec §4: one JSON capture per turn) ───────────────────────
|
|
265
|
+
/** The marker that tells a capture row's `variablesJson` holds a messages call. */
|
|
266
|
+
exports.MESSAGES_CAPTURE_KIND = "modelrig.messages";
|
|
267
|
+
/** Render a messages call's replay input for the capture row (JSON). */
|
|
268
|
+
function renderForCapture(history, variables = {}, tools = {}) {
|
|
269
|
+
const capture = {
|
|
270
|
+
kind: exports.MESSAGES_CAPTURE_KIND,
|
|
271
|
+
v: 1,
|
|
272
|
+
variables,
|
|
273
|
+
history,
|
|
274
|
+
...(tools.tools !== undefined ? { tools: tools.tools } : {}),
|
|
275
|
+
...(tools.toolChoice !== undefined ? { toolChoice: tools.toolChoice } : {}),
|
|
276
|
+
};
|
|
277
|
+
return JSON.stringify(capture);
|
|
278
|
+
}
|
|
279
|
+
function parseCaptureInput(variablesJson) {
|
|
280
|
+
const parsed = JSON.parse(variablesJson);
|
|
281
|
+
if (isRecord(parsed) && parsed.kind === exports.MESSAGES_CAPTURE_KIND && Array.isArray(parsed.history)) {
|
|
282
|
+
const variables = isRecord(parsed.variables) ? parsed.variables : {};
|
|
283
|
+
return {
|
|
284
|
+
input: variables,
|
|
285
|
+
messages: assertValidHistory(parsed.history),
|
|
286
|
+
...(Array.isArray(parsed.tools) ? { tools: parsed.tools } : {}),
|
|
287
|
+
...(parsed.toolChoice !== undefined ? { toolChoice: parsed.toolChoice } : {}),
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
return { input: parsed };
|
|
291
|
+
}
|
|
292
|
+
/** The capture-row input for a run: the variables alone (legacy, byte-identical)
|
|
293
|
+
* or the messages envelope. Returned as a value for `JSON.stringify` by the
|
|
294
|
+
* capture writer. */
|
|
295
|
+
function captureInputFor(input, messages, tools = {}) {
|
|
296
|
+
return messages === undefined ? input : JSON.parse(renderForCapture(messages, input, tools));
|
|
297
|
+
}
|
|
298
|
+
function routedHistory(route, opts) {
|
|
299
|
+
if (route.inputKind !== "messages") {
|
|
300
|
+
if (opts.messages === undefined)
|
|
301
|
+
return { history: undefined, route, skipTag: {} };
|
|
302
|
+
throw new loader_1.RouteConfigError(`route "${route.route}" does not accept messages — declare \`input: { kind: messages }\` in its bundle to ` +
|
|
303
|
+
`serve a conversation, or pass the content as \`input\` variables (see ${exports.MESSAGES_DOCS_ANCHOR})`);
|
|
304
|
+
}
|
|
305
|
+
const conflict = (0, input_loader_1.messagesGuardConflict)({
|
|
306
|
+
maskDeclared: route.mask !== undefined,
|
|
307
|
+
screenDeclared: route.screen !== undefined,
|
|
308
|
+
repairDeclared: route.repair !== undefined || (route.variants ?? []).some((v) => v.repair !== undefined),
|
|
309
|
+
});
|
|
310
|
+
if (conflict !== null)
|
|
311
|
+
throw new loader_1.RouteConfigError(`route "${route.route}": ${conflict}`);
|
|
312
|
+
if (opts.messages === undefined) {
|
|
313
|
+
throw new MessagesInputError([
|
|
314
|
+
`route "${route.route}" declares input: messages — pass the conversation as \`messages\` (the system prompt still comes from the route template)`,
|
|
315
|
+
]);
|
|
316
|
+
}
|
|
317
|
+
const history = assertValidHistory(opts.messages);
|
|
318
|
+
return { history, ...compatibleCandidates(route, history, opts.tools) };
|
|
319
|
+
}
|
|
320
|
+
/** The `history_skipped` tag key (the precedent is `zdr_enforced`). */
|
|
321
|
+
exports.HISTORY_SKIPPED_TAG = "history_skipped";
|
|
322
|
+
/** Merge the skip tag onto the run's tags; the SAME object when there is none. */
|
|
323
|
+
function withSkipTag(tags, skipTag) {
|
|
324
|
+
return Object.keys(skipTag).length === 0 ? tags : { ...tags, ...skipTag };
|
|
325
|
+
}
|
|
326
|
+
/**
|
|
327
|
+
* Ruling R2-2: a candidate that cannot take this history natively is SKIPPED
|
|
328
|
+
* before the ladder (no dispatch, no row), and the run proceeds on the
|
|
329
|
+
* compatible candidates in their declared order. The skips ride every row of
|
|
330
|
+
* the call as the `history_skipped` tag. Only when NO candidate is compatible
|
|
331
|
+
* does the run fail closed — the terminal `invariant_violation`, listing each
|
|
332
|
+
* candidate's reason (no new FailureClass).
|
|
333
|
+
*/
|
|
334
|
+
function compatibleCandidates(route, history, tools) {
|
|
335
|
+
const skipped = [];
|
|
336
|
+
const kept = route.candidates.filter((c) => {
|
|
337
|
+
const why = historyIncompatibilityCode(c.provider, c.model, history, tools);
|
|
338
|
+
if (why !== null)
|
|
339
|
+
skipped.push({ c, why });
|
|
340
|
+
return why === null;
|
|
341
|
+
});
|
|
342
|
+
if (skipped.length === 0)
|
|
343
|
+
return { route, skipTag: {} };
|
|
344
|
+
if (kept.length === 0) {
|
|
345
|
+
const only = route.candidates.length === 1 ? route.candidates[0] : undefined;
|
|
346
|
+
throw new failures_1.RigFailureError({
|
|
347
|
+
class: "invariant_violation",
|
|
348
|
+
message: `${exports.HISTORY_INCOMPATIBLE_PREFIX}: no candidate of route "${route.route}" can take this conversation natively — ` +
|
|
349
|
+
skipped.map((s) => s.why.message.slice(exports.HISTORY_INCOMPATIBLE_PREFIX.length + 2)).join("; "),
|
|
350
|
+
fixHint: "add a candidate that accepts tool-loop history (and pass the loop's `tools` on every turn)",
|
|
351
|
+
...(only !== undefined ? { provider: only.provider, model: only.model } : {}),
|
|
352
|
+
});
|
|
353
|
+
}
|
|
354
|
+
return {
|
|
355
|
+
route: Object.freeze({ ...route, candidates: Object.freeze(kept) }),
|
|
356
|
+
skipTag: { [exports.HISTORY_SKIPPED_TAG]: skipped.map((s) => `${s.c.provider}/${s.c.model}=${s.why.code}`).join(",") },
|
|
357
|
+
};
|
|
358
|
+
}
|
|
359
|
+
/** A messages route's final user instruction: its rendered `prompt.user` when
|
|
360
|
+
* declared, else nothing (the history's last turn IS the prompt — the fixed
|
|
361
|
+
* single-turn trigger is never appended to a conversation). */
|
|
362
|
+
function messagesUserTurn(route, ctx) {
|
|
363
|
+
return route.userTemplate === undefined ? "" : (0, template_1.renderTemplate)(route.userTemplate, ctx);
|
|
364
|
+
}
|
|
365
|
+
// ── Provider acceptance (architect ruling M3(b), 2026-10-24) ─────────────────
|
|
366
|
+
/** Prefix of every "this provider cannot take this history natively" refusal —
|
|
367
|
+
* the stable marker turn replay keys on to mark a case INCOMPARABLE for a
|
|
368
|
+
* candidate (bakeoff/turn-replay.ts) rather than score it as a failure. */
|
|
369
|
+
exports.HISTORY_INCOMPATIBLE_PREFIX = "history not accepted natively";
|
|
370
|
+
const hasToolBlocks = (h) => h.some((m) => m.role === "tool" || (m.role === "assistant" && (m.toolCalls ?? []).length > 0));
|
|
371
|
+
/**
|
|
372
|
+
* Why `provider/model` cannot be sent this history natively, or null. Fail-closed
|
|
373
|
+
* PRE-DISPATCH (no provider call) on two documented provider rules:
|
|
374
|
+
* - Anthropic rejects `tool_use` / `tool_result` blocks unless the request
|
|
375
|
+
* declares `tools`;
|
|
376
|
+
* - Gemini 3 models require thought signatures on earlier `functionCall` parts,
|
|
377
|
+
* which ModelRig neither captures nor invents (no placeholder signatures).
|
|
378
|
+
* Every other provider/model proceeds.
|
|
379
|
+
*/
|
|
380
|
+
function historyIncompatibility(provider, model, history, declaredTools) {
|
|
381
|
+
return historyIncompatibilityCode(provider, model, history, declaredTools)?.message ?? null;
|
|
382
|
+
}
|
|
383
|
+
/** R2-4: `models/gemini-3…` and case variants normalize to `gemini-3…`. An
|
|
384
|
+
* unregistered alias (e.g. `gemini-pro-latest`) does not match and falls
|
|
385
|
+
* through to the provider's own 400 — documented, never guessed. */
|
|
386
|
+
function isGemini3Model(model) {
|
|
387
|
+
return /^gemini-3/.test(model.trim().toLowerCase().replace(/^models\//, ""));
|
|
388
|
+
}
|
|
389
|
+
function historyIncompatibilityCode(provider, model, history, declaredTools) {
|
|
390
|
+
if (history === undefined || !hasToolBlocks(history))
|
|
391
|
+
return null;
|
|
392
|
+
if (provider === "anthropic" && (declaredTools === undefined || declaredTools.length === 0)) {
|
|
393
|
+
return {
|
|
394
|
+
code: "tools_required",
|
|
395
|
+
message: `${exports.HISTORY_INCOMPATIBLE_PREFIX}: anthropic/${model} rejects tool_use / tool_result blocks in the history unless ` +
|
|
396
|
+
"the request declares `tools`, and this request declared none — pass the same `tools` on every turn of the tool loop",
|
|
397
|
+
};
|
|
398
|
+
}
|
|
399
|
+
if (provider === "gemini" && isGemini3Model(model) && history.some((m) => m.role === "assistant" && (m.toolCalls ?? []).length > 0)) {
|
|
400
|
+
return {
|
|
401
|
+
code: "thought_signatures",
|
|
402
|
+
message: `${exports.HISTORY_INCOMPATIBLE_PREFIX}: gemini/${model} requires thought signatures on the function calls in the history, ` +
|
|
403
|
+
"which ModelRig does not capture or invent — serve tool-loop turns with a non-Gemini-3 model (Gemini 2.x and other providers accept them)",
|
|
404
|
+
};
|
|
405
|
+
}
|
|
406
|
+
return null;
|
|
407
|
+
}
|
|
408
|
+
/** The raw lane's history (runRaw / runRawStream), or `undefined` for the
|
|
409
|
+
* single-turn call. `userPrompt` is optional only when `messages` is given;
|
|
410
|
+
* neither is a teaching error. A history the target provider cannot take
|
|
411
|
+
* natively is refused here, pre-dispatch (no provider call, no telemetry row). */
|
|
412
|
+
function rawHistory(input) {
|
|
413
|
+
if (input.messages === undefined) {
|
|
414
|
+
if (input.userPrompt === undefined) {
|
|
415
|
+
throw new MessagesInputError(["runRaw needs a `userPrompt` or a `messages` conversation — neither was given"]);
|
|
416
|
+
}
|
|
417
|
+
return undefined;
|
|
418
|
+
}
|
|
419
|
+
const history = assertValidHistory(input.messages);
|
|
420
|
+
const incompatible = historyIncompatibility(input.provider, input.model, history, input.tools);
|
|
421
|
+
if (incompatible !== null)
|
|
422
|
+
throw new MessagesInputError([incompatible]);
|
|
423
|
+
return history;
|
|
424
|
+
}
|
|
425
|
+
/** The user side of the rendered request for the attempt envelope's
|
|
426
|
+
* input_hash / size (WI-1): the canonical history followed by the final user
|
|
427
|
+
* instruction; exactly `userPrompt` when there is no history (byte-identical). */
|
|
428
|
+
function userSideForHash(userPrompt, history) {
|
|
429
|
+
return history === undefined ? userPrompt : `${canonicalize(history)}${userPrompt}`;
|
|
430
|
+
}
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* recorder — NO route/registry/capability-gate dependency.
|
|
10
10
|
*/
|
|
11
11
|
import { type ActiveRunContext } from "../artifacts/run-context";
|
|
12
|
-
import type { AdapterResult, AdapterUsage, ProviderId, ServiceTier } from "../types/provider";
|
|
12
|
+
import type { AdapterResult, AdapterUsage, ProviderId, ServiceTier, ToolCall } from "../types/provider";
|
|
13
13
|
import type { RunResult, RunTags } from "../types/route";
|
|
14
14
|
import type { AttemptMetaV1 } from "../telemetry/attempt-meta";
|
|
15
15
|
import type { TelemetryRow, TelemetryStore } from "../types/telemetry";
|
|
@@ -80,7 +80,10 @@ export declare function prepareRawStep(deps: Pick<RawRunDeps, "manifest">, input
|
|
|
80
80
|
* ground-truth step — parity with the routed lane's record-attempt. No-op when
|
|
81
81
|
* no run context was captured at entry. Fail-open is handled inside
|
|
82
82
|
* `emitGroundTruthStep` (buffer errors are swallowed). */
|
|
83
|
-
export declare function emitRawStep(step: RawStepContext | null, task: string, inferenceId: string, failureClass: string | null
|
|
83
|
+
export declare function emitRawStep(step: RawStepContext | null, task: string, inferenceId: string, failureClass: string | null, limitInputs?: {
|
|
84
|
+
readonly costUsd?: number;
|
|
85
|
+
readonly toolCalls?: readonly ToolCall[];
|
|
86
|
+
}): void;
|
|
84
87
|
/**
|
|
85
88
|
* Turn an `AdapterResult` into a RunResult, recording exactly one telemetry row.
|
|
86
89
|
* Shared by the buffered and streaming paths so cost/telemetry are byte-identical.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"raw-finalize.d.ts","sourceRoot":"","sources":["../../src/run/raw-finalize.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;
|
|
1
|
+
{"version":3,"file":"raw-finalize.d.ts","sourceRoot":"","sources":["../../src/run/raw-finalize.ts"],"names":[],"mappings":"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"}
|
package/dist/run/raw-finalize.js
CHANGED
|
@@ -15,6 +15,7 @@ exports.recordRawRow = recordRawRow;
|
|
|
15
15
|
exports.prepareRawStep = prepareRawStep;
|
|
16
16
|
exports.emitRawStep = emitRawStep;
|
|
17
17
|
exports.finalizeRaw = finalizeRaw;
|
|
18
|
+
const messages_1 = require("./messages");
|
|
18
19
|
const node_crypto_1 = require("node:crypto");
|
|
19
20
|
const cost_1 = require("./cost");
|
|
20
21
|
const raw_defaults_1 = require("./raw-defaults");
|
|
@@ -112,7 +113,7 @@ function prepareRawStep(deps, input) {
|
|
|
112
113
|
* ground-truth step — parity with the routed lane's record-attempt. No-op when
|
|
113
114
|
* no run context was captured at entry. Fail-open is handled inside
|
|
114
115
|
* `emitGroundTruthStep` (buffer errors are swallowed). */
|
|
115
|
-
function emitRawStep(step, task, inferenceId, failureClass) {
|
|
116
|
+
function emitRawStep(step, task, inferenceId, failureClass, limitInputs) {
|
|
116
117
|
if (step === null)
|
|
117
118
|
return;
|
|
118
119
|
step.runCtx.emitGroundTruthStep({
|
|
@@ -122,6 +123,8 @@ function emitRawStep(step, task, inferenceId, failureClass) {
|
|
|
122
123
|
startedAt: step.startedAtIso,
|
|
123
124
|
endedAt: new Date().toISOString(),
|
|
124
125
|
failureClass,
|
|
126
|
+
...(limitInputs?.costUsd !== undefined ? { costUsd: limitInputs.costUsd } : {}),
|
|
127
|
+
...(limitInputs?.toolCalls !== undefined ? { toolCalls: limitInputs.toolCalls.map((t) => ({ name: t.function.name, arguments: t.function.arguments })) } : {}),
|
|
125
128
|
});
|
|
126
129
|
}
|
|
127
130
|
/**
|
|
@@ -171,7 +174,7 @@ function finalizeRaw(deps, input, result, latencyMs, step) {
|
|
|
171
174
|
const declaredToolNames = input.tools?.map((t) => t.function.name);
|
|
172
175
|
const rawMeta = (usage) => (0, attempt_meta_1.summarizeAttemptMeta)({
|
|
173
176
|
...(result.meta !== undefined ? { adapterMeta: result.meta } : {}),
|
|
174
|
-
renderedRequest: (0, attempt_meta_1.renderRequestForHash)({ system: input.systemPrompt, user: input.userPrompt, schema }),
|
|
177
|
+
renderedRequest: (0, attempt_meta_1.renderRequestForHash)({ system: input.systemPrompt, user: (0, messages_1.userSideForHash)(input.userPrompt ?? "", input.messages), schema }),
|
|
175
178
|
outputText: result.ok ? result.text : null,
|
|
176
179
|
...(declaredToolNames !== undefined ? { declaredToolNames } : {}),
|
|
177
180
|
...(schemaSha !== undefined ? { schemaSha } : {}),
|
|
@@ -182,7 +185,7 @@ function finalizeRaw(deps, input, result, latencyMs, step) {
|
|
|
182
185
|
// G1: mirror this raw inference into a ground-truth step (parity with the
|
|
183
186
|
// routed lane's record-attempt). task = tags.step ?? provider/model (spec F1).
|
|
184
187
|
// Fail-open is handled inside emitGroundTruthStep (buffer errors are swallowed).
|
|
185
|
-
const emitStep = (inferenceId, failureClass) => emitRawStep(step, input.tags?.step ?? rawModelId, inferenceId, failureClass);
|
|
188
|
+
const emitStep = (inferenceId, failureClass, limitInputs) => emitRawStep(step, input.tags?.step ?? rawModelId, inferenceId, failureClass, limitInputs);
|
|
186
189
|
if (!result.ok) {
|
|
187
190
|
let costUsd = 0;
|
|
188
191
|
let pricingMissing = false;
|
|
@@ -208,7 +211,7 @@ function finalizeRaw(deps, input, result, latencyMs, step) {
|
|
|
208
211
|
meta: rawMeta(result.usage),
|
|
209
212
|
...(input.cache !== undefined ? { cacheKey: input.cache.key } : {}),
|
|
210
213
|
});
|
|
211
|
-
emitStep(failId, result.failure.class);
|
|
214
|
+
emitStep(failId, result.failure.class, { costUsd });
|
|
212
215
|
// R1: a raw-lane timeout is the #245 footgun — attach the tier-aware teaching
|
|
213
216
|
// hint (the customer is looking at THIS error). Any other failure surfaces
|
|
214
217
|
// unchanged. Overrides any adapter-set hint: the tier + budget is the fix.
|
|
@@ -245,7 +248,7 @@ function finalizeRaw(deps, input, result, latencyMs, step) {
|
|
|
245
248
|
meta: rawMeta(result.usage),
|
|
246
249
|
...(input.cache !== undefined ? { cacheKey: input.cache.key } : {}),
|
|
247
250
|
});
|
|
248
|
-
emitStep(id, schemaOutcome.ok ? null : "content_invalid");
|
|
251
|
+
emitStep(id, schemaOutcome.ok ? null : "content_invalid", { costUsd, toolCalls: toolCalls ?? [] });
|
|
249
252
|
if (!schemaOutcome.ok)
|
|
250
253
|
throw new failures_1.RigFailureError((0, raw_schema_1.rawSchemaMismatchFailure)(input, schemaOutcome.errors));
|
|
251
254
|
// NWS N3: a NATIVE-grounded success writes one extra, legible `rung:"search"`
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"raw-request.d.ts","sourceRoot":"","sources":["../../src/run/raw-request.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,KAAK,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACtE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,OAAO,CAAC;AAEzC;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,WAAW,GAAG,MAAM,CAE9D;AAED,4EAA4E;AAC5E,wBAAgB,eAAe,CAAC,SAAS,EAAE,YAAY,EAAE,KAAK,EAAE,WAAW,GAAG,cAAc,
|
|
1
|
+
{"version":3,"file":"raw-request.d.ts","sourceRoot":"","sources":["../../src/run/raw-request.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,KAAK,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACtE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,OAAO,CAAC;AAEzC;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,WAAW,GAAG,MAAM,CAE9D;AAED,4EAA4E;AAC5E,wBAAgB,eAAe,CAAC,SAAS,EAAE,YAAY,EAAE,KAAK,EAAE,WAAW,GAAG,cAAc,CA2C3F"}
|
package/dist/run/raw-request.js
CHANGED
|
@@ -39,7 +39,8 @@ function buildRawRequest(candidate, input) {
|
|
|
39
39
|
return {
|
|
40
40
|
candidate,
|
|
41
41
|
systemPrompt: input.systemPrompt,
|
|
42
|
-
userPrompt: input.userPrompt,
|
|
42
|
+
userPrompt: input.userPrompt ?? "", // ARC-β: optional only alongside `messages` (rawHistory guards)
|
|
43
|
+
...(input.messages !== undefined ? { history: input.messages } : {}), // ARC-β; absent ⇒ no key
|
|
43
44
|
// R2 (issue #6): a declared `responseSchema` rides the adapters' existing
|
|
44
45
|
// NATIVE schema path (outputSchema) — gemini responseJsonSchema, openai/grok
|
|
45
46
|
// strict json_schema, anthropic forced tool. Absent ⇒ null, byte-identical:
|
package/dist/run/raw.d.ts
CHANGED
|
@@ -16,9 +16,9 @@
|
|
|
16
16
|
* touches an adapter or the brand.
|
|
17
17
|
*
|
|
18
18
|
* KNOWN LIMITATIONS (flagged for the architect, not hidden):
|
|
19
|
-
* -
|
|
20
|
-
*
|
|
21
|
-
* (
|
|
19
|
+
* - ARC-β: a multi-turn transcript rides `messages` (→ `AdapterRequest.history`,
|
|
20
|
+
* mapped per provider); the single-turn `systemPrompt` + `userPrompt` path is
|
|
21
|
+
* unchanged (byte-identical, adapters/one-turn-parity.test.ts).
|
|
22
22
|
* - CE-6: the raw lane now forwards `temperature`/`topP` (via
|
|
23
23
|
* `AdapterRequest.sampling`) when the caller supplies them; absent, they are
|
|
24
24
|
* still dropped and the dispatch is byte-identical to pre-CE-6. `max_tokens`
|
|
@@ -52,6 +52,7 @@ import type { ProviderId, ReasoningParams, ServiceTier, ToolChoice, ToolDefiniti
|
|
|
52
52
|
import type { RunResult, RunTags } from "../types/route";
|
|
53
53
|
import type { RunStreamHandle } from "./stream";
|
|
54
54
|
import type { TelemetryStore } from "../types/telemetry";
|
|
55
|
+
import type { HistoryMessage } from "./messages";
|
|
55
56
|
export { assertRawKnobsSupported } from "./raw-knobs";
|
|
56
57
|
export { buildRawRequest } from "./raw-request";
|
|
57
58
|
/** Raw-lane request. `systemPrompt`/`userPrompt` are already flattened from the
|
|
@@ -65,7 +66,12 @@ export interface RawRunInput {
|
|
|
65
66
|
* in memory for this one call, then dropped (never logged, never stored). */
|
|
66
67
|
readonly apiKey: string;
|
|
67
68
|
readonly systemPrompt: string;
|
|
68
|
-
|
|
69
|
+
/** The single-turn user prompt. ARC-β: optional when `messages` is given (then a
|
|
70
|
+
* non-empty value is appended as a final user message); one of them is required. */
|
|
71
|
+
readonly userPrompt?: string;
|
|
72
|
+
/** ARC-β messages lane — the conversation (user / assistant+toolCalls / tool);
|
|
73
|
+
* `systemPrompt` stays the system prompt. Absent ⇒ byte-identical single turn. */
|
|
74
|
+
readonly messages?: readonly HistoryMessage[];
|
|
69
75
|
readonly tags?: RunTags;
|
|
70
76
|
/** G1 (spec §3): names the ground-truth step this raw call records inside an
|
|
71
77
|
* active `run.start` context — the SAME field the routed lane exposes, so a
|
package/dist/run/raw.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"raw.d.ts","sourceRoot":"","sources":["../../src/run/raw.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgDG;
|
|
1
|
+
{"version":3,"file":"raw.d.ts","sourceRoot":"","sources":["../../src/run/raw.ts"],"names":[],"mappings":"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"}
|
package/dist/run/raw.js
CHANGED
|
@@ -17,9 +17,9 @@
|
|
|
17
17
|
* touches an adapter or the brand.
|
|
18
18
|
*
|
|
19
19
|
* KNOWN LIMITATIONS (flagged for the architect, not hidden):
|
|
20
|
-
* -
|
|
21
|
-
*
|
|
22
|
-
* (
|
|
20
|
+
* - ARC-β: a multi-turn transcript rides `messages` (→ `AdapterRequest.history`,
|
|
21
|
+
* mapped per provider); the single-turn `systemPrompt` + `userPrompt` path is
|
|
22
|
+
* unchanged (byte-identical, adapters/one-turn-parity.test.ts).
|
|
23
23
|
* - CE-6: the raw lane now forwards `temperature`/`topP` (via
|
|
24
24
|
* `AdapterRequest.sampling`) when the caller supplies them; absent, they are
|
|
25
25
|
* still dropped and the dispatch is byte-identical to pre-CE-6. `max_tokens`
|
|
@@ -66,6 +66,8 @@ const resolve_1 = require("./resolve");
|
|
|
66
66
|
const raw_knobs_1 = require("./raw-knobs");
|
|
67
67
|
const raw_request_1 = require("./raw-request");
|
|
68
68
|
const raw_finalize_1 = require("./raw-finalize");
|
|
69
|
+
const run_context_1 = require("../artifacts/run-context"); // ARC-5
|
|
70
|
+
const messages_1 = require("./messages");
|
|
69
71
|
var raw_knobs_2 = require("./raw-knobs");
|
|
70
72
|
Object.defineProperty(exports, "assertRawKnobsSupported", { enumerable: true, get: function () { return raw_knobs_2.assertRawKnobsSupported; } });
|
|
71
73
|
var raw_request_2 = require("./raw-request");
|
|
@@ -110,9 +112,11 @@ async function runRaw(deps, input) {
|
|
|
110
112
|
// CE-2 F1/F3: fail closed on an unsupported declared directive BEFORE building
|
|
111
113
|
// an adapter — no meter, no provider call, no telemetry row.
|
|
112
114
|
(0, raw_knobs_1.assertRawKnobsSupported)(input);
|
|
115
|
+
(0, messages_1.rawHistory)(input); // ARC-β: validate messages / require a prompt, pre-dispatch
|
|
113
116
|
// G1: bind the ambient run context at ENTRY (ALS boundary) so a raw call inside
|
|
114
117
|
// a run.start records a ground-truth step (spec §4). Null ⇒ uninstrumented.
|
|
115
118
|
const step = (0, raw_finalize_1.prepareRawStep)(deps, input);
|
|
119
|
+
(0, run_context_1.enforceRunLimits)(); // ARC-5: pre-dispatch run-limit check (throws RunLimitExceeded; no-op without limits)
|
|
116
120
|
const adapter = buildRawAdapter(input.provider, input.apiKey);
|
|
117
121
|
const candidate = (0, resolve_1.mintRawCandidate)(input.provider, input.model);
|
|
118
122
|
const started = Date.now();
|
|
@@ -143,6 +147,7 @@ function runRawStream(deps, input) {
|
|
|
143
147
|
// boundary — NOT inside the async generator below (which defers to drain time,
|
|
144
148
|
// possibly on a different async context, where getActiveRunContext would miss).
|
|
145
149
|
const step = (0, raw_finalize_1.prepareRawStep)(deps, input);
|
|
150
|
+
const limitRefusal = (0, run_context_1.runLimitRefusal)(); // ARC-5: checked at the ALS entry, thrown through the handle below
|
|
146
151
|
let settle;
|
|
147
152
|
let fail;
|
|
148
153
|
const result = new Promise((res, rej) => {
|
|
@@ -158,6 +163,9 @@ function runRawStream(deps, input) {
|
|
|
158
163
|
// adapter/dispatch — surfaced through the handle (result rejects, the
|
|
159
164
|
// events iterator throws), so no meter, no provider call, no telemetry row.
|
|
160
165
|
(0, raw_knobs_1.assertRawKnobsSupported)(input);
|
|
166
|
+
(0, messages_1.rawHistory)(input); // ARC-β: validate messages / require a prompt, pre-dispatch
|
|
167
|
+
if (limitRefusal !== null)
|
|
168
|
+
throw limitRefusal; // ARC-5
|
|
161
169
|
const adapter = buildRawAdapter(input.provider, input.apiKey);
|
|
162
170
|
const candidate = (0, resolve_1.mintRawCandidate)(input.provider, input.model);
|
|
163
171
|
const req = (0, raw_request_1.buildRawRequest)(candidate, input);
|