mirror of
https://github.com/Crosstalk-Solutions/project-nomad.git
synced 2026-03-28 03:29:25 +01:00
fix: improve type-safety for KVStore values
This commit is contained in:
parent
e12e7c1696
commit
6817e2e47e
|
|
@ -2,7 +2,6 @@ import { inject } from '@adonisjs/core'
|
|||
import type { HttpContext } from '@adonisjs/core/http'
|
||||
import { ChatService } from '#services/chat_service'
|
||||
import { createSessionSchema, updateSessionSchema, addMessageSchema } from '#validators/chat'
|
||||
import { parseBoolean } from '../utils/misc.js'
|
||||
import KVStore from '#models/kv_store'
|
||||
import { SystemService } from '#services/system_service'
|
||||
import { SERVICE_NAMES } from '../../constants/service_names.js'
|
||||
|
|
@ -20,7 +19,7 @@ export default class ChatsController {
|
|||
const chatSuggestionsEnabled = await KVStore.getValue('chat.suggestionsEnabled')
|
||||
return inertia.render('chat', {
|
||||
settings: {
|
||||
chatSuggestionsEnabled: parseBoolean(chatSuggestionsEnabled),
|
||||
chatSuggestionsEnabled: chatSuggestionsEnabled ?? false,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import { SystemService } from '#services/system_service';
|
|||
import { updateSettingSchema } from '#validators/settings';
|
||||
import { inject } from '@adonisjs/core';
|
||||
import type { HttpContext } from '@adonisjs/core/http'
|
||||
import { parseBoolean } from '../utils/misc.js';
|
||||
import type { KVStoreKey } from '../../types/kv_store.js';
|
||||
|
||||
@inject()
|
||||
export default class SettingsController {
|
||||
|
|
@ -59,7 +59,7 @@ export default class SettingsController {
|
|||
availableModels: availableModels?.models || [],
|
||||
installedModels: installedModels || [],
|
||||
settings: {
|
||||
chatSuggestionsEnabled: parseBoolean(chatSuggestionsEnabled)
|
||||
chatSuggestionsEnabled: chatSuggestionsEnabled ?? false
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
@ -98,7 +98,7 @@ export default class SettingsController {
|
|||
|
||||
async getSetting({ request, response }: HttpContext) {
|
||||
const key = request.qs().key;
|
||||
const value = await KVStore.getValue(key);
|
||||
const value = await KVStore.getValue(key as KVStoreKey);
|
||||
return response.status(200).send({ key, value });
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ export class CheckUpdateJob {
|
|||
`[CheckUpdateJob] Update available: ${result.currentVersion} → ${result.latestVersion}`
|
||||
)
|
||||
} else {
|
||||
await KVStore.setValue('system.updateAvailable', "false")
|
||||
await KVStore.setValue('system.updateAvailable', false)
|
||||
logger.info(
|
||||
`[CheckUpdateJob] System is up to date (${result.currentVersion})`
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import { DateTime } from 'luxon'
|
||||
import { BaseModel, column, SnakeCaseNamingStrategy } from '@adonisjs/lucid/orm'
|
||||
import type { KVStoreKey, KVStoreValue } from '../../types/kv_store.js'
|
||||
import { KV_STORE_SCHEMA, type KVStoreKey, type KVStoreValue } from '../../types/kv_store.js'
|
||||
import { parseBoolean } from '../utils/misc.js'
|
||||
|
||||
/**
|
||||
* Generic key-value store model for storing various settings
|
||||
|
|
@ -17,7 +18,7 @@ export default class KVStore extends BaseModel {
|
|||
declare key: KVStoreKey
|
||||
|
||||
@column()
|
||||
declare value: KVStoreValue
|
||||
declare value: string | null
|
||||
|
||||
@column.dateTime({ autoCreate: true })
|
||||
declare created_at: DateTime
|
||||
|
|
@ -26,26 +27,25 @@ export default class KVStore extends BaseModel {
|
|||
declare updated_at: DateTime
|
||||
|
||||
/**
|
||||
* Get a setting value by key
|
||||
* Get a setting value by key, automatically deserializing to the correct type.
|
||||
*/
|
||||
static async getValue(key: KVStoreKey): Promise<KVStoreValue> {
|
||||
static async getValue<K extends KVStoreKey>(key: K): Promise<KVStoreValue<K> | null> {
|
||||
const setting = await this.findBy('key', key)
|
||||
if (!setting || setting.value === undefined || setting.value === null) {
|
||||
return null
|
||||
}
|
||||
if (typeof setting.value === 'string') {
|
||||
return setting.value
|
||||
}
|
||||
return String(setting.value)
|
||||
const raw = String(setting.value)
|
||||
return (KV_STORE_SCHEMA[key] === 'boolean' ? parseBoolean(raw) : raw) as KVStoreValue<K>
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a setting value by key (creates if not exists)
|
||||
* Set a setting value by key (creates if not exists), automatically serializing to string.
|
||||
*/
|
||||
static async setValue(key: KVStoreKey, value: KVStoreValue): Promise<KVStore> {
|
||||
const setting = await this.firstOrCreate({ key }, { key, value })
|
||||
if (setting.value !== value) {
|
||||
setting.value = value
|
||||
static async setValue<K extends KVStoreKey>(key: K, value: KVStoreValue<K>): Promise<KVStore> {
|
||||
const serialized = String(value)
|
||||
const setting = await this.firstOrCreate({ key }, { key, value: serialized })
|
||||
if (setting.value !== serialized) {
|
||||
setting.value = serialized
|
||||
await setting.save()
|
||||
}
|
||||
return setting
|
||||
|
|
|
|||
|
|
@ -546,7 +546,7 @@ export class DockerService {
|
|||
// If Ollama was just installed, trigger Nomad docs discovery and embedding
|
||||
if (service.service_name === SERVICE_NAMES.OLLAMA) {
|
||||
logger.info('[DockerService] Ollama installation complete. Default behavior is to not enable chat suggestions.')
|
||||
await KVStore.setValue('chat.suggestionsEnabled', "false")
|
||||
await KVStore.setValue('chat.suggestionsEnabled', false)
|
||||
|
||||
logger.info('[DockerService] Ollama installation complete. Triggering Nomad docs discovery...')
|
||||
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@ import { removeStopwords } from 'stopword'
|
|||
import { randomUUID } from 'node:crypto'
|
||||
import { join } from 'node:path'
|
||||
import KVStore from '#models/kv_store'
|
||||
import { parseBoolean } from '../utils/misc.js'
|
||||
import { ZIMExtractionService } from './zim_extraction_service.js'
|
||||
import { ZIM_BATCH_SIZE } from '../../constants/zim_extraction.js'
|
||||
|
||||
|
|
@ -885,7 +884,7 @@ export class RagService {
|
|||
const DOCS_DIR = join(process.cwd(), 'docs')
|
||||
|
||||
const alreadyEmbeddedRaw = await KVStore.getValue('rag.docsEmbedded')
|
||||
if (parseBoolean(alreadyEmbeddedRaw) && !force) {
|
||||
if (alreadyEmbeddedRaw && !force) {
|
||||
logger.info('[RAG] Nomad docs have already been discovered and queued. Skipping.')
|
||||
return { success: true, message: 'Nomad docs have already been discovered and queued. Skipping.' }
|
||||
}
|
||||
|
|
@ -927,7 +926,7 @@ export class RagService {
|
|||
}
|
||||
|
||||
// Update KV store to mark docs as discovered so we don't redo this unnecessarily
|
||||
await KVStore.setValue('rag.docsEmbedded', 'true')
|
||||
await KVStore.setValue('rag.docsEmbedded', true)
|
||||
|
||||
return { success: true, message: `Nomad docs discovery completed. Dispatched ${filesToEmbed.length} embedding jobs.` }
|
||||
} catch (error) {
|
||||
|
|
|
|||
|
|
@ -1,3 +1,13 @@
|
|||
|
||||
export type KVStoreKey = 'chat.suggestionsEnabled' | 'rag.docsEmbedded' | 'system.updateAvailable' | 'system.latestVersion' | 'ui.hasVisitedEasySetup'
|
||||
export type KVStoreValue = string | null
|
||||
export const KV_STORE_SCHEMA = {
|
||||
'chat.suggestionsEnabled': 'boolean',
|
||||
'rag.docsEmbedded': 'boolean',
|
||||
'system.updateAvailable': 'boolean',
|
||||
'system.latestVersion': 'string',
|
||||
'ui.hasVisitedEasySetup': 'boolean',
|
||||
} as const
|
||||
|
||||
type KVTagToType<T extends string> = T extends 'boolean' ? boolean : string
|
||||
|
||||
export type KVStoreKey = keyof typeof KV_STORE_SCHEMA
|
||||
export type KVStoreValue<K extends KVStoreKey> = KVTagToType<(typeof KV_STORE_SCHEMA)[K]>
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user