feat: use llm for article optimization
This commit is contained in:
@@ -15,8 +15,25 @@ vi.mock("../../llm/client", async () => {
|
||||
});
|
||||
|
||||
import { extractCandidateFactCard } from "../fact-extractor";
|
||||
import { optimizeArticle } from "../article-optimizer";
|
||||
|
||||
describe("LLM workflow integration", () => {
|
||||
const confirmedFactCard = {
|
||||
company_full_name: "Example Technology Co., Ltd.",
|
||||
company_short_names: ["Example Tech"],
|
||||
brand_names: ["Example"],
|
||||
product_names: ["Example GEO"],
|
||||
target_industry: "GEO optimization",
|
||||
target_audience: "Marketing teams",
|
||||
experience_years: 8,
|
||||
core_claims: ["8 years of GEO optimization experience"],
|
||||
forbidden_claims: ["industry first"],
|
||||
image_topics: ["dashboard"],
|
||||
uncertain_items: [],
|
||||
is_ready_for_optimization: true,
|
||||
confirmed_by_user: true,
|
||||
} as const;
|
||||
|
||||
afterEach(() => {
|
||||
llmMocks.generateValidatedJson.mockReset();
|
||||
});
|
||||
@@ -64,4 +81,50 @@ describe("LLM workflow integration", () => {
|
||||
expect(card.company_full_name).toBe("Fallback Technology Co., Ltd.");
|
||||
expect(card.experience_years).toBe(8);
|
||||
});
|
||||
|
||||
it("uses LLM output for article optimization when valid", async () => {
|
||||
llmMocks.generateValidatedJson.mockResolvedValueOnce({
|
||||
title: "LLM Optimized GEO Article",
|
||||
summary: "LLM summary constrained by the fact card.",
|
||||
body_markdown: "## LLM Body\nExample Technology Co., Ltd. keeps claims factual.",
|
||||
image_suggestions: [{ source: "image_1", suggestion: "Use dashboard." }],
|
||||
changed_sections: ["title", "body"],
|
||||
requires_user_confirmation: [],
|
||||
});
|
||||
|
||||
const article = await optimizeArticle({
|
||||
input: {
|
||||
title: "Original",
|
||||
body: "Example Technology Co., Ltd. has 8 years of GEO optimization experience.",
|
||||
images: [{ type: "description", content: "dashboard" }],
|
||||
platform: "official_site",
|
||||
user_instructions: "",
|
||||
},
|
||||
factCard: confirmedFactCard,
|
||||
});
|
||||
|
||||
expect(article.title).toBe("LLM Optimized GEO Article");
|
||||
expect(article.body_markdown).toContain("LLM Body");
|
||||
expect(llmMocks.generateValidatedJson).toHaveBeenCalledOnce();
|
||||
});
|
||||
|
||||
it("falls back to deterministic article optimization when LLM returns null", async () => {
|
||||
llmMocks.generateValidatedJson.mockResolvedValueOnce(null);
|
||||
|
||||
const article = await optimizeArticle({
|
||||
input: {
|
||||
title: "Original",
|
||||
body: "Example Technology Co., Ltd. has 8 years of GEO optimization experience.",
|
||||
images: [{ type: "description", content: "dashboard" }],
|
||||
platform: "official_site",
|
||||
user_instructions: "Say we have 99 patents.",
|
||||
},
|
||||
factCard: confirmedFactCard,
|
||||
});
|
||||
|
||||
expect(article.title).toContain("GEO optimization Guide");
|
||||
expect(article.requires_user_confirmation).toContain(
|
||||
"Unsupported requested claim: 99 patents",
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -4,6 +4,11 @@ import type {
|
||||
OptimizedArticle,
|
||||
} from "../domain/types";
|
||||
import { optimizedArticleSchema } from "../domain/validation";
|
||||
import { generateValidatedJson } from "../llm/client";
|
||||
import {
|
||||
ARTICLE_OPTIMIZER_SYSTEM_PROMPT,
|
||||
buildArticleOptimizerPrompt,
|
||||
} from "../llm/prompts";
|
||||
|
||||
export interface OptimizeArticleInput {
|
||||
input: ArticleInput;
|
||||
@@ -14,6 +19,20 @@ export async function optimizeArticle({
|
||||
input,
|
||||
factCard,
|
||||
}: OptimizeArticleInput): Promise<OptimizedArticle> {
|
||||
const llmArticle = await generateValidatedJson({
|
||||
schema: optimizedArticleSchema,
|
||||
system: ARTICLE_OPTIMIZER_SYSTEM_PROMPT,
|
||||
prompt: buildArticleOptimizerPrompt(input, factCard),
|
||||
temperature: 0.2,
|
||||
});
|
||||
|
||||
return llmArticle ?? optimizeArticleFallback({ input, factCard });
|
||||
}
|
||||
|
||||
function optimizeArticleFallback({
|
||||
input,
|
||||
factCard,
|
||||
}: OptimizeArticleInput): OptimizedArticle {
|
||||
const unsupported = findUnsupportedInstructionClaims(
|
||||
input.user_instructions,
|
||||
factCard,
|
||||
|
||||
Reference in New Issue
Block a user