Lumi/plugins/lumi_ai/backend/config_manager.js
2026-06-12 11:54:46 +02:00

125 lines
5.1 KiB
JavaScript

const fs = require("fs");
const { resolveData, ensureDataDirs } = require("./paths");
const { DEFAULT_SCOPE, normalizeScope } = require("./scope_manager");
const { DEFAULT_RATE_LIMITS, mergeLimits } = require("./rate_limits");
const DEFAULT_CONFIG = {
enabled: false,
selected_model_id: "qwen3-1.7b-q4",
context_size: 4096,
internal_generation_char_budget: 16000,
threads: 0,
gpu_allocation_intent_percent: 0,
concurrency: 1,
max_queue_length: 8,
request_timeout_ms: 120000,
per_user_requests_per_minute: 6,
admin_bypass_rate_limit: false,
assistant_enabled: true,
assistant_debug_logging: false,
assistant_visibility: { admins: true, mods: false, users: false },
commands: {
enabled: true,
triggers: ["assistant", "lumi"],
platforms: { discord: true, twitch: true, youtube: true, kick: false, other: false },
roles: { admins: true, mods: true, users: true },
unavailable_message: "Lumi Assistant is currently unavailable.",
denied_message: "Lumi Assistant access is unavailable for your account."
},
rate_limits: DEFAULT_RATE_LIMITS,
support_scope: DEFAULT_SCOPE,
instructions: {
out_of_scope_response: "I am sorry, but that is outside my scope.",
roleplay_intensity: 0,
community_tone: "",
admin_custom: ""
},
logging: {
log_prompts: false,
log_responses: false,
log_tool_calls: true,
log_metrics: true,
log_internal_audit: true
}
};
function readJson(name, fallback) {
ensureDataDirs();
const file = resolveData("config", name);
if (!fs.existsSync(file)) {
writeJson(name, fallback);
return structuredClone(fallback);
}
try { return { ...structuredClone(fallback), ...JSON.parse(fs.readFileSync(file, "utf8")) }; }
catch { return structuredClone(fallback); }
}
function writeJson(name, value) {
const file = resolveData("config", name);
const tmp = `${file}.tmp`;
fs.writeFileSync(tmp, `${JSON.stringify(value, null, 2)}\n`);
fs.renameSync(tmp, file);
}
function getConfig() {
const config = readJson("ai_config.json", DEFAULT_CONFIG);
if (config.gpu_workload_percent != null && config.gpu_allocation_intent_percent === DEFAULT_CONFIG.gpu_allocation_intent_percent) {
config.gpu_allocation_intent_percent = Math.max(0, Math.min(100, Number(config.gpu_workload_percent) || 0));
}
delete config.gpu_workload_percent;
config.support_scope = normalizeScope(config.support_scope || {
allowed_topics: config.instructions?.allowed_topics,
answer_style: config.instructions?.style,
max_answer_length: config.instructions?.maximum_answer_length
});
config.assistant_visibility = { ...DEFAULT_CONFIG.assistant_visibility, ...(config.assistant_visibility || {}) };
config.instructions = { ...DEFAULT_CONFIG.instructions, ...(config.instructions || {}) };
config.logging = { ...DEFAULT_CONFIG.logging, ...(config.logging || {}) };
config.commands = mergeCommands(config.commands);
config.rate_limits = mergeLimits(config.rate_limits);
return config;
}
function saveConfig(value) {
const merged = { ...DEFAULT_CONFIG, ...value };
const legacyIntent = value.gpu_workload_percent;
merged.gpu_allocation_intent_percent = Math.max(
0,
Math.min(100, Number(value.gpu_allocation_intent_percent ?? legacyIntent) || 0)
);
merged.internal_generation_char_budget = Math.max(
2000,
Math.min(64000, Number(value.internal_generation_char_budget) || DEFAULT_CONFIG.internal_generation_char_budget)
);
delete merged.gpu_workload_percent;
merged.assistant_visibility = { ...DEFAULT_CONFIG.assistant_visibility, ...(value.assistant_visibility || {}) };
merged.support_scope = normalizeScope(value.support_scope);
merged.instructions = { ...DEFAULT_CONFIG.instructions, ...(value.instructions || {}) };
merged.logging = { ...DEFAULT_CONFIG.logging, ...(value.logging || {}) };
merged.commands = mergeCommands(value.commands);
merged.rate_limits = mergeLimits(value.rate_limits);
writeJson("ai_config.json", merged);
return merged;
}
function getRuntimeState() {
return readJson("runtime_state.json", {
desired_state: "stopped", last_known_state: "stopped", last_stop_reason: "never_started",
last_manual_stop: true, last_crashed: false, last_exit_code: null,
last_diagnostic_category: null, selected_model_id: null,
gpu_allocation_actual_percent: 0, gpu_allocation_max_safe_percent: 0,
gpu_allocation_clamped_reason: null, updated_at: new Date().toISOString()
});
}
function saveRuntimeState(value) { writeJson("runtime_state.json", { ...value, updated_at: new Date().toISOString() }); }
function mergeCommands(value = {}) {
return {
...DEFAULT_CONFIG.commands,
...value,
platforms: { ...DEFAULT_CONFIG.commands.platforms, ...(value.platforms || {}) },
roles: { ...DEFAULT_CONFIG.commands.roles, ...(value.roles || {}) },
triggers: Array.isArray(value.triggers) && value.triggers.length
? value.triggers.map((entry) => String(entry).trim().replace(/^!+/, "").toLowerCase()).filter(Boolean)
: [...DEFAULT_CONFIG.commands.triggers]
};
}
module.exports = { DEFAULT_CONFIG, getConfig, saveConfig, getRuntimeState, saveRuntimeState, readJson, writeJson };