levix-bot 2.2.1 → 3.0.0-alpha
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +156 -87
- package/package.json +4 -1
- package/public/dashboard.js +1 -1
- package/src/commands/gemini.cjs +40 -88
- package/src/commands/stt.cjs +8 -3
- package/src/config/settings.cjs +68 -8
- package/src/core/proxy.js +3 -3
- package/src/routes/dashboard.api.esm.js +7 -2
- package/src/services/aiAgent.cjs +35 -5
- package/src/services/aiProviders.cjs +666 -0
- package/src/utils/memory.cjs +1 -1
|
@@ -48,7 +48,10 @@ const settings = require("../config/settings.cjs");
|
|
|
48
48
|
const memory = require("../utils/memory.cjs");
|
|
49
49
|
const secrets = require("../config/secrets.cjs");
|
|
50
50
|
const { DATA_DIR } = require("../config/paths.cjs");
|
|
51
|
-
const {
|
|
51
|
+
const {
|
|
52
|
+
PERSONA_FILE,
|
|
53
|
+
activeProviderKeySetting,
|
|
54
|
+
} = require("../services/aiAgent.cjs");
|
|
52
55
|
const { deleteScheduledJob, retryScheduledJob } = require("../../scheduler.cjs");
|
|
53
56
|
const { describeScheduledJob } = require("../utils/recurrence.cjs");
|
|
54
57
|
|
|
@@ -140,7 +143,9 @@ router.get("/stats", (req, res) => {
|
|
|
140
143
|
|
|
141
144
|
prefix: runtimeConfig.getPrefix(),
|
|
142
145
|
commandCount: getCommandCatalog().length,
|
|
143
|
-
|
|
146
|
+
// The active provider's key — "AI key: Not set" must mean what the
|
|
147
|
+
// bot will actually use, not whatever gemini_api_key happens to hold.
|
|
148
|
+
aiConfigured: !!settings.get(activeProviderKeySetting()),
|
|
144
149
|
|
|
145
150
|
uptime: process.uptime(),
|
|
146
151
|
memory: process.memoryUsage(),
|
package/src/services/aiAgent.cjs
CHANGED
|
@@ -22,6 +22,10 @@ const aiIdentity = require("../config/ai-identity.cjs");
|
|
|
22
22
|
const settings = require("../config/settings.cjs");
|
|
23
23
|
const memory = require("../utils/memory.cjs");
|
|
24
24
|
const { toolDeclarations, describeCall, runTool } = require("./aiTools.cjs");
|
|
25
|
+
const {
|
|
26
|
+
runProviderAgent,
|
|
27
|
+
activeProviderKeySetting,
|
|
28
|
+
} = require("./aiProviders.cjs");
|
|
25
29
|
|
|
26
30
|
// Model, key, tool budget and history length all come from config/settings.cjs
|
|
27
31
|
// (what the dashboard saved, else the default) and are read per call, so changing
|
|
@@ -36,13 +40,18 @@ const { toolDeclarations, describeCall, runTool } = require("./aiTools.cjs");
|
|
|
36
40
|
// client whose model and per-request configuration are passed at call time —
|
|
37
41
|
// which suits Levix, because the model and the tool set are settings that may
|
|
38
42
|
// change between one message and the next.
|
|
39
|
-
let clientCache = { key: null, client: null };
|
|
43
|
+
let clientCache = { key: null, baseUrl: null, client: null };
|
|
40
44
|
|
|
41
45
|
function geminiClient() {
|
|
42
46
|
const key = settings.get("gemini_api_key");
|
|
43
47
|
if (!key) return null;
|
|
44
|
-
|
|
45
|
-
|
|
48
|
+
const baseUrl = String(settings.get("gemini_base_url")).replace(/\/+$/, "");
|
|
49
|
+
if (clientCache.key !== key || clientCache.baseUrl !== baseUrl) {
|
|
50
|
+
clientCache = {
|
|
51
|
+
key,
|
|
52
|
+
baseUrl,
|
|
53
|
+
client: new GoogleGenAI({ apiKey: key, httpOptions: { baseUrl } }),
|
|
54
|
+
};
|
|
46
55
|
}
|
|
47
56
|
return clientCache.client;
|
|
48
57
|
}
|
|
@@ -225,7 +234,9 @@ function withTimeout(promise, ms, label) {
|
|
|
225
234
|
}
|
|
226
235
|
|
|
227
236
|
function isAgentEnabled() {
|
|
228
|
-
|
|
237
|
+
// The key that matters is the active provider's: a panel switch from gemini
|
|
238
|
+
// to openai must not keep the agent gated on (or running on) a Gemini key.
|
|
239
|
+
return Boolean(settings.get(activeProviderKeySetting())) && settings.get("ai_agent");
|
|
229
240
|
}
|
|
230
241
|
|
|
231
242
|
// ===========================================================================
|
|
@@ -247,7 +258,7 @@ function isAgentEnabled() {
|
|
|
247
258
|
|
|
248
259
|
const GOOGLE_SEARCH_TOOL = Object.freeze({ googleSearch: {} });
|
|
249
260
|
|
|
250
|
-
/** Gemini-only.
|
|
261
|
+
/** Gemini-only. The openai and anthropic providers are never offered it. */
|
|
251
262
|
function googleSearchEnabled() {
|
|
252
263
|
return settings.get("ai_google_search") === true;
|
|
253
264
|
}
|
|
@@ -368,6 +379,10 @@ function formatSources(sources) {
|
|
|
368
379
|
/**
|
|
369
380
|
* Run one agent turn.
|
|
370
381
|
*
|
|
382
|
+
* Dispatches on the `ai_provider` setting: gemini runs the native loop below
|
|
383
|
+
* (built-in Google Search, Files uploads); openai and anthropic run the same
|
|
384
|
+
* loop over their own wire formats in aiProviders.cjs.
|
|
385
|
+
*
|
|
371
386
|
* @param {object} options
|
|
372
387
|
* @param {Array} options.parts - the Gemini parts for this message
|
|
373
388
|
* @param {Array} [options.history]- prior conversation
|
|
@@ -384,6 +399,20 @@ async function runAgent({
|
|
|
384
399
|
useTools = true,
|
|
385
400
|
maxSteps = null,
|
|
386
401
|
} = {}) {
|
|
402
|
+
const provider = settings.get("ai_provider");
|
|
403
|
+
if (provider !== "gemini") {
|
|
404
|
+
const result = await runProviderAgent(provider, {
|
|
405
|
+
parts,
|
|
406
|
+
history: trimHistory(history),
|
|
407
|
+
systemInstruction: buildSystemInstruction(context),
|
|
408
|
+
status,
|
|
409
|
+
context,
|
|
410
|
+
useTools,
|
|
411
|
+
maxSteps,
|
|
412
|
+
});
|
|
413
|
+
return { ...result, history: trimHistory(result.history) };
|
|
414
|
+
}
|
|
415
|
+
|
|
387
416
|
const genAI = geminiClient();
|
|
388
417
|
if (!genAI) throw new Error("GEMINI_API_KEY غير معرف");
|
|
389
418
|
|
|
@@ -540,6 +569,7 @@ module.exports = {
|
|
|
540
569
|
buildSystemInstruction,
|
|
541
570
|
loadPersona,
|
|
542
571
|
isAgentEnabled,
|
|
572
|
+
activeProviderKeySetting,
|
|
543
573
|
isFileReferenceError,
|
|
544
574
|
sanitizeHistoryForFiles,
|
|
545
575
|
trimHistory,
|