diff --git a/lib/analysis.js b/lib/analysis.js index 414783d..2c02134 100644 --- a/lib/analysis.js +++ b/lib/analysis.js @@ -80,23 +80,40 @@ export async function analyseScenario(scenario, opts = {}) { let providerApiPath; let providerExecution; try { - const providerResult = await provider.generateReconstruction( - promptObj.prompt, - reconstructionModelName, - opts.generationSchema ?? reconstructionGenerationJsonSchema, - { outputZodSchema: reconstructionGenerationSchema }, - ); - if ( - providerResult && - typeof providerResult === "object" && - "response" in providerResult && - "providerApiPath" in providerResult - ) { - rawResponse = providerResult.response; - providerApiPath = providerResult.providerApiPath; - providerExecution = providerResult.providerExecution; + // Condition D freeform mode: call without schema enforcement → genuine raw text. + if (opts.freeform) { + const providerResult = await provider.generateReconstruction( + promptObj.prompt, + reconstructionModelName, + null, // no JSON schema + { outputZodSchema: reconstructionGenerationSchema }, + ); + if (providerResult && typeof providerResult === "object" && "response" in providerResult) { + rawResponse = providerResult.response; + providerApiPath = providerResult.providerApiPath ?? null; + providerExecution = providerResult.providerExecution ?? null; + } else { + rawResponse = typeof providerResult === "string" ? providerResult : JSON.stringify(providerResult); + } } else { - rawResponse = providerResult; + const providerResult = await provider.generateReconstruction( + promptObj.prompt, + reconstructionModelName, + opts.generationSchema ?? reconstructionGenerationJsonSchema, + { outputZodSchema: reconstructionGenerationSchema }, + ); + if ( + providerResult && + typeof providerResult === "object" && + "response" in providerResult && + "providerApiPath" in providerResult + ) { + rawResponse = providerResult.response; + providerApiPath = providerResult.providerApiPath; + providerExecution = providerResult.providerExecution; + } else { + rawResponse = providerResult; + } } } catch (e) { return buildErrorResponse( @@ -111,6 +128,22 @@ export async function analyseScenario(scenario, opts = {}) { const duration = Date.now() - startTime; + // ── Condition D freeform: return raw text without parsing ── + if (opts.freeform) { + const text = typeof rawResponse === "string" + ? rawResponse + : JSON.stringify(rawResponse); + return buildSuccessResultFreeform( + text, + reconstructionModelName, + duration, + promptVersion, + null, // no compatibility for freeform + providerApiPath, + providerExecution, + ); + } + // Try to capture raw response for diagnostics let rawResponseStr; try { @@ -228,6 +261,34 @@ function buildSuccessResultV2(data, model, duration, version, compatibility) { }; } +/** Build a success result for Condition D freeform output — no schema validation. */ +function buildSuccessResultFreeform( + text, + model, + duration, + version, + compatibility, + providerApiPath, + providerExecution, +) { + return { + success: true, + validationStatus: "freeform", + modelName: model, + responseDurationMs: duration, + rawResponse: text.slice(0, 3000), + promptVersion: version, + inputClassification: null, + reconstruction: text, // genuine freeform text — not JSON-structured output + evidence: undefined, + nextQuestion: undefined, + errors: undefined, + presentationValidationStatus: "valid", + presentationValidationIssues: [], + ...(compatibility ? buildCompatibilityDiagnostics(compatibility) : {}), + }; +} + function buildSuccessResultV1(data, model, duration, version, compatibility) { return { success: true, diff --git a/lib/graph/orchestrator.js b/lib/graph/orchestrator.js index b2aa023..9b51542 100644 --- a/lib/graph/orchestrator.js +++ b/lib/graph/orchestrator.js @@ -373,6 +373,9 @@ export async function startCase(body, dependencies = {}) { if (dependencies.generationSchema) { analysisOptions.generationSchema = dependencies.generationSchema; } + if (dependencies.freeform) { + analysisOptions.freeform = dependencies.freeform; + } const analysis = await analyseScenario(scenario, analysisOptions); if (!analysis.success) { @@ -394,6 +397,23 @@ export async function startCase(body, dependencies = {}) { }; } + // Condition D freeform + reconstruction-only: skip structured graph pipeline. + if (dependencies.reconstructionOnly && dependencies.freeform) { + return { + success: true, + summary: analysis.summary ?? null, + reconstruction: analysis.reconstruction, // genuine freeform text + situationGraph: null, + selectedQuestion: null, + diagnostics: buildDiagnostics({ + analysis, + graph: null, + graphReferenceValidation: null, + }), + statusCode: 200, + }; + } + const initialGraph = buildInitialGraph({ reconstruction: analysis.reconstruction, evidence: analysis.evidence,