Files
OmniRoute/src/types/databaseSettings.ts
Paijo 2a865aaaa7 feat(settings): configurable model catalog cache TTL (#8219)
* feat(settings): configurable model catalog cache TTL

Add modelCatalogCacheTtlMs to DatabaseSettings with default 1500ms.
Extend cache-config API route to accept the new field.
Replace hardcoded catalog cache TTL with dynamic settings value.
Add 'Cache' settings tab at /dashboard/settings/cache with sidebar entry,
i18n keys, header description, and legacy route redirect.

* feat(combo): add model connection filter toggle to ModelSelectModal

Adds a 'Show configured only' checkbox below the search bar in
ModelSelectModal that filters each provider group's models through
hasEligibleConnectionForModel. Toggle state persists in localStorage.

- Import hasEligibleConnectionForModel from domain/connectionModelRules
- showConfiguredOnly state + localStorage persistence
- connectionFilteredGroups memo layered on filteredGroups
- Renders both provider section and empty state from connectionFilteredGroups

* test(combo): add connection filter toggle tests for ModelSelectModal

Three test cases: (1) hide excluded models when toggle on,
(2) show empty state when all models excluded,
(3) drop provider group when all its models excluded.
All 3 tests pass.

* fix(settings): correct cache-config route import + add route/tab coverage

The cache-config route imported get/update helpers from a nonexistent
module (@/lib/localDb/databaseSettings) and called an undefined
updateSettings() in PUT, crashing every request. Import the real
databaseSettings module (matching the sibling database/route.ts
convention) and call updateDatabaseSettings(); idempotencyWindowMs is
routed through the flat @/lib/db/settings module instead, since that is
where it is actually read at runtime (idempotencyLayer.ts,
runtimeSettings.ts) — it was never part of the databaseSettings "cache"
section type.

Also fixes a dashboard-typecheck regression in ModelSelectModal.tsx: the
new connection-filter toggle called hasEligibleConnectionForModel() with
activeProviders entries typed too narrowly to include
providerSpecificData, which real connection objects carry at runtime.

Adds:
- tests/unit/cache-config-route-8219.test.ts: GET/PUT resolve without
  crashing, modelCatalogCacheTtlMs and idempotencyWindowMs round-trip.
- tests/unit/ui/cache-settings-tab-bounds-8219.test.tsx: CacheSettingsTab
  min/max TTL bounds (100ms/60000ms) gate the Save button and surface a
  validation message; in-bounds values PUT correctly.

Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>

* chore(quality): rebaseline sections.ts for #8219 own-growth

---------

Co-authored-by: oyi77 <oyi77@users.noreply.github.com>
Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
2026-07-23 08:46:22 -03:00

136 lines
3.4 KiB
TypeScript

/**
* Database performance optimization settings stored in SQLite key-value pairs.
* User-configurable aggregation, retention, and optimization settings.
*/
export interface DatabaseSettings {
/** 1. Location (read-only display) */
location: {
databasePath: string;
dataDir: string;
walSizeBytes: number;
schemaVersion: number;
};
/** 2. Logs (what gets captured) */
logs: {
detailedLogsEnabled: boolean;
callLogPipelineEnabled: boolean;
maxDetailSizeKb: number;
ringBufferSize: number;
};
/** 3. Backup (backup/restore/import/export) */
backup: {
autoBackupEnabled: boolean;
autoBackupFrequency: "never" | "daily" | "weekly" | "monthly";
keepLastNBackups: number;
};
/** 4. Cache (moved from CacheSettingsTab) */
cache: {
semanticCacheEnabled: boolean;
semanticCacheMaxSize: number;
semanticCacheTTL: number;
promptCacheEnabled: boolean;
promptCacheStrategy: "auto" | "system-only" | "manual";
alwaysPreserveClientCache: "auto" | "always" | "never";
/** Model catalog /v1/models response cache TTL in milliseconds. */
modelCatalogCacheTtlMs: number;
};
/** 5. Retention (per-table cleanup policies) */
retention: {
quotaSnapshots: number;
compressionAnalytics: number;
mcpAudit: number;
a2aEvents: number;
callLogs: number;
usageHistory: number;
memoryEntries: number;
domainCostHistory: number;
compressionCacheStats: number;
xpAuditLog: number;
compressionRunTelemetry: number;
autoCleanupEnabled: boolean;
};
/** 6. Compression (aggregation) */
aggregation: {
enabled: boolean;
rawDataRetentionDays: number;
granularity: "hourly" | "daily" | "weekly";
};
/** 7. Optimization (auto_vacuum, VACUUM, page/cache) */
optimization: {
autoVacuumMode: "NONE" | "FULL" | "INCREMENTAL";
scheduledVacuum: "never" | "daily" | "weekly" | "monthly";
vacuumHour: number;
pageSize: number;
cacheSize: number;
optimizeOnStartup: boolean;
};
/** Read-only stats */
stats: {
databaseSizeBytes: number;
pageCount: number;
freelistCount: number;
lastVacuumAt: string | null;
lastOptimizationAt: string | null;
integrityCheck: "ok" | "error" | null;
};
}
/** Default database settings */
export const DEFAULT_DATABASE_SETTINGS: Omit<DatabaseSettings, "location" | "stats"> = {
logs: {
detailedLogsEnabled: false,
callLogPipelineEnabled: false,
maxDetailSizeKb: 10,
ringBufferSize: 500,
},
backup: {
autoBackupEnabled: false,
autoBackupFrequency: "never",
keepLastNBackups: 5,
},
cache: {
semanticCacheEnabled: true,
semanticCacheMaxSize: 100,
semanticCacheTTL: 1800000,
promptCacheEnabled: true,
promptCacheStrategy: "auto",
alwaysPreserveClientCache: "auto",
modelCatalogCacheTtlMs: 1500,
},
retention: {
quotaSnapshots: 7,
compressionAnalytics: 30,
mcpAudit: 30,
a2aEvents: 30,
callLogs: 30,
usageHistory: 30,
memoryEntries: 30,
domainCostHistory: 30,
compressionCacheStats: 30,
xpAuditLog: 30,
compressionRunTelemetry: 30,
autoCleanupEnabled: true,
},
aggregation: {
enabled: true,
rawDataRetentionDays: 30,
granularity: "daily",
},
optimization: {
autoVacuumMode: "FULL",
scheduledVacuum: "weekly",
vacuumHour: 2,
pageSize: 4096,
cacheSize: 65536,
optimizeOnStartup: true,
},
};