@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
@@ -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
  };
@@ -1657,13 +1657,17 @@ var init_gateway_service = __esm({
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
1659
  const url = `${this._baseUrl}${endpoint}`;
1660
+ const normalizedModel = request.provider === "bandit" ? (() => {
1661
+ const trimmed = (request.model ?? "").replace(/^bandit:/, "").trim();
1662
+ return trimmed !== "" ? trimmed : "bandit-core-1";
1663
+ })() : request.model;
1660
1664
  debugLogger.debug(`Gateway chat request to ${url} with provider: ${request.provider || "default"}`, {
1661
- model: request.model,
1665
+ model: normalizedModel,
1662
1666
  messageCount: request.messages.length,
1663
1667
  hasImages: !!(request.images && request.images.length > 0),
1664
1668
  imageCount: request.images?.length || 0
1665
1669
  });
1666
- const requestBody = { ...request, stream: request.stream !== false };
1670
+ const requestBody = { ...request, model: normalizedModel, stream: request.stream !== false };
1667
1671
  return new import_rxjs6.Observable((observer) => {
1668
1672
  const controller = new AbortController();
1669
1673
  const task = fetch(url, {
@@ -1803,12 +1807,18 @@ var init_gateway_service = __esm({
1803
1807
  generate(request) {
1804
1808
  const endpoint = request.provider ? `/api/${request.provider}/generate` : "/api/generate";
1805
1809
  const url = `${this._baseUrl}${endpoint}`;
1806
- debugLogger.debug(`Gateway generate request to ${url} with provider: ${request.provider || "default"}`);
1810
+ const normalizedModel = request.provider === "bandit" ? (() => {
1811
+ const trimmed = (request.model ?? "").replace(/^bandit:/, "").trim();
1812
+ return trimmed !== "" ? trimmed : "bandit-core-1";
1813
+ })() : request.model;
1814
+ debugLogger.debug(`Gateway generate request to ${url} with provider: ${request.provider || "default"}`, {
1815
+ model: normalizedModel
1816
+ });
1807
1817
  return new import_rxjs6.Observable((observer) => {
1808
1818
  const task = fetch(url, {
1809
1819
  method: "POST",
1810
1820
  headers: this._getHeaders(),
1811
- body: JSON.stringify({ ...request, stream: request.stream !== false })
1821
+ body: JSON.stringify({ ...request, model: normalizedModel, stream: request.stream !== false })
1812
1822
  });
1813
1823
  task.then(async (response) => {
1814
1824
  if (!response.ok) {
@@ -2406,6 +2416,112 @@ var init_ollama_gateway_service = __esm({
2406
2416
  }
2407
2417
  });
2408
2418
 
2419
+ // src/services/gateway/bandit-gateway.service.ts
2420
+ var import_operators5, normalizeBanditModel, isGatewayMessageContent, normalizeBanditMessages, BanditAIGatewayService;
2421
+ var init_bandit_gateway_service = __esm({
2422
+ "src/services/gateway/bandit-gateway.service.ts"() {
2423
+ "use strict";
2424
+ init_gateway_service();
2425
+ import_operators5 = require("rxjs/operators");
2426
+ init_debugLogger();
2427
+ normalizeBanditModel = (model) => {
2428
+ if (typeof model !== "string" || model.trim() === "") {
2429
+ return "bandit-core-1";
2430
+ }
2431
+ const normalized = model.replace(/^bandit:/, "").trim();
2432
+ return normalized === "" ? "bandit-core-1" : normalized;
2433
+ };
2434
+ isGatewayMessageContent = (value) => {
2435
+ if (!value || typeof value !== "object") return false;
2436
+ const candidate = value;
2437
+ if (candidate.type !== "text" && candidate.type !== "image_url") {
2438
+ return false;
2439
+ }
2440
+ if (candidate.type === "text") {
2441
+ return typeof candidate.text === "string";
2442
+ }
2443
+ if (candidate.type === "image_url") {
2444
+ return !!candidate.image_url && typeof candidate.image_url.url === "string";
2445
+ }
2446
+ return false;
2447
+ };
2448
+ normalizeBanditMessages = (messages) => messages.map((message) => {
2449
+ const content = message.content;
2450
+ if (typeof content === "string") {
2451
+ return { role: message.role, content };
2452
+ }
2453
+ if (Array.isArray(content)) {
2454
+ const filtered = content.filter(isGatewayMessageContent);
2455
+ if (filtered.length === 0) {
2456
+ return { role: message.role, content: JSON.stringify(content) };
2457
+ }
2458
+ return {
2459
+ role: message.role,
2460
+ content: filtered
2461
+ };
2462
+ }
2463
+ return { role: message.role, content: content != null ? String(content) : "" };
2464
+ });
2465
+ BanditAIGatewayService = class {
2466
+ _gatewayService;
2467
+ constructor(gatewayUrl, tokenFactory) {
2468
+ this._gatewayService = new GatewayService(gatewayUrl, tokenFactory);
2469
+ debugLogger.info("BanditAIGatewayService initialized", { gatewayUrl });
2470
+ }
2471
+ async validateServiceAvailability(args) {
2472
+ return this._gatewayService.validateServiceAvailability(args);
2473
+ }
2474
+ chat(request) {
2475
+ const model = normalizeBanditModel(request.model);
2476
+ const messages = normalizeBanditMessages(request.messages);
2477
+ const gatewayRequest = {
2478
+ ...request,
2479
+ messages,
2480
+ model,
2481
+ provider: "bandit",
2482
+ stream: request.stream
2483
+ };
2484
+ debugLogger.debug("Bandit Gateway chat request", {
2485
+ model,
2486
+ messageCount: request.messages.length,
2487
+ stream: request.stream
2488
+ });
2489
+ return this._gatewayService.chat(gatewayRequest);
2490
+ }
2491
+ complete(prompt, options) {
2492
+ const model = normalizeBanditModel(options.model);
2493
+ const gatewayRequest = {
2494
+ model,
2495
+ prompt,
2496
+ temperature: options.temperature,
2497
+ max_tokens: options.max_tokens,
2498
+ stream: options.stream,
2499
+ stop: options.stop,
2500
+ provider: "bandit"
2501
+ };
2502
+ debugLogger.debug("Bandit Gateway generate request", {
2503
+ model,
2504
+ promptLength: prompt.length,
2505
+ stream: options.stream
2506
+ });
2507
+ return this._gatewayService.generate(gatewayRequest);
2508
+ }
2509
+ listModels() {
2510
+ debugLogger.debug("Fetching Bandit models through gateway");
2511
+ return this._gatewayService.listModelsByProvider("bandit");
2512
+ }
2513
+ getHealth() {
2514
+ return this._gatewayService.getHealth().pipe(
2515
+ (0, import_operators5.map)((health) => ({
2516
+ ...health,
2517
+ bandit_status: health.providers.find((p) => p.name === "bandit")?.status || "unavailable"
2518
+ }))
2519
+ );
2520
+ }
2521
+ };
2522
+ }
2523
+ });
2524
+
2409
2525
  // src/services/ai-provider/providers/gateway.provider.ts
2410
2526
  var import_rxjs7, GatewayProvider;
2411
2527
  var init_gateway_provider = __esm({
@@ -2419,6 +2535,7 @@ var init_gateway_provider = __esm({
2419
2535
  init_azure_openai_gateway_service();
2420
2536
  init_anthropic_gateway_service();
2421
2537
  init_ollama_gateway_service();
2538
+ init_bandit_gateway_service();
2422
2539
  GatewayProvider = class {
2423
2540
  config;
2424
2541
  gatewayService;
@@ -2460,6 +2577,9 @@ var init_gateway_provider = __esm({
2460
2577
  case "anthropic":
2461
2578
  this.providerSpecificService = new AnthropicGatewayService(gatewayUrl, tokenFactory);
2462
2579
  break;
2580
+ case "bandit":
2581
+ this.providerSpecificService = new BanditAIGatewayService(gatewayUrl, tokenFactory);
2582
+ break;
2463
2583
  case "ollama":
2464
2584
  this.providerSpecificService = new OllamaGatewayService(gatewayUrl, tokenFactory);
2465
2585
  break;
@@ -2474,6 +2594,16 @@ var init_gateway_provider = __esm({
2474
2594
  role: msg.role,
2475
2595
  content: msg.content
2476
2596
  }));
2597
+ const normalizeImageUrl2 = (value) => {
2598
+ if (!value) {
2599
+ return value;
2600
+ }
2601
+ const trimmed = value.trim();
2602
+ if (/^data:/i.test(trimmed) || /^https?:\/\//i.test(trimmed)) {
2603
+ return trimmed;
2604
+ }
2605
+ return `data:image/jpeg;base64,${trimmed}`;
2606
+ };
2477
2607
  if (request.images && request.images.length > 0) {
2478
2608
  const lastUserMessageIndex = messages.map((m) => m.role).lastIndexOf("user");
2479
2609
  if (this.config.provider === "ollama") {
@@ -2483,7 +2613,7 @@ var init_gateway_provider = __esm({
2483
2613
  images: request.images
2484
2614
  };
2485
2615
  }
2486
- } else if (["openai", "azure-openai", "anthropic"].includes(this.config.provider || "")) {
2616
+ } else if (["openai", "azure-openai", "anthropic", "bandit"].includes(this.config.provider || "")) {
2487
2617
  if (lastUserMessageIndex !== -1) {
2488
2618
  const currentMessage = messages[lastUserMessageIndex];
2489
2619
  const contentArray = [
@@ -2492,11 +2622,11 @@ var init_gateway_provider = __esm({
2492
2622
  text: currentMessage.content
2493
2623
  }
2494
2624
  ];
2495
- request.images.forEach((base64Image) => {
2625
+ request.images.forEach((imageRef) => {
2496
2626
  contentArray.push({
2497
2627
  type: "image_url",
2498
2628
  image_url: {
2499
- url: base64Image.startsWith("data:") ? base64Image : `data:image/jpeg;base64,${base64Image}`,
2629
+ url: normalizeImageUrl2(imageRef),
2500
2630
  detail: "auto"
2501
2631
  }
2502
2632
  });
@@ -2505,6 +2635,11 @@ var init_gateway_provider = __esm({
2505
2635
  ...messages[lastUserMessageIndex],
2506
2636
  content: contentArray
2507
2637
  };
2638
+ debugLogger.debug("Gateway provider injected image attachments", {
2639
+ provider: this.config.provider,
2640
+ imageCount: request.images.length,
2641
+ messageIndex: lastUserMessageIndex
2642
+ });
2508
2643
  }
2509
2644
  }
2510
2645
  }
@@ -3013,6 +3148,244 @@ var init_xai_provider = __esm({
3013
3148
  }
3014
3149
  });
3015
3150
 
3151
+ // src/services/ai-provider/providers/bandit-ai.provider.ts
3152
+ var import_rxjs10, DEFAULT_BANDIT_BASE, normalizeImageUrl, injectImagesIntoMessages, BanditAIProvider;
3153
+ var init_bandit_ai_provider = __esm({
3154
+ "src/services/ai-provider/providers/bandit-ai.provider.ts"() {
3155
+ "use strict";
3156
+ import_rxjs10 = require("rxjs");
3157
+ init_common_types();
3158
+ init_debugLogger();
3159
+ DEFAULT_BANDIT_BASE = "https://api.burtson.ai";
3160
+ normalizeImageUrl = (value) => {
3161
+ if (!value) {
3162
+ return null;
3163
+ }
3164
+ const trimmed = value.trim();
3165
+ if (!trimmed) {
3166
+ return null;
3167
+ }
3168
+ if (/^data:/i.test(trimmed) || /^https?:\/\//i.test(trimmed)) {
3169
+ return trimmed;
3170
+ }
3171
+ return `data:image/jpeg;base64,${trimmed}`;
3172
+ };
3173
+ injectImagesIntoMessages = (messages, images) => {
3174
+ const normalized = messages.map((message) => ({
3175
+ role: message.role,
3176
+ content: message.content
3177
+ }));
3178
+ if (!images || images.length === 0) {
3179
+ return normalized;
3180
+ }
3181
+ const normalizedImages = images.map(normalizeImageUrl).filter((url) => Boolean(url));
3182
+ if (normalizedImages.length === 0) {
3183
+ return normalized;
3184
+ }
3185
+ const lastUserIndex = normalized.map((msg) => msg.role).lastIndexOf("user");
3186
+ if (lastUserIndex === -1) {
3187
+ return normalized;
3188
+ }
3189
+ const target = normalized[lastUserIndex];
3190
+ const baseContent = typeof target.content === "string" && target.content.trim().length > 0 ? [
3191
+ {
3192
+ type: "text",
3193
+ text: target.content
3194
+ }
3195
+ ] : [];
3196
+ const imageContent = normalizedImages.map((url) => ({
3197
+ type: "image_url",
3198
+ image_url: { url, detail: "auto" }
3199
+ }));
3200
+ normalized[lastUserIndex] = {
3201
+ role: target.role,
3202
+ content: [...baseContent, ...imageContent]
3203
+ };
3204
+ return normalized;
3205
+ };
3206
+ BanditAIProvider = class {
3207
+ config;
3208
+ baseUrl;
3209
+ constructor(config) {
3210
+ this.config = config;
3211
+ this.baseUrl = (config.baseUrl || DEFAULT_BANDIT_BASE).replace(/\/$/, "");
3212
+ }
3213
+ chat(request) {
3214
+ const url = `${this.baseUrl}/chat/completions`;
3215
+ const messages = injectImagesIntoMessages(request.messages, request.images);
3216
+ const payload = {
3217
+ model: request.model,
3218
+ messages,
3219
+ stream: Boolean(request.stream),
3220
+ temperature: request.temperature,
3221
+ max_tokens: request.maxTokens
3222
+ };
3223
+ if (request.stream) {
3224
+ return this.streamChatRequest(url, payload);
3225
+ }
3226
+ return this.nonStreamChatRequest(url, payload);
3227
+ }
3228
+ generate(request) {
3229
+ const chatRequest = {
3230
+ model: request.model,
3231
+ messages: [{ role: "user", content: request.prompt }],
3232
+ stream: request.stream,
3233
+ options: request.options
3234
+ };
3235
+ return this.chat(chatRequest).pipe(
3236
+ (0, import_rxjs10.map)((response) => ({
3237
+ response: response.message.content,
3238
+ done: response.done
3239
+ }))
3240
+ );
3241
+ }
3242
+ listModels() {
3243
+ const url = `${this.baseUrl}/models`;
3244
+ return (0, import_rxjs10.from)(fetch(url, { headers: this.getHeaders() })).pipe(
3245
+ (0, import_rxjs10.switchMap)((response) => {
3246
+ if (!response.ok) {
3247
+ debugLogger.error("BanditAI listModels failed", { status: response.status, url });
3248
+ return (0, import_rxjs10.throwError)(() => new Error(`Failed to list Bandit models: ${response.status}`));
3249
+ }
3250
+ return (0, import_rxjs10.from)(response.json());
3251
+ }),
3252
+ (0, import_rxjs10.map)(
3253
+ (data) => data.data.map((model) => ({
3254
+ name: model.id,
3255
+ details: {
3256
+ format: "bandit",
3257
+ family: model.object
3258
+ }
3259
+ }))
3260
+ )
3261
+ );
3262
+ }
3263
+ async validateServiceAvailability(args) {
3264
+ const attempt = async (url) => {
3265
+ try {
3266
+ const controller = new AbortController();
3267
+ const timeoutId = setTimeout(() => controller.abort(), args.timeoutMs);
3268
+ const response = await fetch(`${url}/models`, {
3269
+ headers: this.getHeaders(),
3270
+ signal: controller.signal
3271
+ });
3272
+ clearTimeout(timeoutId);
3273
+ return response.ok;
3274
+ } catch (error) {
3275
+ debugLogger.warn("BanditAI availability check failed", { url, error });
3276
+ return false;
3277
+ }
3278
+ };
3279
+ const primary = await attempt(this.baseUrl);
3280
+ if (primary) {
3281
+ return { url: this.baseUrl, isAvailable: true };
3282
+ }
3283
+ if (args.fallbackUrl) {
3284
+ const fallback = args.fallbackUrl.replace(/\/$/, "");
3285
+ if (await attempt(fallback)) {
3286
+ this.baseUrl = fallback;
3287
+ return { url: fallback, isAvailable: true };
3288
+ }
3289
+ }
3290
+ return { url: this.baseUrl, isAvailable: false };
3291
+ }
3292
+ getProviderType() {
3293
+ return "bandit" /* BANDIT */;
3294
+ }
3295
+ getConfig() {
3296
+ return this.config;
3297
+ }
3298
+ streamChatRequest(url, payload) {
3299
+ return new import_rxjs10.Observable((observer) => {
3300
+ const task = fetch(url, {
3301
+ method: "POST",
3302
+ headers: {
3303
+ ...this.getHeaders(),
3304
+ "Content-Type": "application/json"
3305
+ },
3306
+ body: JSON.stringify(payload)
3307
+ });
3308
+ task.then((response) => {
3309
+ if (!response.ok) {
3310
+ observer.error(new Error(`BanditAI request failed: ${response.status}`));
3311
+ return;
3312
+ }
3313
+ const reader = response.body?.getReader();
3314
+ const decoder = new TextDecoder();
3315
+ let buffer = "";
3316
+ const read = () => {
3317
+ reader?.read().then(({ done, value }) => {
3318
+ if (done) {
3319
+ observer.next({ message: { content: "", role: "assistant" }, done: true });
3320
+ observer.complete();
3321
+ return;
3322
+ }
3323
+ buffer += decoder.decode(value, { stream: true });
3324
+ const lines = buffer.split("\n");
3325
+ buffer = lines.pop() ?? "";
3326
+ for (const rawLine of lines) {
3327
+ const line = rawLine.trim();
3328
+ if (!line.startsWith("data: ")) {
3329
+ continue;
3330
+ }
3331
+ const data = line.slice(6).trim();
3332
+ if (data === "[DONE]") {
3333
+ observer.next({ message: { content: "", role: "assistant" }, done: true });
3334
+ observer.complete();
3335
+ return;
3336
+ }
3337
+ try {
3338
+ const parsed = JSON.parse(data);
3339
+ const content = parsed.choices?.[0]?.delta?.content ?? "";
3340
+ if (content) {
3341
+ observer.next({ message: { content, role: "assistant" }, done: false });
3342
+ }
3343
+ } catch (error) {
3344
+ debugLogger.error("BanditAI stream chunk parse failure", { data, error });
3345
+ }
3346
+ }
3347
+ read();
3348
+ }).catch((err) => observer.error(err));
3349
+ };
3350
+ read();
3351
+ }).catch((err) => observer.error(err));
3352
+ });
3353
+ }
3354
+ nonStreamChatRequest(url, payload) {
3355
+ return (0, import_rxjs10.from)(fetch(url, {
3356
+ method: "POST",
3357
+ headers: {
3358
+ ...this.getHeaders(),
3359
+ "Content-Type": "application/json"
3360
+ },
3361
+ body: JSON.stringify(payload)
3362
+ })).pipe(
3363
+ (0, import_rxjs10.switchMap)((response) => {
3364
+ if (!response.ok) {
3365
+ return (0, import_rxjs10.throwError)(() => new Error(`BanditAI request failed: ${response.status}`));
3366
+ }
3367
+ return (0, import_rxjs10.from)(response.json());
3368
+ }),
3369
+ (0, import_rxjs10.map)((data) => ({
3370
+ message: {
3371
+ content: data.choices?.[0]?.message?.content ?? "",
3372
+ role: "assistant"
3373
+ },
3374
+ done: true
3375
+ }))
3376
+ );
3377
+ }
3378
+ getHeaders() {
3379
+ const headers = {};
3380
+ if (this.config.apiKey) {
3381
+ headers["Authorization"] = `Bearer ${this.config.apiKey}`;
3382
+ }
3383
+ return headers;
3384
+ }
3385
+ };
3386
+ }
3387
+ });
3388
+
3016
3389
  // src/services/ai-provider/ai-provider.factory.ts
3017
3390
  var AIProviderFactory;
3018
3391
  var init_ai_provider_factory = __esm({
@@ -3026,6 +3399,7 @@ var init_ai_provider_factory = __esm({
3026
3399
  init_gateway_provider();
3027
3400
  init_playground_provider();
3028
3401
  init_xai_provider();
3402
+ init_bandit_ai_provider();
3029
3403
  AIProviderFactory = class {
3030
3404
  static createProvider(config) {
3031
3405
  switch (config.type) {
@@ -3039,6 +3413,8 @@ var init_ai_provider_factory = __esm({
3039
3413
  return new AnthropicProvider(config);
3040
3414
  case "xai" /* XAI */:
3041
3415
  return new XAIProvider(config);
3416
+ case "bandit" /* BANDIT */:
3417
+ return new BanditAIProvider(config);
3042
3418
  case "gateway" /* GATEWAY */:
3043
3419
  return new GatewayProvider(config);
3044
3420
  case "playground" /* PLAYGROUND */:
@@ -3053,6 +3429,7 @@ var init_ai_provider_factory = __esm({
3053
3429
  "openai" /* OPENAI */,
3054
3430
  "azure-openai" /* AZURE_OPENAI */,
3055
3431
  "xai" /* XAI */,
3432
+ "bandit" /* BANDIT */,
3056
3433
  "gateway" /* GATEWAY */,
3057
3434
  "playground" /* PLAYGROUND */
3058
3435
  ];
@@ -3070,6 +3447,8 @@ var init_ai_provider_factory = __esm({
3070
3447
  return !!config.apiKey;
3071
3448
  case "xai" /* XAI */:
3072
3449
  return !!config.apiKey;
3450
+ case "bandit" /* BANDIT */:
3451
+ return !!config.apiKey;
3073
3452
  case "gateway" /* GATEWAY */:
3074
3453
  return !!(config.gatewayUrl && config.provider);
3075
3454
  case "playground" /* PLAYGROUND */:
@@ -3445,11 +3824,11 @@ var init_notificationService = __esm({
3445
3824
  });
3446
3825
 
3447
3826
  // src/services/prompts/conversationStarters.ts
3448
- var import_rxjs10, generateConversationStarters;
3827
+ var import_rxjs11, generateConversationStarters;
3449
3828
  var init_conversationStarters = __esm({
3450
3829
  "src/services/prompts/conversationStarters.ts"() {
3451
3830
  "use strict";
3452
- import_rxjs10 = require("rxjs");
3831
+ import_rxjs11 = require("rxjs");
3453
3832
  init_aiProviderStore();
3454
3833
  init_packageSettingsStore();
3455
3834
  init_getStableQuestionPrompt();
@@ -3470,7 +3849,7 @@ var init_conversationStarters = __esm({
3470
3849
  stream: false,
3471
3850
  options: { temperature: 1.5, num_predict: 250 }
3472
3851
  });
3473
- const questions$ = data$.pipe((0, import_rxjs10.map)((d) => {
3852
+ const questions$ = data$.pipe((0, import_rxjs11.map)((d) => {
3474
3853
  const sanitizeLine = (line) => {
3475
3854
  const withoutNumbering = line.replace(/^[0-9]+[.)\-\s:]+/, "").replace(/^[•*+-]\s+/, "");
3476
3855
  const withoutQuotes = withoutNumbering.replace(/^[“"']+/, "").replace(/[”"']+$/, "");
@@ -3492,7 +3871,7 @@ var init_conversationStarters = __esm({
3492
3871
  });
3493
3872
  return unique;
3494
3873
  }));
3495
- const starters = await (0, import_rxjs10.lastValueFrom)(questions$);
3874
+ const starters = await (0, import_rxjs11.lastValueFrom)(questions$);
3496
3875
  if (starters.length === 0) {
3497
3876
  debugLogger.warn("No meaningful conversation starters generated");
3498
3877
  return [];
@@ -3508,11 +3887,11 @@ var init_conversationStarters = __esm({
3508
3887
  });
3509
3888
 
3510
3889
  // src/services/prompts/moodDetection.ts
3511
- var import_rxjs11, detectMessageMood;
3890
+ var import_rxjs12, detectMessageMood;
3512
3891
  var init_moodDetection = __esm({
3513
3892
  "src/services/prompts/moodDetection.ts"() {
3514
3893
  "use strict";
3515
- import_rxjs11 = require("rxjs");
3894
+ import_rxjs12 = require("rxjs");
3516
3895
  init_aiProviderStore();
3517
3896
  init_packageSettingsStore();
3518
3897
  init_debugLogger();
@@ -3543,8 +3922,8 @@ Response:`;
3543
3922
  options: { temperature: 0.3, num_predict: 10 }
3544
3923
  });
3545
3924
  const chunks$ = response$.pipe(
3546
- (0, import_rxjs11.map)((chunk) => chunk.response.trim().toLowerCase()),
3547
- (0, import_rxjs11.toArray)()
3925
+ (0, import_rxjs12.map)((chunk) => chunk.response.trim().toLowerCase()),
3926
+ (0, import_rxjs12.toArray)()
3548
3927
  );
3549
3928
  const result = await chunks$.toPromise();
3550
3929
  const finalResult = (result || []).join("").trim();
@@ -3561,11 +3940,11 @@ Response:`;
3561
3940
  });
3562
3941
 
3563
3942
  // src/services/prompts/detectUserInterestAndExcitement.ts
3564
- var import_rxjs12, detectUserInterestAndExcitement;
3943
+ var import_rxjs13, detectUserInterestAndExcitement;
3565
3944
  var init_detectUserInterestAndExcitement = __esm({
3566
3945
  "src/services/prompts/detectUserInterestAndExcitement.ts"() {
3567
3946
  "use strict";
3568
- import_rxjs12 = require("rxjs");
3947
+ import_rxjs13 = require("rxjs");
3569
3948
  init_aiProviderStore();
3570
3949
  init_packageSettingsStore();
3571
3950
  init_debugLogger();
@@ -3612,10 +3991,10 @@ var init_detectUserInterestAndExcitement = __esm({
3612
3991
  options: { temperature: 0.1, num_predict: 5 }
3613
3992
  });
3614
3993
  const chunks$ = response$.pipe(
3615
- (0, import_rxjs12.map)((chunk) => chunk.response.trim().toUpperCase()),
3616
- (0, import_rxjs12.toArray)()
3994
+ (0, import_rxjs13.map)((chunk) => chunk.response.trim().toUpperCase()),
3995
+ (0, import_rxjs13.toArray)()
3617
3996
  );
3618
- const result = await (0, import_rxjs12.lastValueFrom)(chunks$);
3997
+ const result = await (0, import_rxjs13.lastValueFrom)(chunks$);
3619
3998
  const decision = result.join("").trim();
3620
3999
  debugLogger.llmDebug("detectUserInterestAndExcitement result", { decision });
3621
4000
  return decision.includes("YES");
@@ -3628,11 +4007,11 @@ var init_detectUserInterestAndExcitement = __esm({
3628
4007
  });
3629
4008
 
3630
4009
  // src/services/prompts/documentSummarization.ts
3631
- var import_rxjs13, summarizeDocument;
4010
+ var import_rxjs14, summarizeDocument;
3632
4011
  var init_documentSummarization = __esm({
3633
4012
  "src/services/prompts/documentSummarization.ts"() {
3634
4013
  "use strict";
3635
- import_rxjs13 = require("rxjs");
4014
+ import_rxjs14 = require("rxjs");
3636
4015
  init_aiProviderStore();
3637
4016
  init_packageSettingsStore();
3638
4017
  init_debugLogger();
@@ -3660,8 +4039,8 @@ ${content.slice(0, 4e3)}
3660
4039
  stream: false,
3661
4040
  options: { temperature: 0.3, num_predict: 100 }
3662
4041
  });
3663
- const summary$ = data$.pipe((0, import_rxjs13.map)((d) => d.response.trim()));
3664
- const summary = await (0, import_rxjs13.lastValueFrom)(summary$);
4042
+ const summary$ = data$.pipe((0, import_rxjs14.map)((d) => d.response.trim()));
4043
+ const summary = await (0, import_rxjs14.lastValueFrom)(summary$);
3665
4044
  debugLogger.ragDebug("summarizeDocument result", { name, summary });
3666
4045
  return summary || `Document summary for ${name}`;
3667
4046
  } catch (error) {
@@ -3673,11 +4052,11 @@ ${content.slice(0, 4e3)}
3673
4052
  });
3674
4053
 
3675
4054
  // src/services/prompts/documentRelevance.ts
3676
- var import_rxjs14, determineRelevantDocuments;
4055
+ var import_rxjs15, determineRelevantDocuments;
3677
4056
  var init_documentRelevance = __esm({
3678
4057
  "src/services/prompts/documentRelevance.ts"() {
3679
4058
  "use strict";
3680
- import_rxjs14 = require("rxjs");
4059
+ import_rxjs15 = require("rxjs");
3681
4060
  init_aiProviderStore();
3682
4061
  init_packageSettingsStore();
3683
4062
  init_debugLogger();
@@ -3718,10 +4097,10 @@ Response:`;
3718
4097
  options: { temperature: 0.2, num_predict: 50 }
3719
4098
  });
3720
4099
  const chunks$ = response$.pipe(
3721
- (0, import_rxjs14.map)((chunk) => chunk.response.trim()),
3722
- (0, import_rxjs14.toArray)()
4100
+ (0, import_rxjs15.map)((chunk) => chunk.response.trim()),
4101
+ (0, import_rxjs15.toArray)()
3723
4102
  );
3724
- const result = await (0, import_rxjs14.lastValueFrom)(chunks$);
4103
+ const result = await (0, import_rxjs15.lastValueFrom)(chunks$);
3725
4104
  const vetResult = result.join("").trim().toLowerCase();
3726
4105
  debugLogger.ragDebug("determineRelevantDocuments result", { vetResult });
3727
4106
  if (vetResult.includes("none") || !vetResult) {
@@ -7106,13 +7485,17 @@ var init_conversationStore = __esm({
7106
7485
  const updatedConversations = conversations.map((c) => {
7107
7486
  if (c.id === currentId && c.history.length > 0) {
7108
7487
  const updatedHistory = [...c.history];
7488
+ const existingImages = updatedHistory[updatedHistory.length - 1].images;
7489
+ const nextImages = Array.isArray(images) && images.length > 0 ? [...images] : Array.isArray(existingImages) && existingImages.length > 0 ? [...existingImages] : existingImages;
7109
7490
  updatedHistory[updatedHistory.length - 1] = {
7110
7491
  ...updatedHistory[updatedHistory.length - 1],
7111
7492
  answer,
7112
7493
  memoryUpdated,
7113
- images: images ?? updatedHistory[updatedHistory.length - 1].images,
7494
+ images: nextImages,
7114
7495
  sourceFiles: sourceFiles ?? updatedHistory[updatedHistory.length - 1].sourceFiles,
7115
- cancelled: cancelled ?? updatedHistory[updatedHistory.length - 1].cancelled
7496
+ cancelled: cancelled ?? updatedHistory[updatedHistory.length - 1].cancelled,
7497
+ placeholder: false,
7498
+ rawQuestion: void 0
7116
7499
  };
7117
7500
  return normalizeConversation({ ...c, history: updatedHistory, updatedAt: /* @__PURE__ */ new Date() });
7118
7501
  }
@@ -7193,6 +7576,24 @@ var init_conversationStore = __esm({
7193
7576
  });
7194
7577
  continue;
7195
7578
  }
7579
+ if (Array.isArray(existing.history) && Array.isArray(conversation.history)) {
7580
+ const mergedHistory = conversation.history.map((incomingEntry, index) => {
7581
+ const existingEntry = existing.history[index];
7582
+ if (!existingEntry) {
7583
+ return incomingEntry;
7584
+ }
7585
+ const mergedImagesSource = Array.isArray(incomingEntry.images) && incomingEntry.images.length > 0 ? incomingEntry.images : existingEntry.images;
7586
+ const mergedImages = Array.isArray(mergedImagesSource) && mergedImagesSource.length > 0 ? [...mergedImagesSource] : mergedImagesSource;
7587
+ return {
7588
+ ...existingEntry,
7589
+ ...incomingEntry,
7590
+ images: mergedImages,
7591
+ placeholder: incomingEntry.placeholder ?? existingEntry.placeholder,
7592
+ rawQuestion: incomingEntry.rawQuestion ?? existingEntry.rawQuestion
7593
+ };
7594
+ });
7595
+ conversation.history = mergedHistory;
7596
+ }
7196
7597
  }
7197
7598
  next.set(conversation.id, conversation);
7198
7599
  toPersist.push(conversation);
@@ -11005,11 +11406,11 @@ var init_ttsSanitizer = __esm({
11005
11406
  });
11006
11407
 
11007
11408
  // src/services/tts/tts-client.ts
11008
- var import_rxjs15, getOrAppendAuthHeader;
11409
+ var import_rxjs16, getOrAppendAuthHeader;
11009
11410
  var init_tts_client = __esm({
11010
11411
  "src/services/tts/tts-client.ts"() {
11011
11412
  "use strict";
11012
- import_rxjs15 = require("rxjs");
11413
+ import_rxjs16 = require("rxjs");
11013
11414
  init_authenticationService();
11014
11415
  init_voiceStore();
11015
11416
  init_ttsSanitizer();
@@ -11029,11 +11430,11 @@ var init_tts_client = __esm({
11029
11430
  });
11030
11431
 
11031
11432
  // src/services/tts/streaming-tts.ts
11032
- var import_rxjs16, StreamingTTSClient, getStreamingTTSClient, speakStream, stopTTS;
11433
+ var import_rxjs17, StreamingTTSClient, getStreamingTTSClient, speakStream, stopTTS;
11033
11434
  var init_streaming_tts = __esm({
11034
11435
  "src/services/tts/streaming-tts.ts"() {
11035
11436
  "use strict";
11036
- import_rxjs16 = require("rxjs");
11437
+ import_rxjs17 = require("rxjs");
11037
11438
  init_debugLogger();
11038
11439
  init_packageSettingsStore();
11039
11440
  init_tts_client();
@@ -11045,8 +11446,8 @@ var init_streaming_tts = __esm({
11045
11446
  // Store event handler references for proper cleanup
11046
11447
  audioHandlers = /* @__PURE__ */ new Map();
11047
11448
  // State management
11048
- stateSubject = new import_rxjs16.BehaviorSubject("IDLE" /* IDLE */);
11049
- progressSubject = new import_rxjs16.Subject();
11449
+ stateSubject = new import_rxjs17.BehaviorSubject("IDLE" /* IDLE */);
11450
+ progressSubject = new import_rxjs17.Subject();
11050
11451
  constructor() {
11051
11452
  }
11052
11453
  static getInstance() {
@@ -11077,7 +11478,7 @@ var init_streaming_tts = __esm({
11077
11478
  * Speak text with simple streaming
11078
11479
  */
11079
11480
  speakStream(text, voice, options = {}) {
11080
- return new import_rxjs16.Observable((subscriber) => {
11481
+ return new import_rxjs17.Observable((subscriber) => {
11081
11482
  this.performSimpleStreaming(text, voice, options, subscriber);
11082
11483
  });
11083
11484
  }
@@ -18799,6 +19200,8 @@ var init_chat_scroll_to_bottom_button = __esm({
18799
19200
  drawerOpen = false,
18800
19201
  isMobile = false
18801
19202
  }) => {
19203
+ const verticalBuffer = isMobile ? 36 : 56;
19204
+ const bottomOffset = Math.max(inputHeight + verticalBuffer, verticalBuffer + 72);
18802
19205
  return /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
18803
19206
  import_material28.IconButton,
18804
19207
  {
@@ -18807,12 +19210,12 @@ var init_chat_scroll_to_bottom_button = __esm({
18807
19210
  position: "fixed",
18808
19211
  left: drawerOpen && !isMobile ? "calc(50% + 170px)" : "50%",
18809
19212
  transform: "translateX(-50%)",
18810
- bottom: inputHeight + 10,
19213
+ bottom: bottomOffset,
18811
19214
  bgcolor: (theme) => theme.palette.background.paper,
18812
19215
  color: (theme) => theme.palette.text.primary,
18813
19216
  border: "1px solid",
18814
19217
  borderColor: (theme) => theme.palette.divider,
18815
- zIndex: 999,
19218
+ zIndex: (theme) => Math.max(theme.zIndex.modal + 1, 1400),
18816
19219
  boxShadow: 3,
18817
19220
  transition: "bottom 0.3s ease, left 0.3s ease-in-out",
18818
19221
  "&:hover": {
@@ -19474,28 +19877,28 @@ var init_create_audio_blob = __esm({
19474
19877
  });
19475
19878
 
19476
19879
  // src/services/stt/sound-recorder.service.ts
19477
- var import_rxjs20, SoundRecorderService;
19880
+ var import_rxjs21, SoundRecorderService;
19478
19881
  var init_sound_recorder_service = __esm({
19479
19882
  "src/services/stt/sound-recorder.service.ts"() {
19480
19883
  "use strict";
19481
- import_rxjs20 = require("rxjs");
19884
+ import_rxjs21 = require("rxjs");
19482
19885
  init_create_audio_blob();
19483
19886
  SoundRecorderService = class {
19484
19887
  _mediaRecorder;
19485
19888
  start() {
19486
- const mediaStream = (0, import_rxjs20.from)(navigator.mediaDevices.getUserMedia({ audio: true }));
19487
- this._mediaRecorder = mediaStream.pipe((0, import_rxjs20.map)((stream) => {
19889
+ const mediaStream = (0, import_rxjs21.from)(navigator.mediaDevices.getUserMedia({ audio: true }));
19890
+ this._mediaRecorder = mediaStream.pipe((0, import_rxjs21.map)((stream) => {
19488
19891
  const rec = new MediaRecorder(stream);
19489
19892
  rec.start();
19490
19893
  return rec;
19491
- }), (0, import_rxjs20.shareReplay)(1));
19894
+ }), (0, import_rxjs21.shareReplay)(1));
19492
19895
  const dataAvailableEvent = this._mediaRecorder.pipe(
19493
- (0, import_rxjs20.switchMap)((recorder) => (0, import_rxjs20.fromEvent)(recorder, "dataavailable"))
19896
+ (0, import_rxjs21.switchMap)((recorder) => (0, import_rxjs21.fromEvent)(recorder, "dataavailable"))
19494
19897
  );
19495
19898
  const blob = dataAvailableEvent.pipe(
19496
- (0, import_rxjs20.first)(),
19497
- (0, import_rxjs20.map)((event) => createAudioBlob(event.data)),
19498
- (0, import_rxjs20.shareReplay)(1)
19899
+ (0, import_rxjs21.first)(),
19900
+ (0, import_rxjs21.map)((event) => createAudioBlob(event.data)),
19901
+ (0, import_rxjs21.shareReplay)(1)
19499
19902
  );
19500
19903
  return blob;
19501
19904
  }
@@ -19503,7 +19906,7 @@ var init_sound_recorder_service = __esm({
19503
19906
  if (!this._mediaRecorder) {
19504
19907
  return;
19505
19908
  }
19506
- this._mediaRecorder.pipe((0, import_rxjs20.first)()).subscribe((recorder) => {
19909
+ this._mediaRecorder.pipe((0, import_rxjs21.first)()).subscribe((recorder) => {
19507
19910
  recorder.stop();
19508
19911
  });
19509
19912
  }
@@ -19623,7 +20026,7 @@ var init_stt_client = __esm({
19623
20026
  });
19624
20027
 
19625
20028
  // 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;
20029
+ var import_react38, import_Mic, import_Check6, import_Close7, import_material31, import_rxjs22, import_jsx_runtime31, initialButtonStyles, Transcriber, transcriber_default;
19627
20030
  var init_transcriber = __esm({
19628
20031
  "src/services/stt/transcriber.tsx"() {
19629
20032
  "use strict";
@@ -19634,7 +20037,7 @@ var init_transcriber = __esm({
19634
20037
  init_sound_recorder_service();
19635
20038
  init_stt_client();
19636
20039
  import_material31 = require("@mui/material");
19637
- import_rxjs21 = require("rxjs");
20040
+ import_rxjs22 = require("rxjs");
19638
20041
  init_debugLogger();
19639
20042
  import_jsx_runtime31 = require("react/jsx-runtime");
19640
20043
  initialButtonStyles = (badgeBackground, fileText, hoverBadgeBackground) => ({
@@ -19653,14 +20056,14 @@ var init_transcriber = __esm({
19653
20056
  const [status, setStatus] = (0, import_react38.useState)("IDLE");
19654
20057
  const recorderRef = (0, import_react38.useRef)(new SoundRecorderService());
19655
20058
  const [iconButtonStyles] = (0, import_react38.useState)(() => initialButtonStyles(badgeBackground, fileText, hoverBadgeBackground));
19656
- const [recordingSub, setRecordingSub] = (0, import_react38.useState)(() => new import_rxjs21.Subscription());
20059
+ const [recordingSub, setRecordingSub] = (0, import_react38.useState)(() => new import_rxjs22.Subscription());
19657
20060
  const start = () => {
19658
20061
  recordingSub.unsubscribe();
19659
20062
  const recording = recorderRef.current.start();
19660
20063
  const text = recording.pipe(
19661
- (0, import_rxjs21.switchMap)((blob) => {
20064
+ (0, import_rxjs22.switchMap)((blob) => {
19662
20065
  debugLogger.debug("Processing audio blob for transcription");
19663
- return (0, import_rxjs21.from)(STTClient.transcribe(blob));
20066
+ return (0, import_rxjs22.from)(STTClient.transcribe(blob));
19664
20067
  })
19665
20068
  );
19666
20069
  const sub = text.subscribe({
@@ -20532,11 +20935,11 @@ ${sanitize(
20532
20935
  });
20533
20936
 
20534
20937
  // 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;
20938
+ 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
20939
  var init_useMemoryEnhancer = __esm({
20537
20940
  "src/chat/hooks/useMemoryEnhancer.tsx"() {
20538
20941
  "use strict";
20539
- import_rxjs22 = require("rxjs");
20942
+ import_rxjs23 = require("rxjs");
20540
20943
  init_memoryStore();
20541
20944
  init_aiProviderStore();
20542
20945
  init_packageSettingsStore();
@@ -20928,8 +21331,8 @@ var init_useMemoryEnhancer = __esm({
20928
21331
  stream: false,
20929
21332
  options: { temperature: 0.1, num_predict: 150 }
20930
21333
  });
20931
- const suggestion = await (0, import_rxjs22.lastValueFrom)(
20932
- result$.pipe((0, import_rxjs22.map)((chunk) => chunk.response))
21334
+ const suggestion = await (0, import_rxjs23.lastValueFrom)(
21335
+ result$.pipe((0, import_rxjs23.map)((chunk) => chunk.response))
20933
21336
  );
20934
21337
  debugLogger.memoryDebug(`LLM memory suggestion received (${attempt})`, {
20935
21338
  suggestion: typeof suggestion === "string" ? suggestion.slice(0, 200) : suggestion,
@@ -21620,10 +22023,22 @@ var init_useAIProvider = __esm({
21620
22023
  setResponse("");
21621
22024
  setStreamBuffer("");
21622
22025
  clearFlushTimer();
21623
- setPendingMessage({ question, images });
22026
+ const imageList = Array.isArray(images) ? [...images] : [];
22027
+ const conversationStoreState = useConversationStore.getState();
22028
+ const { addToCurrent, replaceLastAnswer, conversations, currentId } = conversationStoreState;
22029
+ const currentConv = conversations.find((c) => c.id === currentId);
22030
+ const lastEntry = currentConv?.history.at(-1);
22031
+ const lastWasPlaceholder = !!lastEntry && lastEntry.answer === "..." && (lastEntry.placeholder === true || lastEntry.rawQuestion === question || lastEntry.question === question);
22032
+ const pendingQuestion = lastWasPlaceholder ? lastEntry?.question ?? question : question;
22033
+ const pendingImagesRaw = lastWasPlaceholder && Array.isArray(lastEntry?.images) && lastEntry.images.length > 0 ? lastEntry.images : imageList;
22034
+ const pendingImages = Array.isArray(pendingImagesRaw) && pendingImagesRaw.length > 0 ? [...pendingImagesRaw] : void 0;
22035
+ setPendingMessage({
22036
+ question: pendingQuestion,
22037
+ images: pendingImages
22038
+ });
21624
22039
  const modelName = usePackageSettingsStore.getState().settings?.defaultModel || "bandit-core:4b-it-qat";
21625
22040
  const CONFIG = modelConfigs[modelName] ?? modelConfigs["bandit-core:4b-it-qat"];
21626
- const base64Images = images.map((img) => img.split(",")[1]);
22041
+ const base64Images = imageList.map((img) => img.split(",")[1]);
21627
22042
  const latestEntries = history.slice(-CONFIG.historyMessages);
21628
22043
  const contextMessages = latestEntries.flatMap((entry) => [
21629
22044
  { role: "user", content: entry.question },
@@ -22009,11 +22424,8 @@ ${protocol}`;
22009
22424
  }, delay);
22010
22425
  };
22011
22426
  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 };
22427
+ const initialPlaceholderQuestion = lastEntry?.question;
22428
+ lastPartialRef.current = { text: "", images: [...imageList], usedDocs, question };
22017
22429
  if (currentSubRef.current) {
22018
22430
  try {
22019
22431
  currentSubRef.current.unsubscribe();
@@ -22272,14 +22684,24 @@ ${items.map((item, index) => {
22272
22684
  } else {
22273
22685
  debugLogger.info("Memory scan skipped - disabled in preferences");
22274
22686
  }
22275
- const { addToCurrent: addToCurrent2, replaceLastAnswer: replaceLastAnswer2 } = useConversationStore.getState();
22276
- const current = useConversationStore.getState();
22277
- const conv = current.conversations.find((c) => c.id === current.currentId);
22687
+ const currentState = useConversationStore.getState();
22688
+ const conv = currentState.conversations.find((c) => c.id === currentState.currentId);
22278
22689
  const last = conv?.history.at(-1);
22279
- if (!last || last.answer !== "..." || last.question !== question) {
22280
- addToCurrent2({ question, answer: enhancedMessage, images, memoryUpdated, sourceFiles: usedDocs });
22690
+ const lastIsPlaceholder = !!last && last.answer === "..." && last.placeholder !== false;
22691
+ const preservedImagesSource = imageList.length > 0 ? imageList : lastPartialRef.current.images.length > 0 ? lastPartialRef.current.images : last?.images;
22692
+ const preservedImages = Array.isArray(preservedImagesSource) && preservedImagesSource.length > 0 ? [...preservedImagesSource] : void 0;
22693
+ if (lastIsPlaceholder) {
22694
+ replaceLastAnswer(enhancedMessage, preservedImages, memoryUpdated, usedDocs);
22281
22695
  } else {
22282
- replaceLastAnswer2(enhancedMessage, images, memoryUpdated, usedDocs);
22696
+ const historyQuestion = last && last.answer === "..." && last.question || initialPlaceholderQuestion || question;
22697
+ addToCurrent({
22698
+ question: historyQuestion,
22699
+ answer: enhancedMessage,
22700
+ images: preservedImages,
22701
+ memoryUpdated,
22702
+ sourceFiles: usedDocs,
22703
+ rawQuestion: question
22704
+ });
22283
22705
  }
22284
22706
  setInputValue("");
22285
22707
  setPastedImages([]);
@@ -26522,15 +26944,15 @@ var init_chat_app_bar = __esm({
26522
26944
  });
26523
26945
 
26524
26946
  // src/chat/hooks/useConversationNameGenerator.tsx
26525
- var import_rxjs23, import_operators5, useConversationNameGenerator;
26947
+ var import_rxjs24, import_operators6, useConversationNameGenerator;
26526
26948
  var init_useConversationNameGenerator = __esm({
26527
26949
  "src/chat/hooks/useConversationNameGenerator.tsx"() {
26528
26950
  "use strict";
26529
26951
  init_aiProviderStore();
26530
26952
  init_packageSettingsStore();
26531
26953
  init_debugLogger();
26532
- import_rxjs23 = require("rxjs");
26533
- import_operators5 = require("rxjs/operators");
26954
+ import_rxjs24 = require("rxjs");
26955
+ import_operators6 = require("rxjs/operators");
26534
26956
  init_conversationStore();
26535
26957
  useConversationNameGenerator = () => {
26536
26958
  const generateName = async (userMessage) => {
@@ -26565,8 +26987,8 @@ Respond with just the title and nothing else.
26565
26987
  num_predict: 20
26566
26988
  }
26567
26989
  });
26568
- const title = await (0, import_rxjs23.lastValueFrom)(
26569
- result$.pipe((0, import_operators5.map)((d) => d.response?.trim().replace(/["']/g, "")))
26990
+ const title = await (0, import_rxjs24.lastValueFrom)(
26991
+ result$.pipe((0, import_operators6.map)((d) => d.response?.trim().replace(/["']/g, "")))
26570
26992
  );
26571
26993
  if (title && title.length > 0) {
26572
26994
  const sanitizedTitle = sanitizeConversationName(title, 60);
@@ -27846,12 +28268,20 @@ var init_chat2 = __esm({
27846
28268
  (question, images, displayQuestion) => {
27847
28269
  const requestStartTime = trackRequestStart();
27848
28270
  const questionForDisplay = displayQuestion || question;
27849
- setPendingMessage({ question: questionForDisplay, images });
28271
+ const pendingImages = images.length > 0 ? [...images] : void 0;
28272
+ setPendingMessage({ question: questionForDisplay, images: pendingImages });
27850
28273
  setIsStreaming(true);
27851
28274
  setResponseStarted(true);
27852
28275
  setStreamBuffer("");
27853
28276
  const { addToCurrent } = useConversationStore.getState();
27854
- addToCurrent({ question: questionForDisplay, answer: "...", images });
28277
+ const placeholderImages = pendingImages ? [...pendingImages] : void 0;
28278
+ addToCurrent({
28279
+ question: questionForDisplay,
28280
+ answer: "...",
28281
+ images: placeholderImages,
28282
+ placeholder: true,
28283
+ rawQuestion: question
28284
+ });
27855
28285
  const getCurrentModel = useModelStore.getState().getCurrentModel;
27856
28286
  const systemPrompt = getCurrentModel()?.systemPrompt ?? "You are a helpful assistant.";
27857
28287
  const { currentId: currentId2, conversations: conversations2, createConversation, renameConversation } = useConversationStore.getState();
@@ -27865,8 +28295,16 @@ var init_chat2 = __esm({
27865
28295
  if (!newCurrentId) return;
27866
28296
  setResponse("");
27867
28297
  const { addToCurrent: addToNew } = useConversationStore.getState();
27868
- addToNew({ question: questionForDisplay, answer: "...", images });
27869
- aiProvider(systemPrompt, question, images);
28298
+ const newPlaceholderImages = pendingImages ? [...pendingImages] : void 0;
28299
+ addToNew({
28300
+ question: questionForDisplay,
28301
+ answer: "...",
28302
+ images: newPlaceholderImages,
28303
+ placeholder: true,
28304
+ rawQuestion: question
28305
+ });
28306
+ const providerImages2 = pendingImages ? [...pendingImages] : [];
28307
+ aiProvider(systemPrompt, question, providerImages2);
27870
28308
  }, 0);
27871
28309
  });
27872
28310
  return;
@@ -27916,7 +28354,8 @@ var init_chat2 = __esm({
27916
28354
  }, 50);
27917
28355
  }
27918
28356
  setResponse("");
27919
- aiProvider(systemPrompt, question, images);
28357
+ const providerImages = pendingImages ? [...pendingImages] : [];
28358
+ aiProvider(systemPrompt, question, providerImages);
27920
28359
  },
27921
28360
  [
27922
28361
  aiProvider,
@@ -28750,7 +29189,7 @@ init_modelStore();
28750
29189
  init_ai_response_text_field();
28751
29190
  init_memory_modal();
28752
29191
  init_streaming_tts();
28753
- var import_rxjs17 = require("rxjs");
29192
+ var import_rxjs18 = require("rxjs");
28754
29193
  init_debugLogger();
28755
29194
  init_util();
28756
29195
  var import_jsx_runtime10 = require("react/jsx-runtime");
@@ -28779,7 +29218,7 @@ var AIQueriesDrawer = ({ drawerOpen, onClose, onClearComplete, onNavigateToMain
28779
29218
  const [memoryModalOpen, setMemoryModalOpen] = (0, import_react15.useState)(false);
28780
29219
  const [contextMode, setContextMode] = (0, import_react15.useState)("local");
28781
29220
  const [expandedSections, setExpandedSections] = (0, import_react15.useState)(/* @__PURE__ */ new Set(["history", "voice"]));
28782
- const [audioSub, setAudioSub] = (0, import_react15.useState)(new import_rxjs17.Subscription());
29221
+ const [audioSub, setAudioSub] = (0, import_react15.useState)(new import_rxjs18.Subscription());
28783
29222
  const [isContextSwitching, setIsContextSwitching] = (0, import_react15.useState)(false);
28784
29223
  const [isDrawerLoading, setIsDrawerLoading] = (0, import_react15.useState)(false);
28785
29224
  (0, import_react15.useEffect)(() => {
@@ -30442,7 +30881,7 @@ init_debugLogger();
30442
30881
  init_banditTheme();
30443
30882
  init_themeMap();
30444
30883
  init_useTTS();
30445
- var import_rxjs18 = require("rxjs");
30884
+ var import_rxjs19 = require("rxjs");
30446
30885
  var import_jsx_runtime15 = require("react/jsx-runtime");
30447
30886
  var FULL_SCREEN_THRESHOLD = 100;
30448
30887
  var CDN_BASE = "https://cdn.burtson.ai/";
@@ -30509,7 +30948,7 @@ var ChatModal = ({
30509
30948
  const [modalLogo, setModalLogo] = (0, import_react22.useState)("https://cdn.burtson.ai/logos/bandit-ai-logo.png");
30510
30949
  const [modelAnchorEl, setModelAnchorEl] = (0, import_react22.useState)(null);
30511
30950
  const [voiceAnchorEl, setVoiceAnchorEl] = (0, import_react22.useState)(null);
30512
- const [audioSub, setAudioSub] = (0, import_react22.useState)(new import_rxjs18.Subscription());
30951
+ const [audioSub, setAudioSub] = (0, import_react22.useState)(new import_rxjs19.Subscription());
30513
30952
  const [selectedTheme, setSelectedTheme] = (0, import_react22.useState)(null);
30514
30953
  const [themeLoading, setThemeLoading] = (0, import_react22.useState)(true);
30515
30954
  const [autoFullscreenTriggered, setAutoFullscreenTriggered] = (0, import_react22.useState)(false);
@@ -33050,7 +33489,7 @@ init_conversationSyncStore();
33050
33489
 
33051
33490
  // src/hooks/useGatewayQueries.ts
33052
33491
  var import_react25 = require("react");
33053
- var import_rxjs19 = require("rxjs");
33492
+ var import_rxjs20 = require("rxjs");
33054
33493
  var import_react_query = require("@tanstack/react-query");
33055
33494
  init_packageSettingsStore();
33056
33495
  init_gateway_service();
@@ -33081,7 +33520,7 @@ var useGatewayHealth = (options) => {
33081
33520
  if (!service) {
33082
33521
  throw new Error("Gateway service is not configured");
33083
33522
  }
33084
- return (0, import_rxjs19.lastValueFrom)(service.getHealth());
33523
+ return (0, import_rxjs20.lastValueFrom)(service.getHealth());
33085
33524
  },
33086
33525
  enabled
33087
33526
  });
@@ -38087,6 +38526,8 @@ var ProviderTab = () => {
38087
38526
  return "gpt-4o-mini";
38088
38527
  case "xai" /* XAI */:
38089
38528
  return "grok-beta";
38529
+ case "bandit" /* BANDIT */:
38530
+ return "bandit-core-1";
38090
38531
  default:
38091
38532
  return "";
38092
38533
  }
@@ -38094,7 +38535,7 @@ var ProviderTab = () => {
38094
38535
  const applyDefaultModel = (0, import_react33.useCallback)((config) => {
38095
38536
  const normalized = { ...config };
38096
38537
  const trimmed = typeof normalized.defaultModel === "string" ? normalized.defaultModel.trim() : void 0;
38097
- const requiresModel = normalized.type === "openai" /* OPENAI */ || normalized.type === "xai" /* XAI */;
38538
+ const requiresModel = normalized.type === "openai" /* OPENAI */ || normalized.type === "xai" /* XAI */ || normalized.type === "bandit" /* BANDIT */;
38098
38539
  if (trimmed) {
38099
38540
  normalized.defaultModel = trimmed;
38100
38541
  return normalized;
@@ -38245,6 +38686,14 @@ var ProviderTab = () => {
38245
38686
  apiKey: ""
38246
38687
  }));
38247
38688
  break;
38689
+ case "bandit" /* BANDIT */:
38690
+ setProviderConfig(applyDefaultModel({
38691
+ ...baseConfig,
38692
+ baseUrl: "https://api.burtson.ai",
38693
+ apiKey: "",
38694
+ defaultModel: "bandit-core-1"
38695
+ }));
38696
+ break;
38248
38697
  case "gateway" /* GATEWAY */:
38249
38698
  setProviderConfig(applyDefaultModel({
38250
38699
  ...baseConfig,
@@ -38263,7 +38712,7 @@ var ProviderTab = () => {
38263
38712
  try {
38264
38713
  const normalizedConfigIntermediate = sanitizeConfigForSave(providerConfig);
38265
38714
  const normalizedConfig = convertAnthropicConfig(normalizedConfigIntermediate) || normalizedConfigIntermediate;
38266
- const requiresModel = normalizedConfig.type === "openai" /* OPENAI */ || normalizedConfig.type === "xai" /* XAI */;
38715
+ const requiresModel = normalizedConfig.type === "openai" /* OPENAI */ || normalizedConfig.type === "xai" /* XAI */ || normalizedConfig.type === "bandit" /* BANDIT */;
38267
38716
  if (requiresModel && !normalizedConfig.defaultModel) {
38268
38717
  showMessage("Please provide a default model ID for the selected provider.", "error");
38269
38718
  return;
@@ -38388,6 +38837,7 @@ var ProviderTab = () => {
38388
38837
  /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_material25.MenuItem, { value: "ollama", children: "Ollama" }),
38389
38838
  /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_material25.MenuItem, { value: "openai", children: "OpenAI" }),
38390
38839
  /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_material25.MenuItem, { value: "azure-openai", children: "Azure OpenAI" }),
38840
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_material25.MenuItem, { value: "bandit", children: "Bandit AI" }),
38391
38841
  /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_material25.MenuItem, { value: "xai", children: "xAI" }),
38392
38842
  /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_material25.MenuItem, { value: "playground", children: "Playground (Mock Demo)" })
38393
38843
  ]
@@ -38422,6 +38872,7 @@ var ProviderTab = () => {
38422
38872
  /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_material25.MenuItem, { value: "azure-openai", children: "Azure OpenAI" }),
38423
38873
  /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_material25.MenuItem, { value: "anthropic", children: "Anthropic" }),
38424
38874
  /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_material25.MenuItem, { value: "ollama", children: "Ollama" }),
38875
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_material25.MenuItem, { value: "bandit", children: "Bandit AI" }),
38425
38876
  /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_material25.MenuItem, { value: "xai", children: "xAI" })
38426
38877
  ]
38427
38878
  }
@@ -38437,6 +38888,52 @@ var ProviderTab = () => {
38437
38888
  placeholder: "http://localhost:11434"
38438
38889
  }
38439
38890
  ),
38891
+ providerConfig.type === "bandit" && /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(import_material25.Box, { children: [
38892
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
38893
+ import_material25.TextField,
38894
+ {
38895
+ label: "API Base URL",
38896
+ value: providerConfig.baseUrl || "",
38897
+ onChange: (e) => setProviderConfig((prev) => ({
38898
+ ...prev,
38899
+ baseUrl: e.target.value
38900
+ })),
38901
+ fullWidth: true,
38902
+ sx: { mb: 2 },
38903
+ placeholder: "https://api.burtson.ai",
38904
+ helperText: "Defaults to https://api.burtson.ai"
38905
+ }
38906
+ ),
38907
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
38908
+ import_material25.TextField,
38909
+ {
38910
+ label: "API Key",
38911
+ type: "password",
38912
+ value: providerConfig.apiKey || "",
38913
+ onChange: (e) => setProviderConfig((prev) => ({
38914
+ ...prev,
38915
+ apiKey: e.target.value
38916
+ })),
38917
+ fullWidth: true,
38918
+ sx: { mb: 2 },
38919
+ placeholder: "bai_..."
38920
+ }
38921
+ ),
38922
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
38923
+ import_material25.TextField,
38924
+ {
38925
+ label: "Default Model ID",
38926
+ value: providerConfig.defaultModel || "",
38927
+ onChange: (e) => setProviderConfig((prev) => ({
38928
+ ...prev,
38929
+ defaultModel: e.target.value
38930
+ })),
38931
+ fullWidth: true,
38932
+ placeholder: "bandit-core-1",
38933
+ helperText: "Example: bandit-core-1 (Bandit Core canonical alias)."
38934
+ }
38935
+ )
38936
+ ] }),
38440
38937
  providerConfig.type === "openai" && /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(import_material25.Box, { children: [
38441
38938
  /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
38442
38939
  import_material25.TextField,
@@ -38777,20 +39274,20 @@ var MCPToolsTabV2 = () => {
38777
39274
  }
38778
39275
  }, [isLoaded]);
38779
39276
  const localEnabledMap = (0, import_react34.useMemo)(() => {
38780
- const map21 = /* @__PURE__ */ new Map();
39277
+ const map23 = /* @__PURE__ */ new Map();
38781
39278
  const sortedTools = [...localTools].sort((a, b) => {
38782
39279
  if (a.isBuiltIn && !b.isBuiltIn) return -1;
38783
39280
  if (!a.isBuiltIn && b.isBuiltIn) return 1;
38784
39281
  return a.id.length - b.id.length;
38785
39282
  });
38786
39283
  sortedTools.forEach((t) => {
38787
- map21.set(t.function.name, t.enabled);
38788
- map21.set(t.id, t.enabled);
39284
+ map23.set(t.function.name, t.enabled);
39285
+ map23.set(t.id, t.enabled);
38789
39286
  if (t.name) {
38790
- map21.set(t.name, t.enabled);
39287
+ map23.set(t.name, t.enabled);
38791
39288
  }
38792
39289
  });
38793
- return map21;
39290
+ return map23;
38794
39291
  }, [localTools]);
38795
39292
  return /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(import_material26.Box, { children: [
38796
39293
  /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(import_material26.Box, { sx: { display: "flex", alignItems: "center", justifyContent: "space-between", mb: 2 }, children: [