Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
114 lines
3.7 KiB
TypeScript
114 lines
3.7 KiB
TypeScript
import { sdk } from '../sdk'
|
|
import { configFile } from '../file-models/config'
|
|
|
|
const { InputSpec, Value, List } = sdk
|
|
|
|
const inputSpec = InputSpec.of({
|
|
reviewers: Value.list(
|
|
List.obj(
|
|
{
|
|
name: 'Review Panel',
|
|
description:
|
|
'Who sits on the panel — one entry per review. Add as many as you like. ' +
|
|
'Each reviewer is a model from your catalog plus a PERSONA: the lens it ' +
|
|
'reads through, so the same document gets examined from different angles.',
|
|
default: [],
|
|
minLength: 1,
|
|
maxLength: 32,
|
|
},
|
|
{
|
|
uniqueBy: 'name',
|
|
displayAs: '{{name}} ({{model}})',
|
|
spec: InputSpec.of({
|
|
name: Value.text({
|
|
name: 'Name',
|
|
description: 'Unique reviewer name. Becomes its container and report filename.',
|
|
required: true,
|
|
default: null,
|
|
placeholder: 'risk-counsel',
|
|
patterns: [
|
|
{ regex: '^[A-Za-z0-9][A-Za-z0-9 _-]{0,40}$',
|
|
description: 'Letters, numbers, spaces, dashes, underscores (max 41 chars).' },
|
|
],
|
|
}),
|
|
model: Value.text({
|
|
name: 'Model Alias',
|
|
description: 'Which catalog model this reviewer uses (must match an alias from "Configure Models").',
|
|
required: true,
|
|
default: null,
|
|
placeholder: 'reviewer-a',
|
|
}),
|
|
persona: Value.textarea({
|
|
name: 'Persona / Lens',
|
|
description:
|
|
'How THIS reviewer should read the documents — its priorities and ' +
|
|
'weighting. Injected into its system prompt. e.g. "You are skeptical ' +
|
|
'legal counsel: weight liability, ambiguous obligations, and missing ' +
|
|
'clauses above everything." Leave empty for a neutral reviewer.',
|
|
required: false,
|
|
default: null,
|
|
minRows: 3,
|
|
maxRows: 16,
|
|
placeholder:
|
|
'You are a financial-controls reviewer. Focus on numbers that do not ' +
|
|
'reconcile, unstated assumptions behind projections, and anything that ' +
|
|
'would concern an auditor. Organize findings by severity.',
|
|
}),
|
|
temperature: Value.number({
|
|
name: 'Sampling Temperature (optional)',
|
|
description:
|
|
'Best-effort per-reviewer sampling temperature for extra diversity. ' +
|
|
'Persona is the primary lever. Leave empty to use the model default.',
|
|
required: false,
|
|
default: null,
|
|
integer: false,
|
|
min: 0,
|
|
max: 2,
|
|
}),
|
|
}),
|
|
},
|
|
),
|
|
),
|
|
})
|
|
|
|
export const configureReviewers = sdk.Action.withInput(
|
|
'configure-reviewers',
|
|
|
|
async ({ effects }) => ({
|
|
name: 'Configure Reviewers',
|
|
description: 'Define the review panel: which models and which personas, and how many reviews.',
|
|
warning: null,
|
|
allowedStatuses: 'any',
|
|
group: null,
|
|
visibility: 'enabled',
|
|
}),
|
|
|
|
inputSpec,
|
|
|
|
async ({ effects }) => {
|
|
const cfg = await configFile.read().const(effects)
|
|
if (!cfg) return {}
|
|
return { reviewers: cfg.reviewers }
|
|
},
|
|
|
|
async ({ effects, input }) => {
|
|
await configFile.merge(effects, { reviewers: input.reviewers })
|
|
|
|
return {
|
|
version: '1',
|
|
title: 'Panel Configured',
|
|
message:
|
|
'Saved a panel of ' + input.reviewers.length + ' reviewer(s). Each model ' +
|
|
'alias must exist in "Configure Models". Set the rubric in "Configure ' +
|
|
'Review", then drop documents and run a review.',
|
|
result: {
|
|
type: 'single',
|
|
value: input.reviewers.map((r) => r.name).join(', '),
|
|
copyable: false,
|
|
qr: false,
|
|
masked: false,
|
|
},
|
|
}
|
|
},
|
|
)
|