@burtson-labs/bandit-engine 2.0.60 → 2.0.62

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/dist/index.js CHANGED
@@ -19435,6 +19435,64 @@ var init_telemetry = __esm({
19435
19435
  }
19436
19436
  });
19437
19437
 
19438
+ // src/store/engineStore.ts
19439
+ var import_zustand15, STORAGE_KEY2, readStored, useEngineStore;
19440
+ var init_engineStore = __esm({
19441
+ "src/store/engineStore.ts"() {
19442
+ "use strict";
19443
+ import_zustand15 = require("zustand");
19444
+ init_packageSettingsStore();
19445
+ init_authenticationService();
19446
+ init_debugLogger();
19447
+ STORAGE_KEY2 = "bandit.selectedEngine";
19448
+ readStored = () => {
19449
+ try {
19450
+ return typeof window !== "undefined" ? window.localStorage.getItem(STORAGE_KEY2) : null;
19451
+ } catch {
19452
+ return null;
19453
+ }
19454
+ };
19455
+ useEngineStore = (0, import_zustand15.create)((set, get) => ({
19456
+ selectedEngine: readStored(),
19457
+ engines: [],
19458
+ loaded: false,
19459
+ setSelectedEngine: (id) => {
19460
+ set({ selectedEngine: id });
19461
+ try {
19462
+ window.localStorage.setItem(STORAGE_KEY2, id);
19463
+ } catch {
19464
+ }
19465
+ },
19466
+ getSelectedEngine: () => get().selectedEngine || usePackageSettingsStore.getState().settings?.defaultModel || "bandit-core",
19467
+ fetchEngines: async () => {
19468
+ const settings = usePackageSettingsStore.getState().settings;
19469
+ const base = settings?.gatewayApiUrl?.replace(/\/$/, "") ?? "";
19470
+ if (!base || settings?.playgroundMode || base.toLowerCase().startsWith("playground://")) {
19471
+ set({ loaded: true });
19472
+ return;
19473
+ }
19474
+ try {
19475
+ const headers = { "Content-Type": "application/json" };
19476
+ const token = authenticationService.getToken();
19477
+ if (token) headers["Authorization"] = `Bearer ${token}`;
19478
+ const res = await fetch(`${base}/models`, { headers });
19479
+ const data = await res.json();
19480
+ if (res.ok && Array.isArray(data?.models)) {
19481
+ set({ engines: data.models, loaded: true });
19482
+ } else {
19483
+ set({ loaded: true });
19484
+ }
19485
+ } catch (error) {
19486
+ debugLogger.error("Failed to fetch engines", {
19487
+ error: error instanceof Error ? error.message : String(error)
19488
+ });
19489
+ set({ loaded: true });
19490
+ }
19491
+ }
19492
+ }));
19493
+ }
19494
+ });
19495
+
19438
19496
  // src/chat/hooks/useMemoryEnhancer.tsx
19439
19497
  var import_rxjs20, MEMORY_LIMIT, MIN_MEMORY_WORDS, MERGE_THRESHOLD, REJECT_ECHO_THRESHOLD, REJECT_DUPLICATE_THRESHOLD, CONTEXTUAL_DIVERGENCE_THRESHOLD, normalizeText, isStructurallyDuplicate, isAboutBandit, hasEngagementValue, isMemoryTooShortOrGeneric, isPersonalText, mergeMemory, isVoiceShifted, sanitizeMemory, sanitizeMemoryText, shouldAcceptMemory, isContextuallyDivergent, useMemoryEnhancer;
19440
19498
  var init_useMemoryEnhancer = __esm({
@@ -20177,12 +20235,12 @@ var init_useAIProvider = __esm({
20177
20235
  init_knowledgeStore();
20178
20236
  init_aiProviderStore();
20179
20237
  init_telemetry();
20238
+ init_engineStore();
20180
20239
  init_conversationStore();
20181
20240
  init_useMemoryEnhancer();
20182
20241
  init_useVectorStore();
20183
20242
  init_embeddingService();
20184
20243
  init_useMoodEngine();
20185
- init_packageSettingsStore();
20186
20244
  init_prompts();
20187
20245
  init_preferencesStore();
20188
20246
  init_mcp();
@@ -20546,7 +20604,7 @@ var init_useAIProvider = __esm({
20546
20604
  question: pendingQuestion,
20547
20605
  images: pendingImages
20548
20606
  });
20549
- const modelName = usePackageSettingsStore.getState().settings?.defaultModel || "bandit-core:4b-it-qat";
20607
+ const modelName = useEngineStore.getState().getSelectedEngine();
20550
20608
  const CONFIG = modelConfigs[modelName] ?? modelConfigs["bandit-core:4b-it-qat"];
20551
20609
  const base64Images = imageList.map((img) => img.split(",")[1]);
20552
20610
  const latestEntries = history.slice(-CONFIG.historyMessages);
@@ -20919,6 +20977,7 @@ ${protocol}`;
20919
20977
  let fullMessage = "";
20920
20978
  let latestDisplayMessage = "";
20921
20979
  let sawToolBlock = false;
20980
+ const nativeToolCalls = [];
20922
20981
  const stripThinking = (text) => {
20923
20982
  let result = text.replace(/<think>[\s\S]*?<\/think>/g, "");
20924
20983
  const openIdx = result.indexOf("<think>");
@@ -20957,6 +21016,11 @@ ${protocol}`;
20957
21016
  const sub = stream.subscribe({
20958
21017
  next: (data) => {
20959
21018
  if (!data?.message?.content && !data?.message?.tool_calls) return;
21019
+ if (Array.isArray(data.message.tool_calls) && data.message.tool_calls.length > 0) {
21020
+ nativeToolCalls.push(...data.message.tool_calls);
21021
+ sawToolBlock = true;
21022
+ clearFlushTimer();
21023
+ }
20960
21024
  if (data.message.content) {
20961
21025
  fullMessage += data.message.content;
20962
21026
  telemetryEvent("tool_loop:llm_chunk", { chunk: data.message.content });
@@ -21005,6 +21069,20 @@ ${protocol}`;
21005
21069
  if (!sawToolBlock) {
21006
21070
  flushNow();
21007
21071
  }
21072
+ if (nativeToolCalls.length > 0 && !/```(?:tool_code|TOOL_CODE)/.test(fullMessage)) {
21073
+ for (const raw of nativeToolCalls) {
21074
+ const tc = raw;
21075
+ const fn = tc.function?.name ?? tc.name;
21076
+ if (!fn) continue;
21077
+ const rawArgs = tc.function?.arguments ?? tc.arguments ?? {};
21078
+ const argStr = typeof rawArgs === "string" ? rawArgs : JSON.stringify(rawArgs ?? {});
21079
+ fullMessage += `
21080
+
21081
+ \`\`\`tool_code
21082
+ ${fn}(${argStr})
21083
+ \`\`\``;
21084
+ }
21085
+ }
21008
21086
  const toolCallMatches = fullMessage.match(/```(?:tool_code|TOOL_CODE)\s*\n([^`]+)\n```/gi);
21009
21087
  let enhancedMessage = fullMessage;
21010
21088
  const summarizableResults = [];
@@ -24778,6 +24856,7 @@ var init_chat_app_bar = __esm({
24778
24856
  init_packageSettingsStore();
24779
24857
  init_useFeatures();
24780
24858
  init_conversationSyncStore();
24859
+ init_engineStore();
24781
24860
  import_shallow2 = require("zustand/shallow");
24782
24861
  import_jsx_runtime24 = require("react/jsx-runtime");
24783
24862
  CDN_BASE = "https://cdn.burtson.ai/";
@@ -24827,6 +24906,7 @@ var init_chat_app_bar = __esm({
24827
24906
  menuText
24828
24907
  } = theme.palette.chat.appBar;
24829
24908
  const [modelAnchorEl, setModelAnchorEl] = (0, import_react31.useState)(null);
24909
+ const [engineAnchorEl, setEngineAnchorEl] = (0, import_react31.useState)(null);
24830
24910
  const [voiceAnchorEl, setVoiceAnchorEl] = (0, import_react31.useState)(null);
24831
24911
  const [modalOpen, setModalOpen] = (0, import_react31.useState)(false);
24832
24912
  const [confirmModelChangeOpen, setConfirmModelChangeOpen] = (0, import_react31.useState)(false);
@@ -24947,6 +25027,16 @@ var init_chat_app_bar = __esm({
24947
25027
  const selectedModel = useModelStore((s) => s.selectedModel);
24948
25028
  const currentModel = useModelStore((s) => s.availableModels.find((m) => m.name === selectedModel));
24949
25029
  const currentAvatar = currentModel?.avatarBase64 || modelAvatars2[selectedModel] || banditHead3;
25030
+ const engines = useEngineStore((s) => s.engines);
25031
+ const selectedEngine = useEngineStore((s) => s.selectedEngine);
25032
+ const effectiveEngineId = selectedEngine || usePackageSettingsStore.getState().settings?.defaultModel || "bandit-core";
25033
+ const currentEngine = engines.find((e) => e.id === effectiveEngineId) || engines.find((e) => effectiveEngineId.startsWith(e.id + ":"));
25034
+ const resolvedEngineId = currentEngine?.id ?? effectiveEngineId;
25035
+ const cleanEngineName = (name) => (name || "").replace(/\s*\([^)]*\)\s*$/, "").trim();
25036
+ const engineDisplay = cleanEngineName(currentEngine?.displayName) || "Engine";
25037
+ (0, import_react31.useEffect)(() => {
25038
+ useEngineStore.getState().fetchEngines();
25039
+ }, []);
24950
25040
  const pendingModelAvatar = useModelStore.getState().availableModels.find((m) => m.name === pendingModel)?.avatarBase64 || modelAvatars2[pendingModel || ""] || banditHead3;
24951
25041
  const resolvedHomeUrl = preferences.homeUrl?.trim() || packageSettings?.homeUrl?.trim() || "";
24952
25042
  const homeTooltip = (() => {
@@ -25259,6 +25349,81 @@ var init_chat_app_bar = __esm({
25259
25349
  )
25260
25350
  }
25261
25351
  ) }),
25352
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_material23.Tooltip, { title: `Engine \xB7 ${engineDisplay}`, arrow: true, children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
25353
+ import_material23.IconButton,
25354
+ {
25355
+ onClick: (e) => setEngineAnchorEl(e.currentTarget),
25356
+ sx: pillButtonStyles,
25357
+ "aria-label": `Change base model (engine). Currently ${engineDisplay}`,
25358
+ children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(PsychologyIcon, { fontSize: "small" })
25359
+ }
25360
+ ) }),
25361
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(
25362
+ import_material23.Menu,
25363
+ {
25364
+ anchorEl: engineAnchorEl,
25365
+ open: Boolean(engineAnchorEl),
25366
+ onClose: () => setEngineAnchorEl(null),
25367
+ transformOrigin: { horizontal: "right", vertical: "top" },
25368
+ anchorOrigin: { horizontal: "right", vertical: "bottom" },
25369
+ children: [
25370
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_material23.Typography, { variant: "overline", sx: { px: 2, color: theme.palette.text.secondary }, children: "Engine \xB7 base model" }),
25371
+ engines.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_material23.MenuItem, { disabled: true, children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_material23.Typography, { variant: "body2", children: "No engines available" }) }),
25372
+ engines.map((engine) => {
25373
+ const badges = [
25374
+ engine.vision && "vision",
25375
+ engine.tools && "tools",
25376
+ engine.thinking && "thinking",
25377
+ engine.cloud && "cloud"
25378
+ ].filter(Boolean);
25379
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(
25380
+ import_material23.MenuItem,
25381
+ {
25382
+ selected: engine.id === resolvedEngineId,
25383
+ disabled: !engine.available,
25384
+ onClick: () => {
25385
+ useEngineStore.getState().setSelectedEngine(engine.id);
25386
+ setEngineAnchorEl(null);
25387
+ },
25388
+ sx: {
25389
+ display: "flex",
25390
+ flexDirection: "column",
25391
+ alignItems: "flex-start",
25392
+ gap: 0.5,
25393
+ py: 1,
25394
+ px: 2,
25395
+ maxWidth: 360,
25396
+ whiteSpace: "normal"
25397
+ },
25398
+ children: [
25399
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(import_material23.Box, { sx: { display: "flex", alignItems: "center", gap: 1, width: "100%" }, children: [
25400
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_material23.Typography, { variant: "body2", sx: { fontWeight: 600, flex: 1 }, children: cleanEngineName(engine.displayName) }),
25401
+ engine.id === resolvedEngineId && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_material23.Box, { sx: { width: 8, height: 8, borderRadius: "50%", bgcolor: theme.palette.primary.main } })
25402
+ ] }),
25403
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_material23.Typography, { variant: "caption", sx: { color: theme.palette.text.secondary }, children: engine.available ? engine.description : engine.unavailableReason || "Unavailable" }),
25404
+ badges.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_material23.Box, { sx: { display: "flex", gap: 0.5, flexWrap: "wrap", mt: 0.25 }, children: badges.map((b) => /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
25405
+ import_material23.Box,
25406
+ {
25407
+ sx: {
25408
+ fontSize: "0.65rem",
25409
+ px: 0.75,
25410
+ py: 0.1,
25411
+ borderRadius: 1,
25412
+ bgcolor: theme.palette.primary.main + "22",
25413
+ color: theme.palette.primary.main
25414
+ },
25415
+ children: b
25416
+ },
25417
+ b
25418
+ )) })
25419
+ ]
25420
+ },
25421
+ engine.id
25422
+ );
25423
+ })
25424
+ ]
25425
+ }
25426
+ ),
25262
25427
  /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
25263
25428
  import_material23.Menu,
25264
25429
  {