Skip to content

Commit 82816eb

Browse files
author
Vikhyath Mondreti
committed
Generate benchmark redactions from exact passages
1 parent 0a7d3a6 commit 82816eb

5 files changed

Lines changed: 64 additions & 7 deletions

File tree

‎apps/sim/lib/benchmarks/README.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ Open **Settings → Admin → Open benchmarks** (or `/benchmark`). Select an org
1111
Each case fixes the execution user when it is created. Changing the selector shows that user's saved cases; it never changes an existing case's identity. Legacy cases retain their original owner's execution identity. **New benchmark** always opens a creation form, and saved cases appear in a list with an **Open** action.
1212

1313
1. Select a workspace. Generate the reference by letting Mothership inspect its workflows and supporting resources, or paste/import a reference produced by another client. Review the original task brief and reference.
14-
2. Generate the blanks and review their expected answers. Only select facts discoverable from the permitted enterprise sources. Replacing `[[BLANK:id]]` with each exact answer must restore the reference byte for byte. Remove answer hints left elsewhere in the reference.
14+
2. Generate the blanks and review their expected answers. The LLM selects exact passages; the server masks all matching occurrences without rewriting the reference. Only select facts discoverable from the permitted enterprise sources. Replacing `[[BLANK:id]]` with each exact answer must restore the reference byte for byte. Remove answer hints left elsewhere in the reference.
1515
3. Run the planner. It receives the brief in a fresh organization Plan conversation, with enterprise search/document reads and isolated memory. It receives neither the completed workspace inventory nor the reference, masks, or expected answers. Its final response is the generated spec.
1616
4. Reconstruct and grade separately. Reconstruction is a fresh execution with the redacted questions in context and one immutable document, `generated-spec.md`, available through a bounded read/search tool. That document contains only the planner’s generated spec; enterprise tools, other documents and history are unavailable. Grading compares the recovered answers to the hidden targets and checks support in the generated spec. An exact supporting passage is required for credit. The score is correct details divided by the fixed number of blanks.
1717

‎apps/sim/lib/benchmarks/application/run-stage.ts‎

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,11 @@ import {
1010
BENCHMARK_LEASE_MS,
1111
withBenchmarkStageLease,
1212
} from '@/lib/benchmarks/application/stage-lease'
13-
import { applyBenchmarkPatch, validateBenchmarkRedaction } from '@/lib/benchmarks/artifacts'
13+
import {
14+
applyBenchmarkPatch,
15+
redactBenchmarkSpec,
16+
validateBenchmarkRedaction,
17+
} from '@/lib/benchmarks/artifacts'
1418
import { getBenchmarkMothershipUrl } from '@/lib/benchmarks/config'
1519
import { gradeReconstruction, validateReconstruction } from '@/lib/benchmarks/evaluation'
1620
import {
@@ -44,7 +48,6 @@ const distillationSchema = z
4448
.strict()
4549
const redactionSchema = z
4650
.object({
47-
redactedSpec: benchmarkSpecSchema.min(1),
4851
blanks: z.array(benchmarkBlankSchema).min(1),
4952
})
5053
.strict()
@@ -110,7 +113,12 @@ async function performStage(
110113
schema: redactionSchema,
111114
messages: redactionMessages(artifacts.referenceSpec),
112115
})
113-
return { artifacts: applyBenchmarkPatch(artifacts, result) }
116+
return {
117+
artifacts: applyBenchmarkPatch(artifacts, {
118+
...result,
119+
redactedSpec: redactBenchmarkSpec(artifacts.referenceSpec, result.blanks),
120+
}),
121+
}
114122
}
115123
case 'plan': {
116124
const result = await executeBenchmarkPlan({ principal, benchmark, signal })

‎apps/sim/lib/benchmarks/artifacts.test.ts‎

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
import { describe, expect, it } from 'vitest'
2-
import { applyBenchmarkPatch, validateBenchmarkRedaction } from '@/lib/benchmarks/artifacts'
2+
import {
3+
applyBenchmarkPatch,
4+
redactBenchmarkSpec,
5+
validateBenchmarkRedaction,
6+
} from '@/lib/benchmarks/artifacts'
37
import type { BenchmarkArtifacts } from '@/lib/benchmarks/types'
48

59
const artifacts: BenchmarkArtifacts = {
@@ -14,6 +18,32 @@ const artifacts: BenchmarkArtifacts = {
1418
}
1519

1620
describe('benchmark reference integrity', () => {
21+
it('builds masks from exact selected passages without rewriting the reference', () => {
22+
const referenceSpec = 'Queue: Ops [L2].\nNotify Ops [L2] and team+$ at $5.\n'
23+
expect(
24+
redactBenchmarkSpec(referenceSpec, [
25+
{ id: 'queue', answer: 'Ops [L2]' },
26+
{ id: 'team', answer: 'team+$' },
27+
])
28+
).toBe('Queue: [[BLANK:queue]].\nNotify [[BLANK:queue]] and [[BLANK:team]] at $5.\n')
29+
})
30+
31+
it('rejects missing, duplicate, or overlapping passages that cannot produce every blank', () => {
32+
for (const blanks of [
33+
[{ id: 'missing', answer: 'Elsewhere' }],
34+
[
35+
{ id: 'first', answer: 'Support' },
36+
{ id: 'second', answer: 'Support' },
37+
],
38+
[
39+
{ id: 'short', answer: 'Support' },
40+
{ id: 'long', answer: 'Support team' },
41+
],
42+
]) {
43+
expect(() => redactBenchmarkSpec('Support team owns follow-up.', blanks)).toThrow()
44+
}
45+
})
46+
1747
it('accepts repeated masks for one requirement without changing surviving reference text', () => {
1848
expect(() => validateBenchmarkRedaction(artifacts)).not.toThrow()
1949
})

‎apps/sim/lib/benchmarks/artifacts.ts‎

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,29 @@
1+
import { escapeRegExp } from '@sim/utils/string'
12
import {
23
type BenchmarkArtifacts,
34
type BenchmarkEditablePatch,
45
benchmarkArtifactsSchema,
56
} from '@/lib/benchmarks/types'
67
import { OrchestrationError } from '@/lib/core/orchestration/types'
78

9+
/** Replace selected passages in one pass so markers and surviving text cannot be rewritten. */
10+
export function redactBenchmarkSpec(
11+
referenceSpec: string,
12+
blanks: BenchmarkArtifacts['blanks']
13+
): string {
14+
if (!blanks.length || blanks.some((blank) => !blank.answer)) {
15+
throw new OrchestrationError('validation', 'Select at least one nonempty passage to redact')
16+
}
17+
const ids = new Map(blanks.map((blank) => [blank.answer, blank.id]))
18+
const passages = [...ids.keys()].sort((left, right) => right.length - left.length)
19+
const redactedSpec = referenceSpec.replace(
20+
new RegExp(passages.map(escapeRegExp).join('|'), 'g'),
21+
(answer) => `[[BLANK:${ids.get(answer)}]]`
22+
)
23+
validateBenchmarkRedaction({ referenceSpec, redactedSpec, blanks })
24+
return redactedSpec
25+
}
26+
827
/** Every mask must restore the exact reference, including repeated occurrences. */
928
export function validateBenchmarkRedaction(
1029
artifacts: Pick<BenchmarkArtifacts, 'referenceSpec' | 'redactedSpec' | 'blanks'>

‎apps/sim/lib/benchmarks/prompts.ts‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,13 @@ function messages(instruction: string, data: unknown): ExecuteMessage[] {
1414
export function distillationMessages(taskBrief: string): ExecuteMessage[] {
1515
return messages(
1616
'Explore the selected workspace using the read-only workspace CLI and describe its implemented behavior as a detailed, self-contained reference specification. Begin by listing all active workflows, follow list pagination, inspect each workflow and its referenced workspace resources, and follow large-output continuations. Cover exact triggers and input shapes, conditions, ownership, field mappings, prompts and code behavior, actions, destinations, cross-workflow relationships, outputs and failure/recovery behavior. Preserve concrete names, values and business rules; do not compress them into a high-level overview. Distinguish implemented behavior from unresolved configuration or inferred intent. Do not invent missing values. Omit editor layout and credential values. Also draft a short taskBrief expressing the business goal a user would originally request, without disclosing the enterprise-specific answers. If a taskBrief was supplied, preserve it. The reference is for human review before evaluation. Use tools to inspect; return the final JSON only when inspection is complete.',
17-
{ taskBrief }
17+
taskBrief.trim() ? { taskBrief } : {}
1818
)
1919
}
2020

2121
export function redactionMessages(referenceSpec: string): ExecuteMessage[] {
2222
return messages(
23-
'Select 5 to 15 meaningful enterprise-specific facts from this reference specification that an agent should discover from enterprise context. Prefer ownership, conditions, mappings, existing mechanisms and business destinations. Exclude arbitrary implementation choices or details unlikely to exist outside this completed workflow. Produce redactedSpec by replacing exact passages with [[BLANK:id]] markers and blanks containing each id and its exact original answer. Preserve every other character of the reference, including whitespace and punctuation. Use the same marker for repeated identical answers and redact all occurrences that reveal an answer. Use short meaningful IDs. Do not paraphrase or add text. Return at least one blank; the human will review whether each is discoverable.',
23+
'Select 5 to 15 meaningful enterprise-specific facts from this reference specification that an agent should discover from enterprise context. Prefer ownership, conditions, mappings, existing mechanisms and business destinations. Exclude arbitrary implementation choices or details unlikely to exist outside this completed workflow. Return blanks containing a short meaningful id and an answer copied as an exact contiguous passage of the reference. Select distinct, non-overlapping answers; the server replaces every exact occurrence with the same marker and preserves all other text. Avoid facts whose answer is revealed by a different surviving passage. Do not paraphrase or return a rewritten reference. Return at least one blank; the human will review whether each is discoverable.',
2424
{ referenceSpec }
2525
)
2626
}

0 commit comments

Comments
 (0)