diff --git a/.env.example b/.env.example index 5ba3fed9..962e1bcf 100644 --- a/.env.example +++ b/.env.example @@ -83,10 +83,10 @@ # AI_HISTORY_MAX_TURNS=10 # Older turns dropped (1–100) # AI_HISTORY_MAX_TURN_CHARS=4096 # Per-turn char cap (100–100000) # AI_HISTORY_MAX_TOTAL_CHARS=32768 # Total cap across all turns (100–1000000) -# Feature flag: when true, the inventory query path matches bins server-side -# and the LLM only formats the answer + relevance strings. Default false until -# validated. -# AI_DETERMINISTIC_MATCH=false +# Inventory query: when true (default), uses LLM planner + SQL executor. +# When false, falls back to legacy LLM-as-matcher. Disable for providers +# without structured-output support (some Ollama models). +# AI_DETERMINISTIC_MATCH=true # ── Backups ─────────────────────────────────── # BACKUP_ENABLED=false diff --git a/server/src/__tests__/aiStreamDeterministicFlag.test.ts b/server/src/__tests__/aiStreamDeterministicFlag.test.ts index 4818c846..cd35911f 100644 --- a/server/src/__tests__/aiStreamDeterministicFlag.test.ts +++ b/server/src/__tests__/aiStreamDeterministicFlag.test.ts @@ -9,7 +9,7 @@ beforeEach(() => { app = createApp(); }); afterEach(() => { delete process.env.AI_DETERMINISTIC_MATCH; }); describe('AI_DETERMINISTIC_MATCH flag', () => { - it('legacy path is used when the flag is unset (no AI mock — just verifies the route still accepts the request)', async () => { + it('planner path is used by default (no AI mock — just verifies the route still accepts the request)', async () => { const { token } = await createTestUser(app); const loc = await createTestLocation(app, token); await createTestBin(app, token, loc.id, { name: 'Battery Bin' }); diff --git a/server/src/lib/__tests__/configDeterministicMatch.test.ts b/server/src/lib/__tests__/configDeterministicMatch.test.ts index 503ed2b5..90543447 100644 --- a/server/src/lib/__tests__/configDeterministicMatch.test.ts +++ b/server/src/lib/__tests__/configDeterministicMatch.test.ts @@ -14,16 +14,16 @@ describe('config.aiDeterministicMatch', () => { await initialize(); }); - it('defaults to false when env var unset', async () => { + it('defaults to true when env var unset', async () => { delete process.env.AI_DETERMINISTIC_MATCH; const { config } = await import('../config.js'); - expect(config.aiDeterministicMatch).toBe(false); + expect(config.aiDeterministicMatch).toBe(true); }); - it('is true when AI_DETERMINISTIC_MATCH=true', async () => { - process.env.AI_DETERMINISTIC_MATCH = 'true'; + it('is false when AI_DETERMINISTIC_MATCH=false', async () => { + process.env.AI_DETERMINISTIC_MATCH = 'false'; const { config } = await import('../config.js'); - expect(config.aiDeterministicMatch).toBe(true); + expect(config.aiDeterministicMatch).toBe(false); delete process.env.AI_DETERMINISTIC_MATCH; }); }); diff --git a/server/src/lib/config.ts b/server/src/lib/config.ts index cd0fe6bc..1041a406 100644 --- a/server/src/lib/config.ts +++ b/server/src/lib/config.ts @@ -250,10 +250,10 @@ export const config = Object.freeze({ aiHistoryMaxTurns: clamp(parseInt(process.env.AI_HISTORY_MAX_TURNS || '10', 10), 1, 100, 10), aiHistoryMaxTurnChars: clamp(parseInt(process.env.AI_HISTORY_MAX_TURN_CHARS || '4096', 10), 100, 100000, 4096), aiHistoryMaxTotalChars: clamp(parseInt(process.env.AI_HISTORY_MAX_TOTAL_CHARS || '32768', 10), 100, 1_000_000, 32768), - // Feature flag: when true, the inventory query path uses deterministic - // server-side bin matching and reduces the LLM to a formatter. Default off - // — flip on per-environment to validate before retiring the legacy path. - aiDeterministicMatch: parseBool(process.env.AI_DETERMINISTIC_MATCH, false), + // Inventory query: when true (default), uses LLM planner + SQL executor. + // When false, falls back to legacy LLM-as-matcher. Disable for providers + // without structured-output support (some Ollama models). + aiDeterministicMatch: parseBool(process.env.AI_DETERMINISTIC_MATCH, true), // Backup backupEnabled: parseBool(process.env.BACKUP_ENABLED, false),