171 lines
5.4 KiB
TypeScript
171 lines
5.4 KiB
TypeScript
import { z } from "zod";
|
|
|
|
const nonEmptyTextSchema = z.string().trim().min(1);
|
|
|
|
export const legacyFindingItemSchema = z.object({
|
|
section: z.string(),
|
|
finding: z.string(),
|
|
suggestion: z.string(),
|
|
});
|
|
|
|
export const v3FindingItemSchema = z.object({
|
|
skill_id: nonEmptyTextSchema,
|
|
observation: nonEmptyTextSchema,
|
|
customer_benefit: nonEmptyTextSchema,
|
|
public_phrasing: nonEmptyTextSchema,
|
|
severity: z.union([z.literal(1), z.literal(2), z.literal(3)]),
|
|
evidence: nonEmptyTextSchema,
|
|
applies: z.boolean(),
|
|
});
|
|
|
|
export const findingItemSchema = legacyFindingItemSchema;
|
|
|
|
export const auditFindingEvidenceRefSchema = z.object({
|
|
id: nonEmptyTextSchema,
|
|
type: z.enum([
|
|
"crawl_page",
|
|
"technical_check",
|
|
"screenshot",
|
|
"pagespeed",
|
|
"jina_excerpt",
|
|
"generation_stage",
|
|
]),
|
|
label: nonEmptyTextSchema,
|
|
sourceUrl: z.string().trim(),
|
|
});
|
|
|
|
export const auditSpecialistFindingSchema = z
|
|
.object({
|
|
skillId: nonEmptyTextSchema,
|
|
claim: nonEmptyTextSchema,
|
|
recommendation: nonEmptyTextSchema,
|
|
customerBenefit: nonEmptyTextSchema,
|
|
severity: z.union([z.literal(1), z.literal(2), z.literal(3)]),
|
|
confidence: z.number().min(0).max(1),
|
|
evidenceRefs: z.array(auditFindingEvidenceRefSchema).min(1),
|
|
applies: z.boolean(),
|
|
unknowns: z.array(z.string()),
|
|
})
|
|
.superRefine((finding, ctx) => {
|
|
const combined = [
|
|
finding.claim,
|
|
finding.recommendation,
|
|
finding.customerBenefit,
|
|
].join(" ");
|
|
if (/\bunbekannt\b|\bunknown\b/i.test(combined)) {
|
|
ctx.addIssue({
|
|
code: z.ZodIssueCode.custom,
|
|
message: "unknown-only findings are not valid audit claims",
|
|
path: ["claim"],
|
|
});
|
|
}
|
|
});
|
|
|
|
export const auditSpecialistResultSchema = z.object({
|
|
status: z.enum(["success", "partial", "skipped", "failed"]),
|
|
findings: z.array(auditSpecialistFindingSchema),
|
|
notes: z.array(z.string()),
|
|
});
|
|
|
|
export const auditRejectedFindingSchema = z.object({
|
|
findingId: nonEmptyTextSchema,
|
|
skillId: nonEmptyTextSchema,
|
|
claim: nonEmptyTextSchema,
|
|
rejectionReason: nonEmptyTextSchema,
|
|
});
|
|
|
|
export const auditEvidenceVerificationSchema = z.object({
|
|
verifiedFindingIds: z.array(nonEmptyTextSchema),
|
|
rejectedFindings: z.array(auditRejectedFindingSchema),
|
|
contradictions: z.array(z.string()),
|
|
notes: z.array(z.string()),
|
|
});
|
|
|
|
export const internalFindingsSchema = z.object({
|
|
findings: z.array(findingItemSchema),
|
|
summary: z.string(),
|
|
});
|
|
|
|
export const auditClassificationSchema = z.object({
|
|
findings: z.array(v3FindingItemSchema).min(1),
|
|
summary: nonEmptyTextSchema,
|
|
usedSkills: z.array(nonEmptyTextSchema).nullable(),
|
|
});
|
|
|
|
export const auditGenerationResultSchema = z.object({
|
|
findings: z.array(v3FindingItemSchema).min(1),
|
|
usedSkills: z.array(nonEmptyTextSchema).min(1),
|
|
publicAuditText: nonEmptyTextSchema,
|
|
finalSummary: nonEmptyTextSchema,
|
|
emailSubject: nonEmptyTextSchema,
|
|
emailBody: nonEmptyTextSchema,
|
|
phoneScript: nonEmptyTextSchema,
|
|
ctaType: z.enum(["anruf", "termin", "rueckruf"]),
|
|
});
|
|
|
|
export const auditSummarySchema = z.object({
|
|
summary: z.string(),
|
|
keyFindings: z.array(z.string()),
|
|
});
|
|
|
|
export const publicAuditTextSchema = z.object({
|
|
publicText: z.string(),
|
|
});
|
|
|
|
export const emailDraftSchema = z.object({
|
|
body: z.string(),
|
|
});
|
|
|
|
export const emailSubjectSchema = z.object({
|
|
subject: z.string(),
|
|
});
|
|
|
|
export const callScriptSchema = z.object({
|
|
openingLine: z.string(),
|
|
callScript: z.array(z.string()),
|
|
closeLine: z.string(),
|
|
});
|
|
|
|
export const followUpDraftSchema = z.object({
|
|
message: z.string(),
|
|
followInDays: z.number().int().min(0).nullable(),
|
|
goals: z.array(z.string()).nullable(),
|
|
});
|
|
|
|
export const qualityReviewRevisedCopySchema = z.object({
|
|
publicSummary: nonEmptyTextSchema,
|
|
publicBody: nonEmptyTextSchema,
|
|
emailSubject: nonEmptyTextSchema,
|
|
emailBody: nonEmptyTextSchema,
|
|
phoneScript: callScriptSchema,
|
|
followUpDraft: followUpDraftSchema,
|
|
});
|
|
|
|
export const qualityReviewSchema = z.object({
|
|
isValid: z.boolean(),
|
|
severity: z.enum(["ok", "warning", "unsafe"]),
|
|
issues: z.array(z.string()),
|
|
suggestions: z.array(z.string()),
|
|
rewriteRequired: z.boolean(),
|
|
revisedCopy: qualityReviewRevisedCopySchema.nullable(),
|
|
notes: z.array(z.string()).nullable(),
|
|
});
|
|
|
|
export type FindingItem = z.infer<typeof findingItemSchema>;
|
|
export type V3FindingItem = z.infer<typeof v3FindingItemSchema>;
|
|
export type AuditFindingEvidenceRef = z.infer<typeof auditFindingEvidenceRefSchema>;
|
|
export type AuditSpecialistFinding = z.infer<typeof auditSpecialistFindingSchema>;
|
|
export type AuditSpecialistResult = z.infer<typeof auditSpecialistResultSchema>;
|
|
export type AuditEvidenceVerification = z.infer<typeof auditEvidenceVerificationSchema>;
|
|
export type InternalFindings = z.infer<typeof internalFindingsSchema>;
|
|
export type AuditClassification = z.infer<typeof auditClassificationSchema>;
|
|
export type AuditGenerationResult = z.infer<typeof auditGenerationResultSchema>;
|
|
export type AuditSummary = z.infer<typeof auditSummarySchema>;
|
|
export type PublicAuditText = z.infer<typeof publicAuditTextSchema>;
|
|
export type EmailDraft = z.infer<typeof emailDraftSchema>;
|
|
export type EmailSubject = z.infer<typeof emailSubjectSchema>;
|
|
export type CallScript = z.infer<typeof callScriptSchema>;
|
|
export type FollowUpDraft = z.infer<typeof followUpDraftSchema>;
|
|
export type QualityReviewRevisedCopy = z.infer<typeof qualityReviewRevisedCopySchema>;
|
|
export type QualityReview = z.infer<typeof qualityReviewSchema>;
|