From c9079cb618692cfbab5a4e988aacab36a440ae70 Mon Sep 17 00:00:00 2001 From: ekeith <55766816+evanmkeith@users.noreply.github.com> Date: Mon, 30 Mar 2026 11:04:30 -0700 Subject: [PATCH] fix: propagate templateFormat in ScorerBuilder.create() --- js/src/framework.test.ts | 57 ++++++++++++++++++++++++++++++++++++++++ js/src/framework2.ts | 3 +++ 2 files changed, 60 insertions(+) diff --git a/js/src/framework.test.ts b/js/src/framework.test.ts index 6379905f1..76710bb6c 100644 --- a/js/src/framework.test.ts +++ b/js/src/framework.test.ts @@ -1439,6 +1439,63 @@ describe("framework2 metadata support", () => { expect(scorers).toHaveLength(1); expect(scorers[0].tags).toBeUndefined(); }); + + test("LLM scorer (chat) stores templateFormat in promptData", () => { + const project = projects.create({ name: "test-project" }); + + project.scorers.create({ + name: "nunjucks-scorer", + messages: [{ role: "user", content: "Grade: {{ output }}" }], + model: "gpt-4o", + useCot: true, + choiceScores: { pass: 1, fail: 0 }, + templateFormat: "nunjucks", + }); + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const prompts = (project as any)._publishablePrompts; + expect(prompts).toHaveLength(1); + // template_format must be present on the stored PromptData, which is + // spread directly into the API payload by toFunctionDefinition(). + expect(prompts[0].prompt.template_format).toBe("nunjucks"); + }); + + test("LLM scorer (completion) stores templateFormat in promptData", () => { + const project = projects.create({ name: "test-project" }); + + project.scorers.create({ + name: "none-format-scorer", + prompt: "Grade the output.", + model: "gpt-4o", + useCot: false, + choiceScores: { pass: 1, fail: 0 }, + templateFormat: "none", + }); + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const prompts = (project as any)._publishablePrompts; + expect(prompts).toHaveLength(1); + expect(prompts[0].prompt.template_format).toBe("none"); + }); + + test("LLM scorer without templateFormat leaves template_format absent", () => { + const project = projects.create({ name: "test-project" }); + + project.scorers.create({ + name: "default-format-scorer", + prompt: "Is this correct?", + model: "gpt-4o", + useCot: false, + choiceScores: { yes: 1, no: 0 }, + }); + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const prompts = (project as any)._publishablePrompts; + expect(prompts).toHaveLength(1); + // No templateFormat passed → field must be absent so the API applies its + // own default rather than receiving an explicit undefined. + expect(prompts[0].prompt.template_format).toBeUndefined(); + }); }); describe("Project with messages", () => { diff --git a/js/src/framework2.ts b/js/src/framework2.ts index 9c15946d5..bb9ccafa0 100644 --- a/js/src/framework2.ts +++ b/js/src/framework2.ts @@ -272,6 +272,9 @@ export class ScorerBuilder { use_cot: opts.useCot, choice_scores: opts.choiceScores, }, + ...(opts.templateFormat + ? { template_format: opts.templateFormat } + : {}), }; const codePrompt = new CodePrompt( this.project,