@burtson-labs/bandit-engine 2.0.38 → 2.0.40

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 (45) hide show
  1. package/dist/{aiProviderStore-XN7GCBHJ.mjs → aiProviderStore-JMA5RWX7.mjs} +2 -2
  2. package/dist/{chat-5QJNWB7I.mjs → chat-JMWPOSQ4.mjs} +5 -5
  3. package/dist/chat-provider.js +421 -20
  4. package/dist/chat-provider.js.map +1 -1
  5. package/dist/chat-provider.mjs +4 -4
  6. package/dist/{chunk-WO5KFNNW.mjs → chunk-26QQ4CLA.mjs} +62 -24
  7. package/dist/chunk-26QQ4CLA.mjs.map +1 -0
  8. package/dist/{chunk-CDQYBO3Q.mjs → chunk-2ZCR2TDY.mjs} +27 -5
  9. package/dist/chunk-2ZCR2TDY.mjs.map +1 -0
  10. package/dist/{chunk-ECRNIAG6.mjs → chunk-6ELNWXKC.mjs} +4 -4
  11. package/dist/{chunk-EOKIE5HZ.mjs → chunk-75W5VWPV.mjs} +3 -3
  12. package/dist/{chunk-JRCDANLN.mjs → chunk-7KEBNVCO.mjs} +67 -9
  13. package/dist/{chunk-JRCDANLN.mjs.map → chunk-7KEBNVCO.mjs.map} +1 -1
  14. package/dist/{chunk-3A2527TE.mjs → chunk-D3AGKOM6.mjs} +3 -3
  15. package/dist/{chunk-QU5S5QQP.mjs → chunk-QJYPWWA5.mjs} +379 -18
  16. package/dist/chunk-QJYPWWA5.mjs.map +1 -0
  17. package/dist/{chunk-QYH2T4L5.mjs → chunk-VIYBZO5W.mjs} +3 -3
  18. package/dist/{cli/cli.js → cli.js} +424 -12
  19. package/dist/cli.js.map +1 -0
  20. package/dist/{gateway-B0LJ3-jT.d.ts → gateway-5yt_3QDP.d.mts} +4 -4
  21. package/dist/{gateway-B0LJ3-jT.d.mts → gateway-5yt_3QDP.d.ts} +4 -4
  22. package/dist/index.d.mts +2 -2
  23. package/dist/index.d.ts +2 -2
  24. package/dist/index.js +598 -101
  25. package/dist/index.js.map +1 -1
  26. package/dist/index.mjs +8 -8
  27. package/dist/management/management.js +596 -99
  28. package/dist/management/management.js.map +1 -1
  29. package/dist/management/management.mjs +6 -6
  30. package/dist/modals/chat-modal/chat-modal.js +430 -29
  31. package/dist/modals/chat-modal/chat-modal.js.map +1 -1
  32. package/dist/modals/chat-modal/chat-modal.mjs +4 -4
  33. package/dist/public-types.d.mts +1 -1
  34. package/dist/public-types.d.ts +1 -1
  35. package/package.json +2 -3
  36. package/dist/chunk-CDQYBO3Q.mjs.map +0 -1
  37. package/dist/chunk-QU5S5QQP.mjs.map +0 -1
  38. package/dist/chunk-WO5KFNNW.mjs.map +0 -1
  39. package/dist/cli/cli.js.map +0 -1
  40. /package/dist/{aiProviderStore-XN7GCBHJ.mjs.map → aiProviderStore-JMA5RWX7.mjs.map} +0 -0
  41. /package/dist/{chat-5QJNWB7I.mjs.map → chat-JMWPOSQ4.mjs.map} +0 -0
  42. /package/dist/{chunk-ECRNIAG6.mjs.map → chunk-6ELNWXKC.mjs.map} +0 -0
  43. /package/dist/{chunk-EOKIE5HZ.mjs.map → chunk-75W5VWPV.mjs.map} +0 -0
  44. /package/dist/{chunk-3A2527TE.mjs.map → chunk-D3AGKOM6.mjs.map} +0 -0
  45. /package/dist/{chunk-QYH2T4L5.mjs.map → chunk-VIYBZO5W.mjs.map} +0 -0
package/dist/index.js CHANGED
@@ -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
  };
@@ -1567,13 +1567,17 @@ var init_conversationStore = __esm({
1567
1567
  const updatedConversations = conversations.map((c) => {
1568
1568
  if (c.id === currentId && c.history.length > 0) {
1569
1569
  const updatedHistory = [...c.history];
1570
+ const existingImages = updatedHistory[updatedHistory.length - 1].images;
1571
+ const nextImages = Array.isArray(images) && images.length > 0 ? [...images] : Array.isArray(existingImages) && existingImages.length > 0 ? [...existingImages] : existingImages;
1570
1572
  updatedHistory[updatedHistory.length - 1] = {
1571
1573
  ...updatedHistory[updatedHistory.length - 1],
1572
1574
  answer,
1573
1575
  memoryUpdated,
1574
- images: images ?? updatedHistory[updatedHistory.length - 1].images,
1576
+ images: nextImages,
1575
1577
  sourceFiles: sourceFiles ?? updatedHistory[updatedHistory.length - 1].sourceFiles,
1576
- cancelled: cancelled ?? updatedHistory[updatedHistory.length - 1].cancelled
1578
+ cancelled: cancelled ?? updatedHistory[updatedHistory.length - 1].cancelled,
1579
+ placeholder: false,
1580
+ rawQuestion: void 0
1577
1581
  };
1578
1582
  return normalizeConversation({ ...c, history: updatedHistory, updatedAt: /* @__PURE__ */ new Date() });
1579
1583
  }
@@ -1654,6 +1658,24 @@ var init_conversationStore = __esm({
1654
1658
  });
1655
1659
  continue;
1656
1660
  }
1661
+ if (Array.isArray(existing.history) && Array.isArray(conversation.history)) {
1662
+ const mergedHistory = conversation.history.map((incomingEntry, index) => {
1663
+ const existingEntry = existing.history[index];
1664
+ if (!existingEntry) {
1665
+ return incomingEntry;
1666
+ }
1667
+ const mergedImagesSource = Array.isArray(incomingEntry.images) && incomingEntry.images.length > 0 ? incomingEntry.images : existingEntry.images;
1668
+ const mergedImages = Array.isArray(mergedImagesSource) && mergedImagesSource.length > 0 ? [...mergedImagesSource] : mergedImagesSource;
1669
+ return {
1670
+ ...existingEntry,
1671
+ ...incomingEntry,
1672
+ images: mergedImages,
1673
+ placeholder: incomingEntry.placeholder ?? existingEntry.placeholder,
1674
+ rawQuestion: incomingEntry.rawQuestion ?? existingEntry.rawQuestion
1675
+ };
1676
+ });
1677
+ conversation.history = mergedHistory;
1678
+ }
1657
1679
  }
1658
1680
  next.set(conversation.id, conversation);
1659
1681
  toPersist.push(conversation);
@@ -4447,13 +4469,17 @@ var init_gateway_service = __esm({
4447
4469
  chat(request) {
4448
4470
  const endpoint = request.provider === "ollama" ? `/api/${request.provider}/chat` : request.provider ? `/api/${request.provider}/chat/completions` : "/api/chat/completions";
4449
4471
  const url = `${this._baseUrl}${endpoint}`;
4472
+ const normalizedModel = request.provider === "bandit" ? (() => {
4473
+ const trimmed = (request.model ?? "").replace(/^bandit:/, "").trim();
4474
+ return trimmed !== "" ? trimmed : "bandit-core-1";
4475
+ })() : request.model;
4450
4476
  debugLogger.debug(`Gateway chat request to ${url} with provider: ${request.provider || "default"}`, {
4451
- model: request.model,
4477
+ model: normalizedModel,
4452
4478
  messageCount: request.messages.length,
4453
4479
  hasImages: !!(request.images && request.images.length > 0),
4454
4480
  imageCount: request.images?.length || 0
4455
4481
  });
4456
- const requestBody = { ...request, stream: request.stream !== false };
4482
+ const requestBody = { ...request, model: normalizedModel, stream: request.stream !== false };
4457
4483
  return new import_rxjs6.Observable((observer) => {
4458
4484
  const controller = new AbortController();
4459
4485
  const task = fetch(url, {
@@ -4593,12 +4619,18 @@ var init_gateway_service = __esm({
4593
4619
  generate(request) {
4594
4620
  const endpoint = request.provider ? `/api/${request.provider}/generate` : "/api/generate";
4595
4621
  const url = `${this._baseUrl}${endpoint}`;
4596
- debugLogger.debug(`Gateway generate request to ${url} with provider: ${request.provider || "default"}`);
4622
+ const normalizedModel = request.provider === "bandit" ? (() => {
4623
+ const trimmed = (request.model ?? "").replace(/^bandit:/, "").trim();
4624
+ return trimmed !== "" ? trimmed : "bandit-core-1";
4625
+ })() : request.model;
4626
+ debugLogger.debug(`Gateway generate request to ${url} with provider: ${request.provider || "default"}`, {
4627
+ model: normalizedModel
4628
+ });
4597
4629
  return new import_rxjs6.Observable((observer) => {
4598
4630
  const task = fetch(url, {
4599
4631
  method: "POST",
4600
4632
  headers: this._getHeaders(),
4601
- body: JSON.stringify({ ...request, stream: request.stream !== false })
4633
+ body: JSON.stringify({ ...request, model: normalizedModel, stream: request.stream !== false })
4602
4634
  });
4603
4635
  task.then(async (response) => {
4604
4636
  if (!response.ok) {
@@ -5196,6 +5228,112 @@ var init_ollama_gateway_service = __esm({
5196
5228
  }
5197
5229
  });
5198
5230
 
5231
+ // src/services/gateway/bandit-gateway.service.ts
5232
+ var import_operators5, normalizeBanditModel, isGatewayMessageContent, normalizeBanditMessages, BanditAIGatewayService;
5233
+ var init_bandit_gateway_service = __esm({
5234
+ "src/services/gateway/bandit-gateway.service.ts"() {
5235
+ "use strict";
5236
+ init_gateway_service();
5237
+ import_operators5 = require("rxjs/operators");
5238
+ init_debugLogger();
5239
+ normalizeBanditModel = (model) => {
5240
+ if (typeof model !== "string" || model.trim() === "") {
5241
+ return "bandit-core-1";
5242
+ }
5243
+ const normalized = model.replace(/^bandit:/, "").trim();
5244
+ return normalized === "" ? "bandit-core-1" : normalized;
5245
+ };
5246
+ isGatewayMessageContent = (value) => {
5247
+ if (!value || typeof value !== "object") return false;
5248
+ const candidate = value;
5249
+ if (candidate.type !== "text" && candidate.type !== "image_url") {
5250
+ return false;
5251
+ }
5252
+ if (candidate.type === "text") {
5253
+ return typeof candidate.text === "string";
5254
+ }
5255
+ if (candidate.type === "image_url") {
5256
+ return !!candidate.image_url && typeof candidate.image_url.url === "string";
5257
+ }
5258
+ return false;
5259
+ };
5260
+ normalizeBanditMessages = (messages) => messages.map((message) => {
5261
+ const content = message.content;
5262
+ if (typeof content === "string") {
5263
+ return { role: message.role, content };
5264
+ }
5265
+ if (Array.isArray(content)) {
5266
+ const filtered = content.filter(isGatewayMessageContent);
5267
+ if (filtered.length === 0) {
5268
+ return { role: message.role, content: JSON.stringify(content) };
5269
+ }
5270
+ return {
5271
+ role: message.role,
5272
+ content: filtered
5273
+ };
5274
+ }
5275
+ return { role: message.role, content: content != null ? String(content) : "" };
5276
+ });
5277
+ BanditAIGatewayService = class {
5278
+ _gatewayService;
5279
+ constructor(gatewayUrl, tokenFactory) {
5280
+ this._gatewayService = new GatewayService(gatewayUrl, tokenFactory);
5281
+ debugLogger.info("BanditAIGatewayService initialized", { gatewayUrl });
5282
+ }
5283
+ async validateServiceAvailability(args) {
5284
+ return this._gatewayService.validateServiceAvailability(args);
5285
+ }
5286
+ chat(request) {
5287
+ const model = normalizeBanditModel(request.model);
5288
+ const messages = normalizeBanditMessages(request.messages);
5289
+ const gatewayRequest = {
5290
+ ...request,
5291
+ messages,
5292
+ model,
5293
+ provider: "bandit",
5294
+ stream: request.stream
5295
+ };
5296
+ debugLogger.debug("Bandit Gateway chat request", {
5297
+ model,
5298
+ messageCount: request.messages.length,
5299
+ stream: request.stream
5300
+ });
5301
+ return this._gatewayService.chat(gatewayRequest);
5302
+ }
5303
+ complete(prompt, options) {
5304
+ const model = normalizeBanditModel(options.model);
5305
+ const gatewayRequest = {
5306
+ model,
5307
+ prompt,
5308
+ temperature: options.temperature,
5309
+ max_tokens: options.max_tokens,
5310
+ stream: options.stream,
5311
+ stop: options.stop,
5312
+ provider: "bandit"
5313
+ };
5314
+ debugLogger.debug("Bandit Gateway generate request", {
5315
+ model,
5316
+ promptLength: prompt.length,
5317
+ stream: options.stream
5318
+ });
5319
+ return this._gatewayService.generate(gatewayRequest);
5320
+ }
5321
+ listModels() {
5322
+ debugLogger.debug("Fetching Bandit models through gateway");
5323
+ return this._gatewayService.listModelsByProvider("bandit");
5324
+ }
5325
+ getHealth() {
5326
+ return this._gatewayService.getHealth().pipe(
5327
+ (0, import_operators5.map)((health) => ({
5328
+ ...health,
5329
+ bandit_status: health.providers.find((p) => p.name === "bandit")?.status || "unavailable"
5330
+ }))
5331
+ );
5332
+ }
5333
+ };
5334
+ }
5335
+ });
5336
+
5199
5337
  // src/services/ai-provider/providers/gateway.provider.ts
5200
5338
  var import_rxjs7, GatewayProvider;
5201
5339
  var init_gateway_provider = __esm({
@@ -5209,6 +5347,7 @@ var init_gateway_provider = __esm({
5209
5347
  init_azure_openai_gateway_service();
5210
5348
  init_anthropic_gateway_service();
5211
5349
  init_ollama_gateway_service();
5350
+ init_bandit_gateway_service();
5212
5351
  GatewayProvider = class {
5213
5352
  config;
5214
5353
  gatewayService;
@@ -5250,6 +5389,9 @@ var init_gateway_provider = __esm({
5250
5389
  case "anthropic":
5251
5390
  this.providerSpecificService = new AnthropicGatewayService(gatewayUrl, tokenFactory);
5252
5391
  break;
5392
+ case "bandit":
5393
+ this.providerSpecificService = new BanditAIGatewayService(gatewayUrl, tokenFactory);
5394
+ break;
5253
5395
  case "ollama":
5254
5396
  this.providerSpecificService = new OllamaGatewayService(gatewayUrl, tokenFactory);
5255
5397
  break;
@@ -5264,6 +5406,16 @@ var init_gateway_provider = __esm({
5264
5406
  role: msg.role,
5265
5407
  content: msg.content
5266
5408
  }));
5409
+ const normalizeImageUrl2 = (value) => {
5410
+ if (!value) {
5411
+ return value;
5412
+ }
5413
+ const trimmed = value.trim();
5414
+ if (/^data:/i.test(trimmed) || /^https?:\/\//i.test(trimmed)) {
5415
+ return trimmed;
5416
+ }
5417
+ return `data:image/jpeg;base64,${trimmed}`;
5418
+ };
5267
5419
  if (request.images && request.images.length > 0) {
5268
5420
  const lastUserMessageIndex = messages.map((m) => m.role).lastIndexOf("user");
5269
5421
  if (this.config.provider === "ollama") {
@@ -5273,7 +5425,7 @@ var init_gateway_provider = __esm({
5273
5425
  images: request.images
5274
5426
  };
5275
5427
  }
5276
- } else if (["openai", "azure-openai", "anthropic"].includes(this.config.provider || "")) {
5428
+ } else if (["openai", "azure-openai", "anthropic", "bandit"].includes(this.config.provider || "")) {
5277
5429
  if (lastUserMessageIndex !== -1) {
5278
5430
  const currentMessage = messages[lastUserMessageIndex];
5279
5431
  const contentArray = [
@@ -5282,11 +5434,11 @@ var init_gateway_provider = __esm({
5282
5434
  text: currentMessage.content
5283
5435
  }
5284
5436
  ];
5285
- request.images.forEach((base64Image) => {
5437
+ request.images.forEach((imageRef) => {
5286
5438
  contentArray.push({
5287
5439
  type: "image_url",
5288
5440
  image_url: {
5289
- url: base64Image.startsWith("data:") ? base64Image : `data:image/jpeg;base64,${base64Image}`,
5441
+ url: normalizeImageUrl2(imageRef),
5290
5442
  detail: "auto"
5291
5443
  }
5292
5444
  });
@@ -5295,6 +5447,11 @@ var init_gateway_provider = __esm({
5295
5447
  ...messages[lastUserMessageIndex],
5296
5448
  content: contentArray
5297
5449
  };
5450
+ debugLogger.debug("Gateway provider injected image attachments", {
5451
+ provider: this.config.provider,
5452
+ imageCount: request.images.length,
5453
+ messageIndex: lastUserMessageIndex
5454
+ });
5298
5455
  }
5299
5456
  }
5300
5457
  }
@@ -5803,6 +5960,244 @@ var init_xai_provider = __esm({
5803
5960
  }
5804
5961
  });
5805
5962
 
5963
+ // src/services/ai-provider/providers/bandit-ai.provider.ts
5964
+ var import_rxjs10, DEFAULT_BANDIT_BASE, normalizeImageUrl, injectImagesIntoMessages, BanditAIProvider;
5965
+ var init_bandit_ai_provider = __esm({
5966
+ "src/services/ai-provider/providers/bandit-ai.provider.ts"() {
5967
+ "use strict";
5968
+ import_rxjs10 = require("rxjs");
5969
+ init_common_types();
5970
+ init_debugLogger();
5971
+ DEFAULT_BANDIT_BASE = "https://api.burtson.ai";
5972
+ normalizeImageUrl = (value) => {
5973
+ if (!value) {
5974
+ return null;
5975
+ }
5976
+ const trimmed = value.trim();
5977
+ if (!trimmed) {
5978
+ return null;
5979
+ }
5980
+ if (/^data:/i.test(trimmed) || /^https?:\/\//i.test(trimmed)) {
5981
+ return trimmed;
5982
+ }
5983
+ return `data:image/jpeg;base64,${trimmed}`;
5984
+ };
5985
+ injectImagesIntoMessages = (messages, images) => {
5986
+ const normalized = messages.map((message) => ({
5987
+ role: message.role,
5988
+ content: message.content
5989
+ }));
5990
+ if (!images || images.length === 0) {
5991
+ return normalized;
5992
+ }
5993
+ const normalizedImages = images.map(normalizeImageUrl).filter((url) => Boolean(url));
5994
+ if (normalizedImages.length === 0) {
5995
+ return normalized;
5996
+ }
5997
+ const lastUserIndex = normalized.map((msg) => msg.role).lastIndexOf("user");
5998
+ if (lastUserIndex === -1) {
5999
+ return normalized;
6000
+ }
6001
+ const target = normalized[lastUserIndex];
6002
+ const baseContent = typeof target.content === "string" && target.content.trim().length > 0 ? [
6003
+ {
6004
+ type: "text",
6005
+ text: target.content
6006
+ }
6007
+ ] : [];
6008
+ const imageContent = normalizedImages.map((url) => ({
6009
+ type: "image_url",
6010
+ image_url: { url, detail: "auto" }
6011
+ }));
6012
+ normalized[lastUserIndex] = {
6013
+ role: target.role,
6014
+ content: [...baseContent, ...imageContent]
6015
+ };
6016
+ return normalized;
6017
+ };
6018
+ BanditAIProvider = class {
6019
+ config;
6020
+ baseUrl;
6021
+ constructor(config) {
6022
+ this.config = config;
6023
+ this.baseUrl = (config.baseUrl || DEFAULT_BANDIT_BASE).replace(/\/$/, "");
6024
+ }
6025
+ chat(request) {
6026
+ const url = `${this.baseUrl}/chat/completions`;
6027
+ const messages = injectImagesIntoMessages(request.messages, request.images);
6028
+ const payload = {
6029
+ model: request.model,
6030
+ messages,
6031
+ stream: Boolean(request.stream),
6032
+ temperature: request.temperature,
6033
+ max_tokens: request.maxTokens
6034
+ };
6035
+ if (request.stream) {
6036
+ return this.streamChatRequest(url, payload);
6037
+ }
6038
+ return this.nonStreamChatRequest(url, payload);
6039
+ }
6040
+ generate(request) {
6041
+ const chatRequest = {
6042
+ model: request.model,
6043
+ messages: [{ role: "user", content: request.prompt }],
6044
+ stream: request.stream,
6045
+ options: request.options
6046
+ };
6047
+ return this.chat(chatRequest).pipe(
6048
+ (0, import_rxjs10.map)((response) => ({
6049
+ response: response.message.content,
6050
+ done: response.done
6051
+ }))
6052
+ );
6053
+ }
6054
+ listModels() {
6055
+ const url = `${this.baseUrl}/models`;
6056
+ return (0, import_rxjs10.from)(fetch(url, { headers: this.getHeaders() })).pipe(
6057
+ (0, import_rxjs10.switchMap)((response) => {
6058
+ if (!response.ok) {
6059
+ debugLogger.error("BanditAI listModels failed", { status: response.status, url });
6060
+ return (0, import_rxjs10.throwError)(() => new Error(`Failed to list Bandit models: ${response.status}`));
6061
+ }
6062
+ return (0, import_rxjs10.from)(response.json());
6063
+ }),
6064
+ (0, import_rxjs10.map)(
6065
+ (data) => data.data.map((model) => ({
6066
+ name: model.id,
6067
+ details: {
6068
+ format: "bandit",
6069
+ family: model.object
6070
+ }
6071
+ }))
6072
+ )
6073
+ );
6074
+ }
6075
+ async validateServiceAvailability(args) {
6076
+ const attempt = async (url) => {
6077
+ try {
6078
+ const controller = new AbortController();
6079
+ const timeoutId = setTimeout(() => controller.abort(), args.timeoutMs);
6080
+ const response = await fetch(`${url}/models`, {
6081
+ headers: this.getHeaders(),
6082
+ signal: controller.signal
6083
+ });
6084
+ clearTimeout(timeoutId);
6085
+ return response.ok;
6086
+ } catch (error) {
6087
+ debugLogger.warn("BanditAI availability check failed", { url, error });
6088
+ return false;
6089
+ }
6090
+ };
6091
+ const primary = await attempt(this.baseUrl);
6092
+ if (primary) {
6093
+ return { url: this.baseUrl, isAvailable: true };
6094
+ }
6095
+ if (args.fallbackUrl) {
6096
+ const fallback = args.fallbackUrl.replace(/\/$/, "");
6097
+ if (await attempt(fallback)) {
6098
+ this.baseUrl = fallback;
6099
+ return { url: fallback, isAvailable: true };
6100
+ }
6101
+ }
6102
+ return { url: this.baseUrl, isAvailable: false };
6103
+ }
6104
+ getProviderType() {
6105
+ return "bandit" /* BANDIT */;
6106
+ }
6107
+ getConfig() {
6108
+ return this.config;
6109
+ }
6110
+ streamChatRequest(url, payload) {
6111
+ return new import_rxjs10.Observable((observer) => {
6112
+ const task = fetch(url, {
6113
+ method: "POST",
6114
+ headers: {
6115
+ ...this.getHeaders(),
6116
+ "Content-Type": "application/json"
6117
+ },
6118
+ body: JSON.stringify(payload)
6119
+ });
6120
+ task.then((response) => {
6121
+ if (!response.ok) {
6122
+ observer.error(new Error(`BanditAI request failed: ${response.status}`));
6123
+ return;
6124
+ }
6125
+ const reader = response.body?.getReader();
6126
+ const decoder = new TextDecoder();
6127
+ let buffer = "";
6128
+ const read = () => {
6129
+ reader?.read().then(({ done, value }) => {
6130
+ if (done) {
6131
+ observer.next({ message: { content: "", role: "assistant" }, done: true });
6132
+ observer.complete();
6133
+ return;
6134
+ }
6135
+ buffer += decoder.decode(value, { stream: true });
6136
+ const lines = buffer.split("\n");
6137
+ buffer = lines.pop() ?? "";
6138
+ for (const rawLine of lines) {
6139
+ const line = rawLine.trim();
6140
+ if (!line.startsWith("data: ")) {
6141
+ continue;
6142
+ }
6143
+ const data = line.slice(6).trim();
6144
+ if (data === "[DONE]") {
6145
+ observer.next({ message: { content: "", role: "assistant" }, done: true });
6146
+ observer.complete();
6147
+ return;
6148
+ }
6149
+ try {
6150
+ const parsed = JSON.parse(data);
6151
+ const content = parsed.choices?.[0]?.delta?.content ?? "";
6152
+ if (content) {
6153
+ observer.next({ message: { content, role: "assistant" }, done: false });
6154
+ }
6155
+ } catch (error) {
6156
+ debugLogger.error("BanditAI stream chunk parse failure", { data, error });
6157
+ }
6158
+ }
6159
+ read();
6160
+ }).catch((err) => observer.error(err));
6161
+ };
6162
+ read();
6163
+ }).catch((err) => observer.error(err));
6164
+ });
6165
+ }
6166
+ nonStreamChatRequest(url, payload) {
6167
+ return (0, import_rxjs10.from)(fetch(url, {
6168
+ method: "POST",
6169
+ headers: {
6170
+ ...this.getHeaders(),
6171
+ "Content-Type": "application/json"
6172
+ },
6173
+ body: JSON.stringify(payload)
6174
+ })).pipe(
6175
+ (0, import_rxjs10.switchMap)((response) => {
6176
+ if (!response.ok) {
6177
+ return (0, import_rxjs10.throwError)(() => new Error(`BanditAI request failed: ${response.status}`));
6178
+ }
6179
+ return (0, import_rxjs10.from)(response.json());
6180
+ }),
6181
+ (0, import_rxjs10.map)((data) => ({
6182
+ message: {
6183
+ content: data.choices?.[0]?.message?.content ?? "",
6184
+ role: "assistant"
6185
+ },
6186
+ done: true
6187
+ }))
6188
+ );
6189
+ }
6190
+ getHeaders() {
6191
+ const headers = {};
6192
+ if (this.config.apiKey) {
6193
+ headers["Authorization"] = `Bearer ${this.config.apiKey}`;
6194
+ }
6195
+ return headers;
6196
+ }
6197
+ };
6198
+ }
6199
+ });
6200
+
5806
6201
  // src/services/ai-provider/ai-provider.factory.ts
5807
6202
  var AIProviderFactory;
5808
6203
  var init_ai_provider_factory = __esm({
@@ -5816,6 +6211,7 @@ var init_ai_provider_factory = __esm({
5816
6211
  init_gateway_provider();
5817
6212
  init_playground_provider();
5818
6213
  init_xai_provider();
6214
+ init_bandit_ai_provider();
5819
6215
  AIProviderFactory = class {
5820
6216
  static createProvider(config) {
5821
6217
  switch (config.type) {
@@ -5829,6 +6225,8 @@ var init_ai_provider_factory = __esm({
5829
6225
  return new AnthropicProvider(config);
5830
6226
  case "xai" /* XAI */:
5831
6227
  return new XAIProvider(config);
6228
+ case "bandit" /* BANDIT */:
6229
+ return new BanditAIProvider(config);
5832
6230
  case "gateway" /* GATEWAY */:
5833
6231
  return new GatewayProvider(config);
5834
6232
  case "playground" /* PLAYGROUND */:
@@ -5843,6 +6241,7 @@ var init_ai_provider_factory = __esm({
5843
6241
  "openai" /* OPENAI */,
5844
6242
  "azure-openai" /* AZURE_OPENAI */,
5845
6243
  "xai" /* XAI */,
6244
+ "bandit" /* BANDIT */,
5846
6245
  "gateway" /* GATEWAY */,
5847
6246
  "playground" /* PLAYGROUND */
5848
6247
  ];
@@ -5860,6 +6259,8 @@ var init_ai_provider_factory = __esm({
5860
6259
  return !!config.apiKey;
5861
6260
  case "xai" /* XAI */:
5862
6261
  return !!config.apiKey;
6262
+ case "bandit" /* BANDIT */:
6263
+ return !!config.apiKey;
5863
6264
  case "gateway" /* GATEWAY */:
5864
6265
  return !!(config.gatewayUrl && config.provider);
5865
6266
  case "playground" /* PLAYGROUND */:
@@ -6220,11 +6621,11 @@ var init_notificationService = __esm({
6220
6621
  });
6221
6622
 
6222
6623
  // src/services/prompts/conversationStarters.ts
6223
- var import_rxjs10, generateConversationStarters;
6624
+ var import_rxjs11, generateConversationStarters;
6224
6625
  var init_conversationStarters = __esm({
6225
6626
  "src/services/prompts/conversationStarters.ts"() {
6226
6627
  "use strict";
6227
- import_rxjs10 = require("rxjs");
6628
+ import_rxjs11 = require("rxjs");
6228
6629
  init_aiProviderStore();
6229
6630
  init_packageSettingsStore();
6230
6631
  init_getStableQuestionPrompt();
@@ -6245,7 +6646,7 @@ var init_conversationStarters = __esm({
6245
6646
  stream: false,
6246
6647
  options: { temperature: 1.5, num_predict: 250 }
6247
6648
  });
6248
- const questions$ = data$.pipe((0, import_rxjs10.map)((d) => {
6649
+ const questions$ = data$.pipe((0, import_rxjs11.map)((d) => {
6249
6650
  const sanitizeLine = (line) => {
6250
6651
  const withoutNumbering = line.replace(/^[0-9]+[.)\-\s:]+/, "").replace(/^[•*+-]\s+/, "");
6251
6652
  const withoutQuotes = withoutNumbering.replace(/^[“"']+/, "").replace(/[”"']+$/, "");
@@ -6267,7 +6668,7 @@ var init_conversationStarters = __esm({
6267
6668
  });
6268
6669
  return unique;
6269
6670
  }));
6270
- const starters = await (0, import_rxjs10.lastValueFrom)(questions$);
6671
+ const starters = await (0, import_rxjs11.lastValueFrom)(questions$);
6271
6672
  if (starters.length === 0) {
6272
6673
  debugLogger.warn("No meaningful conversation starters generated");
6273
6674
  return [];
@@ -6283,11 +6684,11 @@ var init_conversationStarters = __esm({
6283
6684
  });
6284
6685
 
6285
6686
  // src/services/prompts/moodDetection.ts
6286
- var import_rxjs11, detectMessageMood;
6687
+ var import_rxjs12, detectMessageMood;
6287
6688
  var init_moodDetection = __esm({
6288
6689
  "src/services/prompts/moodDetection.ts"() {
6289
6690
  "use strict";
6290
- import_rxjs11 = require("rxjs");
6691
+ import_rxjs12 = require("rxjs");
6291
6692
  init_aiProviderStore();
6292
6693
  init_packageSettingsStore();
6293
6694
  init_debugLogger();
@@ -6318,8 +6719,8 @@ Response:`;
6318
6719
  options: { temperature: 0.3, num_predict: 10 }
6319
6720
  });
6320
6721
  const chunks$ = response$.pipe(
6321
- (0, import_rxjs11.map)((chunk) => chunk.response.trim().toLowerCase()),
6322
- (0, import_rxjs11.toArray)()
6722
+ (0, import_rxjs12.map)((chunk) => chunk.response.trim().toLowerCase()),
6723
+ (0, import_rxjs12.toArray)()
6323
6724
  );
6324
6725
  const result = await chunks$.toPromise();
6325
6726
  const finalResult = (result || []).join("").trim();
@@ -6336,11 +6737,11 @@ Response:`;
6336
6737
  });
6337
6738
 
6338
6739
  // src/services/prompts/detectUserInterestAndExcitement.ts
6339
- var import_rxjs12, detectUserInterestAndExcitement;
6740
+ var import_rxjs13, detectUserInterestAndExcitement;
6340
6741
  var init_detectUserInterestAndExcitement = __esm({
6341
6742
  "src/services/prompts/detectUserInterestAndExcitement.ts"() {
6342
6743
  "use strict";
6343
- import_rxjs12 = require("rxjs");
6744
+ import_rxjs13 = require("rxjs");
6344
6745
  init_aiProviderStore();
6345
6746
  init_packageSettingsStore();
6346
6747
  init_debugLogger();
@@ -6387,10 +6788,10 @@ var init_detectUserInterestAndExcitement = __esm({
6387
6788
  options: { temperature: 0.1, num_predict: 5 }
6388
6789
  });
6389
6790
  const chunks$ = response$.pipe(
6390
- (0, import_rxjs12.map)((chunk) => chunk.response.trim().toUpperCase()),
6391
- (0, import_rxjs12.toArray)()
6791
+ (0, import_rxjs13.map)((chunk) => chunk.response.trim().toUpperCase()),
6792
+ (0, import_rxjs13.toArray)()
6392
6793
  );
6393
- const result = await (0, import_rxjs12.lastValueFrom)(chunks$);
6794
+ const result = await (0, import_rxjs13.lastValueFrom)(chunks$);
6394
6795
  const decision = result.join("").trim();
6395
6796
  debugLogger.llmDebug("detectUserInterestAndExcitement result", { decision });
6396
6797
  return decision.includes("YES");
@@ -6403,11 +6804,11 @@ var init_detectUserInterestAndExcitement = __esm({
6403
6804
  });
6404
6805
 
6405
6806
  // src/services/prompts/documentSummarization.ts
6406
- var import_rxjs13, summarizeDocument;
6807
+ var import_rxjs14, summarizeDocument;
6407
6808
  var init_documentSummarization = __esm({
6408
6809
  "src/services/prompts/documentSummarization.ts"() {
6409
6810
  "use strict";
6410
- import_rxjs13 = require("rxjs");
6811
+ import_rxjs14 = require("rxjs");
6411
6812
  init_aiProviderStore();
6412
6813
  init_packageSettingsStore();
6413
6814
  init_debugLogger();
@@ -6435,8 +6836,8 @@ ${content.slice(0, 4e3)}
6435
6836
  stream: false,
6436
6837
  options: { temperature: 0.3, num_predict: 100 }
6437
6838
  });
6438
- const summary$ = data$.pipe((0, import_rxjs13.map)((d) => d.response.trim()));
6439
- const summary = await (0, import_rxjs13.lastValueFrom)(summary$);
6839
+ const summary$ = data$.pipe((0, import_rxjs14.map)((d) => d.response.trim()));
6840
+ const summary = await (0, import_rxjs14.lastValueFrom)(summary$);
6440
6841
  debugLogger.ragDebug("summarizeDocument result", { name, summary });
6441
6842
  return summary || `Document summary for ${name}`;
6442
6843
  } catch (error) {
@@ -6448,11 +6849,11 @@ ${content.slice(0, 4e3)}
6448
6849
  });
6449
6850
 
6450
6851
  // src/services/prompts/documentRelevance.ts
6451
- var import_rxjs14, determineRelevantDocuments;
6852
+ var import_rxjs15, determineRelevantDocuments;
6452
6853
  var init_documentRelevance = __esm({
6453
6854
  "src/services/prompts/documentRelevance.ts"() {
6454
6855
  "use strict";
6455
- import_rxjs14 = require("rxjs");
6856
+ import_rxjs15 = require("rxjs");
6456
6857
  init_aiProviderStore();
6457
6858
  init_packageSettingsStore();
6458
6859
  init_debugLogger();
@@ -6493,10 +6894,10 @@ Response:`;
6493
6894
  options: { temperature: 0.2, num_predict: 50 }
6494
6895
  });
6495
6896
  const chunks$ = response$.pipe(
6496
- (0, import_rxjs14.map)((chunk) => chunk.response.trim()),
6497
- (0, import_rxjs14.toArray)()
6897
+ (0, import_rxjs15.map)((chunk) => chunk.response.trim()),
6898
+ (0, import_rxjs15.toArray)()
6498
6899
  );
6499
- const result = await (0, import_rxjs14.lastValueFrom)(chunks$);
6900
+ const result = await (0, import_rxjs15.lastValueFrom)(chunks$);
6500
6901
  const vetResult = result.join("").trim().toLowerCase();
6501
6902
  debugLogger.ragDebug("determineRelevantDocuments result", { vetResult });
6502
6903
  if (vetResult.includes("none") || !vetResult) {
@@ -10180,6 +10581,8 @@ var init_chat_scroll_to_bottom_button = __esm({
10180
10581
  drawerOpen = false,
10181
10582
  isMobile = false
10182
10583
  }) => {
10584
+ const verticalBuffer = isMobile ? 36 : 56;
10585
+ const bottomOffset = Math.max(inputHeight + verticalBuffer, verticalBuffer + 72);
10183
10586
  return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
10184
10587
  import_material3.IconButton,
10185
10588
  {
@@ -10188,12 +10591,12 @@ var init_chat_scroll_to_bottom_button = __esm({
10188
10591
  position: "fixed",
10189
10592
  left: drawerOpen && !isMobile ? "calc(50% + 170px)" : "50%",
10190
10593
  transform: "translateX(-50%)",
10191
- bottom: inputHeight + 10,
10594
+ bottom: bottomOffset,
10192
10595
  bgcolor: (theme) => theme.palette.background.paper,
10193
10596
  color: (theme) => theme.palette.text.primary,
10194
10597
  border: "1px solid",
10195
10598
  borderColor: (theme) => theme.palette.divider,
10196
- zIndex: 999,
10599
+ zIndex: (theme) => Math.max(theme.zIndex.modal + 1, 1400),
10197
10600
  boxShadow: 3,
10198
10601
  transition: "bottom 0.3s ease, left 0.3s ease-in-out",
10199
10602
  "&:hover": {
@@ -11884,11 +12287,11 @@ var init_ttsSanitizer = __esm({
11884
12287
  });
11885
12288
 
11886
12289
  // src/services/tts/tts-client.ts
11887
- var import_rxjs15, getOrAppendAuthHeader;
12290
+ var import_rxjs16, getOrAppendAuthHeader;
11888
12291
  var init_tts_client = __esm({
11889
12292
  "src/services/tts/tts-client.ts"() {
11890
12293
  "use strict";
11891
- import_rxjs15 = require("rxjs");
12294
+ import_rxjs16 = require("rxjs");
11892
12295
  init_authenticationService();
11893
12296
  init_voiceStore();
11894
12297
  init_ttsSanitizer();
@@ -11908,11 +12311,11 @@ var init_tts_client = __esm({
11908
12311
  });
11909
12312
 
11910
12313
  // src/services/tts/streaming-tts.ts
11911
- var import_rxjs16, TTSState, StreamingTTSClient, getStreamingTTSClient, speakStream, stopTTS, pauseTTS, resumeTTS, getTTSState;
12314
+ var import_rxjs17, TTSState, StreamingTTSClient, getStreamingTTSClient, speakStream, stopTTS, pauseTTS, resumeTTS, getTTSState;
11912
12315
  var init_streaming_tts = __esm({
11913
12316
  "src/services/tts/streaming-tts.ts"() {
11914
12317
  "use strict";
11915
- import_rxjs16 = require("rxjs");
12318
+ import_rxjs17 = require("rxjs");
11916
12319
  init_debugLogger();
11917
12320
  init_packageSettingsStore();
11918
12321
  init_tts_client();
@@ -11932,8 +12335,8 @@ var init_streaming_tts = __esm({
11932
12335
  // Store event handler references for proper cleanup
11933
12336
  audioHandlers = /* @__PURE__ */ new Map();
11934
12337
  // State management
11935
- stateSubject = new import_rxjs16.BehaviorSubject("IDLE" /* IDLE */);
11936
- progressSubject = new import_rxjs16.Subject();
12338
+ stateSubject = new import_rxjs17.BehaviorSubject("IDLE" /* IDLE */);
12339
+ progressSubject = new import_rxjs17.Subject();
11937
12340
  constructor() {
11938
12341
  }
11939
12342
  static getInstance() {
@@ -11964,7 +12367,7 @@ var init_streaming_tts = __esm({
11964
12367
  * Speak text with simple streaming
11965
12368
  */
11966
12369
  speakStream(text, voice, options = {}) {
11967
- return new import_rxjs16.Observable((subscriber) => {
12370
+ return new import_rxjs17.Observable((subscriber) => {
11968
12371
  this.performSimpleStreaming(text, voice, options, subscriber);
11969
12372
  });
11970
12373
  }
@@ -14493,28 +14896,28 @@ var init_create_audio_blob = __esm({
14493
14896
  });
14494
14897
 
14495
14898
  // src/services/stt/sound-recorder.service.ts
14496
- var import_rxjs17, SoundRecorderService;
14899
+ var import_rxjs18, SoundRecorderService;
14497
14900
  var init_sound_recorder_service = __esm({
14498
14901
  "src/services/stt/sound-recorder.service.ts"() {
14499
14902
  "use strict";
14500
- import_rxjs17 = require("rxjs");
14903
+ import_rxjs18 = require("rxjs");
14501
14904
  init_create_audio_blob();
14502
14905
  SoundRecorderService = class {
14503
14906
  _mediaRecorder;
14504
14907
  start() {
14505
- const mediaStream = (0, import_rxjs17.from)(navigator.mediaDevices.getUserMedia({ audio: true }));
14506
- this._mediaRecorder = mediaStream.pipe((0, import_rxjs17.map)((stream) => {
14908
+ const mediaStream = (0, import_rxjs18.from)(navigator.mediaDevices.getUserMedia({ audio: true }));
14909
+ this._mediaRecorder = mediaStream.pipe((0, import_rxjs18.map)((stream) => {
14507
14910
  const rec = new MediaRecorder(stream);
14508
14911
  rec.start();
14509
14912
  return rec;
14510
- }), (0, import_rxjs17.shareReplay)(1));
14913
+ }), (0, import_rxjs18.shareReplay)(1));
14511
14914
  const dataAvailableEvent = this._mediaRecorder.pipe(
14512
- (0, import_rxjs17.switchMap)((recorder) => (0, import_rxjs17.fromEvent)(recorder, "dataavailable"))
14915
+ (0, import_rxjs18.switchMap)((recorder) => (0, import_rxjs18.fromEvent)(recorder, "dataavailable"))
14513
14916
  );
14514
14917
  const blob = dataAvailableEvent.pipe(
14515
- (0, import_rxjs17.first)(),
14516
- (0, import_rxjs17.map)((event) => createAudioBlob(event.data)),
14517
- (0, import_rxjs17.shareReplay)(1)
14918
+ (0, import_rxjs18.first)(),
14919
+ (0, import_rxjs18.map)((event) => createAudioBlob(event.data)),
14920
+ (0, import_rxjs18.shareReplay)(1)
14518
14921
  );
14519
14922
  return blob;
14520
14923
  }
@@ -14522,7 +14925,7 @@ var init_sound_recorder_service = __esm({
14522
14925
  if (!this._mediaRecorder) {
14523
14926
  return;
14524
14927
  }
14525
- this._mediaRecorder.pipe((0, import_rxjs17.first)()).subscribe((recorder) => {
14928
+ this._mediaRecorder.pipe((0, import_rxjs18.first)()).subscribe((recorder) => {
14526
14929
  recorder.stop();
14527
14930
  });
14528
14931
  }
@@ -14642,7 +15045,7 @@ var init_stt_client = __esm({
14642
15045
  });
14643
15046
 
14644
15047
  // src/services/stt/transcriber.tsx
14645
- var import_react16, import_Mic, import_Check4, import_Close3, import_material10, import_rxjs18, import_jsx_runtime13, initialButtonStyles, Transcriber, transcriber_default;
15048
+ var import_react16, import_Mic, import_Check4, import_Close3, import_material10, import_rxjs19, import_jsx_runtime13, initialButtonStyles, Transcriber, transcriber_default;
14646
15049
  var init_transcriber = __esm({
14647
15050
  "src/services/stt/transcriber.tsx"() {
14648
15051
  "use strict";
@@ -14653,7 +15056,7 @@ var init_transcriber = __esm({
14653
15056
  init_sound_recorder_service();
14654
15057
  init_stt_client();
14655
15058
  import_material10 = require("@mui/material");
14656
- import_rxjs18 = require("rxjs");
15059
+ import_rxjs19 = require("rxjs");
14657
15060
  init_debugLogger();
14658
15061
  import_jsx_runtime13 = require("react/jsx-runtime");
14659
15062
  initialButtonStyles = (badgeBackground, fileText, hoverBadgeBackground) => ({
@@ -14672,14 +15075,14 @@ var init_transcriber = __esm({
14672
15075
  const [status, setStatus] = (0, import_react16.useState)("IDLE");
14673
15076
  const recorderRef = (0, import_react16.useRef)(new SoundRecorderService());
14674
15077
  const [iconButtonStyles] = (0, import_react16.useState)(() => initialButtonStyles(badgeBackground, fileText, hoverBadgeBackground));
14675
- const [recordingSub, setRecordingSub] = (0, import_react16.useState)(() => new import_rxjs18.Subscription());
15078
+ const [recordingSub, setRecordingSub] = (0, import_react16.useState)(() => new import_rxjs19.Subscription());
14676
15079
  const start = () => {
14677
15080
  recordingSub.unsubscribe();
14678
15081
  const recording = recorderRef.current.start();
14679
15082
  const text = recording.pipe(
14680
- (0, import_rxjs18.switchMap)((blob) => {
15083
+ (0, import_rxjs19.switchMap)((blob) => {
14681
15084
  debugLogger.debug("Processing audio blob for transcription");
14682
- return (0, import_rxjs18.from)(STTClient.transcribe(blob));
15085
+ return (0, import_rxjs19.from)(STTClient.transcribe(blob));
14683
15086
  })
14684
15087
  );
14685
15088
  const sub = text.subscribe({
@@ -18293,11 +18696,11 @@ ${sanitize(
18293
18696
  });
18294
18697
 
18295
18698
  // src/chat/hooks/useMemoryEnhancer.tsx
18296
- var import_rxjs19, 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;
18699
+ 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;
18297
18700
  var init_useMemoryEnhancer = __esm({
18298
18701
  "src/chat/hooks/useMemoryEnhancer.tsx"() {
18299
18702
  "use strict";
18300
- import_rxjs19 = require("rxjs");
18703
+ import_rxjs20 = require("rxjs");
18301
18704
  init_memoryStore();
18302
18705
  init_aiProviderStore();
18303
18706
  init_packageSettingsStore();
@@ -18689,8 +19092,8 @@ var init_useMemoryEnhancer = __esm({
18689
19092
  stream: false,
18690
19093
  options: { temperature: 0.1, num_predict: 150 }
18691
19094
  });
18692
- const suggestion = await (0, import_rxjs19.lastValueFrom)(
18693
- result$.pipe((0, import_rxjs19.map)((chunk) => chunk.response))
19095
+ const suggestion = await (0, import_rxjs20.lastValueFrom)(
19096
+ result$.pipe((0, import_rxjs20.map)((chunk) => chunk.response))
18694
19097
  );
18695
19098
  debugLogger.memoryDebug(`LLM memory suggestion received (${attempt})`, {
18696
19099
  suggestion: typeof suggestion === "string" ? suggestion.slice(0, 200) : suggestion,
@@ -19381,10 +19784,22 @@ var init_useAIProvider = __esm({
19381
19784
  setResponse("");
19382
19785
  setStreamBuffer("");
19383
19786
  clearFlushTimer();
19384
- setPendingMessage({ question, images });
19787
+ const imageList = Array.isArray(images) ? [...images] : [];
19788
+ const conversationStoreState = useConversationStore.getState();
19789
+ const { addToCurrent, replaceLastAnswer, conversations, currentId } = conversationStoreState;
19790
+ const currentConv = conversations.find((c) => c.id === currentId);
19791
+ const lastEntry = currentConv?.history.at(-1);
19792
+ const lastWasPlaceholder = !!lastEntry && lastEntry.answer === "..." && (lastEntry.placeholder === true || lastEntry.rawQuestion === question || lastEntry.question === question);
19793
+ const pendingQuestion = lastWasPlaceholder ? lastEntry?.question ?? question : question;
19794
+ const pendingImagesRaw = lastWasPlaceholder && Array.isArray(lastEntry?.images) && lastEntry.images.length > 0 ? lastEntry.images : imageList;
19795
+ const pendingImages = Array.isArray(pendingImagesRaw) && pendingImagesRaw.length > 0 ? [...pendingImagesRaw] : void 0;
19796
+ setPendingMessage({
19797
+ question: pendingQuestion,
19798
+ images: pendingImages
19799
+ });
19385
19800
  const modelName = usePackageSettingsStore.getState().settings?.defaultModel || "bandit-core:4b-it-qat";
19386
19801
  const CONFIG = modelConfigs[modelName] ?? modelConfigs["bandit-core:4b-it-qat"];
19387
- const base64Images = images.map((img) => img.split(",")[1]);
19802
+ const base64Images = imageList.map((img) => img.split(",")[1]);
19388
19803
  const latestEntries = history.slice(-CONFIG.historyMessages);
19389
19804
  const contextMessages = latestEntries.flatMap((entry) => [
19390
19805
  { role: "user", content: entry.question },
@@ -19770,11 +20185,8 @@ ${protocol}`;
19770
20185
  }, delay);
19771
20186
  };
19772
20187
  const stream = provider.chat(request);
19773
- const { addToCurrent, conversations, currentId, replaceLastAnswer } = useConversationStore.getState();
19774
- const currentConv = conversations.find((c) => c.id === currentId);
19775
- const lastEntry = currentConv?.history.at(-1);
19776
- const isPlaceholder = lastEntry?.question === question && lastEntry?.answer === "...";
19777
- lastPartialRef.current = { text: "", images, usedDocs, question };
20188
+ const initialPlaceholderQuestion = lastEntry?.question;
20189
+ lastPartialRef.current = { text: "", images: [...imageList], usedDocs, question };
19778
20190
  if (currentSubRef.current) {
19779
20191
  try {
19780
20192
  currentSubRef.current.unsubscribe();
@@ -20033,14 +20445,24 @@ ${items.map((item, index) => {
20033
20445
  } else {
20034
20446
  debugLogger.info("Memory scan skipped - disabled in preferences");
20035
20447
  }
20036
- const { addToCurrent: addToCurrent2, replaceLastAnswer: replaceLastAnswer2 } = useConversationStore.getState();
20037
- const current = useConversationStore.getState();
20038
- const conv = current.conversations.find((c) => c.id === current.currentId);
20448
+ const currentState = useConversationStore.getState();
20449
+ const conv = currentState.conversations.find((c) => c.id === currentState.currentId);
20039
20450
  const last = conv?.history.at(-1);
20040
- if (!last || last.answer !== "..." || last.question !== question) {
20041
- addToCurrent2({ question, answer: enhancedMessage, images, memoryUpdated, sourceFiles: usedDocs });
20451
+ const lastIsPlaceholder = !!last && last.answer === "..." && last.placeholder !== false;
20452
+ const preservedImagesSource = imageList.length > 0 ? imageList : lastPartialRef.current.images.length > 0 ? lastPartialRef.current.images : last?.images;
20453
+ const preservedImages = Array.isArray(preservedImagesSource) && preservedImagesSource.length > 0 ? [...preservedImagesSource] : void 0;
20454
+ if (lastIsPlaceholder) {
20455
+ replaceLastAnswer(enhancedMessage, preservedImages, memoryUpdated, usedDocs);
20042
20456
  } else {
20043
- replaceLastAnswer2(enhancedMessage, images, memoryUpdated, usedDocs);
20457
+ const historyQuestion = last && last.answer === "..." && last.question || initialPlaceholderQuestion || question;
20458
+ addToCurrent({
20459
+ question: historyQuestion,
20460
+ answer: enhancedMessage,
20461
+ images: preservedImages,
20462
+ memoryUpdated,
20463
+ sourceFiles: usedDocs,
20464
+ rawQuestion: question
20465
+ });
20044
20466
  }
20045
20467
  setInputValue("");
20046
20468
  setPastedImages([]);
@@ -24283,15 +24705,15 @@ var init_chat_app_bar = __esm({
24283
24705
  });
24284
24706
 
24285
24707
  // src/chat/hooks/useConversationNameGenerator.tsx
24286
- var import_rxjs20, import_operators5, useConversationNameGenerator;
24708
+ var import_rxjs21, import_operators6, useConversationNameGenerator;
24287
24709
  var init_useConversationNameGenerator = __esm({
24288
24710
  "src/chat/hooks/useConversationNameGenerator.tsx"() {
24289
24711
  "use strict";
24290
24712
  init_aiProviderStore();
24291
24713
  init_packageSettingsStore();
24292
24714
  init_debugLogger();
24293
- import_rxjs20 = require("rxjs");
24294
- import_operators5 = require("rxjs/operators");
24715
+ import_rxjs21 = require("rxjs");
24716
+ import_operators6 = require("rxjs/operators");
24295
24717
  init_conversationStore();
24296
24718
  useConversationNameGenerator = () => {
24297
24719
  const generateName = async (userMessage) => {
@@ -24326,8 +24748,8 @@ Respond with just the title and nothing else.
24326
24748
  num_predict: 20
24327
24749
  }
24328
24750
  });
24329
- const title = await (0, import_rxjs20.lastValueFrom)(
24330
- result$.pipe((0, import_operators5.map)((d) => d.response?.trim().replace(/["']/g, "")))
24751
+ const title = await (0, import_rxjs21.lastValueFrom)(
24752
+ result$.pipe((0, import_operators6.map)((d) => d.response?.trim().replace(/["']/g, "")))
24331
24753
  );
24332
24754
  if (title && title.length > 0) {
24333
24755
  const sanitizedTitle = sanitizeConversationName(title, 60);
@@ -28236,12 +28658,20 @@ var init_chat2 = __esm({
28236
28658
  (question, images, displayQuestion) => {
28237
28659
  const requestStartTime = trackRequestStart();
28238
28660
  const questionForDisplay = displayQuestion || question;
28239
- setPendingMessage({ question: questionForDisplay, images });
28661
+ const pendingImages = images.length > 0 ? [...images] : void 0;
28662
+ setPendingMessage({ question: questionForDisplay, images: pendingImages });
28240
28663
  setIsStreaming(true);
28241
28664
  setResponseStarted(true);
28242
28665
  setStreamBuffer("");
28243
28666
  const { addToCurrent } = useConversationStore.getState();
28244
- addToCurrent({ question: questionForDisplay, answer: "...", images });
28667
+ const placeholderImages = pendingImages ? [...pendingImages] : void 0;
28668
+ addToCurrent({
28669
+ question: questionForDisplay,
28670
+ answer: "...",
28671
+ images: placeholderImages,
28672
+ placeholder: true,
28673
+ rawQuestion: question
28674
+ });
28245
28675
  const getCurrentModel = useModelStore.getState().getCurrentModel;
28246
28676
  const systemPrompt = getCurrentModel()?.systemPrompt ?? "You are a helpful assistant.";
28247
28677
  const { currentId: currentId2, conversations: conversations2, createConversation, renameConversation } = useConversationStore.getState();
@@ -28255,8 +28685,16 @@ var init_chat2 = __esm({
28255
28685
  if (!newCurrentId) return;
28256
28686
  setResponse("");
28257
28687
  const { addToCurrent: addToNew } = useConversationStore.getState();
28258
- addToNew({ question: questionForDisplay, answer: "...", images });
28259
- aiProvider(systemPrompt, question, images);
28688
+ const newPlaceholderImages = pendingImages ? [...pendingImages] : void 0;
28689
+ addToNew({
28690
+ question: questionForDisplay,
28691
+ answer: "...",
28692
+ images: newPlaceholderImages,
28693
+ placeholder: true,
28694
+ rawQuestion: question
28695
+ });
28696
+ const providerImages2 = pendingImages ? [...pendingImages] : [];
28697
+ aiProvider(systemPrompt, question, providerImages2);
28260
28698
  }, 0);
28261
28699
  });
28262
28700
  return;
@@ -28306,7 +28744,8 @@ var init_chat2 = __esm({
28306
28744
  }, 50);
28307
28745
  }
28308
28746
  setResponse("");
28309
- aiProvider(systemPrompt, question, images);
28747
+ const providerImages = pendingImages ? [...pendingImages] : [];
28748
+ aiProvider(systemPrompt, question, providerImages);
28310
28749
  },
28311
28750
  [
28312
28751
  aiProvider,
@@ -29620,7 +30059,7 @@ init_modelStore();
29620
30059
  init_ai_response_text_field();
29621
30060
  init_memory_modal();
29622
30061
  init_streaming_tts();
29623
- var import_rxjs21 = require("rxjs");
30062
+ var import_rxjs22 = require("rxjs");
29624
30063
  init_debugLogger();
29625
30064
  init_util();
29626
30065
  var import_jsx_runtime32 = require("react/jsx-runtime");
@@ -29649,7 +30088,7 @@ var AIQueriesDrawer = ({ drawerOpen, onClose, onClearComplete, onNavigateToMain
29649
30088
  const [memoryModalOpen, setMemoryModalOpen] = (0, import_react39.useState)(false);
29650
30089
  const [contextMode, setContextMode] = (0, import_react39.useState)("local");
29651
30090
  const [expandedSections, setExpandedSections] = (0, import_react39.useState)(/* @__PURE__ */ new Set(["history", "voice"]));
29652
- const [audioSub, setAudioSub] = (0, import_react39.useState)(new import_rxjs21.Subscription());
30091
+ const [audioSub, setAudioSub] = (0, import_react39.useState)(new import_rxjs22.Subscription());
29653
30092
  const [isContextSwitching, setIsContextSwitching] = (0, import_react39.useState)(false);
29654
30093
  const [isDrawerLoading, setIsDrawerLoading] = (0, import_react39.useState)(false);
29655
30094
  (0, import_react39.useEffect)(() => {
@@ -31312,7 +31751,7 @@ init_debugLogger();
31312
31751
  init_banditTheme();
31313
31752
  init_themeMap();
31314
31753
  init_useTTS();
31315
- var import_rxjs22 = require("rxjs");
31754
+ var import_rxjs23 = require("rxjs");
31316
31755
  var import_jsx_runtime34 = require("react/jsx-runtime");
31317
31756
  var FULL_SCREEN_THRESHOLD = 100;
31318
31757
  var CDN_BASE2 = "https://cdn.burtson.ai/";
@@ -31379,7 +31818,7 @@ var ChatModal = ({
31379
31818
  const [modalLogo, setModalLogo] = (0, import_react42.useState)("https://cdn.burtson.ai/logos/bandit-ai-logo.png");
31380
31819
  const [modelAnchorEl, setModelAnchorEl] = (0, import_react42.useState)(null);
31381
31820
  const [voiceAnchorEl, setVoiceAnchorEl] = (0, import_react42.useState)(null);
31382
- const [audioSub, setAudioSub] = (0, import_react42.useState)(new import_rxjs22.Subscription());
31821
+ const [audioSub, setAudioSub] = (0, import_react42.useState)(new import_rxjs23.Subscription());
31383
31822
  const [selectedTheme, setSelectedTheme] = (0, import_react42.useState)(null);
31384
31823
  const [themeLoading, setThemeLoading] = (0, import_react42.useState)(true);
31385
31824
  const [autoFullscreenTriggered, setAutoFullscreenTriggered] = (0, import_react42.useState)(false);
@@ -33920,7 +34359,7 @@ init_conversationSyncStore();
33920
34359
 
33921
34360
  // src/hooks/useGatewayQueries.ts
33922
34361
  var import_react45 = require("react");
33923
- var import_rxjs23 = require("rxjs");
34362
+ var import_rxjs24 = require("rxjs");
33924
34363
  var import_react_query2 = require("@tanstack/react-query");
33925
34364
  init_packageSettingsStore();
33926
34365
  init_gateway_service();
@@ -33951,7 +34390,7 @@ var useGatewayHealth = (options) => {
33951
34390
  if (!service) {
33952
34391
  throw new Error("Gateway service is not configured");
33953
34392
  }
33954
- return (0, import_rxjs23.lastValueFrom)(service.getHealth());
34393
+ return (0, import_rxjs24.lastValueFrom)(service.getHealth());
33955
34394
  },
33956
34395
  enabled
33957
34396
  });
@@ -33966,7 +34405,7 @@ var useGatewayModels = (options) => {
33966
34405
  if (!service) {
33967
34406
  throw new Error("Gateway service is not configured");
33968
34407
  }
33969
- return (0, import_rxjs23.lastValueFrom)(service.listModels());
34408
+ return (0, import_rxjs24.lastValueFrom)(service.listModels());
33970
34409
  },
33971
34410
  enabled
33972
34411
  });
@@ -33981,7 +34420,7 @@ var useGatewayMemory = (options) => {
33981
34420
  if (!service) {
33982
34421
  throw new Error("Gateway service is not configured");
33983
34422
  }
33984
- return (0, import_rxjs23.lastValueFrom)(service.getMemory());
34423
+ return (0, import_rxjs24.lastValueFrom)(service.getMemory());
33985
34424
  },
33986
34425
  enabled
33987
34426
  });
@@ -38715,6 +39154,8 @@ var ProviderTab = () => {
38715
39154
  return "gpt-4o-mini";
38716
39155
  case "xai" /* XAI */:
38717
39156
  return "grok-beta";
39157
+ case "bandit" /* BANDIT */:
39158
+ return "bandit-core-1";
38718
39159
  default:
38719
39160
  return "";
38720
39161
  }
@@ -38722,7 +39163,7 @@ var ProviderTab = () => {
38722
39163
  const applyDefaultModel = (0, import_react53.useCallback)((config) => {
38723
39164
  const normalized = { ...config };
38724
39165
  const trimmed = typeof normalized.defaultModel === "string" ? normalized.defaultModel.trim() : void 0;
38725
- const requiresModel = normalized.type === "openai" /* OPENAI */ || normalized.type === "xai" /* XAI */;
39166
+ const requiresModel = normalized.type === "openai" /* OPENAI */ || normalized.type === "xai" /* XAI */ || normalized.type === "bandit" /* BANDIT */;
38726
39167
  if (trimmed) {
38727
39168
  normalized.defaultModel = trimmed;
38728
39169
  return normalized;
@@ -38873,6 +39314,14 @@ var ProviderTab = () => {
38873
39314
  apiKey: ""
38874
39315
  }));
38875
39316
  break;
39317
+ case "bandit" /* BANDIT */:
39318
+ setProviderConfig(applyDefaultModel({
39319
+ ...baseConfig,
39320
+ baseUrl: "https://api.burtson.ai",
39321
+ apiKey: "",
39322
+ defaultModel: "bandit-core-1"
39323
+ }));
39324
+ break;
38876
39325
  case "gateway" /* GATEWAY */:
38877
39326
  setProviderConfig(applyDefaultModel({
38878
39327
  ...baseConfig,
@@ -38891,7 +39340,7 @@ var ProviderTab = () => {
38891
39340
  try {
38892
39341
  const normalizedConfigIntermediate = sanitizeConfigForSave(providerConfig);
38893
39342
  const normalizedConfig = convertAnthropicConfig(normalizedConfigIntermediate) || normalizedConfigIntermediate;
38894
- const requiresModel = normalizedConfig.type === "openai" /* OPENAI */ || normalizedConfig.type === "xai" /* XAI */;
39343
+ const requiresModel = normalizedConfig.type === "openai" /* OPENAI */ || normalizedConfig.type === "xai" /* XAI */ || normalizedConfig.type === "bandit" /* BANDIT */;
38895
39344
  if (requiresModel && !normalizedConfig.defaultModel) {
38896
39345
  showMessage("Please provide a default model ID for the selected provider.", "error");
38897
39346
  return;
@@ -39016,6 +39465,7 @@ var ProviderTab = () => {
39016
39465
  /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(import_material43.MenuItem, { value: "ollama", children: "Ollama" }),
39017
39466
  /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(import_material43.MenuItem, { value: "openai", children: "OpenAI" }),
39018
39467
  /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(import_material43.MenuItem, { value: "azure-openai", children: "Azure OpenAI" }),
39468
+ /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(import_material43.MenuItem, { value: "bandit", children: "Bandit AI" }),
39019
39469
  /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(import_material43.MenuItem, { value: "xai", children: "xAI" }),
39020
39470
  /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(import_material43.MenuItem, { value: "playground", children: "Playground (Mock Demo)" })
39021
39471
  ]
@@ -39050,6 +39500,7 @@ var ProviderTab = () => {
39050
39500
  /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(import_material43.MenuItem, { value: "azure-openai", children: "Azure OpenAI" }),
39051
39501
  /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(import_material43.MenuItem, { value: "anthropic", children: "Anthropic" }),
39052
39502
  /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(import_material43.MenuItem, { value: "ollama", children: "Ollama" }),
39503
+ /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(import_material43.MenuItem, { value: "bandit", children: "Bandit AI" }),
39053
39504
  /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(import_material43.MenuItem, { value: "xai", children: "xAI" })
39054
39505
  ]
39055
39506
  }
@@ -39065,6 +39516,52 @@ var ProviderTab = () => {
39065
39516
  placeholder: "http://localhost:11434"
39066
39517
  }
39067
39518
  ),
39519
+ providerConfig.type === "bandit" && /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)(import_material43.Box, { children: [
39520
+ /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
39521
+ import_material43.TextField,
39522
+ {
39523
+ label: "API Base URL",
39524
+ value: providerConfig.baseUrl || "",
39525
+ onChange: (e) => setProviderConfig((prev) => ({
39526
+ ...prev,
39527
+ baseUrl: e.target.value
39528
+ })),
39529
+ fullWidth: true,
39530
+ sx: { mb: 2 },
39531
+ placeholder: "https://api.burtson.ai",
39532
+ helperText: "Defaults to https://api.burtson.ai"
39533
+ }
39534
+ ),
39535
+ /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
39536
+ import_material43.TextField,
39537
+ {
39538
+ label: "API Key",
39539
+ type: "password",
39540
+ value: providerConfig.apiKey || "",
39541
+ onChange: (e) => setProviderConfig((prev) => ({
39542
+ ...prev,
39543
+ apiKey: e.target.value
39544
+ })),
39545
+ fullWidth: true,
39546
+ sx: { mb: 2 },
39547
+ placeholder: "bai_..."
39548
+ }
39549
+ ),
39550
+ /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
39551
+ import_material43.TextField,
39552
+ {
39553
+ label: "Default Model ID",
39554
+ value: providerConfig.defaultModel || "",
39555
+ onChange: (e) => setProviderConfig((prev) => ({
39556
+ ...prev,
39557
+ defaultModel: e.target.value
39558
+ })),
39559
+ fullWidth: true,
39560
+ placeholder: "bandit-core-1",
39561
+ helperText: "Example: bandit-core-1 (Bandit Core canonical alias)."
39562
+ }
39563
+ )
39564
+ ] }),
39068
39565
  providerConfig.type === "openai" && /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)(import_material43.Box, { children: [
39069
39566
  /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
39070
39567
  import_material43.TextField,
@@ -39405,20 +39902,20 @@ var MCPToolsTabV2 = () => {
39405
39902
  }
39406
39903
  }, [isLoaded]);
39407
39904
  const localEnabledMap = (0, import_react54.useMemo)(() => {
39408
- const map21 = /* @__PURE__ */ new Map();
39905
+ const map23 = /* @__PURE__ */ new Map();
39409
39906
  const sortedTools = [...localTools].sort((a, b) => {
39410
39907
  if (a.isBuiltIn && !b.isBuiltIn) return -1;
39411
39908
  if (!a.isBuiltIn && b.isBuiltIn) return 1;
39412
39909
  return a.id.length - b.id.length;
39413
39910
  });
39414
39911
  sortedTools.forEach((t) => {
39415
- map21.set(t.function.name, t.enabled);
39416
- map21.set(t.id, t.enabled);
39912
+ map23.set(t.function.name, t.enabled);
39913
+ map23.set(t.id, t.enabled);
39417
39914
  if (t.name) {
39418
- map21.set(t.name, t.enabled);
39915
+ map23.set(t.name, t.enabled);
39419
39916
  }
39420
39917
  });
39421
- return map21;
39918
+ return map23;
39422
39919
  }, [localTools]);
39423
39920
  return /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)(import_material44.Box, { children: [
39424
39921
  /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)(import_material44.Box, { sx: { display: "flex", alignItems: "center", justifyContent: "space-between", mb: 2 }, children: [