fix: allow incomplete candidate fact cards
This commit is contained in:
@@ -42,6 +42,62 @@ describe("domain validation", () => {
|
||||
expect(parsed.is_ready_for_optimization).toBe(false);
|
||||
});
|
||||
|
||||
it("normalizes near-valid LLM fact card field types", () => {
|
||||
const emptyExperience = candidateFactCardSchema.parse({
|
||||
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", "Brand teams"],
|
||||
experience_years: "",
|
||||
core_claims: ["GEO optimization experience"],
|
||||
forbidden_claims: [],
|
||||
image_topics: [],
|
||||
uncertain_items: [],
|
||||
});
|
||||
|
||||
const numericExperience = candidateFactCardSchema.parse({
|
||||
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: ["GEO optimization experience"],
|
||||
forbidden_claims: [],
|
||||
image_topics: [],
|
||||
uncertain_items: [],
|
||||
});
|
||||
|
||||
expect(emptyExperience.target_audience).toBe("Marketing teams、Brand teams");
|
||||
expect(emptyExperience.experience_years).toBeNull();
|
||||
expect(numericExperience.experience_years).toBe(8);
|
||||
});
|
||||
|
||||
it("keeps incomplete candidate fact cards editable and ready", () => {
|
||||
const parsed = candidateFactCardSchema.parse({
|
||||
company_full_name: "",
|
||||
company_short_names: [],
|
||||
brand_names: [],
|
||||
product_names: [],
|
||||
target_industry: "",
|
||||
target_audience: "",
|
||||
experience_years: "",
|
||||
core_claims: [],
|
||||
forbidden_claims: [],
|
||||
image_topics: [],
|
||||
uncertain_items: [],
|
||||
});
|
||||
|
||||
expect(parsed.target_industry).toBe("");
|
||||
expect(parsed.target_audience).toBe("");
|
||||
expect(parsed.experience_years).toBeNull();
|
||||
expect(parsed.uncertain_items).toEqual([]);
|
||||
expect(parsed.is_ready_for_optimization).toBe(true);
|
||||
});
|
||||
|
||||
it("rejects a confirmed fact card with an empty company full name", () => {
|
||||
expect(() =>
|
||||
confirmedFactCardSchema.parse({
|
||||
|
||||
@@ -52,14 +52,39 @@ export const articleInputSchema = z.object({
|
||||
user_instructions: z.string().trim().default(""),
|
||||
}) satisfies z.ZodType<ArticleInput>;
|
||||
|
||||
const stringOrStringArraySchema = z.preprocess((value) => {
|
||||
if (Array.isArray(value)) {
|
||||
return value
|
||||
.map((item) => (typeof item === "string" ? item.trim() : ""))
|
||||
.filter(Boolean)
|
||||
.join("、");
|
||||
}
|
||||
return value;
|
||||
}, z.string().trim());
|
||||
|
||||
const experienceYearsSchema = z.preprocess((value) => {
|
||||
if (typeof value !== "string") return value;
|
||||
const trimmed = value.trim();
|
||||
if (
|
||||
trimmed === "" ||
|
||||
/^(?:unknown|none|null|n\/a|not\s+specified|不详|不明确|未知|无)$/i.test(
|
||||
trimmed,
|
||||
)
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
const yearMatch = trimmed.match(/\d{1,3}/);
|
||||
return yearMatch ? Number(yearMatch[0]) : value;
|
||||
}, z.number().int().nonnegative().nullable().default(null));
|
||||
|
||||
const factCardBaseSchema = z.object({
|
||||
company_full_name: z.string().trim(),
|
||||
company_short_names: z.array(z.string().trim().min(1)).default([]),
|
||||
brand_names: z.array(z.string().trim().min(1)).default([]),
|
||||
product_names: z.array(z.string().trim().min(1)).default([]),
|
||||
target_industry: z.string().trim().min(1),
|
||||
target_audience: z.string().trim().min(1),
|
||||
experience_years: z.number().int().nonnegative().nullable().default(null),
|
||||
target_industry: z.string().trim(),
|
||||
target_audience: stringOrStringArraySchema,
|
||||
experience_years: experienceYearsSchema,
|
||||
core_claims: z.array(z.string().trim().min(1)).default([]),
|
||||
forbidden_claims: z.array(z.string().trim().min(1)).default([]),
|
||||
image_topics: z.array(z.string().trim().min(1)).default([]),
|
||||
|
||||
Reference in New Issue
Block a user