fix: make graph-backed questions authoritative
This commit is contained in:
@@ -5,6 +5,7 @@ import {
|
||||
classifyObservationRelationship,
|
||||
COMPARABILITY_REASONING_NODE_ID,
|
||||
formulateQuestion,
|
||||
formulateTieResolutionQuestion,
|
||||
} from "./question-formulator.js";
|
||||
import {
|
||||
graphUpdateSchema,
|
||||
@@ -1623,6 +1624,97 @@ function findNodeById(graph, nodeId) {
|
||||
return (graph.nodes || []).find((node) => node.id === nodeId) || null;
|
||||
}
|
||||
|
||||
export function determineGraphBackedQuestion({ situationGraph }) {
|
||||
const graphSnapshot = cloneJsonSafe(situationGraph);
|
||||
let updatedSituationGraph = cloneJsonSafe(situationGraph);
|
||||
let deterministicSelection = selectActiveUnknownCandidate(
|
||||
updatedSituationGraph,
|
||||
updatedSituationGraph.resolvedNodeIds || [],
|
||||
);
|
||||
|
||||
const decompositionResult = runDeterministicDecomposition({
|
||||
graphSnapshot,
|
||||
proposalSnapshot: {
|
||||
addedNodes: [],
|
||||
updatedNodes: [],
|
||||
addedEdges: [],
|
||||
removedEdgeIds: [],
|
||||
resolvedUnknownNodeIds: [],
|
||||
affectedNodeIds: [],
|
||||
selectedQuestion: null,
|
||||
},
|
||||
updatedSituationGraph,
|
||||
reasoningResolution: { reasoningStateOverride: {} },
|
||||
deterministicSelection,
|
||||
});
|
||||
|
||||
if (!decompositionResult.success) {
|
||||
return decompositionResult;
|
||||
}
|
||||
|
||||
updatedSituationGraph = decompositionResult.updatedSituationGraph;
|
||||
updatedSituationGraph.reasoningState = buildReasoningState(
|
||||
updatedSituationGraph,
|
||||
);
|
||||
deterministicSelection = selectActiveUnknownCandidate(
|
||||
updatedSituationGraph,
|
||||
updatedSituationGraph.resolvedNodeIds || [],
|
||||
);
|
||||
updatedSituationGraph.activeUnknownNodeId =
|
||||
deterministicSelection?.status === "selected"
|
||||
? deterministicSelection.nodeId
|
||||
: null;
|
||||
updatedSituationGraph.currentSummary = describeGraph(updatedSituationGraph);
|
||||
|
||||
const selectedNode =
|
||||
deterministicSelection?.status === "selected"
|
||||
? findNodeById(updatedSituationGraph, deterministicSelection.nodeId)
|
||||
: null;
|
||||
const formulatedQuestion = selectedNode
|
||||
? formulateQuestion({
|
||||
node: selectedNode,
|
||||
graph: updatedSituationGraph,
|
||||
context: { selectionState: deterministicSelection },
|
||||
})
|
||||
: null;
|
||||
|
||||
return {
|
||||
success: true,
|
||||
updatedSituationGraph,
|
||||
deterministicSelection,
|
||||
selectedQuestion:
|
||||
deterministicSelection?.status === "ambiguous"
|
||||
? {
|
||||
id: "q_tie_resolution",
|
||||
...formulateTieResolutionQuestion({ graph: updatedSituationGraph }),
|
||||
nodeId: null,
|
||||
tiedCandidateIds: deterministicSelection.tiedCandidateIds,
|
||||
}
|
||||
: deterministicSelection?.status === "selected" && formulatedQuestion
|
||||
? {
|
||||
nodeId: deterministicSelection.nodeId,
|
||||
question:
|
||||
formulatedQuestion.question || deterministicSelection.question,
|
||||
reason: formulatedQuestion.reason,
|
||||
strategy: formulatedQuestion.strategy,
|
||||
investigationStrategy: formulatedQuestion.investigationStrategy,
|
||||
questionComplexity: formulatedQuestion.questionComplexity,
|
||||
plainLanguageNormalisations:
|
||||
formulatedQuestion.plainLanguageNormalisations,
|
||||
}
|
||||
: null,
|
||||
atomicityAssessment: decompositionResult.atomicityAssessment,
|
||||
decompositionPerformed:
|
||||
decompositionResult.decompositionAttempted &&
|
||||
decompositionResult.decompositionAccepted,
|
||||
decompositionAttempted: decompositionResult.decompositionAttempted,
|
||||
selectedUnknownBefore: decompositionResult.selectedUnknownBefore,
|
||||
selectedUnknownAfter: deterministicSelection?.nodeId ?? null,
|
||||
questionComplexityAssessment:
|
||||
formulatedQuestion?.questionComplexity ?? null,
|
||||
};
|
||||
}
|
||||
|
||||
function runDeterministicDecomposition({
|
||||
graphSnapshot,
|
||||
proposalSnapshot,
|
||||
|
||||
+66
-25
@@ -13,10 +13,14 @@ import {
|
||||
updateCaseRequestSchema,
|
||||
} from "./schema.js";
|
||||
import { buildInitialGraph, describeGraph } from "./builder.js";
|
||||
import { applyValidatedProposal } from "./apply-proposal.js";
|
||||
import {
|
||||
applyValidatedProposal,
|
||||
determineGraphBackedQuestion,
|
||||
} from "./apply-proposal.js";
|
||||
import { buildGraphUpdatePrompt } from "./prompt-builder.js";
|
||||
import {
|
||||
buildReasoningState,
|
||||
formulateQuestion,
|
||||
formulateTieResolutionQuestion,
|
||||
} from "./question-formulator.js";
|
||||
import { parseGraphUpdateProposal } from "./update-proposal.js";
|
||||
@@ -41,6 +45,13 @@ function buildDiagnostics({
|
||||
graph,
|
||||
graphReferenceValidation,
|
||||
unknownSelectionExplanation,
|
||||
reconstructionQuestion,
|
||||
reconstructionQuestionAccepted,
|
||||
reconstructionQuestionRejectionReasons,
|
||||
finalGraphBackedQuestion,
|
||||
selectedUnknownNodeId,
|
||||
decompositionApplied,
|
||||
questionComplexityAssessment,
|
||||
}) {
|
||||
return {
|
||||
promptVersion: analysis?.promptVersion ?? null,
|
||||
@@ -54,6 +65,14 @@ function buildDiagnostics({
|
||||
compatibilityChanges: analysis?.compatibilityChanges ?? [],
|
||||
compatibilityWarnings: analysis?.compatibilityWarnings ?? [],
|
||||
unknownSelectionExplanation: unknownSelectionExplanation ?? null,
|
||||
reconstructionQuestion: reconstructionQuestion ?? null,
|
||||
reconstructionQuestionAccepted: reconstructionQuestionAccepted ?? null,
|
||||
reconstructionQuestionRejectionReasons:
|
||||
reconstructionQuestionRejectionReasons ?? [],
|
||||
finalGraphBackedQuestion: finalGraphBackedQuestion ?? null,
|
||||
selectedUnknownNodeId: selectedUnknownNodeId ?? null,
|
||||
decompositionApplied: decompositionApplied ?? false,
|
||||
questionComplexityAssessment: questionComplexityAssessment ?? null,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -263,23 +282,11 @@ export async function startCase(body) {
|
||||
});
|
||||
|
||||
const currentSummary = describeGraph(initialGraph);
|
||||
const deterministicSelection = selectActiveUnknownCandidate(
|
||||
{
|
||||
...initialGraph,
|
||||
resolvedNodeIds: [],
|
||||
},
|
||||
[],
|
||||
);
|
||||
const activeUnknownNodeId =
|
||||
deterministicSelection?.status === "selected"
|
||||
? deterministicSelection.nodeId
|
||||
: null;
|
||||
|
||||
const situationGraph = makeGraph({
|
||||
const initialSituationGraph = makeGraph({
|
||||
centralStatement: scenario,
|
||||
nodes: initialGraph.nodes,
|
||||
edges: initialGraph.edges,
|
||||
activeUnknownNodeId,
|
||||
activeUnknownNodeId: null,
|
||||
resolvedNodeIds: [],
|
||||
currentSummary,
|
||||
reasoningState: buildReasoningState({
|
||||
@@ -290,17 +297,20 @@ export async function startCase(body) {
|
||||
}),
|
||||
});
|
||||
|
||||
situationGraphSchema.parse(situationGraph);
|
||||
situationGraphSchema.parse(initialSituationGraph);
|
||||
|
||||
const graphReferenceValidation = validateGraphReferences(situationGraph);
|
||||
const selectedQuestion =
|
||||
deterministicSelection?.status === "ambiguous"
|
||||
? {
|
||||
id: "q_tie_resolution",
|
||||
...formulateTieResolutionQuestion({ graph: situationGraph }),
|
||||
tiedCandidateIds: deterministicSelection.tiedCandidateIds,
|
||||
}
|
||||
: (analysis.nextQuestion ?? null);
|
||||
const graphReferenceValidation = validateGraphReferences(
|
||||
initialSituationGraph,
|
||||
);
|
||||
const initialQuestionResult = determineGraphBackedQuestion({
|
||||
situationGraph: initialSituationGraph,
|
||||
});
|
||||
const situationGraph = initialQuestionResult.success
|
||||
? initialQuestionResult.updatedSituationGraph
|
||||
: initialSituationGraph;
|
||||
const selectedQuestion = initialQuestionResult.success
|
||||
? initialQuestionResult.selectedQuestion
|
||||
: null;
|
||||
const unknownSelectionExplanation = buildUnknownSelectionDiagnostics(
|
||||
situationGraph,
|
||||
[],
|
||||
@@ -315,6 +325,22 @@ export async function startCase(body) {
|
||||
graph: situationGraph,
|
||||
graphReferenceValidation,
|
||||
unknownSelectionExplanation,
|
||||
reconstructionQuestion: analysis.nextQuestion?.question ?? null,
|
||||
reconstructionQuestionAccepted: false,
|
||||
reconstructionQuestionRejectionReasons:
|
||||
analysis.nextQuestion?.question != null
|
||||
? [
|
||||
"reconstruction_question_not_authoritative",
|
||||
"graph_backed_pipeline_required",
|
||||
]
|
||||
: [],
|
||||
finalGraphBackedQuestion: selectedQuestion?.question ?? null,
|
||||
selectedUnknownNodeId:
|
||||
initialQuestionResult.selectedUnknownAfter ?? null,
|
||||
decompositionApplied:
|
||||
initialQuestionResult.decompositionPerformed ?? false,
|
||||
questionComplexityAssessment:
|
||||
initialQuestionResult.questionComplexityAssessment ?? null,
|
||||
}),
|
||||
validationErrors: graphReferenceValidation.errors,
|
||||
statusCode: 500,
|
||||
@@ -330,6 +356,21 @@ export async function startCase(body) {
|
||||
graph: situationGraph,
|
||||
graphReferenceValidation,
|
||||
unknownSelectionExplanation,
|
||||
reconstructionQuestion: analysis.nextQuestion?.question ?? null,
|
||||
reconstructionQuestionAccepted: false,
|
||||
reconstructionQuestionRejectionReasons:
|
||||
analysis.nextQuestion?.question != null
|
||||
? [
|
||||
"reconstruction_question_not_authoritative",
|
||||
"graph_backed_pipeline_required",
|
||||
]
|
||||
: [],
|
||||
finalGraphBackedQuestion: selectedQuestion?.question ?? null,
|
||||
selectedUnknownNodeId: initialQuestionResult.selectedUnknownAfter ?? null,
|
||||
decompositionApplied:
|
||||
initialQuestionResult.decompositionPerformed ?? false,
|
||||
questionComplexityAssessment:
|
||||
initialQuestionResult.questionComplexityAssessment ?? null,
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user