@burtson-labs/bandit-engine 2.0.39 → 2.0.41

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.
Files changed (52) hide show
  1. package/README.md +14 -11
  2. package/dist/{aiProviderStore-XN7GCBHJ.mjs → aiProviderStore-UQI33C5E.mjs} +2 -2
  3. package/dist/{chat-5QJNWB7I.mjs → chat-T5ANWWYQ.mjs} +5 -5
  4. package/dist/chat-provider.js +571 -122
  5. package/dist/chat-provider.js.map +1 -1
  6. package/dist/chat-provider.mjs +4 -4
  7. package/dist/{chunk-3A2527TE.mjs → chunk-22EY3ZDC.mjs} +3 -3
  8. package/dist/{chunk-ECRNIAG6.mjs → chunk-3E57HLDV.mjs} +4 -4
  9. package/dist/{chunk-QU5S5QQP.mjs → chunk-54ZQ3FSN.mjs} +481 -77
  10. package/dist/chunk-54ZQ3FSN.mjs.map +1 -0
  11. package/dist/{chunk-JRCDANLN.mjs → chunk-A6OBEF72.mjs} +75 -12
  12. package/dist/{chunk-JRCDANLN.mjs.map → chunk-A6OBEF72.mjs.map} +1 -1
  13. package/dist/{chunk-CDQYBO3Q.mjs → chunk-CX3INLYJ.mjs} +27 -5
  14. package/dist/chunk-CX3INLYJ.mjs.map +1 -0
  15. package/dist/{chunk-QYH2T4L5.mjs → chunk-LYWVYBKU.mjs} +3 -3
  16. package/dist/{chunk-WO5KFNNW.mjs → chunk-QFNEHSY4.mjs} +62 -24
  17. package/dist/chunk-QFNEHSY4.mjs.map +1 -0
  18. package/dist/{chunk-EOKIE5HZ.mjs → chunk-WPWWWUD7.mjs} +51 -46
  19. package/dist/chunk-WPWWWUD7.mjs.map +1 -0
  20. package/dist/{cli/cli.js → cli.js} +423 -10
  21. package/dist/cli.js.map +1 -0
  22. package/dist/{gateway-B0LJ3-jT.d.ts → gateway-5yt_3QDP.d.mts} +4 -4
  23. package/dist/{gateway-B0LJ3-jT.d.mts → gateway-5yt_3QDP.d.ts} +4 -4
  24. package/dist/index.d.mts +2 -2
  25. package/dist/index.d.ts +2 -2
  26. package/dist/index.js +756 -206
  27. package/dist/index.js.map +1 -1
  28. package/dist/index.mjs +8 -8
  29. package/dist/management/management.js +754 -204
  30. package/dist/management/management.js.map +1 -1
  31. package/dist/management/management.mjs +6 -6
  32. package/dist/modals/chat-modal/chat-modal.js +532 -88
  33. package/dist/modals/chat-modal/chat-modal.js.map +1 -1
  34. package/dist/modals/chat-modal/chat-modal.mjs +4 -4
  35. package/dist/public-types.d.mts +1 -1
  36. package/dist/public-types.d.ts +1 -1
  37. package/docs/01_quickstart.md +10 -4
  38. package/docs/02_gateway_api.md +19 -3
  39. package/docs/03_provider_integration.md +5 -4
  40. package/docs/api_reference/media/02_gateway_api.md +19 -3
  41. package/docs/api_reference/media/README.md +3 -1
  42. package/package.json +1 -1
  43. package/dist/chunk-CDQYBO3Q.mjs.map +0 -1
  44. package/dist/chunk-EOKIE5HZ.mjs.map +0 -1
  45. package/dist/chunk-QU5S5QQP.mjs.map +0 -1
  46. package/dist/chunk-WO5KFNNW.mjs.map +0 -1
  47. package/dist/cli/cli.js.map +0 -1
  48. /package/dist/{aiProviderStore-XN7GCBHJ.mjs.map → aiProviderStore-UQI33C5E.mjs.map} +0 -0
  49. /package/dist/{chat-5QJNWB7I.mjs.map → chat-T5ANWWYQ.mjs.map} +0 -0
  50. /package/dist/{chunk-3A2527TE.mjs.map → chunk-22EY3ZDC.mjs.map} +0 -0
  51. /package/dist/{chunk-ECRNIAG6.mjs.map → chunk-3E57HLDV.mjs.map} +0 -0
  52. /package/dist/{chunk-QYH2T4L5.mjs.map → chunk-LYWVYBKU.mjs.map} +0 -0
@@ -12,11 +12,11 @@ var __export = (target, all) => {
12
12
  for (var name in all)
13
13
  __defProp(target, name, { get: all[name], enumerable: true });
14
14
  };
15
- var __copyProps = (to, from9, except, desc) => {
16
- if (from9 && typeof from9 === "object" || typeof from9 === "function") {
17
- for (let key of __getOwnPropNames(from9))
15
+ var __copyProps = (to, from10, except, desc) => {
16
+ if (from10 && typeof from10 === "object" || typeof from10 === "function") {
17
+ for (let key of __getOwnPropNames(from10))
18
18
  if (!__hasOwnProp.call(to, key) && key !== except)
19
- __defProp(to, key, { get: () => from9[key], enumerable: !(desc = __getOwnPropDesc(from9, key)) || desc.enumerable });
19
+ __defProp(to, key, { get: () => from10[key], enumerable: !(desc = __getOwnPropDesc(from10, key)) || desc.enumerable });
20
20
  }
21
21
  return to;
22
22
  };
@@ -1656,57 +1656,15 @@ var init_gateway_service = __esm({
1656
1656
  */
1657
1657
  chat(request) {
1658
1658
  const endpoint = request.provider === "ollama" ? `/api/${request.provider}/chat` : request.provider ? `/api/${request.provider}/chat/completions` : "/api/chat/completions";
1659
- const url = `${this._baseUrl}${endpoint}`;
1660
- debugLogger.debug(`Gateway chat request to ${url} with provider: ${request.provider || "default"}`, {
1661
- model: request.model,
1662
- messageCount: request.messages.length,
1663
- hasImages: !!(request.images && request.images.length > 0),
1664
- imageCount: request.images?.length || 0
1665
- });
1666
- const requestBody = { ...request, stream: request.stream !== false };
1659
+ const fallbackEndpoint = request.provider === "bandit" ? "/completions" : null;
1660
+ const normalizedModel = request.provider === "bandit" ? (() => {
1661
+ const trimmed = (request.model ?? "").replace(/^bandit:/, "").trim();
1662
+ return trimmed !== "" ? trimmed : "bandit-core-1";
1663
+ })() : request.model;
1664
+ const requestBody = { ...request, model: normalizedModel, stream: request.stream !== false };
1667
1665
  return new import_rxjs6.Observable((observer) => {
1668
1666
  const controller = new AbortController();
1669
- const task = fetch(url, {
1670
- method: "POST",
1671
- headers: this._getHeaders(),
1672
- body: JSON.stringify(requestBody),
1673
- signal: controller.signal
1674
- });
1675
- task.then(async (response) => {
1676
- debugLogger.debug(`Gateway chat response status: ${response.status} for provider: ${request.provider || "default"}`);
1677
- if (!response.ok) {
1678
- let errorText = "";
1679
- let errorData = null;
1680
- try {
1681
- errorText = await response.text();
1682
- debugLogger.error("GatewayService chat error response body", {
1683
- status: response.status,
1684
- statusText: response.statusText,
1685
- url: response.url,
1686
- body: errorText
1687
- });
1688
- } catch (readError) {
1689
- debugLogger.error("GatewayService chat failed to read error response body", { error: readError });
1690
- errorText = `Request failed with status ${response.status}`;
1691
- }
1692
- try {
1693
- errorData = JSON.parse(errorText);
1694
- debugLogger.error("GatewayService chat parsed error payload", errorData);
1695
- } catch (parseError) {
1696
- debugLogger.error("GatewayService chat error payload was not valid JSON");
1697
- errorData = { message: errorText };
1698
- }
1699
- const error = this._createHttpError(
1700
- `POST ${url} failed: ${response.status} ${response.statusText ?? ""}`,
1701
- {
1702
- status: response.status,
1703
- statusText: response.statusText ?? "",
1704
- data: errorData,
1705
- url
1706
- }
1707
- );
1708
- throw error;
1709
- }
1667
+ const handleStreamingResponse = async (response) => {
1710
1668
  const reader = response.body?.getReader();
1711
1669
  const decoder = new TextDecoder();
1712
1670
  let buffer = "";
@@ -1781,14 +1739,75 @@ var init_gateway_service = __esm({
1781
1739
  }).catch((err) => observer.error(err));
1782
1740
  };
1783
1741
  read();
1784
- }).catch((err) => {
1785
- debugLogger.error("GatewayService chat fetch error", {
1786
- error: err,
1787
- url,
1788
- provider: request.provider
1742
+ };
1743
+ const sendRequest = (targetEndpoint, allowFallback) => {
1744
+ const url = `${this._baseUrl}${targetEndpoint}`;
1745
+ debugLogger.debug(`Gateway chat request to ${url} with provider: ${request.provider || "default"}`, {
1746
+ model: normalizedModel,
1747
+ messageCount: request.messages.length,
1748
+ hasImages: !!(request.images && request.images.length > 0),
1749
+ imageCount: request.images?.length || 0
1789
1750
  });
1790
- observer.error(err);
1791
- });
1751
+ fetch(url, {
1752
+ method: "POST",
1753
+ headers: this._getHeaders(),
1754
+ body: JSON.stringify(requestBody),
1755
+ signal: controller.signal
1756
+ }).then(async (response) => {
1757
+ debugLogger.debug(`Gateway chat response status: ${response.status} for provider: ${request.provider || "default"}`);
1758
+ if (response.status === 404 && allowFallback && fallbackEndpoint) {
1759
+ debugLogger.warn("GatewayService chat endpoint returned 404, attempting fallback route", {
1760
+ provider: request.provider,
1761
+ attemptedEndpoint: targetEndpoint,
1762
+ fallbackEndpoint
1763
+ });
1764
+ sendRequest(fallbackEndpoint, false);
1765
+ return;
1766
+ }
1767
+ if (!response.ok) {
1768
+ let errorText = "";
1769
+ let errorData = null;
1770
+ try {
1771
+ errorText = await response.text();
1772
+ debugLogger.error("GatewayService chat error response body", {
1773
+ status: response.status,
1774
+ statusText: response.statusText,
1775
+ url: response.url,
1776
+ body: errorText
1777
+ });
1778
+ } catch (readError) {
1779
+ debugLogger.error("GatewayService chat failed to read error response body", { error: readError });
1780
+ errorText = `Request failed with status ${response.status}`;
1781
+ }
1782
+ try {
1783
+ errorData = JSON.parse(errorText);
1784
+ debugLogger.error("GatewayService chat parsed error payload", errorData);
1785
+ } catch (parseError) {
1786
+ debugLogger.error("GatewayService chat error payload was not valid JSON");
1787
+ errorData = { message: errorText };
1788
+ }
1789
+ const error = this._createHttpError(
1790
+ `POST ${url} failed: ${response.status} ${response.statusText ?? ""}`,
1791
+ {
1792
+ status: response.status,
1793
+ statusText: response.statusText ?? "",
1794
+ data: errorData,
1795
+ url
1796
+ }
1797
+ );
1798
+ throw error;
1799
+ }
1800
+ await handleStreamingResponse(response);
1801
+ }).catch((err) => {
1802
+ debugLogger.error("GatewayService chat fetch error", {
1803
+ error: err,
1804
+ url,
1805
+ provider: request.provider
1806
+ });
1807
+ observer.error(err);
1808
+ });
1809
+ };
1810
+ sendRequest(endpoint, true);
1792
1811
  return () => {
1793
1812
  try {
1794
1813
  controller.abort();
@@ -1803,12 +1822,18 @@ var init_gateway_service = __esm({
1803
1822
  generate(request) {
1804
1823
  const endpoint = request.provider ? `/api/${request.provider}/generate` : "/api/generate";
1805
1824
  const url = `${this._baseUrl}${endpoint}`;
1806
- debugLogger.debug(`Gateway generate request to ${url} with provider: ${request.provider || "default"}`);
1825
+ const normalizedModel = request.provider === "bandit" ? (() => {
1826
+ const trimmed = (request.model ?? "").replace(/^bandit:/, "").trim();
1827
+ return trimmed !== "" ? trimmed : "bandit-core-1";
1828
+ })() : request.model;
1829
+ debugLogger.debug(`Gateway generate request to ${url} with provider: ${request.provider || "default"}`, {
1830
+ model: normalizedModel
1831
+ });
1807
1832
  return new import_rxjs6.Observable((observer) => {
1808
1833
  const task = fetch(url, {
1809
1834
  method: "POST",
1810
1835
  headers: this._getHeaders(),
1811
- body: JSON.stringify({ ...request, stream: request.stream !== false })
1836
+ body: JSON.stringify({ ...request, model: normalizedModel, stream: request.stream !== false })
1812
1837
  });
1813
1838
  task.then(async (response) => {
1814
1839
  if (!response.ok) {
@@ -1915,18 +1940,46 @@ var init_gateway_service = __esm({
1915
1940
  );
1916
1941
  }
1917
1942
  _getHeaders() {
1918
- const token = this._tokenFactory();
1943
+ const rawToken2 = this._tokenFactory();
1919
1944
  const headers = {
1920
1945
  "Content-Type": "application/json"
1921
1946
  };
1922
- if (token && token.trim() !== "") {
1923
- headers["Authorization"] = `Bearer ${token}`;
1924
- debugLogger.debug("Authorization header set with token");
1925
- } else {
1947
+ if (!rawToken2) {
1926
1948
  debugLogger.warn("GatewayService: No token found, skipping Authorization header");
1949
+ return headers;
1950
+ }
1951
+ const token = rawToken2.trim();
1952
+ if (token === "") {
1953
+ debugLogger.warn("GatewayService: Token factory returned empty string");
1954
+ return headers;
1955
+ }
1956
+ if (/^(Bearer|ApiKey)\s+/i.test(token)) {
1957
+ headers["Authorization"] = token;
1958
+ debugLogger.debug("GatewayService: Authorization header set with explicit scheme");
1959
+ return headers;
1927
1960
  }
1961
+ if (this._isLikelyBanditApiKey(token)) {
1962
+ headers["Authorization"] = `ApiKey ${token}`;
1963
+ headers["X-Burtson-Api-Key"] = token;
1964
+ debugLogger.debug("GatewayService: Authorization header set using API key");
1965
+ return headers;
1966
+ }
1967
+ if (this._isLikelyJwt(token)) {
1968
+ headers["Authorization"] = `Bearer ${token}`;
1969
+ debugLogger.debug("GatewayService: Authorization header set using bearer token");
1970
+ return headers;
1971
+ }
1972
+ headers["Authorization"] = `Bearer ${token}`;
1973
+ debugLogger.debug("GatewayService: Authorization header defaulted to bearer scheme");
1928
1974
  return headers;
1929
1975
  }
1976
+ _isLikelyJwt(token) {
1977
+ const segments = token.split(".");
1978
+ return segments.length === 3 && segments.every((segment) => segment.length > 0);
1979
+ }
1980
+ _isLikelyBanditApiKey(value) {
1981
+ return /^bai_[a-z0-9]{10,}$/i.test(value);
1982
+ }
1930
1983
  /**
1931
1984
  * Submit feedback to the gateway API
1932
1985
  */
@@ -2406,6 +2459,112 @@ var init_ollama_gateway_service = __esm({
2406
2459
  }
2407
2460
  });
2408
2461
 
2462
+ // src/services/gateway/bandit-gateway.service.ts
2463
+ var import_operators5, normalizeBanditModel, isGatewayMessageContent, normalizeBanditMessages, BanditAIGatewayService;
2464
+ var init_bandit_gateway_service = __esm({
2465
+ "src/services/gateway/bandit-gateway.service.ts"() {
2466
+ "use strict";
2467
+ init_gateway_service();
2468
+ import_operators5 = require("rxjs/operators");
2469
+ init_debugLogger();
2470
+ normalizeBanditModel = (model) => {
2471
+ if (typeof model !== "string" || model.trim() === "") {
2472
+ return "bandit-core-1";
2473
+ }
2474
+ const normalized = model.replace(/^bandit:/, "").trim();
2475
+ return normalized === "" ? "bandit-core-1" : normalized;
2476
+ };
2477
+ isGatewayMessageContent = (value) => {
2478
+ if (!value || typeof value !== "object") return false;
2479
+ const candidate = value;
2480
+ if (candidate.type !== "text" && candidate.type !== "image_url") {
2481
+ return false;
2482
+ }
2483
+ if (candidate.type === "text") {
2484
+ return typeof candidate.text === "string";
2485
+ }
2486
+ if (candidate.type === "image_url") {
2487
+ return !!candidate.image_url && typeof candidate.image_url.url === "string";
2488
+ }
2489
+ return false;
2490
+ };
2491
+ normalizeBanditMessages = (messages) => messages.map((message) => {
2492
+ const content = message.content;
2493
+ if (typeof content === "string") {
2494
+ return { role: message.role, content };
2495
+ }
2496
+ if (Array.isArray(content)) {
2497
+ const filtered = content.filter(isGatewayMessageContent);
2498
+ if (filtered.length === 0) {
2499
+ return { role: message.role, content: JSON.stringify(content) };
2500
+ }
2501
+ return {
2502
+ role: message.role,
2503
+ content: filtered
2504
+ };
2505
+ }
2506
+ return { role: message.role, content: content != null ? String(content) : "" };
2507
+ });
2508
+ BanditAIGatewayService = class {
2509
+ _gatewayService;
2510
+ constructor(gatewayUrl, tokenFactory) {
2511
+ this._gatewayService = new GatewayService(gatewayUrl, tokenFactory);
2512
+ debugLogger.info("BanditAIGatewayService initialized", { gatewayUrl });
2513
+ }
2514
+ async validateServiceAvailability(args) {
2515
+ return this._gatewayService.validateServiceAvailability(args);
2516
+ }
2517
+ chat(request) {
2518
+ const model = normalizeBanditModel(request.model);
2519
+ const messages = normalizeBanditMessages(request.messages);
2520
+ const gatewayRequest = {
2521
+ ...request,
2522
+ messages,
2523
+ model,
2524
+ provider: "bandit",
2525
+ stream: request.stream
2526
+ };
2527
+ debugLogger.debug("Bandit Gateway chat request", {
2528
+ model,
2529
+ messageCount: request.messages.length,
2530
+ stream: request.stream
2531
+ });
2532
+ return this._gatewayService.chat(gatewayRequest);
2533
+ }
2534
+ complete(prompt, options) {
2535
+ const model = normalizeBanditModel(options.model);
2536
+ const gatewayRequest = {
2537
+ model,
2538
+ prompt,
2539
+ temperature: options.temperature,
2540
+ max_tokens: options.max_tokens,
2541
+ stream: options.stream,
2542
+ stop: options.stop,
2543
+ provider: "bandit"
2544
+ };
2545
+ debugLogger.debug("Bandit Gateway generate request", {
2546
+ model,
2547
+ promptLength: prompt.length,
2548
+ stream: options.stream
2549
+ });
2550
+ return this._gatewayService.generate(gatewayRequest);
2551
+ }
2552
+ listModels() {
2553
+ debugLogger.debug("Fetching Bandit models through gateway");
2554
+ return this._gatewayService.listModelsByProvider("bandit");
2555
+ }
2556
+ getHealth() {
2557
+ return this._gatewayService.getHealth().pipe(
2558
+ (0, import_operators5.map)((health) => ({
2559
+ ...health,
2560
+ bandit_status: health.providers.find((p) => p.name === "bandit")?.status || "unavailable"
2561
+ }))
2562
+ );
2563
+ }
2564
+ };
2565
+ }
2566
+ });
2567
+
2409
2568
  // src/services/ai-provider/providers/gateway.provider.ts
2410
2569
  var import_rxjs7, GatewayProvider;
2411
2570
  var init_gateway_provider = __esm({
@@ -2419,6 +2578,7 @@ var init_gateway_provider = __esm({
2419
2578
  init_azure_openai_gateway_service();
2420
2579
  init_anthropic_gateway_service();
2421
2580
  init_ollama_gateway_service();
2581
+ init_bandit_gateway_service();
2422
2582
  GatewayProvider = class {
2423
2583
  config;
2424
2584
  gatewayService;
@@ -2460,6 +2620,9 @@ var init_gateway_provider = __esm({
2460
2620
  case "anthropic":
2461
2621
  this.providerSpecificService = new AnthropicGatewayService(gatewayUrl, tokenFactory);
2462
2622
  break;
2623
+ case "bandit":
2624
+ this.providerSpecificService = new BanditAIGatewayService(gatewayUrl, tokenFactory);
2625
+ break;
2463
2626
  case "ollama":
2464
2627
  this.providerSpecificService = new OllamaGatewayService(gatewayUrl, tokenFactory);
2465
2628
  break;
@@ -2474,6 +2637,16 @@ var init_gateway_provider = __esm({
2474
2637
  role: msg.role,
2475
2638
  content: msg.content
2476
2639
  }));
2640
+ const normalizeImageUrl2 = (value) => {
2641
+ if (!value) {
2642
+ return value;
2643
+ }
2644
+ const trimmed = value.trim();
2645
+ if (/^data:/i.test(trimmed) || /^https?:\/\//i.test(trimmed)) {
2646
+ return trimmed;
2647
+ }
2648
+ return `data:image/jpeg;base64,${trimmed}`;
2649
+ };
2477
2650
  if (request.images && request.images.length > 0) {
2478
2651
  const lastUserMessageIndex = messages.map((m) => m.role).lastIndexOf("user");
2479
2652
  if (this.config.provider === "ollama") {
@@ -2483,7 +2656,7 @@ var init_gateway_provider = __esm({
2483
2656
  images: request.images
2484
2657
  };
2485
2658
  }
2486
- } else if (["openai", "azure-openai", "anthropic"].includes(this.config.provider || "")) {
2659
+ } else if (["openai", "azure-openai", "anthropic", "bandit"].includes(this.config.provider || "")) {
2487
2660
  if (lastUserMessageIndex !== -1) {
2488
2661
  const currentMessage = messages[lastUserMessageIndex];
2489
2662
  const contentArray = [
@@ -2492,11 +2665,11 @@ var init_gateway_provider = __esm({
2492
2665
  text: currentMessage.content
2493
2666
  }
2494
2667
  ];
2495
- request.images.forEach((base64Image) => {
2668
+ request.images.forEach((imageRef) => {
2496
2669
  contentArray.push({
2497
2670
  type: "image_url",
2498
2671
  image_url: {
2499
- url: base64Image.startsWith("data:") ? base64Image : `data:image/jpeg;base64,${base64Image}`,
2672
+ url: normalizeImageUrl2(imageRef),
2500
2673
  detail: "auto"
2501
2674
  }
2502
2675
  });
@@ -2505,6 +2678,11 @@ var init_gateway_provider = __esm({
2505
2678
  ...messages[lastUserMessageIndex],
2506
2679
  content: contentArray
2507
2680
  };
2681
+ debugLogger.debug("Gateway provider injected image attachments", {
2682
+ provider: this.config.provider,
2683
+ imageCount: request.images.length,
2684
+ messageIndex: lastUserMessageIndex
2685
+ });
2508
2686
  }
2509
2687
  }
2510
2688
  }
@@ -3013,6 +3191,244 @@ var init_xai_provider = __esm({
3013
3191
  }
3014
3192
  });
3015
3193
 
3194
+ // src/services/ai-provider/providers/bandit-ai.provider.ts
3195
+ var import_rxjs10, DEFAULT_BANDIT_BASE, normalizeImageUrl, injectImagesIntoMessages, BanditAIProvider;
3196
+ var init_bandit_ai_provider = __esm({
3197
+ "src/services/ai-provider/providers/bandit-ai.provider.ts"() {
3198
+ "use strict";
3199
+ import_rxjs10 = require("rxjs");
3200
+ init_common_types();
3201
+ init_debugLogger();
3202
+ DEFAULT_BANDIT_BASE = "https://api.burtson.ai";
3203
+ normalizeImageUrl = (value) => {
3204
+ if (!value) {
3205
+ return null;
3206
+ }
3207
+ const trimmed = value.trim();
3208
+ if (!trimmed) {
3209
+ return null;
3210
+ }
3211
+ if (/^data:/i.test(trimmed) || /^https?:\/\//i.test(trimmed)) {
3212
+ return trimmed;
3213
+ }
3214
+ return `data:image/jpeg;base64,${trimmed}`;
3215
+ };
3216
+ injectImagesIntoMessages = (messages, images) => {
3217
+ const normalized = messages.map((message) => ({
3218
+ role: message.role,
3219
+ content: message.content
3220
+ }));
3221
+ if (!images || images.length === 0) {
3222
+ return normalized;
3223
+ }
3224
+ const normalizedImages = images.map(normalizeImageUrl).filter((url) => Boolean(url));
3225
+ if (normalizedImages.length === 0) {
3226
+ return normalized;
3227
+ }
3228
+ const lastUserIndex = normalized.map((msg) => msg.role).lastIndexOf("user");
3229
+ if (lastUserIndex === -1) {
3230
+ return normalized;
3231
+ }
3232
+ const target = normalized[lastUserIndex];
3233
+ const baseContent = typeof target.content === "string" && target.content.trim().length > 0 ? [
3234
+ {
3235
+ type: "text",
3236
+ text: target.content
3237
+ }
3238
+ ] : [];
3239
+ const imageContent = normalizedImages.map((url) => ({
3240
+ type: "image_url",
3241
+ image_url: { url, detail: "auto" }
3242
+ }));
3243
+ normalized[lastUserIndex] = {
3244
+ role: target.role,
3245
+ content: [...baseContent, ...imageContent]
3246
+ };
3247
+ return normalized;
3248
+ };
3249
+ BanditAIProvider = class {
3250
+ config;
3251
+ baseUrl;
3252
+ constructor(config) {
3253
+ this.config = config;
3254
+ this.baseUrl = (config.baseUrl || DEFAULT_BANDIT_BASE).replace(/\/$/, "");
3255
+ }
3256
+ chat(request) {
3257
+ const url = `${this.baseUrl}/chat/completions`;
3258
+ const messages = injectImagesIntoMessages(request.messages, request.images);
3259
+ const payload = {
3260
+ model: request.model,
3261
+ messages,
3262
+ stream: Boolean(request.stream),
3263
+ temperature: request.temperature,
3264
+ max_tokens: request.maxTokens
3265
+ };
3266
+ if (request.stream) {
3267
+ return this.streamChatRequest(url, payload);
3268
+ }
3269
+ return this.nonStreamChatRequest(url, payload);
3270
+ }
3271
+ generate(request) {
3272
+ const chatRequest = {
3273
+ model: request.model,
3274
+ messages: [{ role: "user", content: request.prompt }],
3275
+ stream: request.stream,
3276
+ options: request.options
3277
+ };
3278
+ return this.chat(chatRequest).pipe(
3279
+ (0, import_rxjs10.map)((response) => ({
3280
+ response: response.message.content,
3281
+ done: response.done
3282
+ }))
3283
+ );
3284
+ }
3285
+ listModels() {
3286
+ const url = `${this.baseUrl}/models`;
3287
+ return (0, import_rxjs10.from)(fetch(url, { headers: this.getHeaders() })).pipe(
3288
+ (0, import_rxjs10.switchMap)((response) => {
3289
+ if (!response.ok) {
3290
+ debugLogger.error("BanditAI listModels failed", { status: response.status, url });
3291
+ return (0, import_rxjs10.throwError)(() => new Error(`Failed to list Bandit models: ${response.status}`));
3292
+ }
3293
+ return (0, import_rxjs10.from)(response.json());
3294
+ }),
3295
+ (0, import_rxjs10.map)(
3296
+ (data) => data.data.map((model) => ({
3297
+ name: model.id,
3298
+ details: {
3299
+ format: "bandit",
3300
+ family: model.object
3301
+ }
3302
+ }))
3303
+ )
3304
+ );
3305
+ }
3306
+ async validateServiceAvailability(args) {
3307
+ const attempt = async (url) => {
3308
+ try {
3309
+ const controller = new AbortController();
3310
+ const timeoutId = setTimeout(() => controller.abort(), args.timeoutMs);
3311
+ const response = await fetch(`${url}/models`, {
3312
+ headers: this.getHeaders(),
3313
+ signal: controller.signal
3314
+ });
3315
+ clearTimeout(timeoutId);
3316
+ return response.ok;
3317
+ } catch (error) {
3318
+ debugLogger.warn("BanditAI availability check failed", { url, error });
3319
+ return false;
3320
+ }
3321
+ };
3322
+ const primary = await attempt(this.baseUrl);
3323
+ if (primary) {
3324
+ return { url: this.baseUrl, isAvailable: true };
3325
+ }
3326
+ if (args.fallbackUrl) {
3327
+ const fallback = args.fallbackUrl.replace(/\/$/, "");
3328
+ if (await attempt(fallback)) {
3329
+ this.baseUrl = fallback;
3330
+ return { url: fallback, isAvailable: true };
3331
+ }
3332
+ }
3333
+ return { url: this.baseUrl, isAvailable: false };
3334
+ }
3335
+ getProviderType() {
3336
+ return "bandit" /* BANDIT */;
3337
+ }
3338
+ getConfig() {
3339
+ return this.config;
3340
+ }
3341
+ streamChatRequest(url, payload) {
3342
+ return new import_rxjs10.Observable((observer) => {
3343
+ const task = fetch(url, {
3344
+ method: "POST",
3345
+ headers: {
3346
+ ...this.getHeaders(),
3347
+ "Content-Type": "application/json"
3348
+ },
3349
+ body: JSON.stringify(payload)
3350
+ });
3351
+ task.then((response) => {
3352
+ if (!response.ok) {
3353
+ observer.error(new Error(`BanditAI request failed: ${response.status}`));
3354
+ return;
3355
+ }
3356
+ const reader = response.body?.getReader();
3357
+ const decoder = new TextDecoder();
3358
+ let buffer = "";
3359
+ const read = () => {
3360
+ reader?.read().then(({ done, value }) => {
3361
+ if (done) {
3362
+ observer.next({ message: { content: "", role: "assistant" }, done: true });
3363
+ observer.complete();
3364
+ return;
3365
+ }
3366
+ buffer += decoder.decode(value, { stream: true });
3367
+ const lines = buffer.split("\n");
3368
+ buffer = lines.pop() ?? "";
3369
+ for (const rawLine of lines) {
3370
+ const line = rawLine.trim();
3371
+ if (!line.startsWith("data: ")) {
3372
+ continue;
3373
+ }
3374
+ const data = line.slice(6).trim();
3375
+ if (data === "[DONE]") {
3376
+ observer.next({ message: { content: "", role: "assistant" }, done: true });
3377
+ observer.complete();
3378
+ return;
3379
+ }
3380
+ try {
3381
+ const parsed = JSON.parse(data);
3382
+ const content = parsed.choices?.[0]?.delta?.content ?? "";
3383
+ if (content) {
3384
+ observer.next({ message: { content, role: "assistant" }, done: false });
3385
+ }
3386
+ } catch (error) {
3387
+ debugLogger.error("BanditAI stream chunk parse failure", { data, error });
3388
+ }
3389
+ }
3390
+ read();
3391
+ }).catch((err) => observer.error(err));
3392
+ };
3393
+ read();
3394
+ }).catch((err) => observer.error(err));
3395
+ });
3396
+ }
3397
+ nonStreamChatRequest(url, payload) {
3398
+ return (0, import_rxjs10.from)(fetch(url, {
3399
+ method: "POST",
3400
+ headers: {
3401
+ ...this.getHeaders(),
3402
+ "Content-Type": "application/json"
3403
+ },
3404
+ body: JSON.stringify(payload)
3405
+ })).pipe(
3406
+ (0, import_rxjs10.switchMap)((response) => {
3407
+ if (!response.ok) {
3408
+ return (0, import_rxjs10.throwError)(() => new Error(`BanditAI request failed: ${response.status}`));
3409
+ }
3410
+ return (0, import_rxjs10.from)(response.json());
3411
+ }),
3412
+ (0, import_rxjs10.map)((data) => ({
3413
+ message: {
3414
+ content: data.choices?.[0]?.message?.content ?? "",
3415
+ role: "assistant"
3416
+ },
3417
+ done: true
3418
+ }))
3419
+ );
3420
+ }
3421
+ getHeaders() {
3422
+ const headers = {};
3423
+ if (this.config.apiKey) {
3424
+ headers["Authorization"] = `Bearer ${this.config.apiKey}`;
3425
+ }
3426
+ return headers;
3427
+ }
3428
+ };
3429
+ }
3430
+ });
3431
+
3016
3432
  // src/services/ai-provider/ai-provider.factory.ts
3017
3433
  var AIProviderFactory;
3018
3434
  var init_ai_provider_factory = __esm({
@@ -3026,6 +3442,7 @@ var init_ai_provider_factory = __esm({
3026
3442
  init_gateway_provider();
3027
3443
  init_playground_provider();
3028
3444
  init_xai_provider();
3445
+ init_bandit_ai_provider();
3029
3446
  AIProviderFactory = class {
3030
3447
  static createProvider(config) {
3031
3448
  switch (config.type) {
@@ -3039,6 +3456,8 @@ var init_ai_provider_factory = __esm({
3039
3456
  return new AnthropicProvider(config);
3040
3457
  case "xai" /* XAI */:
3041
3458
  return new XAIProvider(config);
3459
+ case "bandit" /* BANDIT */:
3460
+ return new BanditAIProvider(config);
3042
3461
  case "gateway" /* GATEWAY */:
3043
3462
  return new GatewayProvider(config);
3044
3463
  case "playground" /* PLAYGROUND */:
@@ -3053,6 +3472,7 @@ var init_ai_provider_factory = __esm({
3053
3472
  "openai" /* OPENAI */,
3054
3473
  "azure-openai" /* AZURE_OPENAI */,
3055
3474
  "xai" /* XAI */,
3475
+ "bandit" /* BANDIT */,
3056
3476
  "gateway" /* GATEWAY */,
3057
3477
  "playground" /* PLAYGROUND */
3058
3478
  ];
@@ -3070,6 +3490,8 @@ var init_ai_provider_factory = __esm({
3070
3490
  return !!config.apiKey;
3071
3491
  case "xai" /* XAI */:
3072
3492
  return !!config.apiKey;
3493
+ case "bandit" /* BANDIT */:
3494
+ return !!config.apiKey;
3073
3495
  case "gateway" /* GATEWAY */:
3074
3496
  return !!(config.gatewayUrl && config.provider);
3075
3497
  case "playground" /* PLAYGROUND */:
@@ -3445,11 +3867,11 @@ var init_notificationService = __esm({
3445
3867
  });
3446
3868
 
3447
3869
  // src/services/prompts/conversationStarters.ts
3448
- var import_rxjs10, generateConversationStarters;
3870
+ var import_rxjs11, generateConversationStarters;
3449
3871
  var init_conversationStarters = __esm({
3450
3872
  "src/services/prompts/conversationStarters.ts"() {
3451
3873
  "use strict";
3452
- import_rxjs10 = require("rxjs");
3874
+ import_rxjs11 = require("rxjs");
3453
3875
  init_aiProviderStore();
3454
3876
  init_packageSettingsStore();
3455
3877
  init_getStableQuestionPrompt();
@@ -3470,7 +3892,7 @@ var init_conversationStarters = __esm({
3470
3892
  stream: false,
3471
3893
  options: { temperature: 1.5, num_predict: 250 }
3472
3894
  });
3473
- const questions$ = data$.pipe((0, import_rxjs10.map)((d) => {
3895
+ const questions$ = data$.pipe((0, import_rxjs11.map)((d) => {
3474
3896
  const sanitizeLine = (line) => {
3475
3897
  const withoutNumbering = line.replace(/^[0-9]+[.)\-\s:]+/, "").replace(/^[•*+-]\s+/, "");
3476
3898
  const withoutQuotes = withoutNumbering.replace(/^[“"']+/, "").replace(/[”"']+$/, "");
@@ -3492,7 +3914,7 @@ var init_conversationStarters = __esm({
3492
3914
  });
3493
3915
  return unique;
3494
3916
  }));
3495
- const starters = await (0, import_rxjs10.lastValueFrom)(questions$);
3917
+ const starters = await (0, import_rxjs11.lastValueFrom)(questions$);
3496
3918
  if (starters.length === 0) {
3497
3919
  debugLogger.warn("No meaningful conversation starters generated");
3498
3920
  return [];
@@ -3508,11 +3930,11 @@ var init_conversationStarters = __esm({
3508
3930
  });
3509
3931
 
3510
3932
  // src/services/prompts/moodDetection.ts
3511
- var import_rxjs11, detectMessageMood;
3933
+ var import_rxjs12, detectMessageMood;
3512
3934
  var init_moodDetection = __esm({
3513
3935
  "src/services/prompts/moodDetection.ts"() {
3514
3936
  "use strict";
3515
- import_rxjs11 = require("rxjs");
3937
+ import_rxjs12 = require("rxjs");
3516
3938
  init_aiProviderStore();
3517
3939
  init_packageSettingsStore();
3518
3940
  init_debugLogger();
@@ -3543,8 +3965,8 @@ Response:`;
3543
3965
  options: { temperature: 0.3, num_predict: 10 }
3544
3966
  });
3545
3967
  const chunks$ = response$.pipe(
3546
- (0, import_rxjs11.map)((chunk) => chunk.response.trim().toLowerCase()),
3547
- (0, import_rxjs11.toArray)()
3968
+ (0, import_rxjs12.map)((chunk) => chunk.response.trim().toLowerCase()),
3969
+ (0, import_rxjs12.toArray)()
3548
3970
  );
3549
3971
  const result = await chunks$.toPromise();
3550
3972
  const finalResult = (result || []).join("").trim();
@@ -3561,11 +3983,11 @@ Response:`;
3561
3983
  });
3562
3984
 
3563
3985
  // src/services/prompts/detectUserInterestAndExcitement.ts
3564
- var import_rxjs12, detectUserInterestAndExcitement;
3986
+ var import_rxjs13, detectUserInterestAndExcitement;
3565
3987
  var init_detectUserInterestAndExcitement = __esm({
3566
3988
  "src/services/prompts/detectUserInterestAndExcitement.ts"() {
3567
3989
  "use strict";
3568
- import_rxjs12 = require("rxjs");
3990
+ import_rxjs13 = require("rxjs");
3569
3991
  init_aiProviderStore();
3570
3992
  init_packageSettingsStore();
3571
3993
  init_debugLogger();
@@ -3612,10 +4034,10 @@ var init_detectUserInterestAndExcitement = __esm({
3612
4034
  options: { temperature: 0.1, num_predict: 5 }
3613
4035
  });
3614
4036
  const chunks$ = response$.pipe(
3615
- (0, import_rxjs12.map)((chunk) => chunk.response.trim().toUpperCase()),
3616
- (0, import_rxjs12.toArray)()
4037
+ (0, import_rxjs13.map)((chunk) => chunk.response.trim().toUpperCase()),
4038
+ (0, import_rxjs13.toArray)()
3617
4039
  );
3618
- const result = await (0, import_rxjs12.lastValueFrom)(chunks$);
4040
+ const result = await (0, import_rxjs13.lastValueFrom)(chunks$);
3619
4041
  const decision = result.join("").trim();
3620
4042
  debugLogger.llmDebug("detectUserInterestAndExcitement result", { decision });
3621
4043
  return decision.includes("YES");
@@ -3628,11 +4050,11 @@ var init_detectUserInterestAndExcitement = __esm({
3628
4050
  });
3629
4051
 
3630
4052
  // src/services/prompts/documentSummarization.ts
3631
- var import_rxjs13, summarizeDocument;
4053
+ var import_rxjs14, summarizeDocument;
3632
4054
  var init_documentSummarization = __esm({
3633
4055
  "src/services/prompts/documentSummarization.ts"() {
3634
4056
  "use strict";
3635
- import_rxjs13 = require("rxjs");
4057
+ import_rxjs14 = require("rxjs");
3636
4058
  init_aiProviderStore();
3637
4059
  init_packageSettingsStore();
3638
4060
  init_debugLogger();
@@ -3660,8 +4082,8 @@ ${content.slice(0, 4e3)}
3660
4082
  stream: false,
3661
4083
  options: { temperature: 0.3, num_predict: 100 }
3662
4084
  });
3663
- const summary$ = data$.pipe((0, import_rxjs13.map)((d) => d.response.trim()));
3664
- const summary = await (0, import_rxjs13.lastValueFrom)(summary$);
4085
+ const summary$ = data$.pipe((0, import_rxjs14.map)((d) => d.response.trim()));
4086
+ const summary = await (0, import_rxjs14.lastValueFrom)(summary$);
3665
4087
  debugLogger.ragDebug("summarizeDocument result", { name, summary });
3666
4088
  return summary || `Document summary for ${name}`;
3667
4089
  } catch (error) {
@@ -3673,11 +4095,11 @@ ${content.slice(0, 4e3)}
3673
4095
  });
3674
4096
 
3675
4097
  // src/services/prompts/documentRelevance.ts
3676
- var import_rxjs14, determineRelevantDocuments;
4098
+ var import_rxjs15, determineRelevantDocuments;
3677
4099
  var init_documentRelevance = __esm({
3678
4100
  "src/services/prompts/documentRelevance.ts"() {
3679
4101
  "use strict";
3680
- import_rxjs14 = require("rxjs");
4102
+ import_rxjs15 = require("rxjs");
3681
4103
  init_aiProviderStore();
3682
4104
  init_packageSettingsStore();
3683
4105
  init_debugLogger();
@@ -3718,10 +4140,10 @@ Response:`;
3718
4140
  options: { temperature: 0.2, num_predict: 50 }
3719
4141
  });
3720
4142
  const chunks$ = response$.pipe(
3721
- (0, import_rxjs14.map)((chunk) => chunk.response.trim()),
3722
- (0, import_rxjs14.toArray)()
4143
+ (0, import_rxjs15.map)((chunk) => chunk.response.trim()),
4144
+ (0, import_rxjs15.toArray)()
3723
4145
  );
3724
- const result = await (0, import_rxjs14.lastValueFrom)(chunks$);
4146
+ const result = await (0, import_rxjs15.lastValueFrom)(chunks$);
3725
4147
  const vetResult = result.join("").trim().toLowerCase();
3726
4148
  debugLogger.ragDebug("determineRelevantDocuments result", { vetResult });
3727
4149
  if (vetResult.includes("none") || !vetResult) {
@@ -7106,13 +7528,17 @@ var init_conversationStore = __esm({
7106
7528
  const updatedConversations = conversations.map((c) => {
7107
7529
  if (c.id === currentId && c.history.length > 0) {
7108
7530
  const updatedHistory = [...c.history];
7531
+ const existingImages = updatedHistory[updatedHistory.length - 1].images;
7532
+ const nextImages = Array.isArray(images) && images.length > 0 ? [...images] : Array.isArray(existingImages) && existingImages.length > 0 ? [...existingImages] : existingImages;
7109
7533
  updatedHistory[updatedHistory.length - 1] = {
7110
7534
  ...updatedHistory[updatedHistory.length - 1],
7111
7535
  answer,
7112
7536
  memoryUpdated,
7113
- images: images ?? updatedHistory[updatedHistory.length - 1].images,
7537
+ images: nextImages,
7114
7538
  sourceFiles: sourceFiles ?? updatedHistory[updatedHistory.length - 1].sourceFiles,
7115
- cancelled: cancelled ?? updatedHistory[updatedHistory.length - 1].cancelled
7539
+ cancelled: cancelled ?? updatedHistory[updatedHistory.length - 1].cancelled,
7540
+ placeholder: false,
7541
+ rawQuestion: void 0
7116
7542
  };
7117
7543
  return normalizeConversation({ ...c, history: updatedHistory, updatedAt: /* @__PURE__ */ new Date() });
7118
7544
  }
@@ -7193,6 +7619,24 @@ var init_conversationStore = __esm({
7193
7619
  });
7194
7620
  continue;
7195
7621
  }
7622
+ if (Array.isArray(existing.history) && Array.isArray(conversation.history)) {
7623
+ const mergedHistory = conversation.history.map((incomingEntry, index) => {
7624
+ const existingEntry = existing.history[index];
7625
+ if (!existingEntry) {
7626
+ return incomingEntry;
7627
+ }
7628
+ const mergedImagesSource = Array.isArray(incomingEntry.images) && incomingEntry.images.length > 0 ? incomingEntry.images : existingEntry.images;
7629
+ const mergedImages = Array.isArray(mergedImagesSource) && mergedImagesSource.length > 0 ? [...mergedImagesSource] : mergedImagesSource;
7630
+ return {
7631
+ ...existingEntry,
7632
+ ...incomingEntry,
7633
+ images: mergedImages,
7634
+ placeholder: incomingEntry.placeholder ?? existingEntry.placeholder,
7635
+ rawQuestion: incomingEntry.rawQuestion ?? existingEntry.rawQuestion
7636
+ };
7637
+ });
7638
+ conversation.history = mergedHistory;
7639
+ }
7196
7640
  }
7197
7641
  next.set(conversation.id, conversation);
7198
7642
  toPersist.push(conversation);
@@ -11005,11 +11449,11 @@ var init_ttsSanitizer = __esm({
11005
11449
  });
11006
11450
 
11007
11451
  // src/services/tts/tts-client.ts
11008
- var import_rxjs15, getOrAppendAuthHeader;
11452
+ var import_rxjs16, getOrAppendAuthHeader;
11009
11453
  var init_tts_client = __esm({
11010
11454
  "src/services/tts/tts-client.ts"() {
11011
11455
  "use strict";
11012
- import_rxjs15 = require("rxjs");
11456
+ import_rxjs16 = require("rxjs");
11013
11457
  init_authenticationService();
11014
11458
  init_voiceStore();
11015
11459
  init_ttsSanitizer();
@@ -11029,11 +11473,11 @@ var init_tts_client = __esm({
11029
11473
  });
11030
11474
 
11031
11475
  // src/services/tts/streaming-tts.ts
11032
- var import_rxjs16, StreamingTTSClient, getStreamingTTSClient, speakStream, stopTTS;
11476
+ var import_rxjs17, StreamingTTSClient, getStreamingTTSClient, speakStream, stopTTS;
11033
11477
  var init_streaming_tts = __esm({
11034
11478
  "src/services/tts/streaming-tts.ts"() {
11035
11479
  "use strict";
11036
- import_rxjs16 = require("rxjs");
11480
+ import_rxjs17 = require("rxjs");
11037
11481
  init_debugLogger();
11038
11482
  init_packageSettingsStore();
11039
11483
  init_tts_client();
@@ -11045,8 +11489,8 @@ var init_streaming_tts = __esm({
11045
11489
  // Store event handler references for proper cleanup
11046
11490
  audioHandlers = /* @__PURE__ */ new Map();
11047
11491
  // State management
11048
- stateSubject = new import_rxjs16.BehaviorSubject("IDLE" /* IDLE */);
11049
- progressSubject = new import_rxjs16.Subject();
11492
+ stateSubject = new import_rxjs17.BehaviorSubject("IDLE" /* IDLE */);
11493
+ progressSubject = new import_rxjs17.Subject();
11050
11494
  constructor() {
11051
11495
  }
11052
11496
  static getInstance() {
@@ -11077,7 +11521,7 @@ var init_streaming_tts = __esm({
11077
11521
  * Speak text with simple streaming
11078
11522
  */
11079
11523
  speakStream(text, voice, options = {}) {
11080
- return new import_rxjs16.Observable((subscriber) => {
11524
+ return new import_rxjs17.Observable((subscriber) => {
11081
11525
  this.performSimpleStreaming(text, voice, options, subscriber);
11082
11526
  });
11083
11527
  }
@@ -18799,6 +19243,8 @@ var init_chat_scroll_to_bottom_button = __esm({
18799
19243
  drawerOpen = false,
18800
19244
  isMobile = false
18801
19245
  }) => {
19246
+ const verticalBuffer = isMobile ? 36 : 56;
19247
+ const bottomOffset = Math.max(inputHeight + verticalBuffer, verticalBuffer + 72);
18802
19248
  return /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
18803
19249
  import_material28.IconButton,
18804
19250
  {
@@ -18807,12 +19253,12 @@ var init_chat_scroll_to_bottom_button = __esm({
18807
19253
  position: "fixed",
18808
19254
  left: drawerOpen && !isMobile ? "calc(50% + 170px)" : "50%",
18809
19255
  transform: "translateX(-50%)",
18810
- bottom: inputHeight + 10,
19256
+ bottom: bottomOffset,
18811
19257
  bgcolor: (theme) => theme.palette.background.paper,
18812
19258
  color: (theme) => theme.palette.text.primary,
18813
19259
  border: "1px solid",
18814
19260
  borderColor: (theme) => theme.palette.divider,
18815
- zIndex: 999,
19261
+ zIndex: (theme) => Math.max(theme.zIndex.modal + 1, 1400),
18816
19262
  boxShadow: 3,
18817
19263
  transition: "bottom 0.3s ease, left 0.3s ease-in-out",
18818
19264
  "&:hover": {
@@ -19474,28 +19920,28 @@ var init_create_audio_blob = __esm({
19474
19920
  });
19475
19921
 
19476
19922
  // src/services/stt/sound-recorder.service.ts
19477
- var import_rxjs20, SoundRecorderService;
19923
+ var import_rxjs21, SoundRecorderService;
19478
19924
  var init_sound_recorder_service = __esm({
19479
19925
  "src/services/stt/sound-recorder.service.ts"() {
19480
19926
  "use strict";
19481
- import_rxjs20 = require("rxjs");
19927
+ import_rxjs21 = require("rxjs");
19482
19928
  init_create_audio_blob();
19483
19929
  SoundRecorderService = class {
19484
19930
  _mediaRecorder;
19485
19931
  start() {
19486
- const mediaStream = (0, import_rxjs20.from)(navigator.mediaDevices.getUserMedia({ audio: true }));
19487
- this._mediaRecorder = mediaStream.pipe((0, import_rxjs20.map)((stream) => {
19932
+ const mediaStream = (0, import_rxjs21.from)(navigator.mediaDevices.getUserMedia({ audio: true }));
19933
+ this._mediaRecorder = mediaStream.pipe((0, import_rxjs21.map)((stream) => {
19488
19934
  const rec = new MediaRecorder(stream);
19489
19935
  rec.start();
19490
19936
  return rec;
19491
- }), (0, import_rxjs20.shareReplay)(1));
19937
+ }), (0, import_rxjs21.shareReplay)(1));
19492
19938
  const dataAvailableEvent = this._mediaRecorder.pipe(
19493
- (0, import_rxjs20.switchMap)((recorder) => (0, import_rxjs20.fromEvent)(recorder, "dataavailable"))
19939
+ (0, import_rxjs21.switchMap)((recorder) => (0, import_rxjs21.fromEvent)(recorder, "dataavailable"))
19494
19940
  );
19495
19941
  const blob = dataAvailableEvent.pipe(
19496
- (0, import_rxjs20.first)(),
19497
- (0, import_rxjs20.map)((event) => createAudioBlob(event.data)),
19498
- (0, import_rxjs20.shareReplay)(1)
19942
+ (0, import_rxjs21.first)(),
19943
+ (0, import_rxjs21.map)((event) => createAudioBlob(event.data)),
19944
+ (0, import_rxjs21.shareReplay)(1)
19499
19945
  );
19500
19946
  return blob;
19501
19947
  }
@@ -19503,7 +19949,7 @@ var init_sound_recorder_service = __esm({
19503
19949
  if (!this._mediaRecorder) {
19504
19950
  return;
19505
19951
  }
19506
- this._mediaRecorder.pipe((0, import_rxjs20.first)()).subscribe((recorder) => {
19952
+ this._mediaRecorder.pipe((0, import_rxjs21.first)()).subscribe((recorder) => {
19507
19953
  recorder.stop();
19508
19954
  });
19509
19955
  }
@@ -19623,7 +20069,7 @@ var init_stt_client = __esm({
19623
20069
  });
19624
20070
 
19625
20071
  // src/services/stt/transcriber.tsx
19626
- var import_react38, import_Mic, import_Check6, import_Close7, import_material31, import_rxjs21, import_jsx_runtime31, initialButtonStyles, Transcriber, transcriber_default;
20072
+ var import_react38, import_Mic, import_Check6, import_Close7, import_material31, import_rxjs22, import_jsx_runtime31, initialButtonStyles, Transcriber, transcriber_default;
19627
20073
  var init_transcriber = __esm({
19628
20074
  "src/services/stt/transcriber.tsx"() {
19629
20075
  "use strict";
@@ -19634,7 +20080,7 @@ var init_transcriber = __esm({
19634
20080
  init_sound_recorder_service();
19635
20081
  init_stt_client();
19636
20082
  import_material31 = require("@mui/material");
19637
- import_rxjs21 = require("rxjs");
20083
+ import_rxjs22 = require("rxjs");
19638
20084
  init_debugLogger();
19639
20085
  import_jsx_runtime31 = require("react/jsx-runtime");
19640
20086
  initialButtonStyles = (badgeBackground, fileText, hoverBadgeBackground) => ({
@@ -19653,14 +20099,14 @@ var init_transcriber = __esm({
19653
20099
  const [status, setStatus] = (0, import_react38.useState)("IDLE");
19654
20100
  const recorderRef = (0, import_react38.useRef)(new SoundRecorderService());
19655
20101
  const [iconButtonStyles] = (0, import_react38.useState)(() => initialButtonStyles(badgeBackground, fileText, hoverBadgeBackground));
19656
- const [recordingSub, setRecordingSub] = (0, import_react38.useState)(() => new import_rxjs21.Subscription());
20102
+ const [recordingSub, setRecordingSub] = (0, import_react38.useState)(() => new import_rxjs22.Subscription());
19657
20103
  const start = () => {
19658
20104
  recordingSub.unsubscribe();
19659
20105
  const recording = recorderRef.current.start();
19660
20106
  const text = recording.pipe(
19661
- (0, import_rxjs21.switchMap)((blob) => {
20107
+ (0, import_rxjs22.switchMap)((blob) => {
19662
20108
  debugLogger.debug("Processing audio blob for transcription");
19663
- return (0, import_rxjs21.from)(STTClient.transcribe(blob));
20109
+ return (0, import_rxjs22.from)(STTClient.transcribe(blob));
19664
20110
  })
19665
20111
  );
19666
20112
  const sub = text.subscribe({
@@ -20532,11 +20978,11 @@ ${sanitize(
20532
20978
  });
20533
20979
 
20534
20980
  // src/chat/hooks/useMemoryEnhancer.tsx
20535
- var import_rxjs22, 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;
20981
+ var import_rxjs23, 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;
20536
20982
  var init_useMemoryEnhancer = __esm({
20537
20983
  "src/chat/hooks/useMemoryEnhancer.tsx"() {
20538
20984
  "use strict";
20539
- import_rxjs22 = require("rxjs");
20985
+ import_rxjs23 = require("rxjs");
20540
20986
  init_memoryStore();
20541
20987
  init_aiProviderStore();
20542
20988
  init_packageSettingsStore();
@@ -20928,8 +21374,8 @@ var init_useMemoryEnhancer = __esm({
20928
21374
  stream: false,
20929
21375
  options: { temperature: 0.1, num_predict: 150 }
20930
21376
  });
20931
- const suggestion = await (0, import_rxjs22.lastValueFrom)(
20932
- result$.pipe((0, import_rxjs22.map)((chunk) => chunk.response))
21377
+ const suggestion = await (0, import_rxjs23.lastValueFrom)(
21378
+ result$.pipe((0, import_rxjs23.map)((chunk) => chunk.response))
20933
21379
  );
20934
21380
  debugLogger.memoryDebug(`LLM memory suggestion received (${attempt})`, {
20935
21381
  suggestion: typeof suggestion === "string" ? suggestion.slice(0, 200) : suggestion,
@@ -21620,10 +22066,22 @@ var init_useAIProvider = __esm({
21620
22066
  setResponse("");
21621
22067
  setStreamBuffer("");
21622
22068
  clearFlushTimer();
21623
- setPendingMessage({ question, images });
22069
+ const imageList = Array.isArray(images) ? [...images] : [];
22070
+ const conversationStoreState = useConversationStore.getState();
22071
+ const { addToCurrent, replaceLastAnswer, conversations, currentId } = conversationStoreState;
22072
+ const currentConv = conversations.find((c) => c.id === currentId);
22073
+ const lastEntry = currentConv?.history.at(-1);
22074
+ const lastWasPlaceholder = !!lastEntry && lastEntry.answer === "..." && (lastEntry.placeholder === true || lastEntry.rawQuestion === question || lastEntry.question === question);
22075
+ const pendingQuestion = lastWasPlaceholder ? lastEntry?.question ?? question : question;
22076
+ const pendingImagesRaw = lastWasPlaceholder && Array.isArray(lastEntry?.images) && lastEntry.images.length > 0 ? lastEntry.images : imageList;
22077
+ const pendingImages = Array.isArray(pendingImagesRaw) && pendingImagesRaw.length > 0 ? [...pendingImagesRaw] : void 0;
22078
+ setPendingMessage({
22079
+ question: pendingQuestion,
22080
+ images: pendingImages
22081
+ });
21624
22082
  const modelName = usePackageSettingsStore.getState().settings?.defaultModel || "bandit-core:4b-it-qat";
21625
22083
  const CONFIG = modelConfigs[modelName] ?? modelConfigs["bandit-core:4b-it-qat"];
21626
- const base64Images = images.map((img) => img.split(",")[1]);
22084
+ const base64Images = imageList.map((img) => img.split(",")[1]);
21627
22085
  const latestEntries = history.slice(-CONFIG.historyMessages);
21628
22086
  const contextMessages = latestEntries.flatMap((entry) => [
21629
22087
  { role: "user", content: entry.question },
@@ -22009,11 +22467,8 @@ ${protocol}`;
22009
22467
  }, delay);
22010
22468
  };
22011
22469
  const stream = provider.chat(request);
22012
- const { addToCurrent, conversations, currentId, replaceLastAnswer } = useConversationStore.getState();
22013
- const currentConv = conversations.find((c) => c.id === currentId);
22014
- const lastEntry = currentConv?.history.at(-1);
22015
- const isPlaceholder = lastEntry?.question === question && lastEntry?.answer === "...";
22016
- lastPartialRef.current = { text: "", images, usedDocs, question };
22470
+ const initialPlaceholderQuestion = lastEntry?.question;
22471
+ lastPartialRef.current = { text: "", images: [...imageList], usedDocs, question };
22017
22472
  if (currentSubRef.current) {
22018
22473
  try {
22019
22474
  currentSubRef.current.unsubscribe();
@@ -22272,14 +22727,24 @@ ${items.map((item, index) => {
22272
22727
  } else {
22273
22728
  debugLogger.info("Memory scan skipped - disabled in preferences");
22274
22729
  }
22275
- const { addToCurrent: addToCurrent2, replaceLastAnswer: replaceLastAnswer2 } = useConversationStore.getState();
22276
- const current = useConversationStore.getState();
22277
- const conv = current.conversations.find((c) => c.id === current.currentId);
22730
+ const currentState = useConversationStore.getState();
22731
+ const conv = currentState.conversations.find((c) => c.id === currentState.currentId);
22278
22732
  const last = conv?.history.at(-1);
22279
- if (!last || last.answer !== "..." || last.question !== question) {
22280
- addToCurrent2({ question, answer: enhancedMessage, images, memoryUpdated, sourceFiles: usedDocs });
22733
+ const lastIsPlaceholder = !!last && last.answer === "..." && last.placeholder !== false;
22734
+ const preservedImagesSource = imageList.length > 0 ? imageList : lastPartialRef.current.images.length > 0 ? lastPartialRef.current.images : last?.images;
22735
+ const preservedImages = Array.isArray(preservedImagesSource) && preservedImagesSource.length > 0 ? [...preservedImagesSource] : void 0;
22736
+ if (lastIsPlaceholder) {
22737
+ replaceLastAnswer(enhancedMessage, preservedImages, memoryUpdated, usedDocs);
22281
22738
  } else {
22282
- replaceLastAnswer2(enhancedMessage, images, memoryUpdated, usedDocs);
22739
+ const historyQuestion = last && last.answer === "..." && last.question || initialPlaceholderQuestion || question;
22740
+ addToCurrent({
22741
+ question: historyQuestion,
22742
+ answer: enhancedMessage,
22743
+ images: preservedImages,
22744
+ memoryUpdated,
22745
+ sourceFiles: usedDocs,
22746
+ rawQuestion: question
22747
+ });
22283
22748
  }
22284
22749
  setInputValue("");
22285
22750
  setPastedImages([]);
@@ -26522,15 +26987,15 @@ var init_chat_app_bar = __esm({
26522
26987
  });
26523
26988
 
26524
26989
  // src/chat/hooks/useConversationNameGenerator.tsx
26525
- var import_rxjs23, import_operators5, useConversationNameGenerator;
26990
+ var import_rxjs24, import_operators6, useConversationNameGenerator;
26526
26991
  var init_useConversationNameGenerator = __esm({
26527
26992
  "src/chat/hooks/useConversationNameGenerator.tsx"() {
26528
26993
  "use strict";
26529
26994
  init_aiProviderStore();
26530
26995
  init_packageSettingsStore();
26531
26996
  init_debugLogger();
26532
- import_rxjs23 = require("rxjs");
26533
- import_operators5 = require("rxjs/operators");
26997
+ import_rxjs24 = require("rxjs");
26998
+ import_operators6 = require("rxjs/operators");
26534
26999
  init_conversationStore();
26535
27000
  useConversationNameGenerator = () => {
26536
27001
  const generateName = async (userMessage) => {
@@ -26565,8 +27030,8 @@ Respond with just the title and nothing else.
26565
27030
  num_predict: 20
26566
27031
  }
26567
27032
  });
26568
- const title = await (0, import_rxjs23.lastValueFrom)(
26569
- result$.pipe((0, import_operators5.map)((d) => d.response?.trim().replace(/["']/g, "")))
27033
+ const title = await (0, import_rxjs24.lastValueFrom)(
27034
+ result$.pipe((0, import_operators6.map)((d) => d.response?.trim().replace(/["']/g, "")))
26570
27035
  );
26571
27036
  if (title && title.length > 0) {
26572
27037
  const sanitizedTitle = sanitizeConversationName(title, 60);
@@ -27846,12 +28311,20 @@ var init_chat2 = __esm({
27846
28311
  (question, images, displayQuestion) => {
27847
28312
  const requestStartTime = trackRequestStart();
27848
28313
  const questionForDisplay = displayQuestion || question;
27849
- setPendingMessage({ question: questionForDisplay, images });
28314
+ const pendingImages = images.length > 0 ? [...images] : void 0;
28315
+ setPendingMessage({ question: questionForDisplay, images: pendingImages });
27850
28316
  setIsStreaming(true);
27851
28317
  setResponseStarted(true);
27852
28318
  setStreamBuffer("");
27853
28319
  const { addToCurrent } = useConversationStore.getState();
27854
- addToCurrent({ question: questionForDisplay, answer: "...", images });
28320
+ const placeholderImages = pendingImages ? [...pendingImages] : void 0;
28321
+ addToCurrent({
28322
+ question: questionForDisplay,
28323
+ answer: "...",
28324
+ images: placeholderImages,
28325
+ placeholder: true,
28326
+ rawQuestion: question
28327
+ });
27855
28328
  const getCurrentModel = useModelStore.getState().getCurrentModel;
27856
28329
  const systemPrompt = getCurrentModel()?.systemPrompt ?? "You are a helpful assistant.";
27857
28330
  const { currentId: currentId2, conversations: conversations2, createConversation, renameConversation } = useConversationStore.getState();
@@ -27865,8 +28338,16 @@ var init_chat2 = __esm({
27865
28338
  if (!newCurrentId) return;
27866
28339
  setResponse("");
27867
28340
  const { addToCurrent: addToNew } = useConversationStore.getState();
27868
- addToNew({ question: questionForDisplay, answer: "...", images });
27869
- aiProvider(systemPrompt, question, images);
28341
+ const newPlaceholderImages = pendingImages ? [...pendingImages] : void 0;
28342
+ addToNew({
28343
+ question: questionForDisplay,
28344
+ answer: "...",
28345
+ images: newPlaceholderImages,
28346
+ placeholder: true,
28347
+ rawQuestion: question
28348
+ });
28349
+ const providerImages2 = pendingImages ? [...pendingImages] : [];
28350
+ aiProvider(systemPrompt, question, providerImages2);
27870
28351
  }, 0);
27871
28352
  });
27872
28353
  return;
@@ -27916,7 +28397,8 @@ var init_chat2 = __esm({
27916
28397
  }, 50);
27917
28398
  }
27918
28399
  setResponse("");
27919
- aiProvider(systemPrompt, question, images);
28400
+ const providerImages = pendingImages ? [...pendingImages] : [];
28401
+ aiProvider(systemPrompt, question, providerImages);
27920
28402
  },
27921
28403
  [
27922
28404
  aiProvider,
@@ -28750,7 +29232,7 @@ init_modelStore();
28750
29232
  init_ai_response_text_field();
28751
29233
  init_memory_modal();
28752
29234
  init_streaming_tts();
28753
- var import_rxjs17 = require("rxjs");
29235
+ var import_rxjs18 = require("rxjs");
28754
29236
  init_debugLogger();
28755
29237
  init_util();
28756
29238
  var import_jsx_runtime10 = require("react/jsx-runtime");
@@ -28779,7 +29261,7 @@ var AIQueriesDrawer = ({ drawerOpen, onClose, onClearComplete, onNavigateToMain
28779
29261
  const [memoryModalOpen, setMemoryModalOpen] = (0, import_react15.useState)(false);
28780
29262
  const [contextMode, setContextMode] = (0, import_react15.useState)("local");
28781
29263
  const [expandedSections, setExpandedSections] = (0, import_react15.useState)(/* @__PURE__ */ new Set(["history", "voice"]));
28782
- const [audioSub, setAudioSub] = (0, import_react15.useState)(new import_rxjs17.Subscription());
29264
+ const [audioSub, setAudioSub] = (0, import_react15.useState)(new import_rxjs18.Subscription());
28783
29265
  const [isContextSwitching, setIsContextSwitching] = (0, import_react15.useState)(false);
28784
29266
  const [isDrawerLoading, setIsDrawerLoading] = (0, import_react15.useState)(false);
28785
29267
  (0, import_react15.useEffect)(() => {
@@ -30442,7 +30924,7 @@ init_debugLogger();
30442
30924
  init_banditTheme();
30443
30925
  init_themeMap();
30444
30926
  init_useTTS();
30445
- var import_rxjs18 = require("rxjs");
30927
+ var import_rxjs19 = require("rxjs");
30446
30928
  var import_jsx_runtime15 = require("react/jsx-runtime");
30447
30929
  var FULL_SCREEN_THRESHOLD = 100;
30448
30930
  var CDN_BASE = "https://cdn.burtson.ai/";
@@ -30509,7 +30991,7 @@ var ChatModal = ({
30509
30991
  const [modalLogo, setModalLogo] = (0, import_react22.useState)("https://cdn.burtson.ai/logos/bandit-ai-logo.png");
30510
30992
  const [modelAnchorEl, setModelAnchorEl] = (0, import_react22.useState)(null);
30511
30993
  const [voiceAnchorEl, setVoiceAnchorEl] = (0, import_react22.useState)(null);
30512
- const [audioSub, setAudioSub] = (0, import_react22.useState)(new import_rxjs18.Subscription());
30994
+ const [audioSub, setAudioSub] = (0, import_react22.useState)(new import_rxjs19.Subscription());
30513
30995
  const [selectedTheme, setSelectedTheme] = (0, import_react22.useState)(null);
30514
30996
  const [themeLoading, setThemeLoading] = (0, import_react22.useState)(true);
30515
30997
  const [autoFullscreenTriggered, setAutoFullscreenTriggered] = (0, import_react22.useState)(false);
@@ -33050,7 +33532,7 @@ init_conversationSyncStore();
33050
33532
 
33051
33533
  // src/hooks/useGatewayQueries.ts
33052
33534
  var import_react25 = require("react");
33053
- var import_rxjs19 = require("rxjs");
33535
+ var import_rxjs20 = require("rxjs");
33054
33536
  var import_react_query = require("@tanstack/react-query");
33055
33537
  init_packageSettingsStore();
33056
33538
  init_gateway_service();
@@ -33081,7 +33563,7 @@ var useGatewayHealth = (options) => {
33081
33563
  if (!service) {
33082
33564
  throw new Error("Gateway service is not configured");
33083
33565
  }
33084
- return (0, import_rxjs19.lastValueFrom)(service.getHealth());
33566
+ return (0, import_rxjs20.lastValueFrom)(service.getHealth());
33085
33567
  },
33086
33568
  enabled
33087
33569
  });
@@ -37849,26 +38331,7 @@ var AIProviderInitService = class _AIProviderInitService {
37849
38331
  if (providerConfig.type === "anthropic" /* ANTHROPIC */) {
37850
38332
  providerConfig = this.convertAnthropicConfig(providerConfig, settings?.gatewayApiUrl);
37851
38333
  }
37852
- if ((providerConfig.type === "ollama" /* OLLAMA */ || providerConfig.type === "gateway" /* GATEWAY */) && !providerConfig.tokenFactory) {
37853
- providerConfig.tokenFactory = () => {
37854
- let token = authenticationService.getToken();
37855
- if (!token) {
37856
- token = localStorage.getItem("authToken");
37857
- }
37858
- if (!token) {
37859
- try {
37860
- const { useAuthenticationStore: useAuthenticationStore2 } = require("../../store/authenticationStore");
37861
- const authStore = useAuthenticationStore2.getState();
37862
- token = authStore.token;
37863
- } catch (e) {
37864
- }
37865
- }
37866
- debugLogger.info("AI Provider Init: IndexedDB config token factory", {
37867
- hasToken: !!token
37868
- });
37869
- return token;
37870
- };
37871
- }
38334
+ providerConfig = this.ensureTokenFactory(providerConfig);
37872
38335
  try {
37873
38336
  const { createProvider } = useAIProviderStore.getState();
37874
38337
  createProvider(providerConfig);
@@ -37896,27 +38359,7 @@ var AIProviderInitService = class _AIProviderInitService {
37896
38359
  if (providerConfig.type === "anthropic" /* ANTHROPIC */) {
37897
38360
  providerConfig = this.convertAnthropicConfig(providerConfig, settings.gatewayApiUrl);
37898
38361
  }
37899
- if (providerConfig.type === "ollama" /* OLLAMA */ && !providerConfig.tokenFactory) {
37900
- providerConfig.tokenFactory = () => {
37901
- let token = authenticationService.getToken();
37902
- if (!token) {
37903
- token = localStorage.getItem("authToken");
37904
- }
37905
- if (!token) {
37906
- try {
37907
- const { useAuthenticationStore: useAuthenticationStore2 } = require("../../store/authenticationStore");
37908
- const authStore = useAuthenticationStore2.getState();
37909
- token = authStore.token;
37910
- } catch (e) {
37911
- }
37912
- }
37913
- debugLogger.info("AIProviderInit: Explicit config tokenFactory", {
37914
- hasToken: !!token,
37915
- localStorage: !!localStorage.getItem("authToken")
37916
- });
37917
- return token;
37918
- };
37919
- }
38362
+ providerConfig = this.ensureTokenFactory(providerConfig);
37920
38363
  debugLogger.info("Using explicit AI provider config", providerConfig);
37921
38364
  } else {
37922
38365
  providerConfig = {
@@ -38028,9 +38471,10 @@ var AIProviderInitService = class _AIProviderInitService {
38028
38471
  */
38029
38472
  switchProvider(config) {
38030
38473
  try {
38474
+ const normalizedConfig = this.ensureTokenFactory({ ...config });
38031
38475
  const { switchProvider } = useAIProviderStore.getState();
38032
- switchProvider(config);
38033
- debugLogger.info(`Switched to AI provider: ${config.type}`);
38476
+ switchProvider(normalizedConfig);
38477
+ debugLogger.info(`Switched to AI provider: ${normalizedConfig.type}`);
38034
38478
  } catch (error) {
38035
38479
  debugLogger.error("Failed to switch AI provider:", { error });
38036
38480
  throw error;
@@ -38064,6 +38508,49 @@ var AIProviderInitService = class _AIProviderInitService {
38064
38508
  debugLogger.info("AI Provider Init: Converted direct Anthropic provider to gateway configuration");
38065
38509
  return normalized;
38066
38510
  }
38511
+ /**
38512
+ * Ensure providers that require auth have a token factory configured.
38513
+ * Handles both UI auth tokens and API key scenarios.
38514
+ */
38515
+ ensureTokenFactory(config) {
38516
+ if (config.type === "ollama" /* OLLAMA */ || config.type === "gateway" /* GATEWAY */) {
38517
+ const existingFactory = config.tokenFactory;
38518
+ if (existingFactory) {
38519
+ return config;
38520
+ }
38521
+ if (typeof config.apiKey === "string" && config.apiKey.trim() !== "") {
38522
+ const key = config.apiKey.trim();
38523
+ config.tokenFactory = () => key;
38524
+ debugLogger.info("AIProviderInit: Using API key for token factory", {
38525
+ type: config.type,
38526
+ hasKey: true
38527
+ });
38528
+ return config;
38529
+ }
38530
+ config.tokenFactory = () => {
38531
+ let token = authenticationService.getToken();
38532
+ if (!token && typeof localStorage !== "undefined") {
38533
+ try {
38534
+ token = localStorage.getItem("authToken");
38535
+ } catch {
38536
+ }
38537
+ }
38538
+ if (!token) {
38539
+ try {
38540
+ const { useAuthenticationStore: useAuthenticationStore2 } = require("../../store/authenticationStore");
38541
+ const authStore = useAuthenticationStore2.getState();
38542
+ token = authStore.token;
38543
+ } catch {
38544
+ }
38545
+ }
38546
+ debugLogger.info("AIProviderInit: Token factory resolved auth token", {
38547
+ hasToken: !!token
38548
+ });
38549
+ return token;
38550
+ };
38551
+ }
38552
+ return config;
38553
+ }
38067
38554
  };
38068
38555
  var aiProviderInitService = AIProviderInitService.getInstance();
38069
38556
 
@@ -38087,6 +38574,8 @@ var ProviderTab = () => {
38087
38574
  return "gpt-4o-mini";
38088
38575
  case "xai" /* XAI */:
38089
38576
  return "grok-beta";
38577
+ case "bandit" /* BANDIT */:
38578
+ return "bandit-core-1";
38090
38579
  default:
38091
38580
  return "";
38092
38581
  }
@@ -38094,7 +38583,7 @@ var ProviderTab = () => {
38094
38583
  const applyDefaultModel = (0, import_react33.useCallback)((config) => {
38095
38584
  const normalized = { ...config };
38096
38585
  const trimmed = typeof normalized.defaultModel === "string" ? normalized.defaultModel.trim() : void 0;
38097
- const requiresModel = normalized.type === "openai" /* OPENAI */ || normalized.type === "xai" /* XAI */;
38586
+ const requiresModel = normalized.type === "openai" /* OPENAI */ || normalized.type === "xai" /* XAI */ || normalized.type === "bandit" /* BANDIT */;
38098
38587
  if (trimmed) {
38099
38588
  normalized.defaultModel = trimmed;
38100
38589
  return normalized;
@@ -38117,11 +38606,16 @@ var ProviderTab = () => {
38117
38606
  const trimmed = sanitized.defaultModel.trim();
38118
38607
  sanitized.defaultModel = trimmed || void 0;
38119
38608
  }
38609
+ if (typeof sanitized.apiKey === "string") {
38610
+ const trimmedKey = sanitized.apiKey.trim();
38611
+ sanitized.apiKey = trimmedKey || void 0;
38612
+ }
38120
38613
  return sanitized;
38121
38614
  }, []);
38122
38615
  const [providerConfig, setProviderConfig] = (0, import_react33.useState)({
38123
- type: "ollama",
38124
- baseUrl: "http://localhost:11434"
38616
+ type: "gateway",
38617
+ gatewayUrl: packageSettings?.gatewayApiUrl || "",
38618
+ provider: "bandit"
38125
38619
  });
38126
38620
  const [isProviderConfigOpen, setIsProviderConfigOpen] = (0, import_react33.useState)(false);
38127
38621
  const [snackbarMessage, setSnackbarMessage] = (0, import_react33.useState)("");
@@ -38245,11 +38739,19 @@ var ProviderTab = () => {
38245
38739
  apiKey: ""
38246
38740
  }));
38247
38741
  break;
38742
+ case "bandit" /* BANDIT */:
38743
+ setProviderConfig(applyDefaultModel({
38744
+ ...baseConfig,
38745
+ baseUrl: "https://api.burtson.ai",
38746
+ apiKey: "",
38747
+ defaultModel: "bandit-core-1"
38748
+ }));
38749
+ break;
38248
38750
  case "gateway" /* GATEWAY */:
38249
38751
  setProviderConfig(applyDefaultModel({
38250
38752
  ...baseConfig,
38251
38753
  gatewayUrl: packageSettings?.gatewayApiUrl || "",
38252
- provider: "openai"
38754
+ provider: "bandit"
38253
38755
  }));
38254
38756
  break;
38255
38757
  case "playground" /* PLAYGROUND */:
@@ -38263,7 +38765,7 @@ var ProviderTab = () => {
38263
38765
  try {
38264
38766
  const normalizedConfigIntermediate = sanitizeConfigForSave(providerConfig);
38265
38767
  const normalizedConfig = convertAnthropicConfig(normalizedConfigIntermediate) || normalizedConfigIntermediate;
38266
- const requiresModel = normalizedConfig.type === "openai" /* OPENAI */ || normalizedConfig.type === "xai" /* XAI */;
38768
+ const requiresModel = normalizedConfig.type === "openai" /* OPENAI */ || normalizedConfig.type === "xai" /* XAI */ || normalizedConfig.type === "bandit" /* BANDIT */;
38267
38769
  if (requiresModel && !normalizedConfig.defaultModel) {
38268
38770
  showMessage("Please provide a default model ID for the selected provider.", "error");
38269
38771
  return;
@@ -38388,6 +38890,7 @@ var ProviderTab = () => {
38388
38890
  /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_material25.MenuItem, { value: "ollama", children: "Ollama" }),
38389
38891
  /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_material25.MenuItem, { value: "openai", children: "OpenAI" }),
38390
38892
  /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_material25.MenuItem, { value: "azure-openai", children: "Azure OpenAI" }),
38893
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_material25.MenuItem, { value: "bandit", children: "Bandit AI" }),
38391
38894
  /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_material25.MenuItem, { value: "xai", children: "xAI" }),
38392
38895
  /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_material25.MenuItem, { value: "playground", children: "Playground (Mock Demo)" })
38393
38896
  ]
@@ -38422,6 +38925,7 @@ var ProviderTab = () => {
38422
38925
  /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_material25.MenuItem, { value: "azure-openai", children: "Azure OpenAI" }),
38423
38926
  /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_material25.MenuItem, { value: "anthropic", children: "Anthropic" }),
38424
38927
  /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_material25.MenuItem, { value: "ollama", children: "Ollama" }),
38928
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_material25.MenuItem, { value: "bandit", children: "Bandit AI" }),
38425
38929
  /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_material25.MenuItem, { value: "xai", children: "xAI" })
38426
38930
  ]
38427
38931
  }
@@ -38437,6 +38941,52 @@ var ProviderTab = () => {
38437
38941
  placeholder: "http://localhost:11434"
38438
38942
  }
38439
38943
  ),
38944
+ providerConfig.type === "bandit" && /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(import_material25.Box, { children: [
38945
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
38946
+ import_material25.TextField,
38947
+ {
38948
+ label: "API Base URL",
38949
+ value: providerConfig.baseUrl || "",
38950
+ onChange: (e) => setProviderConfig((prev) => ({
38951
+ ...prev,
38952
+ baseUrl: e.target.value
38953
+ })),
38954
+ fullWidth: true,
38955
+ sx: { mb: 2 },
38956
+ placeholder: "https://api.burtson.ai",
38957
+ helperText: "Defaults to https://api.burtson.ai"
38958
+ }
38959
+ ),
38960
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
38961
+ import_material25.TextField,
38962
+ {
38963
+ label: "API Key",
38964
+ type: "password",
38965
+ value: providerConfig.apiKey || "",
38966
+ onChange: (e) => setProviderConfig((prev) => ({
38967
+ ...prev,
38968
+ apiKey: e.target.value
38969
+ })),
38970
+ fullWidth: true,
38971
+ sx: { mb: 2 },
38972
+ placeholder: "bai_..."
38973
+ }
38974
+ ),
38975
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
38976
+ import_material25.TextField,
38977
+ {
38978
+ label: "Default Model ID",
38979
+ value: providerConfig.defaultModel || "",
38980
+ onChange: (e) => setProviderConfig((prev) => ({
38981
+ ...prev,
38982
+ defaultModel: e.target.value
38983
+ })),
38984
+ fullWidth: true,
38985
+ placeholder: "bandit-core-1",
38986
+ helperText: "Example: bandit-core-1 (Bandit Core canonical alias)."
38987
+ }
38988
+ )
38989
+ ] }),
38440
38990
  providerConfig.type === "openai" && /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(import_material25.Box, { children: [
38441
38991
  /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
38442
38992
  import_material25.TextField,
@@ -38777,20 +39327,20 @@ var MCPToolsTabV2 = () => {
38777
39327
  }
38778
39328
  }, [isLoaded]);
38779
39329
  const localEnabledMap = (0, import_react34.useMemo)(() => {
38780
- const map21 = /* @__PURE__ */ new Map();
39330
+ const map23 = /* @__PURE__ */ new Map();
38781
39331
  const sortedTools = [...localTools].sort((a, b) => {
38782
39332
  if (a.isBuiltIn && !b.isBuiltIn) return -1;
38783
39333
  if (!a.isBuiltIn && b.isBuiltIn) return 1;
38784
39334
  return a.id.length - b.id.length;
38785
39335
  });
38786
39336
  sortedTools.forEach((t) => {
38787
- map21.set(t.function.name, t.enabled);
38788
- map21.set(t.id, t.enabled);
39337
+ map23.set(t.function.name, t.enabled);
39338
+ map23.set(t.id, t.enabled);
38789
39339
  if (t.name) {
38790
- map21.set(t.name, t.enabled);
39340
+ map23.set(t.name, t.enabled);
38791
39341
  }
38792
39342
  });
38793
- return map21;
39343
+ return map23;
38794
39344
  }, [localTools]);
38795
39345
  return /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(import_material26.Box, { children: [
38796
39346
  /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(import_material26.Box, { sx: { display: "flex", alignItems: "center", justifyContent: "space-between", mb: 2 }, children: [