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
9 changes: 9 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -621,6 +621,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
# ═══════════════════════════════════════════════════════════════════════════════
Expand Down
Original file line number Diff line number Diff line change
@@ -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))
1 change: 1 addition & 0 deletions docs/reference/ENVIRONMENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,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. |

---

Expand Down
6 changes: 5 additions & 1 deletion open-sse/services/conversationTracker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ export interface ResolveConversationIdInput {
}

export interface ResolveConversationIdResult {
conversationId: string;
conversationId: string | null;
isNewConversation: boolean;
}

Expand Down Expand Up @@ -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.
Expand Down
31 changes: 31 additions & 0 deletions tests/unit/conversationTracker.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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({
Expand Down
Loading