wip(confidence-engine): checkpoint open-question presentation work
This commit is contained in:
@@ -0,0 +1,63 @@
|
||||
const ISSUE_MESSAGES = {
|
||||
question_missing: "No user-facing Open Question was generated.",
|
||||
question_empty: "The generated Open Question was empty.",
|
||||
question_not_interrogative: "The generated Open Question was not a direct interrogative.",
|
||||
question_indirect_proposition:
|
||||
"The generated Open Question used an indirect uncertainty proposition.",
|
||||
};
|
||||
|
||||
/**
|
||||
* Narrow presentation validation only. This does not attempt to prove semantic
|
||||
* equivalence with the canonical uncertainty and never rewrites either value.
|
||||
*/
|
||||
export function validateOpenQuestionCandidate(candidate) {
|
||||
if (candidate === undefined || candidate === null) {
|
||||
return { valid: false, category: "question_missing" };
|
||||
}
|
||||
|
||||
if (typeof candidate !== "string" || candidate.trim().length === 0) {
|
||||
return { valid: false, category: "question_empty" };
|
||||
}
|
||||
|
||||
const question = candidate.trim();
|
||||
if (/^whether\b/i.test(question)) {
|
||||
return { valid: false, category: "question_indirect_proposition" };
|
||||
}
|
||||
|
||||
if (!question.endsWith("?")) {
|
||||
return { valid: false, category: "question_not_interrogative" };
|
||||
}
|
||||
|
||||
return { valid: true, question };
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove invalid presentation candidates without changing canonical descriptions,
|
||||
* IDs, ordering, relationships, or any other reconstruction content.
|
||||
*/
|
||||
export function validateReconstructionOpenQuestions(reconstruction) {
|
||||
const issues = [];
|
||||
const importantUnknowns = (reconstruction?.importantUnknowns ?? []).map(
|
||||
(unknown, index) => {
|
||||
const validation = validateOpenQuestionCandidate(unknown.question);
|
||||
if (validation.valid) {
|
||||
return { ...unknown, question: validation.question };
|
||||
}
|
||||
|
||||
const { question: _invalidQuestion, ...canonicalUnknown } = unknown;
|
||||
issues.push({
|
||||
category: validation.category,
|
||||
path: ["reconstruction", "importantUnknowns", index, "question"],
|
||||
unknownId: unknown.id,
|
||||
message: ISSUE_MESSAGES[validation.category],
|
||||
severity: "nonfatal",
|
||||
});
|
||||
return canonicalUnknown;
|
||||
},
|
||||
);
|
||||
|
||||
return {
|
||||
reconstruction: { ...reconstruction, importantUnknowns },
|
||||
issues,
|
||||
};
|
||||
}
|
||||
@@ -144,6 +144,21 @@ const reconstructionRelationshipSchema = z.object({
|
||||
confidence: confidenceEnum,
|
||||
});
|
||||
|
||||
const importantUnknownSchema = itemSchemaV1.extend({
|
||||
// Canonical uncertainty meaning remains in description. question is an
|
||||
// optional, model-authored user-facing interrogative for the Open Questions UI.
|
||||
// Keep this tolerant for historical data and nonfatal presentation validation.
|
||||
question: z.string().optional(),
|
||||
});
|
||||
|
||||
const generatedImportantUnknownSchema = itemSchemaV1.extend({
|
||||
// New generation requests require a distinct presentation candidate. More
|
||||
// nuanced interrogative validation is intentionally performed locally after
|
||||
// canonical reconstruction validation rather than encoded as provider-specific
|
||||
// JSON Schema grammar.
|
||||
question: z.string().trim().min(1),
|
||||
});
|
||||
|
||||
const reconstructionSchemaV2 = z.object({
|
||||
summary: z.string().min(1),
|
||||
actors: z.array(itemSchemaV1),
|
||||
@@ -167,7 +182,7 @@ const reconstructionSchemaV2 = z.object({
|
||||
}),
|
||||
),
|
||||
contradictions: z.array(itemSchemaV1),
|
||||
importantUnknowns: z.array(itemSchemaV1),
|
||||
importantUnknowns: z.array(importantUnknownSchema),
|
||||
plausibleInterpretations: z.array(
|
||||
z.object({
|
||||
id: z.string().min(1),
|
||||
@@ -205,6 +220,19 @@ export const reconstructionV2Schema = z.object({
|
||||
nextQuestion: nextQuestionSchema,
|
||||
});
|
||||
|
||||
// Generation-only contract. Durable reconstruction parsing remains tolerant so
|
||||
// historical snapshots and otherwise valid canonical reconstructions do not fail
|
||||
// solely because presentation wording is absent or malformed.
|
||||
export const reconstructionGenerationSchema = reconstructionV2Schema.extend({
|
||||
reconstruction: reconstructionSchemaV2.extend({
|
||||
importantUnknowns: z.array(generatedImportantUnknownSchema),
|
||||
}),
|
||||
});
|
||||
|
||||
export const reconstructionGenerationJsonSchema = z.toJSONSchema(
|
||||
reconstructionGenerationSchema,
|
||||
);
|
||||
|
||||
// Outer wrapper for API return (includes diagnostics + v0.2 data)
|
||||
export const analyseResponseV2Schema = z.object({
|
||||
inputClassification: inputClassificationSchema.optional(),
|
||||
|
||||
Reference in New Issue
Block a user