cortad 0.4.2 → 0.4.3
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/lib/proof.mjs +13 -2
- package/lib/text.mjs +26 -4
- package/package.json +1 -1
package/lib/proof.mjs
CHANGED
|
@@ -766,9 +766,20 @@ function problemsIn(ex, reply, find, frame, printedLine) {
|
|
|
766
766
|
placed("leaked-markup", [...tags][0], `The reply carried markup from the prompt your app sent the model: ${listed(tags)}.`, written ?? call,
|
|
767
767
|
written ? "" : " None of it is written in your code; this is the line that made the model call.");
|
|
768
768
|
}
|
|
769
|
+
// A SLOT IS ONLY A SLOT WHERE A LINE OF THEIR CODE WRITES IT.
|
|
770
|
+
//
|
|
771
|
+
// Both branches now need `find(p)`. The first one asked only whether the same braces appear in the
|
|
772
|
+
// runtime prompt, which on a maths tutor they always do: LaTeX. `\text{kg}` in a reply and the same
|
|
773
|
+
// in the prompt reported an unfilled placeholder and addressed it to the line that made the model
|
|
774
|
+
// call, while holding the proof that no code of theirs writes the token. Measured on one customer's
|
|
775
|
+
// 64 replies: 17 of them carry a brace group this pattern matches, and `\begin{bmatrix}`,
|
|
776
|
+
// `\frac{dy}`, `{dx}` and `{T_y}` are not slots. Every `placeholder` problem this rule has ever
|
|
777
|
+
// recorded, 12 of them across six days, was that same false reading, and none was ever real.
|
|
769
778
|
for (const [p] of said.matchAll(PLACEHOLDER)) {
|
|
770
|
-
|
|
771
|
-
|
|
779
|
+
const written = find(p);
|
|
780
|
+
if (!written) continue;
|
|
781
|
+
if (prompt.includes(norm(p))) placed("placeholder", p, `The reply carried "${p}", a placeholder still unfilled in the prompt sent to the model.`, written);
|
|
782
|
+
else if (!words.includes(norm(p))) add("placeholder", `The reply carried "${p}", a placeholder your code never filled.`, written);
|
|
772
783
|
}
|
|
773
784
|
const stopped = endedOnTool(ex);
|
|
774
785
|
if (stopped) add("ended-on-tool", `The request ended right after ${stopped.join(", ")} ran: no model call came after it, so the reply holds no answer written from the tool's result.`, frame(generation(ex).at(-1)?.caller) ?? call);
|
package/lib/text.mjs
CHANGED
|
@@ -99,12 +99,23 @@ const runsSaid = (left, allowed) => (left > 0 ? `${num(left)} of ${plural(allowe
|
|
|
99
99
|
// After the findings: what was measured of the app's own promises, in its own words, then the checks
|
|
100
100
|
// measured and why the rest were not, then how many trials kept every promise. Counts, never a score.
|
|
101
101
|
// The replies that did not count sit before the count: a clean count beside 62 error replies read as a clean bill.
|
|
102
|
-
// Their own eval files, as the run played them: one line for all, one per file.
|
|
102
|
+
// Their own eval files, as the run played them: one line for all, one per file. `open` are questions
|
|
103
|
+
// a file asks with nothing expected of the reply, played and read against the app's own rules; they
|
|
104
|
+
// are said apart because the file made no claim to pass or fail. `left` is what never became a
|
|
105
|
+
// conversation, by the reason, so a count of what was taken never stands alone.
|
|
103
106
|
function evalLines(evals) {
|
|
104
107
|
const t = evals?.total;
|
|
105
|
-
if (!t || !t.played) return [];
|
|
106
|
-
const
|
|
107
|
-
|
|
108
|
+
if (!t || (!t.played && !t.open)) return [];
|
|
109
|
+
const open = (x) => (x.open ? `${x.played ? ", and " : ""}${num(x.open)} more ${x.open === 1 ? "question it asks" : "questions it asks"} with no answer of its own, read against your rules` : "");
|
|
110
|
+
const said = (x) => (x.played
|
|
111
|
+
? `${num(x.passed)} of ${plural(x.played, "case")} passed${x.failed ? `, ${num(x.failed)} failed` : ""}${x.unclear ? `, ${num(x.unclear)} not settled` : ""}${x.cases > x.played ? `, ${num(x.cases - x.played)} not played` : ""}${open(x)}`
|
|
112
|
+
: `${num(x.open)} ${x.open === 1 ? "question" : "questions"} played, read against your rules, since the ${x.open === 1 ? "file expects no answer of its own" : "files expect no answers of their own"}`);
|
|
113
|
+
const left = Object.entries(evals.left ?? {}).sort((a, b) => b[1] - a[1]);
|
|
114
|
+
return [
|
|
115
|
+
`Your own eval files: ${said(t)}.`,
|
|
116
|
+
...(evals.files ?? []).slice(0, 12).map((f) => ` ${f.file}: ${said(f)}.`),
|
|
117
|
+
...left.map(([why, n]) => ` ${num(n)} ${n === 1 ? "case" : "cases"} of yours was left out: ${why}.`.replace("cases of yours was", "cases of yours were")),
|
|
118
|
+
];
|
|
108
119
|
}
|
|
109
120
|
|
|
110
121
|
function measuredLines(d) {
|
|
@@ -118,6 +129,10 @@ function measuredLines(d) {
|
|
|
118
129
|
if (!d.failedReplies && d.replies?.crashed > 0) lines.push(`${num(d.replies.crashed)} of ${plural(d.replies.total, "reply", "replies")} were failures and are not counted.`);
|
|
119
130
|
// The server's own sentence already says the count.
|
|
120
131
|
if (d.promises && !d.promises.said && has(d.promises.decided)) lines.push(promiseLine(d));
|
|
132
|
+
// What the run decided, and what nothing breaking is worth on that many. The server writes the
|
|
133
|
+
// sentence (simulate/report-pack.ts sensitivityOf) because the arithmetic belongs beside the
|
|
134
|
+
// numbers it is made of; printing it here is what stops a count standing with no denominator.
|
|
135
|
+
if (d.sensitivity?.said) lines.push(clip(d.sensitivity.said, 400));
|
|
121
136
|
return lines;
|
|
122
137
|
}
|
|
123
138
|
|
|
@@ -440,6 +455,13 @@ function findingBlock(f, unseen = false) {
|
|
|
440
455
|
const lines = [`${f.id} ${f.asks}`, shareLine(f)].filter(Boolean);
|
|
441
456
|
if (f.sentAway?.said) lines.push(` Of them, ${f.sentAway.said}.`);
|
|
442
457
|
if (f.baseline) lines.push(` Played again: ${plural(f.baseline.trials, "new conversation")} with the same ask in other words; it failed again in ${num(f.baseline.failed)}.`);
|
|
458
|
+
// The same conversation, played again from nothing. A failure that did not come back is one roll of
|
|
459
|
+
// the app's dice, and it was listed beside one that came back twice with nothing to tell them apart.
|
|
460
|
+
if (f.cameBack) {
|
|
461
|
+
lines.push(f.cameBack.again === 0
|
|
462
|
+
? ` Played over again: ${plural(f.cameBack.played, "conversation")} of its own, word for word from nothing, and it did not fail again in any of them. Worth less of your time than one that came back.`
|
|
463
|
+
: ` Played over again: ${plural(f.cameBack.played, "conversation")} of its own, word for word from nothing, and it failed again in ${num(f.cameBack.again)}.`);
|
|
464
|
+
}
|
|
443
465
|
// Chased because production broke it first: real people met this one.
|
|
444
466
|
if (f.production) lines.push(` In production: ${plural(f.production, "conversation")} with real users broke this in the last 30 days.`);
|
|
445
467
|
const logs = { shown: new Set(), room: 30 };
|
package/package.json
CHANGED