fix(confidence-engine): retain successful provider path
This commit is contained in:
@@ -73,6 +73,11 @@
|
|||||||
- The detector fix remains unverified live; a subsequent HTTP 500 left the actually attempted provider path unobservable.
|
- The detector fix remains unverified live; a subsequent HTTP 500 left the actually attempted provider path unobservable.
|
||||||
- Case-start failure diagnostics now preserve `providerApiPath` only when the provider reports an endpoint actually attempted during that request. The next step remains one production call.
|
- Case-start failure diagnostics now preserve `providerApiPath` only when the provider reports an endpoint actually attempted during that request. The next step remains one production call.
|
||||||
|
|
||||||
|
## Successful provider-path observability
|
||||||
|
|
||||||
|
- The 502 live run at `dabd9e2` was PATH NOT OBSERVABLE ON FAILURE: provider path survived provider errors but not successful-provider/later-validation failures.
|
||||||
|
- Successful provider results now retain their factual attempted path through later reconstruction-validation failure diagnostics. This corrects only that observability seam; the next unknown remains one real `/api/cases/start` call establishing `/api/chat` versus `/api/generate`.
|
||||||
|
|
||||||
## Current product architecture
|
## Current product architecture
|
||||||
|
|
||||||
Three distinct routes, not a single page:
|
Three distinct routes, not a single page:
|
||||||
|
|||||||
+16
-1
@@ -72,11 +72,23 @@ export async function analyseScenario(scenario, opts = {}) {
|
|||||||
// ── Call provider ──────────────────────────────────
|
// ── Call provider ──────────────────────────────────
|
||||||
const provider = getProvider();
|
const provider = getProvider();
|
||||||
let rawResponse;
|
let rawResponse;
|
||||||
|
let providerApiPath;
|
||||||
try {
|
try {
|
||||||
rawResponse = await provider.generateReconstruction(
|
const providerResult = await provider.generateReconstruction(
|
||||||
promptObj.prompt,
|
promptObj.prompt,
|
||||||
OLLAMA_MODEL,
|
OLLAMA_MODEL,
|
||||||
);
|
);
|
||||||
|
if (
|
||||||
|
providerResult &&
|
||||||
|
typeof providerResult === "object" &&
|
||||||
|
"response" in providerResult &&
|
||||||
|
"providerApiPath" in providerResult
|
||||||
|
) {
|
||||||
|
rawResponse = providerResult.response;
|
||||||
|
providerApiPath = providerResult.providerApiPath;
|
||||||
|
} else {
|
||||||
|
rawResponse = providerResult;
|
||||||
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return buildErrorResponse(
|
return buildErrorResponse(
|
||||||
e.message || "Provider error during analysis",
|
e.message || "Provider error during analysis",
|
||||||
@@ -137,6 +149,7 @@ export async function analyseScenario(scenario, opts = {}) {
|
|||||||
duration,
|
duration,
|
||||||
promptVersion,
|
promptVersion,
|
||||||
compatibility,
|
compatibility,
|
||||||
|
providerApiPath,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -219,6 +232,7 @@ function buildPartialResult(
|
|||||||
duration,
|
duration,
|
||||||
version,
|
version,
|
||||||
compatibility,
|
compatibility,
|
||||||
|
providerApiPath,
|
||||||
) {
|
) {
|
||||||
let errors = [];
|
let errors = [];
|
||||||
const validationIssues = error?.issues ?? [];
|
const validationIssues = error?.issues ?? [];
|
||||||
@@ -245,6 +259,7 @@ function buildPartialResult(
|
|||||||
nextQuestion: undefined,
|
nextQuestion: undefined,
|
||||||
errors,
|
errors,
|
||||||
validationIssues,
|
validationIssues,
|
||||||
|
providerApiPath,
|
||||||
...buildCompatibilityDiagnostics(compatibility),
|
...buildCompatibilityDiagnostics(compatibility),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-1
@@ -247,7 +247,10 @@ class OllamaLlmProvider {
|
|||||||
// Step 5: Parse and return
|
// Step 5: Parse and return
|
||||||
// ================================================================
|
// ================================================================
|
||||||
try {
|
try {
|
||||||
return recoverJson(rawResponse);
|
return {
|
||||||
|
response: recoverJson(rawResponse),
|
||||||
|
providerApiPath: apiUsed,
|
||||||
|
};
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e instanceof SyntaxError) {
|
if (e instanceof SyntaxError) {
|
||||||
const error = new Error(
|
const error = new Error(
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ describe("OllamaLlmProvider chat capability detection", () => {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const { getProvider } = await import("@/lib/llm/provider.js");
|
const { getProvider } = await import("@/lib/llm/provider.js");
|
||||||
await getProvider().generateReconstruction("prompt", "configured-model");
|
const result = await getProvider().generateReconstruction("prompt", "configured-model");
|
||||||
|
|
||||||
expect(JSON.parse(fetchSpy.mock.calls[0][1].body)).toMatchObject({
|
expect(JSON.parse(fetchSpy.mock.calls[0][1].body)).toMatchObject({
|
||||||
model: "configured-model",
|
model: "configured-model",
|
||||||
@@ -23,6 +23,10 @@ describe("OllamaLlmProvider chat capability detection", () => {
|
|||||||
expect(fetchSpy.mock.calls[0][0]).toBe("http://ollama.test/api/chat");
|
expect(fetchSpy.mock.calls[0][0]).toBe("http://ollama.test/api/chat");
|
||||||
expect(fetchSpy.mock.calls[1][0]).toBe("http://ollama.test/api/chat");
|
expect(fetchSpy.mock.calls[1][0]).toBe("http://ollama.test/api/chat");
|
||||||
expect(fetchSpy.mock.calls[1][0]).not.toContain("/api/generate");
|
expect(fetchSpy.mock.calls[1][0]).not.toContain("/api/generate");
|
||||||
|
expect(result).toMatchObject({
|
||||||
|
response: {},
|
||||||
|
providerApiPath: "/api/chat",
|
||||||
|
});
|
||||||
} finally {
|
} finally {
|
||||||
vi.unstubAllGlobals();
|
vi.unstubAllGlobals();
|
||||||
if (originalBaseUrl === undefined) delete process.env.OLLAMA_BASE_URL;
|
if (originalBaseUrl === undefined) delete process.env.OLLAMA_BASE_URL;
|
||||||
|
|||||||
@@ -210,6 +210,8 @@ describe("analyseScenario compatibility", () => {
|
|||||||
|
|
||||||
it("preserves nested validation issues and complete raw output on reconstruction failure", async () => {
|
it("preserves nested validation issues and complete raw output on reconstruction failure", async () => {
|
||||||
mockGenerateReconstruction.mockResolvedValue({
|
mockGenerateReconstruction.mockResolvedValue({
|
||||||
|
providerApiPath: "/api/chat",
|
||||||
|
response: {
|
||||||
inputClassification: {
|
inputClassification: {
|
||||||
primaryType: "unexplained_change",
|
primaryType: "unexplained_change",
|
||||||
secondaryTypes: [],
|
secondaryTypes: [],
|
||||||
@@ -239,6 +241,7 @@ describe("analyseScenario compatibility", () => {
|
|||||||
reason: "reason",
|
reason: "reason",
|
||||||
expectedInformationValue: "high",
|
expectedInformationValue: "high",
|
||||||
},
|
},
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const { analyseScenario } = await import("@/lib/analysis.js");
|
const { analyseScenario } = await import("@/lib/analysis.js");
|
||||||
@@ -253,6 +256,7 @@ describe("analyseScenario compatibility", () => {
|
|||||||
code: "invalid_type",
|
code: "invalid_type",
|
||||||
message: "Required",
|
message: "Required",
|
||||||
}));
|
}));
|
||||||
|
expect(result.providerApiPath).toBe("/api/chat");
|
||||||
expect(result.errors).toContain("reconstruction: Required");
|
expect(result.errors).toContain("reconstruction: Required");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user