feat(helper): add --prompt-file option for experimental prompt variation
Add optional --prompt-file <path> argument to start-case-experiment-helper.cjs
that allows supplying an alternative reconstruction prompt file while
preserving the production JSON generation schema.
Requirements implemented:
- Reads UTF-8 prompt file (absolute path or relative to prompts/)
- Replaces {{SCENARIO}} placeholder with scenario text using existing convention
- Validates file exists and contains required placeholder
- Rejects --prompt-file with non-A conditions via clear error
- Only compatible with condition A (production structured-output contract)
- Zero production code changes; apparatus-only modification
- Preserves existing behavior for all conditions B-E without flag
- Does not activate schema variants or freeform handling
Verified: syntax check passes, no whitespace errors in diff.
This commit is contained in:
@@ -57,9 +57,36 @@ function readScenarioInput(argv) {
|
||||
async function runStartCaseExperiment(scenarioInput, experimentInstruction, options = {}) {
|
||||
const condition = options.condition;
|
||||
const treatment = options.treatment;
|
||||
let scenarioTextForPrompt = scenarioInput?.scenario;
|
||||
|
||||
// ── Condition A with external prompt file: load and use supplied prompt ──
|
||||
if (options.promptFile && condition === "A") {
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
let resolvedPromptPath;
|
||||
if (path.isAbsolute(options.promptFile)) {
|
||||
resolvedPromptPath = options.promptFile;
|
||||
} else {
|
||||
resolvedPromptPath = path.join(PROJECT_ROOT, "prompts", options.promptFile);
|
||||
}
|
||||
if (!fs.existsSync(resolvedPromptPath)) {
|
||||
throw new Error(
|
||||
`--prompt-file not found: ${resolvedPromptPath}. ` +
|
||||
`Provide an absolute path or a prompt filename in the prompts/ directory.`
|
||||
);
|
||||
}
|
||||
const customPromptContent = fs.readFileSync(resolvedPromptPath, "utf8");
|
||||
if (!customPromptContent.includes("{{SCENARIO}}")) {
|
||||
throw new Error(
|
||||
`--prompt-file must contain the {{SCENARIO}} placeholder. ` +
|
||||
`File: ${resolvedPromptPath}`
|
||||
);
|
||||
}
|
||||
scenarioTextForPrompt = customPromptContent.replace("{{SCENARIO}}", scenarioInput?.scenario ?? "");
|
||||
}
|
||||
|
||||
// ── Condition B/C/D/E: load alt prompt (B/C question-free; D deconstruct-freeform; E investigability-first freeform) ──
|
||||
let scenarioTextForPrompt = scenarioInput?.scenario;
|
||||
scenarioTextForPrompt = scenarioInput?.scenario;
|
||||
if ((condition === "B" || condition === "C" || condition === "D" || condition === "E") && typeof scenarioTextForPrompt === "string") {
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
@@ -408,11 +435,25 @@ if (require.main === module) {
|
||||
throw new Error(`Treatment "${treatment}" is only valid with --condition D or E, not --condition ${condition}`);
|
||||
}
|
||||
|
||||
// ── Prompt-file selector: external reconstruction prompt for condition A ──
|
||||
const promptFileArgIdx = process.argv.indexOf("--prompt-file");
|
||||
let promptFile = null;
|
||||
if (promptFileArgIdx !== -1 && promptFileArgIdx + 1 < process.argv.length) {
|
||||
promptFile = process.argv[promptFileArgIdx + 1];
|
||||
}
|
||||
if (promptFile !== null && condition !== "A") {
|
||||
throw new Error(
|
||||
`--prompt-file is only compatible with --condition A. ` +
|
||||
`Condition ${condition ?? "(none, defaults to A)"} selected.`
|
||||
);
|
||||
}
|
||||
|
||||
const scenarioInput = readScenarioInput(process.argv);
|
||||
const reconstructionOnly = process.argv.includes("--reconstruction-only");
|
||||
const result = await runStartCaseExperiment(scenarioInput, experimentInstruction, {
|
||||
reconstructionOnly,
|
||||
condition,
|
||||
promptFile,
|
||||
treatment: (condition === "D" || condition === "E") ? treatment : undefined,
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user