fix(ai): provide current date to resume analysis (#3353)

This commit is contained in:
Emanuele Tonello
2026-08-19 05:48:31 +02:00
committed by GitHub
parent 4d53a6d1de
commit 8acde4c1ac
3 changed files with 27 additions and 3 deletions
@@ -47,6 +47,7 @@ Do not include markdown, comments, or additional keys.
4. Never invent candidate achievements or facts. 4. Never invent candidate achievements or facts.
5. If data is missing, call it out explicitly in rationale/suggestions. 5. If data is missing, call it out explicitly in rationale/suggestions.
6. Keep scorecard dimensions practical and common for resume review. 6. Keep scorecard dimensions practical and common for resume review.
7. Use the current date from the analysis context for chronology checks. Do not flag a date on or before that date as future-dated.
## Suggestions Requirements ## Suggestions Requirements
+23 -1
View File
@@ -1,4 +1,5 @@
import type { UIMessage } from "ai"; import type { UIMessage } from "ai";
import type { ResumeData } from "@reactive-resume/schema/resume/data";
import { afterEach, describe, expect, it, vi } from "vitest"; import { afterEach, describe, expect, it, vi } from "vitest";
import { convertToModelMessages, modelMessageSchema } from "ai"; import { convertToModelMessages, modelMessageSchema } from "ai";
@@ -10,6 +11,7 @@ vi.mock("@reactive-resume/env/server", () => ({ env: envMock }));
afterEach(() => { afterEach(() => {
vi.unstubAllGlobals(); vi.unstubAllGlobals();
vi.useRealTimers();
}); });
function stubOpenAICompatibleResponse(response?: { content?: string; finishReason?: string }) { function stubOpenAICompatibleResponse(response?: { content?: string; finishReason?: string }) {
@@ -67,7 +69,7 @@ function stubRejectedFetch(error: unknown) {
return fetchMock; return fetchMock;
} }
const { testConnection } = await import("./service"); const { analyzeResume, testConnection } = await import("./service");
describe("AI provider connection test", () => { describe("AI provider connection test", () => {
it("names the rejected key instead of reporting a transport failure", async () => { it("names the rejected key instead of reporting a transport failure", async () => {
@@ -168,6 +170,26 @@ describe("AI provider connection test", () => {
}); });
}); });
describe("AI resume analysis", () => {
it("gives the model the actual current date for chronology checks", async () => {
vi.useFakeTimers();
vi.setSystemTime(new Date("2026-08-18T12:00:00Z"));
const response = stubOpenAICompatibleResponse({
content: JSON.stringify({
overallScore: 80,
scorecard: [{ dimension: "Clarity", score: 80, rationale: "Clear." }],
suggestions: [],
strengths: ["Clear experience."],
}),
});
await analyzeResume({ ...testInput(), resumeData: {} as ResumeData });
expect(JSON.stringify(response.getRequestBody())).toContain("Current date: 2026-08-18");
});
});
describe("AI chat service", () => { describe("AI chat service", () => {
it("tests OpenAI-compatible providers without requiring structured output", async () => { it("tests OpenAI-compatible providers without requiring structured output", async () => {
const openAiCompatible = stubOpenAICompatibleResponse(); const openAiCompatible = stubOpenAICompatibleResponse();
+3 -2
View File
@@ -509,8 +509,9 @@ type AnalyzeResumeInput = z.infer<typeof aiCredentialsSchema> & {
resumeData: ResumeData; resumeData: ResumeData;
}; };
function buildAnalyzeResumeSystemPrompt(resumeData: ResumeData): string { function buildAnalyzeResumeSystemPrompt(resumeData: ResumeData, now = new Date()): string {
return `${analyzeResumeSystemPromptTemplate}\n\n## Resume Data\n\n${JSON.stringify(resumeData, null, 2)}`; const currentDate = now.toISOString().slice(0, 10);
return `${analyzeResumeSystemPromptTemplate}\n\n## Analysis Context\n\nCurrent date: ${currentDate} (UTC). Treat dates on or before this date as not future-dated. Flag a date as future-dated only when it is after the current date.\n\n## Resume Data\n\n${JSON.stringify(resumeData, null, 2)}`;
} }
/** Sends resume data to the AI provider and returns a structured analysis, parsing raw JSON from the response text. */ /** Sends resume data to the AI provider and returns a structured analysis, parsing raw JSON from the response text. */