@parcha/agentrun-pi 0.1.0-beta.2 → 0.1.0-beta.4
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/README.md +46 -19
- package/dist/cli.js +5 -3
- package/dist/demo.js +1 -0
- package/dist/extension-service.js +11 -4
- package/dist/extension.js +329 -57
- package/dist/host-addendum.d.ts +4 -0
- package/dist/host-addendum.js +19 -0
- package/dist/index.d.ts +1 -2
- package/dist/index.js +1 -2
- package/dist/presentation.js +2 -2
- package/dist/skills/author/SKILL.md +135 -0
- package/{skills → dist/skills}/author/references/jev-decisions.md +9 -8
- package/dist/skills/author/references/language.md +130 -0
- package/dist/skills/author/references/sdk-authoring.md +9 -0
- package/{skills → dist/skills}/author/references/workflow-format.md +7 -15
- package/dist/triage-demo.d.ts +62 -0
- package/dist/triage-demo.js +153 -0
- package/dist/types.d.ts +3 -0
- package/dist/workflow-inspector.d.ts +47 -0
- package/dist/workflow-inspector.js +235 -0
- package/dist/workflow-session.d.ts +19 -0
- package/dist/workflow-session.js +167 -0
- package/dist/workflow-store.d.ts +30 -0
- package/dist/workflow-store.js +249 -0
- package/dist/workflow-view.d.ts +64 -0
- package/dist/workflow-view.js +268 -0
- package/package.json +5 -6
- package/dist/admission.d.ts +0 -6
- package/dist/admission.js +0 -51
- package/dist/author.d.ts +0 -21
- package/dist/author.js +0 -72
- package/dist/benchmark-contract.d.ts +0 -88
- package/dist/benchmark-contract.js +0 -50
- package/dist/benchmark-freeze.d.ts +0 -9
- package/dist/benchmark-freeze.js +0 -9
- package/dist/benchmark.d.ts +0 -127
- package/dist/benchmark.js +0 -260
- package/dist/comparison.d.ts +0 -69
- package/dist/comparison.js +0 -229
- package/dist/skill-bundle.d.ts +0 -5
- package/dist/skill-bundle.js +0 -15
- package/skills/author/SKILL.md +0 -102
- package/skills/author/references/sdk-authoring.md +0 -16
- /package/{skills → dist/skills}/author/examples/evidence-gate.input.json +0 -0
- /package/{skills → dist/skills}/author/examples/evidence-gate.json +0 -0
- /package/{skills → dist/skills}/author/examples/read-file.input.json +0 -0
- /package/{skills → dist/skills}/author/examples/read-file.json +0 -0
- /package/{skills → dist/skills}/author/examples/read-source-decision.input.json +0 -0
- /package/{skills → dist/skills}/author/examples/read-source-decision.json +0 -0
- /package/{skills → dist/skills}/author/examples/release-note.txt +0 -0
- /package/{skills → dist/skills}/author/examples/review-release-notes.input.json +0 -0
- /package/{skills → dist/skills}/author/examples/review-release-notes.json +0 -0
package/dist/admission.js
DELETED
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
export function workflowPolicyErrors(candidate, options) {
|
|
2
|
-
const errors = [];
|
|
3
|
-
const sections = Object.keys(options.rubricSections ?? {});
|
|
4
|
-
const record = (value) => value !== null && typeof value === "object" && !Array.isArray(value) ? value : undefined;
|
|
5
|
-
const visit = (value) => {
|
|
6
|
-
const node = record(value);
|
|
7
|
-
if (!node)
|
|
8
|
-
return;
|
|
9
|
-
const kind = node.node;
|
|
10
|
-
if (!options.allowExecutableCandidates && ["code", "call", "artifact"].includes(String(kind)) && !(kind === "call" && node.via === "tool" && typeof node.tool === "string" && options.allowedEffectTools?.includes(node.tool)))
|
|
11
|
-
errors.push(`${kind} requires allowExecutableCandidates`);
|
|
12
|
-
if (sections.length && ["judge", "pick", "sift", "route"].includes(String(kind)))
|
|
13
|
-
errors.push(`${String(kind)} cannot carry supplied SOP sections; use an LLM node with sopSection`);
|
|
14
|
-
const llm = ["agent", "decide", "extract", "report"].includes(String(kind));
|
|
15
|
-
const predicate = kind === "loop" ? record(node.until) : kind === "escalate" ? record(node.when) : undefined;
|
|
16
|
-
if (sections.length && (predicate?.predicate === "ask" || (llm && node.verify !== undefined)))
|
|
17
|
-
errors.push("Semantic predicates and verify clauses require a separately reviewed question contract when rubric sections are supplied");
|
|
18
|
-
const proseArtifact = kind === "artifact" && (node.type === "markdown" || node.type === "report");
|
|
19
|
-
if (llm || proseArtifact) {
|
|
20
|
-
const included = Array.isArray(node.sopSection) ? node.sopSection : [node.sopSection];
|
|
21
|
-
for (const section of sections)
|
|
22
|
-
if (!included.includes(section))
|
|
23
|
-
errors.push(`${String(node.label)} must include rubric section ${section}`);
|
|
24
|
-
}
|
|
25
|
-
switch (kind) {
|
|
26
|
-
case "chain":
|
|
27
|
-
if (Array.isArray(node.steps))
|
|
28
|
-
node.steps.forEach(visit);
|
|
29
|
-
break;
|
|
30
|
-
case "parallel":
|
|
31
|
-
if (Array.isArray(node.branches))
|
|
32
|
-
node.branches.forEach(visit);
|
|
33
|
-
break;
|
|
34
|
-
case "map":
|
|
35
|
-
case "loop":
|
|
36
|
-
visit(node.body);
|
|
37
|
-
break;
|
|
38
|
-
case "route": {
|
|
39
|
-
const branches = record(node.branches);
|
|
40
|
-
if (branches)
|
|
41
|
-
Object.values(branches).forEach(branch => visit(record(branch)?.body));
|
|
42
|
-
break;
|
|
43
|
-
}
|
|
44
|
-
case "workflow":
|
|
45
|
-
visit(record(node.workflow)?.root);
|
|
46
|
-
break;
|
|
47
|
-
}
|
|
48
|
-
};
|
|
49
|
-
visit(record(candidate)?.root);
|
|
50
|
-
return errors;
|
|
51
|
-
}
|
package/dist/author.d.ts
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import { type Workflow } from "@parcha/agentrun-dsl";
|
|
2
|
-
import { type PiRunnerOptions } from "./runner.js";
|
|
3
|
-
export declare const AUTHOR_CONTRACT = "You author AgentRun DSL v2 workflows as JSON data.\nDeliver with the submit tool, never prose or a filename. Make the smallest workflow satisfying the request.\nThe document has v:2, name, schemas (JSON Schema catalog), optional input:{schemaId}, output:{schemaId,path?}, root.\nEvery referenced schema must exist. Each LLM node has node:agent|decide|extract, label, instructions, out (schema id), as (state key), optional requires/tools/sopSection.\nA chain has node:chain, steps:[nodes]. A map has label, itemsPath, body, as, maxConcurrency. A loop has label, body, until, maxIters.\nA judge has label,state,out,as. Judge schemas use flat boolean/enum/integer properties; each property's description states its question. A route has label,state,instructions,branches:{name:{criteria,body}}, optional unsure:{branch,gte} and as.\nLLM instructions are literal text, never interpolated. The node receives workflow state as a JSON user message; refer to its keys in ordinary prose and declare required keys in requires. Use {stateKey} placeholders only in interpolated fields such as judge.state and call.args; whole placeholders preserve their value's type. Output.path selects a final state value validated by output.schemaId.\nOnly use tools named by the host. Never invent a tool, capability, evidence source or successful test.\nKeep the full rubric in its original source text. Every LLM node's sopSection must include ALL supplied rubric section names. Merged judgments inherit their parents' union. Do not paraphrase policy into instructions. If rubric sections are supplied, use LLM nodes for judgment; Jev nodes do not carry sopSection and require a separately reviewed question contract.\nKeep independent acceptance checks host-owned; never change their implementation, fixtures or thresholds to pass. A valid candidate is not automatically activated.\nCode, shell/effect calls and artifact delivery require explicit host authorization. Bound loops and parallelism; uncertainty should escalate or select an explicit fallback.\nExample: {\"v\":2,\"name\":\"summarize\",\"schemas\":{\"Result\":{\"type\":\"object\",\"properties\":{\"summary\":{\"type\":\"string\"}},\"required\":[\"summary\"],\"additionalProperties\":false}},\"output\":{\"schemaId\":\"Result\",\"path\":\"result\"},\"root\":{\"node\":\"extract\",\"label\":\"summarize\",\"instructions\":\"Summarize the text field from the JSON input faithfully.\",\"out\":\"Result\",\"as\":\"result\",\"requires\":[\"text\"]}}";
|
|
4
|
-
export interface AuthorWorkflowOptions {
|
|
5
|
-
request: string;
|
|
6
|
-
outputDir: string;
|
|
7
|
-
pi: PiRunnerOptions;
|
|
8
|
-
inputKeys?: string[];
|
|
9
|
-
rubricSections?: Record<string, string>;
|
|
10
|
-
allowExecutableCandidates?: boolean;
|
|
11
|
-
maxCandidates?: number;
|
|
12
|
-
acceptance?: (candidate: Workflow) => Promise<string[]> | string[];
|
|
13
|
-
}
|
|
14
|
-
export interface AuthoredWorkflow {
|
|
15
|
-
workflow: Workflow;
|
|
16
|
-
path: string;
|
|
17
|
-
directory: string;
|
|
18
|
-
candidates: number;
|
|
19
|
-
checks: "structural" | "structural-and-host";
|
|
20
|
-
}
|
|
21
|
-
export declare function authorWorkflow(options: AuthorWorkflowOptions): Promise<AuthoredWorkflow>;
|
package/dist/author.js
DELETED
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
import { workflowPolicyErrors } from "./admission.js";
|
|
2
|
-
import { randomUUID } from "node:crypto";
|
|
3
|
-
import { mkdir, writeFile } from "node:fs/promises";
|
|
4
|
-
import { resolve, join } from "node:path";
|
|
5
|
-
import { validateWorkflow } from "@parcha/agentrun-dsl";
|
|
6
|
-
import { createPiRunner } from "./runner.js";
|
|
7
|
-
export const AUTHOR_CONTRACT = `You author AgentRun DSL v2 workflows as JSON data.
|
|
8
|
-
Deliver with the submit tool, never prose or a filename. Make the smallest workflow satisfying the request.
|
|
9
|
-
The document has v:2, name, schemas (JSON Schema catalog), optional input:{schemaId}, output:{schemaId,path?}, root.
|
|
10
|
-
Every referenced schema must exist. Each LLM node has node:agent|decide|extract, label, instructions, out (schema id), as (state key), optional requires/tools/sopSection.
|
|
11
|
-
A chain has node:chain, steps:[nodes]. A map has label, itemsPath, body, as, maxConcurrency. A loop has label, body, until, maxIters.
|
|
12
|
-
A judge has label,state,out,as. Judge schemas use flat boolean/enum/integer properties; each property's description states its question. A route has label,state,instructions,branches:{name:{criteria,body}}, optional unsure:{branch,gte} and as.
|
|
13
|
-
LLM instructions are literal text, never interpolated. The node receives workflow state as a JSON user message; refer to its keys in ordinary prose and declare required keys in requires. Use {stateKey} placeholders only in interpolated fields such as judge.state and call.args; whole placeholders preserve their value's type. Output.path selects a final state value validated by output.schemaId.
|
|
14
|
-
Only use tools named by the host. Never invent a tool, capability, evidence source or successful test.
|
|
15
|
-
Keep the full rubric in its original source text. Every LLM node's sopSection must include ALL supplied rubric section names. Merged judgments inherit their parents' union. Do not paraphrase policy into instructions. If rubric sections are supplied, use LLM nodes for judgment; Jev nodes do not carry sopSection and require a separately reviewed question contract.
|
|
16
|
-
Keep independent acceptance checks host-owned; never change their implementation, fixtures or thresholds to pass. A valid candidate is not automatically activated.
|
|
17
|
-
Code, shell/effect calls and artifact delivery require explicit host authorization. Bound loops and parallelism; uncertainty should escalate or select an explicit fallback.
|
|
18
|
-
Example: {"v":2,"name":"summarize","schemas":{"Result":{"type":"object","properties":{"summary":{"type":"string"}},"required":["summary"],"additionalProperties":false}},"output":{"schemaId":"Result","path":"result"},"root":{"node":"extract","label":"summarize","instructions":"Summarize the text field from the JSON input faithfully.","out":"Result","as":"result","requires":["text"]}}`;
|
|
19
|
-
export async function authorWorkflow(options) {
|
|
20
|
-
if (!options.request.trim())
|
|
21
|
-
throw new Error("Workflow request is required");
|
|
22
|
-
const maxCandidates = options.maxCandidates ?? 4;
|
|
23
|
-
if (!Number.isSafeInteger(maxCandidates) || maxCandidates < 1)
|
|
24
|
-
throw new Error("maxCandidates must be a positive integer");
|
|
25
|
-
const directory = resolve(options.outputDir, `candidate-${randomUUID()}`);
|
|
26
|
-
await mkdir(directory, { recursive: true });
|
|
27
|
-
await writeFile(join(directory, "request.json"), JSON.stringify({ request: options.request, inputKeys: options.inputKeys ?? [], rubricSections: options.rubricSections ?? {}, allowExecutableCandidates: options.allowExecutableCandidates ?? false, maxCandidates }, null, 2), { flag: "wx" });
|
|
28
|
-
let candidates = 0;
|
|
29
|
-
let acceptedPath;
|
|
30
|
-
const runner = createPiRunner({ ...options.pi, tools: [], maxSubmissions: maxCandidates });
|
|
31
|
-
try {
|
|
32
|
-
const workflow = await runner({
|
|
33
|
-
kind: "agent", label: "author-workflow", tools: [],
|
|
34
|
-
system: [AUTHOR_CONTRACT, `Executable candidates authorized: ${options.allowExecutableCandidates ?? false}.`, `All supplied rubric sections (authoritative): ${JSON.stringify(options.rubricSections ?? {})}`],
|
|
35
|
-
user: `${options.request}\nAvailable input keys: ${JSON.stringify(options.inputKeys ?? [])}`,
|
|
36
|
-
schema: { type: "object", additionalProperties: true },
|
|
37
|
-
async review(value) {
|
|
38
|
-
candidates++;
|
|
39
|
-
if (candidates > maxCandidates)
|
|
40
|
-
throw new Error("Candidate limit exceeded");
|
|
41
|
-
const candidatePath = join(directory, `${String(candidates).padStart(3, "0")}.json`);
|
|
42
|
-
await writeFile(candidatePath, JSON.stringify(value, null, 2), { flag: "wx" });
|
|
43
|
-
const errors = workflowPolicyErrors(value, options);
|
|
44
|
-
if (!errors.length) {
|
|
45
|
-
try {
|
|
46
|
-
const verdict = validateWorkflow(value, { inputKeys: options.inputKeys });
|
|
47
|
-
if (!verdict.ok)
|
|
48
|
-
errors.push(...verdict.errors);
|
|
49
|
-
}
|
|
50
|
-
catch (error) {
|
|
51
|
-
errors.push(`Invalid workflow: ${error instanceof Error ? error.message : String(error)}`);
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
if (!errors.length && options.acceptance)
|
|
55
|
-
errors.push(...await options.acceptance(structuredClone(value)));
|
|
56
|
-
await writeFile(join(directory, `${String(candidates).padStart(3, "0")}.review.json`), JSON.stringify({ accepted: !errors.length, checks: options.acceptance ? "structural-and-host" : "structural", errors }, null, 2), { flag: "wx" });
|
|
57
|
-
if (errors.length)
|
|
58
|
-
return { accepted: false, message: errors.join("\n") };
|
|
59
|
-
acceptedPath = candidatePath;
|
|
60
|
-
return { accepted: true };
|
|
61
|
-
},
|
|
62
|
-
});
|
|
63
|
-
if (!acceptedPath)
|
|
64
|
-
throw new Error("Author returned without a retained accepted candidate");
|
|
65
|
-
await writeFile(join(directory, "result.json"), JSON.stringify({ status: "candidate", path: acceptedPath, candidates }, null, 2), { flag: "wx" });
|
|
66
|
-
return { workflow, path: acceptedPath, directory, candidates, checks: options.acceptance ? "structural-and-host" : "structural" };
|
|
67
|
-
}
|
|
68
|
-
catch (error) {
|
|
69
|
-
await writeFile(join(directory, "result.json"), JSON.stringify({ status: "failed", candidates, message: error instanceof Error ? error.message : String(error) }, null, 2), { flag: "wx" });
|
|
70
|
-
throw error;
|
|
71
|
-
}
|
|
72
|
-
}
|
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
export type BenchmarkSplit = 'development' | 'heldout';
|
|
2
|
-
export type BenchmarkFinding = {
|
|
3
|
-
topicId: string;
|
|
4
|
-
status: 'supported' | 'conflicting' | 'insufficient';
|
|
5
|
-
summary: string;
|
|
6
|
-
sourceIds: string[];
|
|
7
|
-
};
|
|
8
|
-
export type BenchmarkCase = {
|
|
9
|
-
id: string;
|
|
10
|
-
split: BenchmarkSplit;
|
|
11
|
-
question: string;
|
|
12
|
-
topics: {
|
|
13
|
-
id: string;
|
|
14
|
-
question: string;
|
|
15
|
-
}[];
|
|
16
|
-
sources: {
|
|
17
|
-
id: string;
|
|
18
|
-
text: string;
|
|
19
|
-
}[];
|
|
20
|
-
expected: Omit<BenchmarkFinding, 'summary'>[];
|
|
21
|
-
};
|
|
22
|
-
export declare const benchmarkHash: (value: unknown) => string;
|
|
23
|
-
export declare const benchmarkInstructions = "Answer using only the fictional source corpus. Produce exactly one finding for each supplied topic. Status supported means concrete evidence establishes a finding, including a negative or qualified answer. Conflicting means incompatible accounts. Insufficient means no concrete information answers the question. Preserve conditions and contrary evidence; cite every material source by ID. Do not invent facts or citations. Submit the final structured report.";
|
|
24
|
-
export declare const benchmarkReportSchema: {
|
|
25
|
-
type: string;
|
|
26
|
-
additionalProperties: boolean;
|
|
27
|
-
required: string[];
|
|
28
|
-
properties: {
|
|
29
|
-
findings: {
|
|
30
|
-
type: string;
|
|
31
|
-
minItems: number;
|
|
32
|
-
items: {
|
|
33
|
-
type: string;
|
|
34
|
-
additionalProperties: boolean;
|
|
35
|
-
required: string[];
|
|
36
|
-
properties: {
|
|
37
|
-
topicId: {
|
|
38
|
-
type: string;
|
|
39
|
-
minLength: number;
|
|
40
|
-
};
|
|
41
|
-
status: {
|
|
42
|
-
enum: string[];
|
|
43
|
-
};
|
|
44
|
-
summary: {
|
|
45
|
-
type: string;
|
|
46
|
-
minLength: number;
|
|
47
|
-
};
|
|
48
|
-
sourceIds: {
|
|
49
|
-
type: string;
|
|
50
|
-
uniqueItems: boolean;
|
|
51
|
-
items: {
|
|
52
|
-
type: string;
|
|
53
|
-
minLength: number;
|
|
54
|
-
};
|
|
55
|
-
};
|
|
56
|
-
};
|
|
57
|
-
};
|
|
58
|
-
};
|
|
59
|
-
};
|
|
60
|
-
};
|
|
61
|
-
export declare const benchmarkEvidenceSchema: {
|
|
62
|
-
type: string;
|
|
63
|
-
additionalProperties: boolean;
|
|
64
|
-
required: string[];
|
|
65
|
-
properties: {
|
|
66
|
-
relevant: {
|
|
67
|
-
type: string;
|
|
68
|
-
description: string;
|
|
69
|
-
criteria: {
|
|
70
|
-
true: string;
|
|
71
|
-
false: string;
|
|
72
|
-
};
|
|
73
|
-
};
|
|
74
|
-
};
|
|
75
|
-
};
|
|
76
|
-
export declare function benchmarkCases(split: BenchmarkSplit): BenchmarkCase[];
|
|
77
|
-
/** Frozen independent checks. Never return these results to an authoring agent. */
|
|
78
|
-
export declare function evaluatePiBenchmark(caseId: string, output: unknown): {
|
|
79
|
-
passed: boolean;
|
|
80
|
-
issues: string[];
|
|
81
|
-
};
|
|
82
|
-
export declare function benchmarkContract(): {
|
|
83
|
-
version: number;
|
|
84
|
-
fixturesSha256: string;
|
|
85
|
-
verifierSha256: string;
|
|
86
|
-
outputSchemaSha256: string;
|
|
87
|
-
instructionsSha256: string;
|
|
88
|
-
};
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
import { createHash } from 'node:crypto';
|
|
2
|
-
import { readFileSync } from 'node:fs';
|
|
3
|
-
import { Compile } from 'typebox/compile';
|
|
4
|
-
const cases = [
|
|
5
|
-
{ id: 'docs-editing', split: 'development', question: 'Can contributors edit our docs in the browser?', topics: [{ id: 'editing', question: 'Did contributors edit a page in the browser during our trial?' }], sources: [{ id: 'd1', text: 'All three contributors edited a page in the browser during the trial.' }, { id: 'd2', text: 'The documentation product has a new logo.' }], expected: [{ topicId: 'editing', status: 'supported', sourceIds: ['d1'] }] },
|
|
6
|
-
{ id: 'migration', split: 'development', question: 'What changes with a repository-based wiki?', topics: [{ id: 'review', question: 'How are changes reviewed?' }, { id: 'editing', question: 'Could non-developers edit documentation?' }, { id: 'search', question: 'What did the search trial find?' }], sources: [{ id: 'm1', text: 'Documentation changes require an assigned reviewer to approve the pull request.' }, { id: 'm2', text: 'Non-developers edited in the browser but two needed help with merge conflicts.' }, { id: 'm3', text: 'Search found exact words but missed synonyms during our trial.' }, { id: 'm4', text: 'A revolutionary documentation experience.' }], expected: [{ topicId: 'review', status: 'supported', sourceIds: ['m1'] }, { topicId: 'editing', status: 'supported', sourceIds: ['m2'] }, { topicId: 'search', status: 'supported', sourceIds: ['m3'] }] },
|
|
7
|
-
{ id: 'missing-conflicting', split: 'development', question: 'Can we rely on restricted editing and synonym search?', topics: [{ id: 'permissions', question: 'Who can edit restricted pages?' }, { id: 'search', question: 'Did synonym search work in this trial?' }], sources: [{ id: 'c1', text: 'Restricted-page permissions: details pending.' }, { id: 'c2', text: 'The trial report says all ten synonym searches found their target pages.' }, { id: 'c3', text: 'An audit of the same trial says none of those ten searches found their target pages.' }], expected: [{ topicId: 'permissions', status: 'insufficient', sourceIds: [] }, { topicId: 'search', status: 'conflicting', sourceIds: ['c2', 'c3'] }] },
|
|
8
|
-
{ id: 'backup', split: 'heldout', question: 'Did the restore drill recover the archive?', topics: [{ id: 'restore', question: 'Was the archive recovered during the drill?' }], sources: [{ id: 'h1', text: 'The drill restored the archive and every file matched its recorded checksum.' }, { id: 'h2', text: 'Backups that give you peace of mind.' }], expected: [{ topicId: 'restore', status: 'supported', sourceIds: ['h1'] }] },
|
|
9
|
-
{ id: 'queue-adoption', split: 'heldout', question: 'What should we know before adopting this task queue?', topics: [{ id: 'retries', question: 'What happens to jobs after worker failure?' }, { id: 'ordering', question: 'Is task order guaranteed?' }, { id: 'visibility', question: 'Can operators inspect stuck jobs?' }], sources: [{ id: 'q1', text: 'In the failure test, an unfinished job returned to the queue when its lease expired.' }, { id: 'q2', text: 'Tasks from different workers completed out of submission order in the trial.' }, { id: 'q3', text: 'Operators used the dashboard to inspect job age and the last worker error.' }, { id: 'q4', text: 'Supercharge your asynchronous future.' }], expected: [{ topicId: 'retries', status: 'supported', sourceIds: ['q1'] }, { topicId: 'ordering', status: 'supported', sourceIds: ['q2'] }, { topicId: 'visibility', status: 'supported', sourceIds: ['q3'] }] },
|
|
10
|
-
{ id: 'cache-evidence', split: 'heldout', question: 'What do the cache trials tell us about deletion and encryption?', topics: [{ id: 'deletion', question: 'Did deleted entries disappear immediately in the trial?' }, { id: 'encryption', question: 'Which encryption protects cached entries?' }], sources: [{ id: 'k1', text: 'The trial log says every deleted entry disappeared immediately.' }, { id: 'k2', text: 'A second observer of that same trial recorded deleted entries remaining readable for a full minute.' }, { id: 'k3', text: 'Encryption: documentation forthcoming.' }], expected: [{ topicId: 'deletion', status: 'conflicting', sourceIds: ['k1', 'k2'] }, { topicId: 'encryption', status: 'insufficient', sourceIds: [] }] },
|
|
11
|
-
];
|
|
12
|
-
export const benchmarkHash = (value) => createHash('sha256').update(JSON.stringify(value)).digest('hex');
|
|
13
|
-
export const benchmarkInstructions = 'Answer using only the fictional source corpus. Produce exactly one finding for each supplied topic. Status supported means concrete evidence establishes a finding, including a negative or qualified answer. Conflicting means incompatible accounts. Insufficient means no concrete information answers the question. Preserve conditions and contrary evidence; cite every material source by ID. Do not invent facts or citations. Submit the final structured report.';
|
|
14
|
-
const str = { type: 'string', minLength: 1 };
|
|
15
|
-
export const benchmarkReportSchema = { type: 'object', additionalProperties: false, required: ['findings'], properties: { findings: { type: 'array', minItems: 1, items: { type: 'object', additionalProperties: false, required: ['topicId', 'status', 'summary', 'sourceIds'], properties: { topicId: str, status: { enum: ['supported', 'conflicting', 'insufficient'] }, summary: str, sourceIds: { type: 'array', uniqueItems: true, items: str } } } } } };
|
|
16
|
-
export const benchmarkEvidenceSchema = { type: 'object', additionalProperties: false, required: ['relevant'], properties: { relevant: { type: 'boolean', description: 'Does the passage contain a concrete fact, condition, limitation or contrary evidence bearing on the question?', criteria: { true: 'Concrete evidence relevant to the question, including qualified or contradictory answers.', false: 'Only topic mentions, marketing promises, missing details, or unrelated information.' } } } };
|
|
17
|
-
const validator = Compile(benchmarkReportSchema);
|
|
18
|
-
export function benchmarkCases(split) {
|
|
19
|
-
if (split !== 'development' && split !== 'heldout')
|
|
20
|
-
throw new Error('Choose development or heldout explicitly');
|
|
21
|
-
return structuredClone(cases.filter(row => row.split === split));
|
|
22
|
-
}
|
|
23
|
-
/** Frozen independent checks. Never return these results to an authoring agent. */
|
|
24
|
-
export function evaluatePiBenchmark(caseId, output) {
|
|
25
|
-
const item = cases.find(row => row.id === caseId);
|
|
26
|
-
if (!item)
|
|
27
|
-
throw new Error('Unknown benchmark case');
|
|
28
|
-
if (!validator.Check(output))
|
|
29
|
-
return { passed: false, issues: ['schema'] };
|
|
30
|
-
const findings = output.findings;
|
|
31
|
-
const issues = [];
|
|
32
|
-
if (findings.length !== item.expected.length || new Set(findings.map(row => row.topicId)).size !== findings.length)
|
|
33
|
-
issues.push('coverage');
|
|
34
|
-
for (const expected of item.expected) {
|
|
35
|
-
const actual = findings.find(row => row.topicId === expected.topicId);
|
|
36
|
-
if (actual?.status !== expected.status)
|
|
37
|
-
issues.push(`${expected.topicId}:status`);
|
|
38
|
-
if (!actual || JSON.stringify([...actual.sourceIds].sort()) !== JSON.stringify([...expected.sourceIds].sort()))
|
|
39
|
-
issues.push(`${expected.topicId}:citations`);
|
|
40
|
-
}
|
|
41
|
-
return { passed: issues.length === 0, issues };
|
|
42
|
-
}
|
|
43
|
-
export function benchmarkContract() {
|
|
44
|
-
return {
|
|
45
|
-
version: 1, fixturesSha256: benchmarkHash(cases),
|
|
46
|
-
verifierSha256: createHash('sha256').update(readFileSync(new URL('./benchmark-contract.js', import.meta.url))).digest('hex'),
|
|
47
|
-
outputSchemaSha256: benchmarkHash(benchmarkReportSchema),
|
|
48
|
-
instructionsSha256: benchmarkHash(benchmarkInstructions),
|
|
49
|
-
};
|
|
50
|
-
}
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
/** Frozen before any live sample. Changing these pins requires a new benchmark
|
|
2
|
-
* version and a fresh baseline; never update them to improve a skill's score. */
|
|
3
|
-
export declare const frozenBenchmarkContract: {
|
|
4
|
-
readonly version: 1;
|
|
5
|
-
readonly fixturesSha256: "1a8f9ff022d5cdaf2455e327e59ae4593aeffcc8ae4c510a870b36ea0d9e7de2";
|
|
6
|
-
readonly verifierSha256: "f21d0c8b790186f571003907a5a6c7a6360423d77d1125c4398994cb1ae11f9a";
|
|
7
|
-
readonly outputSchemaSha256: "38a0d7ccd8590ad8a486a5b7b7edd36c0b8bd32e642ead5345a3f93d12f3e574";
|
|
8
|
-
readonly instructionsSha256: "d8d3fc6721ed99bb163ff3dbd93dffc15a1a92ab400f11f781bb25e9f8a61535";
|
|
9
|
-
};
|
package/dist/benchmark-freeze.js
DELETED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
/** Frozen before any live sample. Changing these pins requires a new benchmark
|
|
2
|
-
* version and a fresh baseline; never update them to improve a skill's score. */
|
|
3
|
-
export const frozenBenchmarkContract = {
|
|
4
|
-
version: 1,
|
|
5
|
-
fixturesSha256: '1a8f9ff022d5cdaf2455e327e59ae4593aeffcc8ae4c510a870b36ea0d9e7de2',
|
|
6
|
-
verifierSha256: 'f21d0c8b790186f571003907a5a6c7a6360423d77d1125c4398994cb1ae11f9a',
|
|
7
|
-
outputSchemaSha256: '38a0d7ccd8590ad8a486a5b7b7edd36c0b8bd32e642ead5345a3f93d12f3e574',
|
|
8
|
-
instructionsSha256: 'd8d3fc6721ed99bb163ff3dbd93dffc15a1a92ab400f11f781bb25e9f8a61535',
|
|
9
|
-
};
|
package/dist/benchmark.d.ts
DELETED
|
@@ -1,127 +0,0 @@
|
|
|
1
|
-
import { type WorkflowDeps } from '@parcha/agentrun-dsl';
|
|
2
|
-
import type { PiHostContext } from './types.js';
|
|
3
|
-
import { evaluatePiBenchmark, type BenchmarkSplit } from './benchmark-contract.js';
|
|
4
|
-
export interface PiBenchmarkBudgets {
|
|
5
|
-
timeoutMs: number;
|
|
6
|
-
maxModelRequests: number;
|
|
7
|
-
maxToolCalls: number;
|
|
8
|
-
maxTurns: number;
|
|
9
|
-
maxSubmissions: number;
|
|
10
|
-
}
|
|
11
|
-
export interface PiSkillBenchmarkOptions {
|
|
12
|
-
host: PiHostContext;
|
|
13
|
-
runJudge: NonNullable<WorkflowDeps['runJudge']>;
|
|
14
|
-
/** Exact native skill plus its loaded references, supplied by the caller. No hidden authoring contract is added. */
|
|
15
|
-
skillText: string;
|
|
16
|
-
split: BenchmarkSplit;
|
|
17
|
-
mode: 'live' | 'scripted-transport';
|
|
18
|
-
signal?: AbortSignal;
|
|
19
|
-
budgets?: Partial<PiBenchmarkBudgets>;
|
|
20
|
-
onProgress?: (event: {
|
|
21
|
-
caseId: string;
|
|
22
|
-
arm: 'plain-pi' | 'skill-dsl';
|
|
23
|
-
stage: 'arm' | 'outer' | 'workflow';
|
|
24
|
-
event: 'start' | 'end';
|
|
25
|
-
status?: string;
|
|
26
|
-
}) => unknown;
|
|
27
|
-
}
|
|
28
|
-
type Metrics = {
|
|
29
|
-
pi: number;
|
|
30
|
-
jev: number;
|
|
31
|
-
tools: number;
|
|
32
|
-
submissions: number;
|
|
33
|
-
tokens: number;
|
|
34
|
-
usageSamples: number;
|
|
35
|
-
knownCostUsd: number;
|
|
36
|
-
costSamples: number;
|
|
37
|
-
};
|
|
38
|
-
type Arm = 'plain-pi' | 'skill-dsl';
|
|
39
|
-
export interface PiBenchmarkArm {
|
|
40
|
-
arm: Arm;
|
|
41
|
-
status: 'passed' | 'rejected' | 'failed' | 'cancelled';
|
|
42
|
-
elapsedMs: number;
|
|
43
|
-
total: Metrics;
|
|
44
|
-
stages: {
|
|
45
|
-
outer: Metrics;
|
|
46
|
-
workflow: Metrics;
|
|
47
|
-
};
|
|
48
|
-
costComplete: boolean;
|
|
49
|
-
usageComplete: boolean;
|
|
50
|
-
candidates: {
|
|
51
|
-
digest?: string;
|
|
52
|
-
accepted: boolean;
|
|
53
|
-
workflow?: unknown;
|
|
54
|
-
}[];
|
|
55
|
-
workflowRuns: {
|
|
56
|
-
status: string;
|
|
57
|
-
digest: string;
|
|
58
|
-
output?: unknown;
|
|
59
|
-
}[];
|
|
60
|
-
output?: unknown;
|
|
61
|
-
evaluation?: ReturnType<typeof evaluatePiBenchmark>;
|
|
62
|
-
failure?: string;
|
|
63
|
-
}
|
|
64
|
-
/** End-to-end comparison: DSL authoring, repair, execution and delivery all count.
|
|
65
|
-
* Uses the selected host model, never a model selector or alternate provider. */
|
|
66
|
-
export declare function runPiSkillBenchmark(options: PiSkillBenchmarkOptions): Promise<{
|
|
67
|
-
mode: "live" | "scripted-transport";
|
|
68
|
-
split: BenchmarkSplit;
|
|
69
|
-
status: "complete" | "cancelled";
|
|
70
|
-
passed: boolean;
|
|
71
|
-
contract: {
|
|
72
|
-
version: number;
|
|
73
|
-
fixturesSha256: string;
|
|
74
|
-
verifierSha256: string;
|
|
75
|
-
outputSchemaSha256: string;
|
|
76
|
-
instructionsSha256: string;
|
|
77
|
-
};
|
|
78
|
-
skillSha256: string;
|
|
79
|
-
budgets: {
|
|
80
|
-
timeoutMs: number;
|
|
81
|
-
maxModelRequests: number;
|
|
82
|
-
maxToolCalls: number;
|
|
83
|
-
maxTurns: number;
|
|
84
|
-
maxSubmissions: number;
|
|
85
|
-
};
|
|
86
|
-
runtime: {
|
|
87
|
-
model: string;
|
|
88
|
-
provider: string;
|
|
89
|
-
api: string;
|
|
90
|
-
thinking: import("./types.js").PiThinkingLevel;
|
|
91
|
-
};
|
|
92
|
-
runs: {
|
|
93
|
-
caseId: string;
|
|
94
|
-
inputSha256: string;
|
|
95
|
-
order: Arm[];
|
|
96
|
-
arms: PiBenchmarkArm[];
|
|
97
|
-
}[];
|
|
98
|
-
limitations: string[];
|
|
99
|
-
}>;
|
|
100
|
-
export type PiSkillBenchmarkReport = Awaited<ReturnType<typeof runPiSkillBenchmark>>;
|
|
101
|
-
/** A development comparison receipt, not automatic promotion. Heldout is a final
|
|
102
|
-
* confirmation set, never the optimization target for this helper. */
|
|
103
|
-
export declare function comparePiSkillBenchmarks(baseline: PiSkillBenchmarkReport, candidate: PiSkillBenchmarkReport): {
|
|
104
|
-
mode: "live" | "scripted-transport";
|
|
105
|
-
baselineSkillSha256: string;
|
|
106
|
-
candidateSkillSha256: string;
|
|
107
|
-
regressions: string[];
|
|
108
|
-
previous: {
|
|
109
|
-
passed: number;
|
|
110
|
-
requests: number;
|
|
111
|
-
tokens: number;
|
|
112
|
-
usageComplete: boolean;
|
|
113
|
-
knownCostUsd: number;
|
|
114
|
-
costComplete: boolean;
|
|
115
|
-
};
|
|
116
|
-
current: {
|
|
117
|
-
passed: number;
|
|
118
|
-
requests: number;
|
|
119
|
-
tokens: number;
|
|
120
|
-
usageComplete: boolean;
|
|
121
|
-
knownCostUsd: number;
|
|
122
|
-
costComplete: boolean;
|
|
123
|
-
};
|
|
124
|
-
improved: boolean;
|
|
125
|
-
eligibleForLiveHeldout: boolean;
|
|
126
|
-
};
|
|
127
|
-
export {};
|