@burtson-labs/bandit-engine 2.0.40 → 2.0.41

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/README.md +14 -11
  2. package/dist/{aiProviderStore-JMA5RWX7.mjs → aiProviderStore-UQI33C5E.mjs} +2 -2
  3. package/dist/{chat-JMWPOSQ4.mjs → chat-T5ANWWYQ.mjs} +5 -5
  4. package/dist/chat-provider.js +151 -103
  5. package/dist/chat-provider.js.map +1 -1
  6. package/dist/chat-provider.mjs +4 -4
  7. package/dist/{chunk-D3AGKOM6.mjs → chunk-22EY3ZDC.mjs} +3 -3
  8. package/dist/{chunk-6ELNWXKC.mjs → chunk-3E57HLDV.mjs} +4 -4
  9. package/dist/{chunk-QJYPWWA5.mjs → chunk-54ZQ3FSN.mjs} +104 -61
  10. package/dist/chunk-54ZQ3FSN.mjs.map +1 -0
  11. package/dist/{chunk-7KEBNVCO.mjs → chunk-A6OBEF72.mjs} +15 -10
  12. package/dist/{chunk-7KEBNVCO.mjs.map → chunk-A6OBEF72.mjs.map} +1 -1
  13. package/dist/{chunk-2ZCR2TDY.mjs → chunk-CX3INLYJ.mjs} +3 -3
  14. package/dist/{chunk-VIYBZO5W.mjs → chunk-LYWVYBKU.mjs} +3 -3
  15. package/dist/{chunk-26QQ4CLA.mjs → chunk-QFNEHSY4.mjs} +4 -4
  16. package/dist/{chunk-75W5VWPV.mjs → chunk-WPWWWUD7.mjs} +51 -46
  17. package/dist/chunk-WPWWWUD7.mjs.map +1 -0
  18. package/dist/cli.js +1 -1
  19. package/dist/cli.js.map +1 -1
  20. package/dist/index.js +159 -106
  21. package/dist/index.js.map +1 -1
  22. package/dist/index.mjs +8 -8
  23. package/dist/management/management.js +159 -106
  24. package/dist/management/management.js.map +1 -1
  25. package/dist/management/management.mjs +6 -6
  26. package/dist/modals/chat-modal/chat-modal.js +103 -60
  27. package/dist/modals/chat-modal/chat-modal.js.map +1 -1
  28. package/dist/modals/chat-modal/chat-modal.mjs +4 -4
  29. package/docs/01_quickstart.md +10 -4
  30. package/docs/02_gateway_api.md +19 -3
  31. package/docs/03_provider_integration.md +5 -4
  32. package/docs/api_reference/media/02_gateway_api.md +19 -3
  33. package/docs/api_reference/media/README.md +3 -1
  34. package/package.json +1 -1
  35. package/dist/chunk-75W5VWPV.mjs.map +0 -1
  36. package/dist/chunk-QJYPWWA5.mjs.map +0 -1
  37. /package/dist/{aiProviderStore-JMA5RWX7.mjs.map → aiProviderStore-UQI33C5E.mjs.map} +0 -0
  38. /package/dist/{chat-JMWPOSQ4.mjs.map → chat-T5ANWWYQ.mjs.map} +0 -0
  39. /package/dist/{chunk-D3AGKOM6.mjs.map → chunk-22EY3ZDC.mjs.map} +0 -0
  40. /package/dist/{chunk-6ELNWXKC.mjs.map → chunk-3E57HLDV.mjs.map} +0 -0
  41. /package/dist/{chunk-2ZCR2TDY.mjs.map → chunk-CX3INLYJ.mjs.map} +0 -0
  42. /package/dist/{chunk-VIYBZO5W.mjs.map → chunk-LYWVYBKU.mjs.map} +0 -0
  43. /package/dist/{chunk-26QQ4CLA.mjs.map → chunk-QFNEHSY4.mjs.map} +0 -0
package/dist/index.js CHANGED
@@ -4468,61 +4468,15 @@ var init_gateway_service = __esm({
4468
4468
  */
4469
4469
  chat(request) {
4470
4470
  const endpoint = request.provider === "ollama" ? `/api/${request.provider}/chat` : request.provider ? `/api/${request.provider}/chat/completions` : "/api/chat/completions";
4471
- const url = `${this._baseUrl}${endpoint}`;
4471
+ const fallbackEndpoint = request.provider === "bandit" ? "/completions" : null;
4472
4472
  const normalizedModel = request.provider === "bandit" ? (() => {
4473
4473
  const trimmed = (request.model ?? "").replace(/^bandit:/, "").trim();
4474
4474
  return trimmed !== "" ? trimmed : "bandit-core-1";
4475
4475
  })() : request.model;
4476
- debugLogger.debug(`Gateway chat request to ${url} with provider: ${request.provider || "default"}`, {
4477
- model: normalizedModel,
4478
- messageCount: request.messages.length,
4479
- hasImages: !!(request.images && request.images.length > 0),
4480
- imageCount: request.images?.length || 0
4481
- });
4482
4476
  const requestBody = { ...request, model: normalizedModel, stream: request.stream !== false };
4483
4477
  return new import_rxjs6.Observable((observer) => {
4484
4478
  const controller = new AbortController();
4485
- const task = fetch(url, {
4486
- method: "POST",
4487
- headers: this._getHeaders(),
4488
- body: JSON.stringify(requestBody),
4489
- signal: controller.signal
4490
- });
4491
- task.then(async (response) => {
4492
- debugLogger.debug(`Gateway chat response status: ${response.status} for provider: ${request.provider || "default"}`);
4493
- if (!response.ok) {
4494
- let errorText = "";
4495
- let errorData = null;
4496
- try {
4497
- errorText = await response.text();
4498
- debugLogger.error("GatewayService chat error response body", {
4499
- status: response.status,
4500
- statusText: response.statusText,
4501
- url: response.url,
4502
- body: errorText
4503
- });
4504
- } catch (readError) {
4505
- debugLogger.error("GatewayService chat failed to read error response body", { error: readError });
4506
- errorText = `Request failed with status ${response.status}`;
4507
- }
4508
- try {
4509
- errorData = JSON.parse(errorText);
4510
- debugLogger.error("GatewayService chat parsed error payload", errorData);
4511
- } catch (parseError) {
4512
- debugLogger.error("GatewayService chat error payload was not valid JSON");
4513
- errorData = { message: errorText };
4514
- }
4515
- const error = this._createHttpError(
4516
- `POST ${url} failed: ${response.status} ${response.statusText ?? ""}`,
4517
- {
4518
- status: response.status,
4519
- statusText: response.statusText ?? "",
4520
- data: errorData,
4521
- url
4522
- }
4523
- );
4524
- throw error;
4525
- }
4479
+ const handleStreamingResponse = async (response) => {
4526
4480
  const reader = response.body?.getReader();
4527
4481
  const decoder = new TextDecoder();
4528
4482
  let buffer = "";
@@ -4597,14 +4551,75 @@ var init_gateway_service = __esm({
4597
4551
  }).catch((err) => observer.error(err));
4598
4552
  };
4599
4553
  read();
4600
- }).catch((err) => {
4601
- debugLogger.error("GatewayService chat fetch error", {
4602
- error: err,
4603
- url,
4604
- provider: request.provider
4554
+ };
4555
+ const sendRequest = (targetEndpoint, allowFallback) => {
4556
+ const url = `${this._baseUrl}${targetEndpoint}`;
4557
+ debugLogger.debug(`Gateway chat request to ${url} with provider: ${request.provider || "default"}`, {
4558
+ model: normalizedModel,
4559
+ messageCount: request.messages.length,
4560
+ hasImages: !!(request.images && request.images.length > 0),
4561
+ imageCount: request.images?.length || 0
4605
4562
  });
4606
- observer.error(err);
4607
- });
4563
+ fetch(url, {
4564
+ method: "POST",
4565
+ headers: this._getHeaders(),
4566
+ body: JSON.stringify(requestBody),
4567
+ signal: controller.signal
4568
+ }).then(async (response) => {
4569
+ debugLogger.debug(`Gateway chat response status: ${response.status} for provider: ${request.provider || "default"}`);
4570
+ if (response.status === 404 && allowFallback && fallbackEndpoint) {
4571
+ debugLogger.warn("GatewayService chat endpoint returned 404, attempting fallback route", {
4572
+ provider: request.provider,
4573
+ attemptedEndpoint: targetEndpoint,
4574
+ fallbackEndpoint
4575
+ });
4576
+ sendRequest(fallbackEndpoint, false);
4577
+ return;
4578
+ }
4579
+ if (!response.ok) {
4580
+ let errorText = "";
4581
+ let errorData = null;
4582
+ try {
4583
+ errorText = await response.text();
4584
+ debugLogger.error("GatewayService chat error response body", {
4585
+ status: response.status,
4586
+ statusText: response.statusText,
4587
+ url: response.url,
4588
+ body: errorText
4589
+ });
4590
+ } catch (readError) {
4591
+ debugLogger.error("GatewayService chat failed to read error response body", { error: readError });
4592
+ errorText = `Request failed with status ${response.status}`;
4593
+ }
4594
+ try {
4595
+ errorData = JSON.parse(errorText);
4596
+ debugLogger.error("GatewayService chat parsed error payload", errorData);
4597
+ } catch (parseError) {
4598
+ debugLogger.error("GatewayService chat error payload was not valid JSON");
4599
+ errorData = { message: errorText };
4600
+ }
4601
+ const error = this._createHttpError(
4602
+ `POST ${url} failed: ${response.status} ${response.statusText ?? ""}`,
4603
+ {
4604
+ status: response.status,
4605
+ statusText: response.statusText ?? "",
4606
+ data: errorData,
4607
+ url
4608
+ }
4609
+ );
4610
+ throw error;
4611
+ }
4612
+ await handleStreamingResponse(response);
4613
+ }).catch((err) => {
4614
+ debugLogger.error("GatewayService chat fetch error", {
4615
+ error: err,
4616
+ url,
4617
+ provider: request.provider
4618
+ });
4619
+ observer.error(err);
4620
+ });
4621
+ };
4622
+ sendRequest(endpoint, true);
4608
4623
  return () => {
4609
4624
  try {
4610
4625
  controller.abort();
@@ -4737,18 +4752,46 @@ var init_gateway_service = __esm({
4737
4752
  );
4738
4753
  }
4739
4754
  _getHeaders() {
4740
- const token = this._tokenFactory();
4755
+ const rawToken2 = this._tokenFactory();
4741
4756
  const headers = {
4742
4757
  "Content-Type": "application/json"
4743
4758
  };
4744
- if (token && token.trim() !== "") {
4745
- headers["Authorization"] = `Bearer ${token}`;
4746
- debugLogger.debug("Authorization header set with token");
4747
- } else {
4759
+ if (!rawToken2) {
4748
4760
  debugLogger.warn("GatewayService: No token found, skipping Authorization header");
4761
+ return headers;
4762
+ }
4763
+ const token = rawToken2.trim();
4764
+ if (token === "") {
4765
+ debugLogger.warn("GatewayService: Token factory returned empty string");
4766
+ return headers;
4767
+ }
4768
+ if (/^(Bearer|ApiKey)\s+/i.test(token)) {
4769
+ headers["Authorization"] = token;
4770
+ debugLogger.debug("GatewayService: Authorization header set with explicit scheme");
4771
+ return headers;
4772
+ }
4773
+ if (this._isLikelyBanditApiKey(token)) {
4774
+ headers["Authorization"] = `ApiKey ${token}`;
4775
+ headers["X-Burtson-Api-Key"] = token;
4776
+ debugLogger.debug("GatewayService: Authorization header set using API key");
4777
+ return headers;
4749
4778
  }
4779
+ if (this._isLikelyJwt(token)) {
4780
+ headers["Authorization"] = `Bearer ${token}`;
4781
+ debugLogger.debug("GatewayService: Authorization header set using bearer token");
4782
+ return headers;
4783
+ }
4784
+ headers["Authorization"] = `Bearer ${token}`;
4785
+ debugLogger.debug("GatewayService: Authorization header defaulted to bearer scheme");
4750
4786
  return headers;
4751
4787
  }
4788
+ _isLikelyJwt(token) {
4789
+ const segments = token.split(".");
4790
+ return segments.length === 3 && segments.every((segment) => segment.length > 0);
4791
+ }
4792
+ _isLikelyBanditApiKey(value) {
4793
+ return /^bai_[a-z0-9]{10,}$/i.test(value);
4794
+ }
4752
4795
  /**
4753
4796
  * Submit feedback to the gateway API
4754
4797
  */
@@ -29268,26 +29311,7 @@ var AIProviderInitService = class _AIProviderInitService {
29268
29311
  if (providerConfig.type === "anthropic" /* ANTHROPIC */) {
29269
29312
  providerConfig = this.convertAnthropicConfig(providerConfig, settings?.gatewayApiUrl);
29270
29313
  }
29271
- if ((providerConfig.type === "ollama" /* OLLAMA */ || providerConfig.type === "gateway" /* GATEWAY */) && !providerConfig.tokenFactory) {
29272
- providerConfig.tokenFactory = () => {
29273
- let token = authenticationService.getToken();
29274
- if (!token) {
29275
- token = localStorage.getItem("authToken");
29276
- }
29277
- if (!token) {
29278
- try {
29279
- const { useAuthenticationStore: useAuthenticationStore2 } = require("../../store/authenticationStore");
29280
- const authStore = useAuthenticationStore2.getState();
29281
- token = authStore.token;
29282
- } catch (e) {
29283
- }
29284
- }
29285
- debugLogger.info("AI Provider Init: IndexedDB config token factory", {
29286
- hasToken: !!token
29287
- });
29288
- return token;
29289
- };
29290
- }
29314
+ providerConfig = this.ensureTokenFactory(providerConfig);
29291
29315
  try {
29292
29316
  const { createProvider } = useAIProviderStore.getState();
29293
29317
  createProvider(providerConfig);
@@ -29315,27 +29339,7 @@ var AIProviderInitService = class _AIProviderInitService {
29315
29339
  if (providerConfig.type === "anthropic" /* ANTHROPIC */) {
29316
29340
  providerConfig = this.convertAnthropicConfig(providerConfig, settings.gatewayApiUrl);
29317
29341
  }
29318
- if (providerConfig.type === "ollama" /* OLLAMA */ && !providerConfig.tokenFactory) {
29319
- providerConfig.tokenFactory = () => {
29320
- let token = authenticationService.getToken();
29321
- if (!token) {
29322
- token = localStorage.getItem("authToken");
29323
- }
29324
- if (!token) {
29325
- try {
29326
- const { useAuthenticationStore: useAuthenticationStore2 } = require("../../store/authenticationStore");
29327
- const authStore = useAuthenticationStore2.getState();
29328
- token = authStore.token;
29329
- } catch (e) {
29330
- }
29331
- }
29332
- debugLogger.info("AIProviderInit: Explicit config tokenFactory", {
29333
- hasToken: !!token,
29334
- localStorage: !!localStorage.getItem("authToken")
29335
- });
29336
- return token;
29337
- };
29338
- }
29342
+ providerConfig = this.ensureTokenFactory(providerConfig);
29339
29343
  debugLogger.info("Using explicit AI provider config", providerConfig);
29340
29344
  } else {
29341
29345
  providerConfig = {
@@ -29447,9 +29451,10 @@ var AIProviderInitService = class _AIProviderInitService {
29447
29451
  */
29448
29452
  switchProvider(config) {
29449
29453
  try {
29454
+ const normalizedConfig = this.ensureTokenFactory({ ...config });
29450
29455
  const { switchProvider } = useAIProviderStore.getState();
29451
- switchProvider(config);
29452
- debugLogger.info(`Switched to AI provider: ${config.type}`);
29456
+ switchProvider(normalizedConfig);
29457
+ debugLogger.info(`Switched to AI provider: ${normalizedConfig.type}`);
29453
29458
  } catch (error) {
29454
29459
  debugLogger.error("Failed to switch AI provider:", { error });
29455
29460
  throw error;
@@ -29483,6 +29488,49 @@ var AIProviderInitService = class _AIProviderInitService {
29483
29488
  debugLogger.info("AI Provider Init: Converted direct Anthropic provider to gateway configuration");
29484
29489
  return normalized;
29485
29490
  }
29491
+ /**
29492
+ * Ensure providers that require auth have a token factory configured.
29493
+ * Handles both UI auth tokens and API key scenarios.
29494
+ */
29495
+ ensureTokenFactory(config) {
29496
+ if (config.type === "ollama" /* OLLAMA */ || config.type === "gateway" /* GATEWAY */) {
29497
+ const existingFactory = config.tokenFactory;
29498
+ if (existingFactory) {
29499
+ return config;
29500
+ }
29501
+ if (typeof config.apiKey === "string" && config.apiKey.trim() !== "") {
29502
+ const key = config.apiKey.trim();
29503
+ config.tokenFactory = () => key;
29504
+ debugLogger.info("AIProviderInit: Using API key for token factory", {
29505
+ type: config.type,
29506
+ hasKey: true
29507
+ });
29508
+ return config;
29509
+ }
29510
+ config.tokenFactory = () => {
29511
+ let token = authenticationService.getToken();
29512
+ if (!token && typeof localStorage !== "undefined") {
29513
+ try {
29514
+ token = localStorage.getItem("authToken");
29515
+ } catch {
29516
+ }
29517
+ }
29518
+ if (!token) {
29519
+ try {
29520
+ const { useAuthenticationStore: useAuthenticationStore2 } = require("../../store/authenticationStore");
29521
+ const authStore = useAuthenticationStore2.getState();
29522
+ token = authStore.token;
29523
+ } catch {
29524
+ }
29525
+ }
29526
+ debugLogger.info("AIProviderInit: Token factory resolved auth token", {
29527
+ hasToken: !!token
29528
+ });
29529
+ return token;
29530
+ };
29531
+ }
29532
+ return config;
29533
+ }
29486
29534
  };
29487
29535
  var aiProviderInitService = AIProviderInitService.getInstance();
29488
29536
 
@@ -39186,11 +39234,16 @@ var ProviderTab = () => {
39186
39234
  const trimmed = sanitized.defaultModel.trim();
39187
39235
  sanitized.defaultModel = trimmed || void 0;
39188
39236
  }
39237
+ if (typeof sanitized.apiKey === "string") {
39238
+ const trimmedKey = sanitized.apiKey.trim();
39239
+ sanitized.apiKey = trimmedKey || void 0;
39240
+ }
39189
39241
  return sanitized;
39190
39242
  }, []);
39191
39243
  const [providerConfig, setProviderConfig] = (0, import_react53.useState)({
39192
- type: "ollama",
39193
- baseUrl: "http://localhost:11434"
39244
+ type: "gateway",
39245
+ gatewayUrl: packageSettings?.gatewayApiUrl || "",
39246
+ provider: "bandit"
39194
39247
  });
39195
39248
  const [isProviderConfigOpen, setIsProviderConfigOpen] = (0, import_react53.useState)(false);
39196
39249
  const [snackbarMessage, setSnackbarMessage] = (0, import_react53.useState)("");
@@ -39326,7 +39379,7 @@ var ProviderTab = () => {
39326
39379
  setProviderConfig(applyDefaultModel({
39327
39380
  ...baseConfig,
39328
39381
  gatewayUrl: packageSettings?.gatewayApiUrl || "",
39329
- provider: "openai"
39382
+ provider: "bandit"
39330
39383
  }));
39331
39384
  break;
39332
39385
  case "playground" /* PLAYGROUND */: