mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-09-20 13:52:28 +03:00
Merged after a maintainer rework that kept every one of @HouMinXi's commits intact. **What the rework added on top of the contribution:** the new DB health-check behaviour is gated behind a default-off feature flag (`src/shared/constants/featureFlagDefinitions.ts`, `defaultValue: "false"`), documented in `docs/reference/FEATURE_FLAGS.md` with the description key carried into all 66 locales, so the release default is unchanged and the new bounds only apply when an operator opts in. The optional-FTS5 migration set was reconciled by hand with the "180" entry that landed meanwhile (`src/lib/db/migrationRunner/constants.ts`). **Carried from your rebased head:** the `/api/db/health` local-only classification in `src/server/authz/routeGuard.ts` plus its `routeGuard` assertion — `runManagedDbHealthCheck()` forks native diagnostics into a child process, so Hard Rules #15/#17 apply. Re-verified here: 37 pass / 0 fail. Validated as a combined board first (this PR merged with the 21 siblings of the same wave on the release tip): eslint with the frozen suppressions, typecheck:core, check:open-sse-typecheck, complexity, cognitive-complexity, changelog-integrity, i18n new-key coverage, docs-counts, docs-sync, migration-numbering, provider-consistency and a duplicate-identifier audit all green, plus 176 passing / 0 failing focused node:test cases across the 25 test files the wave touches and the dashboard test under Vitest (2/0). Then re-validated alone on the fresh tip before this merge: conflicts re-resolved, file sizes rebaselined for this PR's own growth, eslint and this PR's focused tests re-run. Thank you for the depth of this one — the resource-bounds suite and the sql.js startup/backup coverage are the kind of tests that keep a database layer honest.
85 lines
3.6 KiB
TypeScript
85 lines
3.6 KiB
TypeScript
/**
|
|
* Regression test for #3396: Mistral returns 400 when the last message is
|
|
* `role: "assistant"` with plain text content.
|
|
*
|
|
* `stripTrailingAssistantOrphanToolUse` only removed tool_use blocks — it left
|
|
* trailing text-only assistant messages intact. Mistral (and providers sharing
|
|
* the same constraint) reject such requests with:
|
|
* "400: Expected last role User or Tool (or Assistant with prefix True)
|
|
* for serving but got assistant"
|
|
*
|
|
* The fix adds `stripTrailingAssistantForProvider(messages, provider)` which
|
|
* also drops a trailing text-only assistant message for providers that require
|
|
* user-last format (e.g. "mistral").
|
|
*/
|
|
import { describe, it } from "node:test";
|
|
import assert from "node:assert/strict";
|
|
import { stripTrailingAssistantOrphanToolUse } from "../../open-sse/services/contextManager.ts";
|
|
import { stripTrailingAssistantForProvider } from "../../open-sse/services/contextManager.ts";
|
|
|
|
const user = (content: string) => ({ role: "user", content });
|
|
const assistant = (content: string) => ({ role: "assistant", content });
|
|
const assistantWithToolUse = () => ({
|
|
role: "assistant",
|
|
content: [{ type: "tool_use", id: "t1", name: "fn", input: {} }],
|
|
});
|
|
|
|
describe("stripTrailingAssistantForProvider (#3396)", () => {
|
|
it("strips trailing text-only assistant message for mistral", () => {
|
|
const msgs = [user("hi"), assistant("hello from model")];
|
|
const result = stripTrailingAssistantForProvider(msgs, "mistral");
|
|
assert.strictEqual(result.length, 1);
|
|
assert.strictEqual(result[0].role, "user");
|
|
});
|
|
|
|
it("strips trailing assistant with array-string content for mistral", () => {
|
|
const msgs = [
|
|
user("hi"),
|
|
{ role: "assistant", content: [{ type: "text", text: "response" }] },
|
|
];
|
|
const result = stripTrailingAssistantForProvider(msgs, "mistral");
|
|
assert.strictEqual(result.length, 1);
|
|
assert.strictEqual(result[0].role, "user");
|
|
});
|
|
|
|
it("does NOT strip trailing tool_use assistant (stripTrailingOrphan handles that)", () => {
|
|
const msgs = [user("hi"), assistantWithToolUse()];
|
|
const result = stripTrailingAssistantForProvider(msgs, "mistral");
|
|
// tool_use trailing is already handled by stripTrailingAssistantOrphanToolUse;
|
|
// this function should not double-strip or corrupt tool-use state
|
|
assert.strictEqual(result.length, 2);
|
|
});
|
|
|
|
it("does NOT strip trailing text assistant for non-mistral providers", () => {
|
|
const msgs = [user("hi"), assistant("response")];
|
|
const result = stripTrailingAssistantForProvider(msgs, "openai");
|
|
assert.strictEqual(result.length, 2);
|
|
});
|
|
|
|
it("does NOT strip trailing text assistant for anthropic/claude", () => {
|
|
const msgs = [user("hi"), assistant("continue from here")];
|
|
const result = stripTrailingAssistantForProvider(msgs, "claude");
|
|
assert.strictEqual(result.length, 2);
|
|
});
|
|
|
|
it("returns messages unchanged when last message is user", () => {
|
|
const msgs = [assistant("a"), user("b")];
|
|
const result = stripTrailingAssistantForProvider(msgs, "mistral");
|
|
assert.strictEqual(result.length, 2);
|
|
assert.strictEqual(result[1].role, "user");
|
|
});
|
|
|
|
it("returns empty array unchanged", () => {
|
|
const result = stripTrailingAssistantForProvider([], "mistral");
|
|
assert.deepStrictEqual(result, []);
|
|
});
|
|
|
|
it("existing stripTrailingAssistantOrphanToolUse still works after change", () => {
|
|
const msgs = [user("hi"), assistantWithToolUse()];
|
|
const result = stripTrailingAssistantOrphanToolUse(msgs);
|
|
// tool_use block removed; empty assistant → dropped
|
|
assert.strictEqual(result.length, 1);
|
|
assert.strictEqual(result[0].role, "user");
|
|
});
|
|
});
|