Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.d/fixes/14551-output-styles-auto-clarity.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- **fix(compression):** the output-styles path honors the dashboard's Auto-Clarity Bypass toggle, so turning it off keeps the selected styles on security, irreversible-action, clarification, and order-sensitive turns ([#14551](https://github.com/diegosouzapw/OmniRoute/pull/14551)) — thanks @woodsonl
4 changes: 3 additions & 1 deletion open-sse/handlers/chatCore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1664,6 +1664,7 @@ export async function handleChatCore({
}
}
// Phase 4A: unified output styles (supersedes cavemanOutputMode via the back-compat shim).
// The Auto-Clarity toggle is read from cavemanOutputMode.autoClarity.
let outputStyleResult:
import("../services/compression/outputStyles/apply.ts").OutputStylesResult | null = null;
if (config.enabled && compressionHeader?.trim().toLowerCase() !== "off") {
Expand All @@ -1681,7 +1682,8 @@ export async function handleChatCore({
outputStyleResult = applyOutputStyles(
body as Parameters<typeof applyOutputStyles>[0],
selection,
outputStyleLanguage
outputStyleLanguage,
{ autoClarity: config.cavemanOutputMode?.autoClarity }
);
if (outputStyleResult.applied) {
body = outputStyleResult.body as typeof body;
Expand Down
13 changes: 9 additions & 4 deletions open-sse/services/compression/outputStyles/apply.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,11 +122,13 @@ function buildStyleInstructions(resolved: OutputStyleSelectionEntry[], language:
* - SHARED_BOUNDARIES applied once at the end (not per style).
* - Single idempotency marker; re-applying is a no-op.
* - Content bypass runs once across the whole turn (all-or-nothing); reason recorded.
* `options.autoClarity: false` (the Auto-Clarity Bypass toggle) skips it.
*/
export function applyOutputStyles(
body: ChatRequestBody,
selection: OutputStyleSelectionEntry[],
language = "en"
language = "en",
options: { autoClarity?: boolean } = {}
): OutputStylesResult {
const resolved = resolveStyles(selection ?? [], language);
if (resolved.length === 0) {
Expand Down Expand Up @@ -172,9 +174,12 @@ export function applyOutputStyles(
);
if (alreadyApplied) return { body, applied: false, skippedReason: "already_applied" };

// Content bypass (all-or-nothing for the turn): reuse the existing rules verbatim.
const bypass = shouldBypassCavemanOutputMode(messages);
if (bypass) return { body, applied: false, skippedReason: bypass };
// Content bypass (all-or-nothing for the turn): reuse the existing rules verbatim,
// gated on the Auto-Clarity toggle the same way applyCavemanOutputMode gates it.
if (options.autoClarity !== false) {
const bypass = shouldBypassCavemanOutputMode(messages);
if (bypass) return { body, applied: false, skippedReason: bypass };
}

return {
body: { ...body, ...placeSystemInstruction(messages, body.system, instruction) },
Expand Down
92 changes: 92 additions & 0 deletions tests/integration/chatcore-compression-integration.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1115,3 +1115,95 @@ test("chatCore integration: caveman output mode injected when both compression a
globalThis.fetch = originalFetch;
}
});

async function styleInstructionReachesUpstream(
autoClarity: boolean,
outputStyles?: Array<{ id: string; level: "lite" | "full" | "ultra" }>
) {
const provider = "openai";
const model = "gpt-4";

await compressionDb.updateCompressionSettings({
enabled: true,
defaultMode: "off",
autoTriggerTokens: 0,
...(outputStyles ? { outputStyles } : {}),
cavemanOutputMode: {
enabled: !outputStyles,
intensity: "full",
autoClarity,
},
});

const connection = await providersDb.createProviderConnection({
provider,
apiKey: "test-key",
isActive: true,
});

let capturedBody = null as { messages?: Array<{ role?: string; content?: string }> } | null;
globalThis.fetch = async (_url: string | URL | Request, init?: RequestInit) => {
if (init?.body) {
capturedBody = JSON.parse(init.body as string);
}
return new Response(
JSON.stringify({
choices: [{ message: { role: "assistant", content: "ok" } }],
usage: { prompt_tokens: 20, completion_tokens: 4, total_tokens: 24 },
}),
{
status: 200,
headers: { "content-type": "application/json" },
}
);
};

try {
const result = await handleChatCore({
body: {
model,
stream: false,
messages: [{ role: "user", content: "Explain this security vulnerability in detail." }],
},
modelInfo: { provider, model },
credentials: { apiKey: "test-key" },
log: { debug: () => {}, info: () => {}, warn: () => {}, error: () => {} },
clientRawRequest: { endpoint: "/v1/chat/completions", headers: new Map() },
connectionId: connection.id,
onCredentialsRefreshed: () => {},
onRequestSuccess: () => {},
onStreamFailure: () => {},
onDisconnect: () => {},
userAgent: "test-agent",
comboName: null,
});

assert.ok(result.success, "Request should succeed");
assert.ok(capturedBody, "the upstream request was captured");
return (
capturedBody.messages?.some(
(message) => message.role === "system" && /Output Styles/.test(message.content ?? "")
) ?? false
);
} finally {
globalThis.fetch = originalFetch;
}
}

test("chatCore integration: output styles stay on a security-topic turn when Auto-Clarity is off", async () => {
assert.equal(await styleInstructionReachesUpstream(false), true);
});

test("chatCore integration: Auto-Clarity on keeps output styles off a security-topic turn", async () => {
assert.equal(await styleInstructionReachesUpstream(true), false);
});

test("chatCore integration: styles picked in the Output Styles panel stay on a security-topic turn when Auto-Clarity is off", async () => {
assert.equal(
await styleInstructionReachesUpstream(false, [
{ id: "terse-prose", level: "full" },
{ id: "less-code", level: "full" },
]),
true
);
});
14 changes: 14 additions & 0 deletions tests/unit/compression/output-styles-apply.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,20 @@ test("content bypass is all-or-nothing across every selected style", () => {
assert.equal(r.body.messages?.at(-1)?.role, "user"); // untouched
});

test("autoClarity: false keeps every selected style on a turn the bypass would skip", () => {
const r = applyOutputStyles(
{ messages: [{ role: "user", content: "Explain this security vulnerability in detail." }] },
sel(["terse-prose", "full"], ["less-code", "full"]),
"en",
{ autoClarity: false }
);
assert.equal(r.applied, true);
assert.deepEqual(
r.appliedStyles?.map((s) => s.id),
["terse-prose", "less-code"]
);
});

test("no styles selected → body untouched", () => {
const body = { messages: [{ role: "user", content: "Tell me a joke." }] };
const r = applyOutputStyles(body, []);
Expand Down
Loading