@@ -9,6 +9,17 @@ import { resolveFileReference } from './file-resolver.js';
99const ANSI_YELLOW = '\u001b[33m' ;
1010const ANSI_RESET = '\u001b[0m' ;
1111
12+ /**
13+ * Prefix for explicit file references in prompt strings.
14+ * Consistent with case-file-loader.ts which uses "file://" for test-case file references.
15+ *
16+ * Usage:
17+ * prompt: "file://prompts/grader.md" → explicit file, error if not found
18+ * prompt: "grader.md" → inline text (never resolved as file)
19+ * prompt: "Evaluate the response" → inline text
20+ */
21+ const PROMPT_FILE_PREFIX = 'file://' ;
22+
1223/**
1324 * Normalize evaluator type names from legacy snake_case to internal kebab-case.
1425 * Accepts both forms for backward compatibility:
@@ -428,14 +439,27 @@ async function parseEvaluatorList(
428439 threshold : thresholdValue ,
429440 } ;
430441 } else {
431- // llm-grader aggregator
432- const aggregatorPrompt = asString ( rawAggregator . prompt ) ;
442+ // llm-grader aggregator — same file:// prefix logic as evaluator prompts
443+ const rawAggPrompt = asString ( rawAggregator . prompt ) ;
444+ let aggregatorPrompt : string | undefined ;
433445 let promptPath : string | undefined ;
434446
435- if ( aggregatorPrompt ) {
436- const resolved = await resolveFileReference ( aggregatorPrompt , searchRoots ) ;
437- if ( resolved . resolvedPath ) {
438- promptPath = path . resolve ( resolved . resolvedPath ) ;
447+ if ( rawAggPrompt ) {
448+ if ( rawAggPrompt . startsWith ( PROMPT_FILE_PREFIX ) ) {
449+ // Explicit file reference — error if not found
450+ const fileRef = rawAggPrompt . slice ( PROMPT_FILE_PREFIX . length ) ;
451+ aggregatorPrompt = fileRef ;
452+ const resolved = await resolveFileReference ( fileRef , searchRoots ) ;
453+ if ( resolved . resolvedPath ) {
454+ promptPath = path . resolve ( resolved . resolvedPath ) ;
455+ } else {
456+ throw new Error (
457+ `Composite aggregator in '${ evalId } ': prompt file not found: ${ resolved . displayPath } ` ,
458+ ) ;
459+ }
460+ } else {
461+ // Bare string — always treat as inline text, no file resolution
462+ aggregatorPrompt = rawAggPrompt ;
439463 }
440464 }
441465
@@ -1144,26 +1168,32 @@ async function parseEvaluatorList(
11441168 promptScriptConfig = rawPrompt . config as Record < string , unknown > ;
11451169 }
11461170 } else if ( typeof rawPrompt === 'string' ) {
1147- // Text template prompt (existing behavior)
1148- prompt = rawPrompt ;
1149- const resolved = await resolveFileReference ( prompt , searchRoots ) ;
1150- if ( resolved . resolvedPath ) {
1151- promptPath = path . resolve ( resolved . resolvedPath ) ;
1152- // Validate custom prompt content upfront - throws error if validation fails
1153- try {
1154- await validateCustomPromptContent ( promptPath ) ;
1155- } catch ( error ) {
1156- const message = error instanceof Error ? error . message : String ( error ) ;
1157- // Add context and re-throw for the caller to handle
1158- throw new Error ( `Evaluator '${ name } ' template (${ promptPath } ): ${ message } ` ) ;
1171+ // Text template prompt — supports explicit file:// prefix for file references.
1172+ // "file://prompts/grader.md" → explicit file reference, error if not found
1173+ // "grader.md" → inline text (no file resolution)
1174+ // "Evaluate the response" → inline text
1175+
1176+ if ( rawPrompt . startsWith ( PROMPT_FILE_PREFIX ) ) {
1177+ // Explicit file reference — strip prefix and resolve. Error if not found.
1178+ const fileRef = rawPrompt . slice ( PROMPT_FILE_PREFIX . length ) ;
1179+ prompt = fileRef ;
1180+ const resolved = await resolveFileReference ( fileRef , searchRoots ) ;
1181+ if ( resolved . resolvedPath ) {
1182+ promptPath = path . resolve ( resolved . resolvedPath ) ;
1183+ try {
1184+ await validateCustomPromptContent ( promptPath ) ;
1185+ } catch ( error ) {
1186+ const message = error instanceof Error ? error . message : String ( error ) ;
1187+ throw new Error ( `Evaluator '${ name } ' template (${ promptPath } ): ${ message } ` ) ;
1188+ }
1189+ } else {
1190+ throw new Error (
1191+ `Evaluator '${ name } ' in '${ evalId } ': prompt file not found: ${ resolved . displayPath } ` ,
1192+ ) ;
11591193 }
11601194 } else {
1161- logWarning (
1162- `Inline prompt used for evaluator '${ name } ' in '${ evalId } ' (file not found: ${ resolved . displayPath } )` ,
1163- resolved . attempted . length > 0
1164- ? resolved . attempted . map ( ( attempt ) => ` Tried: ${ attempt } ` )
1165- : undefined ,
1166- ) ;
1195+ // Bare string — always treat as inline text, no file resolution
1196+ prompt = rawPrompt ;
11671197 }
11681198 }
11691199
0 commit comments