289 lines
13 KiB
JavaScript
289 lines
13 KiB
JavaScript
const INTENTS = new Set([
|
|
"casual",
|
|
"factual",
|
|
"support",
|
|
"explain_internal",
|
|
"admin_task",
|
|
"codex_task",
|
|
"feedback_export",
|
|
"debug",
|
|
"unknown"
|
|
]);
|
|
const COMPLEXITIES = new Set(["fast", "normal", "expanded", "unlimited"]);
|
|
const OKF_DEPTHS = new Set(["none", "light", "deep"]);
|
|
const ANSWER_STYLES = new Set(["compact", "normal", "detailed", "json_only"]);
|
|
const SOURCE_DEFAULTS = Object.freeze({
|
|
webui: { hard_chars: null, target_chars: 2500, allow_sections: true, allow_long_answer: true, allow_split: false },
|
|
discord: { hard_chars: 1900, target_chars: 1200, allow_sections: true, allow_long_answer: false, allow_split: false },
|
|
twitch: { hard_chars: 450, target_chars: 320, allow_sections: false, allow_long_answer: false, allow_split: false },
|
|
youtube: { hard_chars: 1800, target_chars: 900, allow_sections: false, allow_long_answer: false, allow_split: false },
|
|
kick: { hard_chars: 450, target_chars: 320, allow_sections: false, allow_long_answer: false, allow_split: false },
|
|
other: { hard_chars: 1000, target_chars: 700, allow_sections: false, allow_long_answer: false, allow_split: false }
|
|
});
|
|
const MODE_BUDGETS = Object.freeze({
|
|
fast: 1024,
|
|
normal: 4096,
|
|
expanded: 8192,
|
|
unlimited: 16384
|
|
});
|
|
|
|
function buildControllerDecision({
|
|
message = "",
|
|
role = "user",
|
|
scope = "assistant",
|
|
originContext = null,
|
|
gateDecision = null,
|
|
requestClass = "simple_answer",
|
|
config = {}
|
|
} = {}) {
|
|
const text = String(gateDecision?.message || message || "");
|
|
const source = sourceKey(originContext);
|
|
const signals = detectSignals(text, role, scope);
|
|
const lowConfidenceGate = gateDecision && (
|
|
gateDecision.confidence < 0.5 ||
|
|
/(?:low_confidence|timeout|error|invalid|unsafe|escalated)/i.test(gateDecision.reason_code || "")
|
|
);
|
|
let intent = intentFromSignals(signals, requestClass);
|
|
let complexity = complexityFromSignals(signals, requestClass, role);
|
|
let okfRetrieval = okfDepthFromSignals(signals, complexity);
|
|
let answerStyle = answerStyleFromSignals(signals, complexity, source);
|
|
|
|
if (lowConfidenceGate && complexity === "fast") {
|
|
complexity = "normal";
|
|
okfRetrieval = okfRetrieval === "none" && signals.needsOkf ? "light" : okfRetrieval;
|
|
}
|
|
if (gateDecision?.forced && complexity === "fast") {
|
|
complexity = "normal";
|
|
}
|
|
if (signals.adminOnly && role !== "admin" && complexity === "unlimited") {
|
|
complexity = "expanded";
|
|
}
|
|
|
|
const decision = normalizeControllerDecision({
|
|
schema: "lumi.ai.controller.v1",
|
|
route: gateDecision?.route || "main_llm",
|
|
intent,
|
|
complexity,
|
|
okf_retrieval: okfRetrieval,
|
|
answer_style: answerStyle,
|
|
source_scoped: true,
|
|
source_profile: buildSourceProfile(source, config),
|
|
permission_sensitive: signals.permissionSensitive,
|
|
admin_only: signals.adminOnly,
|
|
risk_of_private_data: signals.privateDataRisk,
|
|
confidence: gateDecision ? gateDecision.confidence : confidenceFromSignals(signals),
|
|
reason_code: controllerReason(signals, gateDecision, lowConfidenceGate),
|
|
legacy_request_class: requestClass,
|
|
gate_reason_code: gateDecision?.reason_code || null,
|
|
fallback_used: Boolean(lowConfidenceGate)
|
|
});
|
|
return decision;
|
|
}
|
|
|
|
function normalizeControllerDecision(value = {}) {
|
|
const sourceProfile = value.source_profile && typeof value.source_profile === "object"
|
|
? value.source_profile
|
|
: SOURCE_DEFAULTS.other;
|
|
const requestedSource = String(sourceProfile.source || "other").toLowerCase();
|
|
const source = SOURCE_DEFAULTS[requestedSource] ? requestedSource : "other";
|
|
const fallbackProfile = SOURCE_DEFAULTS[source];
|
|
return {
|
|
schema: "lumi.ai.controller.v1",
|
|
route: ["main_llm", "refusal", "unavailable", "cached_answer", "predefined_answer"].includes(value.route)
|
|
? value.route
|
|
: "main_llm",
|
|
intent: INTENTS.has(value.intent) ? value.intent : "unknown",
|
|
complexity: COMPLEXITIES.has(value.complexity) ? value.complexity : "normal",
|
|
okf_retrieval: OKF_DEPTHS.has(value.okf_retrieval) ? value.okf_retrieval : "light",
|
|
answer_style: ANSWER_STYLES.has(value.answer_style) ? value.answer_style : "normal",
|
|
source_scoped: value.source_scoped !== false,
|
|
source_profile: {
|
|
source,
|
|
hard_chars: sourceProfile.hard_chars === null || sourceProfile.hard_chars === undefined
|
|
? fallbackProfile.hard_chars
|
|
: clampChars(sourceProfile.hard_chars, fallbackProfile.hard_chars || 1000),
|
|
target_chars: clampChars(sourceProfile.target_chars, fallbackProfile.target_chars),
|
|
allow_sections: Boolean(sourceProfile.allow_sections),
|
|
allow_long_answer: Boolean(sourceProfile.allow_long_answer),
|
|
allow_split: Boolean(sourceProfile.allow_split)
|
|
},
|
|
permission_sensitive: Boolean(value.permission_sensitive),
|
|
admin_only: Boolean(value.admin_only),
|
|
risk_of_private_data: Boolean(value.risk_of_private_data),
|
|
confidence: Math.max(0, Math.min(1, Number(value.confidence) || 0)),
|
|
reason_code: /^[a-z0-9_]{2,80}$/.test(String(value.reason_code || ""))
|
|
? value.reason_code
|
|
: "controller_normalized",
|
|
legacy_request_class: String(value.legacy_request_class || "simple_answer"),
|
|
gate_reason_code: value.gate_reason_code ? String(value.gate_reason_code).slice(0, 100) : null,
|
|
fallback_used: Boolean(value.fallback_used)
|
|
};
|
|
}
|
|
|
|
function outputBudgetForController({ config = {}, requestClass = "simple_answer", explicitMaxTokens = null, controllerDecision = null } = {}) {
|
|
const requested = Number(explicitMaxTokens);
|
|
if (Number.isFinite(requested) && requested > 0) return clampBudget(requested);
|
|
const mode = controllerDecision?.complexity || modeFromRequestClass(requestClass);
|
|
const modeBudget = Number(MODE_BUDGETS[mode]);
|
|
if (Number.isFinite(modeBudget) && modeBudget > 0) return clampBudget(modeBudget);
|
|
const classBudget = Number(config.output_budgets?.[requestClass]);
|
|
if (Number.isFinite(classBudget) && classBudget > 0) return clampBudget(classBudget);
|
|
const fallback = Number(config.max_output_tokens);
|
|
return clampBudget(Number.isFinite(fallback) && fallback > 0 ? fallback : 512);
|
|
}
|
|
|
|
function okfLimitForController(decision) {
|
|
if (!decision || decision.okf_retrieval === "none") return 0;
|
|
if (decision.okf_retrieval === "deep") return decision.complexity === "expanded" || decision.complexity === "unlimited" ? 5 : 4;
|
|
return 2;
|
|
}
|
|
|
|
function buildSourceProfile(source, config = {}) {
|
|
const key = SOURCE_DEFAULTS[source] ? source : "other";
|
|
const configured = config.source_profiles?.[key] || {};
|
|
const merged = { ...SOURCE_DEFAULTS[key], ...configured, source: key };
|
|
return {
|
|
source: key,
|
|
hard_chars: merged.hard_chars === null || merged.hard_chars === undefined
|
|
? null
|
|
: Math.max(100, Math.min(12000, Number(merged.hard_chars) || SOURCE_DEFAULTS[key].hard_chars || 1000)),
|
|
target_chars: Math.max(100, Math.min(12000, Number(merged.target_chars) || SOURCE_DEFAULTS[key].target_chars)),
|
|
allow_sections: merged.allow_sections !== false,
|
|
allow_long_answer: merged.allow_long_answer === true,
|
|
allow_split: merged.allow_split === true
|
|
};
|
|
}
|
|
|
|
function detectSignals(message, role, scope) {
|
|
const text = String(message || "");
|
|
const routeOrApi = /\b(?:GET|POST|PUT|PATCH|DELETE|OPTIONS|HEAD)\s+\/[^\s]+/i.test(text) ||
|
|
/\b(?:route|webroute|web route|endpoint|request|api)\b[\s\S]{0,80}\/[a-z0-9_/-]+/i.test(text) ||
|
|
/\/(?:admin|api|setup|auth|plugins|commands|feedback|stats|pages)(?:\/[a-z0-9_/-]*)?\b/i.test(text);
|
|
const lumiInternal = /\b(lumi|webui|route|endpoint|api|plugin|okf|settings?|setup|wizard|admin page|moderator|permission|config|configuration|update|dashboard|navigation)\b/i.test(text) || routeOrApi;
|
|
const codex = /\b(codex|taskfile|task file|implementation plan|acceptance criteria|validation steps|json output)\b/i.test(text);
|
|
const feedbackExport = /\b(feedback.*export|export.*feedback|feedback-to-codex|internal-feedback-to-codex-taskfile)\b/i.test(text);
|
|
const debug = /\b(debug|diagnos|troubleshoot|stack trace|runtime|backend|database|logs?|metrics?|failed|failure|error|exception)\b/i.test(text);
|
|
const security = /\b(security|permission|private|secret|token|password|credential|audit|rbac|role|admin-only|sensitive)\b/i.test(text);
|
|
const unlimited = role === "admin" && /\b(large-scale|architecture analysis|security audit|incident investigation|major migration|major refactor|comprehensive report|extensive synthesis|full system review)\b/i.test(text);
|
|
const support = /\b(how do i|how to|what does|tell me about|explain|configure|set up|setup|support|help with|why does)\b/i.test(text);
|
|
const knowledgeLookup = isSimpleKnowledgeLookup(text);
|
|
const casual = text.split(/\s+/).length <= 18 && !lumiInternal && !debug && !codex && !security && !knowledgeLookup;
|
|
return {
|
|
routeOrApi,
|
|
lumiInternal,
|
|
knowledgeLookup,
|
|
needsOkf: lumiInternal || support || debug || codex || feedbackExport || knowledgeLookup,
|
|
codex,
|
|
feedbackExport,
|
|
debug,
|
|
security,
|
|
unlimited,
|
|
support,
|
|
casual,
|
|
permissionSensitive: security || /\b(admin|moderator|mod|permission|role|private)\b/i.test(text),
|
|
adminOnly: role === "admin" && (debug || security || unlimited || scope === "model_test"),
|
|
privateDataRisk: security || /\b(my|mine|our|token|secret|password|credential|user id|database)\b/i.test(text)
|
|
};
|
|
}
|
|
|
|
function intentFromSignals(signals, requestClass) {
|
|
if (signals.feedbackExport) return "feedback_export";
|
|
if (signals.codex) return "codex_task";
|
|
if (signals.debug) return "debug";
|
|
if (signals.security || signals.adminOnly) return "admin_task";
|
|
if (signals.lumiInternal) return "explain_internal";
|
|
if (signals.knowledgeLookup) return "factual";
|
|
if (signals.support) return "support";
|
|
if (requestClass === "navigation_help") return "support";
|
|
if (signals.casual) return "casual";
|
|
return "factual";
|
|
}
|
|
|
|
function complexityFromSignals(signals, requestClass, role) {
|
|
if (signals.unlimited) return "unlimited";
|
|
if (signals.codex || signals.feedbackExport || signals.debug || signals.security || requestClass === "admin_debug" || requestClass === "explicit_long") {
|
|
return "expanded";
|
|
}
|
|
if (signals.knowledgeLookup) return "fast";
|
|
if (signals.lumiInternal || signals.support || requestClass === "code_custom_command") return "normal";
|
|
if (signals.casual && role !== "admin") return "fast";
|
|
return "normal";
|
|
}
|
|
|
|
function okfDepthFromSignals(signals, complexity) {
|
|
if (!signals.needsOkf) return "none";
|
|
if (complexity === "expanded" || complexity === "unlimited" || signals.routeOrApi || signals.debug) return "deep";
|
|
return "light";
|
|
}
|
|
|
|
function answerStyleFromSignals(signals, complexity, source) {
|
|
if (signals.codex || signals.feedbackExport) return "json_only";
|
|
if (complexity === "expanded" || complexity === "unlimited") return "detailed";
|
|
if (source === "twitch" || source === "kick") return "compact";
|
|
return "normal";
|
|
}
|
|
|
|
function confidenceFromSignals(signals) {
|
|
if (signals.routeOrApi || signals.codex || signals.feedbackExport || signals.debug) return 0.9;
|
|
if (signals.knowledgeLookup) return 0.82;
|
|
if (signals.lumiInternal || signals.support) return 0.8;
|
|
if (signals.casual) return 0.75;
|
|
return 0.6;
|
|
}
|
|
|
|
function controllerReason(signals, gateDecision, lowConfidenceGate) {
|
|
if (lowConfidenceGate) return "gate_fallback_normal";
|
|
if (gateDecision?.forced) return "forced_main_llm";
|
|
if (signals.unlimited) return "admin_unlimited_request";
|
|
if (signals.codex) return "codex_expanded";
|
|
if (signals.feedbackExport) return "feedback_export_expanded";
|
|
if (signals.debug) return "debug_expanded";
|
|
if (signals.routeOrApi) return "route_docs_okf";
|
|
if (signals.lumiInternal) return "lumi_internal_okf";
|
|
if (signals.knowledgeLookup) return "simple_knowledge_okf";
|
|
if (signals.support) return "support_okf";
|
|
if (signals.casual) return "casual_fast";
|
|
return "default_normal";
|
|
}
|
|
|
|
function modeFromRequestClass(requestClass) {
|
|
if (requestClass === "navigation_help" || requestClass === "simple_answer") return "fast";
|
|
if (requestClass === "admin_debug" || requestClass === "explicit_long") return "expanded";
|
|
return "normal";
|
|
}
|
|
|
|
function sourceKey(originContext) {
|
|
const source = String(originContext?.origin || originContext?.platform || "webui").toLowerCase();
|
|
return SOURCE_DEFAULTS[source] ? source : "other";
|
|
}
|
|
|
|
function clampBudget(value) {
|
|
return Math.max(64, Math.min(32768, Math.round(Number(value) || 512)));
|
|
}
|
|
|
|
function clampChars(value, fallback) {
|
|
const number = Number(value);
|
|
return Math.max(100, Math.min(12000, Math.round(Number.isFinite(number) ? number : fallback)));
|
|
}
|
|
|
|
function isSimpleKnowledgeLookup(message) {
|
|
const text = String(message || "").trim();
|
|
if (!text || text.length > 180 || text.split(/\s+/).length > 18) return false;
|
|
if (/\b(who|what)\s+(?:are|r)\s+you\b|\byour\s+(?:name|identity)\b/i.test(text)) return false;
|
|
return (
|
|
/^(?:who|what)\s+(?:is|are|was|were)\s+["'`]?[\p{L}\p{N}_ .'-]{2,80}["'`]?\??$/iu.test(text) ||
|
|
/^(?:tell me about|describe|identify)\s+["'`]?[\p{L}\p{N}_ .'-]{2,80}["'`]?\??$/iu.test(text)
|
|
);
|
|
}
|
|
|
|
module.exports = {
|
|
MODE_BUDGETS,
|
|
SOURCE_DEFAULTS,
|
|
buildControllerDecision,
|
|
buildSourceProfile,
|
|
isSimpleKnowledgeLookup,
|
|
normalizeControllerDecision,
|
|
okfLimitForController,
|
|
outputBudgetForController
|
|
};
|