feat(sse): allow disabling conversation tracking (#13150)

* feat(sse): allow disabling conversation tracking

* docs: document OMNIROUTE_DISABLE_CONVERSATION_TRACKING
This commit is contained in:
Aaron Scherer
2026-09-17 14:24:21 -05:00
committed by GitHub
parent 30451e63af
commit 7e0c9f526a
5 changed files with 47 additions and 1 deletions

View File

@@ -622,6 +622,15 @@ ALLOW_API_KEY_REVEAL=false
# Validated to >= 1, clamped to <= 32. | Default: 3
# COMBO_CONCURRENCY_PER_MODEL=3
# Disable conversation-history tracking (#13150).
# Used by: open-sse/services/conversationTracker.ts. resolveConversationId()
# returns an untracked result before it reads SQLite or parses message history,
# and the switch also covers client-supplied session IDs. Routing sessions are
# unaffected and existing records are not deleted. Use it when the dashboard's
# conversation view is unused and the turn table has grown large.
# Set to 1 to disable. | Default: unset (tracking enabled)
# OMNIROUTE_DISABLE_CONVERSATION_TRACKING=1
# ═══════════════════════════════════════════════════════════════════════════════
# 7. URLS & CLOUD SYNC
# ═══════════════════════════════════════════════════════════════════════════════

View File

@@ -0,0 +1 @@
- **feat(sse):** `OMNIROUTE_DISABLE_CONVERSATION_TRACKING=1` turns off conversation-history collection for operators who do not use the dashboard's conversation view. `resolveConversationId()` returns an untracked result before it reads SQLite or parses message history, and the switch also covers client-supplied session IDs. Routing-session handling is unchanged, tracking stays on by default, and existing records are not deleted. One reporting install held 5.97 million turn records at about 4.26 GB ([#13150](https://github.com/diegosouzapw/OmniRoute/pull/13150))

View File

@@ -313,6 +313,7 @@ OmniRoute provides a two-layer defense: request-side injection scanning and resp
| `OMNIROUTE_PREFER_CLAUDE_CODE_FOR_UNPREFIXED_CLAUDE_MODELS` | `false` | `open-sse/services/model.ts` | Opt-in: route bare `claude-*` model IDs from Claude Code clients through the Claude Code OAuth account instead of requiring a provider prefix. Explicit provider prefixes still win. Also configurable via a dashboard toggle on the Claude provider page. |
| `COMBO_CONCURRENCY_PER_MODEL` | `3` | `open-sse/services/comboConfig.ts` | Per-model concurrency cap for round-robin combos (#9100). The round-robin combo semaphore was hard-capped at 3 concurrent requests per model with no override, serializing higher-concurrency traffic behind that cap. Validated to `>= 1`, clamped to `<= 32`. |
| `DISABLE_CONTEXT_WINDOW_CHECKS` | `false` | `open-sse/handlers/chatCore.ts` | Dangerous opt-in that skips OmniRoute's local context-window / max-input-token check for direct single-model requests. Upstream providers still enforce their actual limits; prompt compression and the model's own output-token cap remain active. Effective precedence is Feature Flags DB override > environment variable > default; no restart is required. |
| `OMNIROUTE_DISABLE_CONVERSATION_TRACKING` | _(unset)_ | `open-sse/services/conversationTracker.ts` | Set `1` to stop collecting conversation history. `resolveConversationId()` returns an untracked result before it reads SQLite or parses message history, and client-supplied session IDs are covered too. Routing-session handling is unchanged and existing records are not deleted. For deployments that do not use the dashboard's conversation view and want the turn table to stop growing. |
---

View File

@@ -77,7 +77,7 @@ export interface ResolveConversationIdInput {
}
export interface ResolveConversationIdResult {
conversationId: string;
conversationId: string | null;
isNewConversation: boolean;
}
@@ -459,6 +459,10 @@ const MAX_STORED_ID_LENGTH = 128;
export async function resolveConversationId(
input: ResolveConversationIdInput
): Promise<ResolveConversationIdResult> {
if (process.env.OMNIROUTE_DISABLE_CONVERSATION_TRACKING === "1") {
return { conversationId: null, isNewConversation: false };
}
// Client override wins outright — deterministic, zero heuristic risk.
// Same header feature #8249 already reads (chatCore.ts); we don't invent a
// new prefix so the existing header's contract/format stays unchanged.

View File

@@ -144,6 +144,37 @@ test("computeFingerprintHash: identical apiKeyId/model/toolNames produce the sam
assert.equal(a, b);
});
test("resolveConversationId: disabled tracking skips database access and message parsing", async () => {
const { getDbInstance } = await import("../../src/lib/db/core.ts");
const db = getDbInstance();
const previous = process.env.OMNIROUTE_DISABLE_CONVERSATION_TRACKING;
const prepare = test.mock.method(db, "prepare", () => {
throw new Error("disabled tracking must not access SQLite");
});
process.env.OMNIROUTE_DISABLE_CONVERSATION_TRACKING = "1";
try {
for (const clientSessionIdHeader of [null, "explicit-session"]) {
const result = await resolveConversationId({
body: {
get messages() {
throw new Error("disabled tracking must not parse messages");
},
},
model: "test-model",
apiKeyId: "test-key",
clientSessionIdHeader,
correlationId: "disabled-tracking",
});
assert.deepEqual(result, { conversationId: null, isNewConversation: false });
}
assert.equal(prepare.mock.callCount(), 0);
} finally {
prepare.mock.restore();
if (previous === undefined) delete process.env.OMNIROUTE_DISABLE_CONVERSATION_TRACKING;
else process.env.OMNIROUTE_DISABLE_CONVERSATION_TRACKING = previous;
}
});
test("resolveConversationId: exact-match continuation reuses the same id", async () => {
const apiKeyId = "key-exact";
const turn1 = await resolveConversationId({