@burtson-labs/bandit-engine 2.0.39 → 2.0.41

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/README.md +14 -11
  2. package/dist/{aiProviderStore-XN7GCBHJ.mjs → aiProviderStore-UQI33C5E.mjs} +2 -2
  3. package/dist/{chat-5QJNWB7I.mjs → chat-T5ANWWYQ.mjs} +5 -5
  4. package/dist/chat-provider.js +571 -122
  5. package/dist/chat-provider.js.map +1 -1
  6. package/dist/chat-provider.mjs +4 -4
  7. package/dist/{chunk-3A2527TE.mjs → chunk-22EY3ZDC.mjs} +3 -3
  8. package/dist/{chunk-ECRNIAG6.mjs → chunk-3E57HLDV.mjs} +4 -4
  9. package/dist/{chunk-QU5S5QQP.mjs → chunk-54ZQ3FSN.mjs} +481 -77
  10. package/dist/chunk-54ZQ3FSN.mjs.map +1 -0
  11. package/dist/{chunk-JRCDANLN.mjs → chunk-A6OBEF72.mjs} +75 -12
  12. package/dist/{chunk-JRCDANLN.mjs.map → chunk-A6OBEF72.mjs.map} +1 -1
  13. package/dist/{chunk-CDQYBO3Q.mjs → chunk-CX3INLYJ.mjs} +27 -5
  14. package/dist/chunk-CX3INLYJ.mjs.map +1 -0
  15. package/dist/{chunk-QYH2T4L5.mjs → chunk-LYWVYBKU.mjs} +3 -3
  16. package/dist/{chunk-WO5KFNNW.mjs → chunk-QFNEHSY4.mjs} +62 -24
  17. package/dist/chunk-QFNEHSY4.mjs.map +1 -0
  18. package/dist/{chunk-EOKIE5HZ.mjs → chunk-WPWWWUD7.mjs} +51 -46
  19. package/dist/chunk-WPWWWUD7.mjs.map +1 -0
  20. package/dist/{cli/cli.js → cli.js} +423 -10
  21. package/dist/cli.js.map +1 -0
  22. package/dist/{gateway-B0LJ3-jT.d.ts → gateway-5yt_3QDP.d.mts} +4 -4
  23. package/dist/{gateway-B0LJ3-jT.d.mts → gateway-5yt_3QDP.d.ts} +4 -4
  24. package/dist/index.d.mts +2 -2
  25. package/dist/index.d.ts +2 -2
  26. package/dist/index.js +756 -206
  27. package/dist/index.js.map +1 -1
  28. package/dist/index.mjs +8 -8
  29. package/dist/management/management.js +754 -204
  30. package/dist/management/management.js.map +1 -1
  31. package/dist/management/management.mjs +6 -6
  32. package/dist/modals/chat-modal/chat-modal.js +532 -88
  33. package/dist/modals/chat-modal/chat-modal.js.map +1 -1
  34. package/dist/modals/chat-modal/chat-modal.mjs +4 -4
  35. package/dist/public-types.d.mts +1 -1
  36. package/dist/public-types.d.ts +1 -1
  37. package/docs/01_quickstart.md +10 -4
  38. package/docs/02_gateway_api.md +19 -3
  39. package/docs/03_provider_integration.md +5 -4
  40. package/docs/api_reference/media/02_gateway_api.md +19 -3
  41. package/docs/api_reference/media/README.md +3 -1
  42. package/package.json +1 -1
  43. package/dist/chunk-CDQYBO3Q.mjs.map +0 -1
  44. package/dist/chunk-EOKIE5HZ.mjs.map +0 -1
  45. package/dist/chunk-QU5S5QQP.mjs.map +0 -1
  46. package/dist/chunk-WO5KFNNW.mjs.map +0 -1
  47. package/dist/cli/cli.js.map +0 -1
  48. /package/dist/{aiProviderStore-XN7GCBHJ.mjs.map → aiProviderStore-UQI33C5E.mjs.map} +0 -0
  49. /package/dist/{chat-5QJNWB7I.mjs.map → chat-T5ANWWYQ.mjs.map} +0 -0
  50. /package/dist/{chunk-3A2527TE.mjs.map → chunk-22EY3ZDC.mjs.map} +0 -0
  51. /package/dist/{chunk-ECRNIAG6.mjs.map → chunk-3E57HLDV.mjs.map} +0 -0
  52. /package/dist/{chunk-QYH2T4L5.mjs.map → chunk-LYWVYBKU.mjs.map} +0 -0
@@ -1015,7 +1015,7 @@ var AnthropicProvider = class {
1015
1015
  };
1016
1016
 
1017
1017
  // src/services/ai-provider/providers/gateway.provider.ts
1018
- import { map as map11 } from "rxjs";
1018
+ import { map as map12 } from "rxjs";
1019
1019
 
1020
1020
  // src/services/gateway/gateway.service.ts
1021
1021
  import axios, { AxiosHeaders } from "axios";
@@ -1163,57 +1163,15 @@ var GatewayService = class {
1163
1163
  */
1164
1164
  chat(request) {
1165
1165
  const endpoint = request.provider === "ollama" ? `/api/${request.provider}/chat` : request.provider ? `/api/${request.provider}/chat/completions` : "/api/chat/completions";
1166
- const url = `${this._baseUrl}${endpoint}`;
1167
- debugLogger.debug(`Gateway chat request to ${url} with provider: ${request.provider || "default"}`, {
1168
- model: request.model,
1169
- messageCount: request.messages.length,
1170
- hasImages: !!(request.images && request.images.length > 0),
1171
- imageCount: request.images?.length || 0
1172
- });
1173
- const requestBody = { ...request, stream: request.stream !== false };
1166
+ const fallbackEndpoint = request.provider === "bandit" ? "/completions" : null;
1167
+ const normalizedModel = request.provider === "bandit" ? (() => {
1168
+ const trimmed = (request.model ?? "").replace(/^bandit:/, "").trim();
1169
+ return trimmed !== "" ? trimmed : "bandit-core-1";
1170
+ })() : request.model;
1171
+ const requestBody = { ...request, model: normalizedModel, stream: request.stream !== false };
1174
1172
  return new Observable6((observer) => {
1175
1173
  const controller = new AbortController();
1176
- const task = fetch(url, {
1177
- method: "POST",
1178
- headers: this._getHeaders(),
1179
- body: JSON.stringify(requestBody),
1180
- signal: controller.signal
1181
- });
1182
- task.then(async (response) => {
1183
- debugLogger.debug(`Gateway chat response status: ${response.status} for provider: ${request.provider || "default"}`);
1184
- if (!response.ok) {
1185
- let errorText = "";
1186
- let errorData = null;
1187
- try {
1188
- errorText = await response.text();
1189
- debugLogger.error("GatewayService chat error response body", {
1190
- status: response.status,
1191
- statusText: response.statusText,
1192
- url: response.url,
1193
- body: errorText
1194
- });
1195
- } catch (readError) {
1196
- debugLogger.error("GatewayService chat failed to read error response body", { error: readError });
1197
- errorText = `Request failed with status ${response.status}`;
1198
- }
1199
- try {
1200
- errorData = JSON.parse(errorText);
1201
- debugLogger.error("GatewayService chat parsed error payload", errorData);
1202
- } catch (parseError) {
1203
- debugLogger.error("GatewayService chat error payload was not valid JSON");
1204
- errorData = { message: errorText };
1205
- }
1206
- const error = this._createHttpError(
1207
- `POST ${url} failed: ${response.status} ${response.statusText ?? ""}`,
1208
- {
1209
- status: response.status,
1210
- statusText: response.statusText ?? "",
1211
- data: errorData,
1212
- url
1213
- }
1214
- );
1215
- throw error;
1216
- }
1174
+ const handleStreamingResponse = async (response) => {
1217
1175
  const reader = response.body?.getReader();
1218
1176
  const decoder = new TextDecoder();
1219
1177
  let buffer = "";
@@ -1288,14 +1246,75 @@ var GatewayService = class {
1288
1246
  }).catch((err) => observer.error(err));
1289
1247
  };
1290
1248
  read();
1291
- }).catch((err) => {
1292
- debugLogger.error("GatewayService chat fetch error", {
1293
- error: err,
1294
- url,
1295
- provider: request.provider
1249
+ };
1250
+ const sendRequest = (targetEndpoint, allowFallback) => {
1251
+ const url = `${this._baseUrl}${targetEndpoint}`;
1252
+ debugLogger.debug(`Gateway chat request to ${url} with provider: ${request.provider || "default"}`, {
1253
+ model: normalizedModel,
1254
+ messageCount: request.messages.length,
1255
+ hasImages: !!(request.images && request.images.length > 0),
1256
+ imageCount: request.images?.length || 0
1296
1257
  });
1297
- observer.error(err);
1298
- });
1258
+ fetch(url, {
1259
+ method: "POST",
1260
+ headers: this._getHeaders(),
1261
+ body: JSON.stringify(requestBody),
1262
+ signal: controller.signal
1263
+ }).then(async (response) => {
1264
+ debugLogger.debug(`Gateway chat response status: ${response.status} for provider: ${request.provider || "default"}`);
1265
+ if (response.status === 404 && allowFallback && fallbackEndpoint) {
1266
+ debugLogger.warn("GatewayService chat endpoint returned 404, attempting fallback route", {
1267
+ provider: request.provider,
1268
+ attemptedEndpoint: targetEndpoint,
1269
+ fallbackEndpoint
1270
+ });
1271
+ sendRequest(fallbackEndpoint, false);
1272
+ return;
1273
+ }
1274
+ if (!response.ok) {
1275
+ let errorText = "";
1276
+ let errorData = null;
1277
+ try {
1278
+ errorText = await response.text();
1279
+ debugLogger.error("GatewayService chat error response body", {
1280
+ status: response.status,
1281
+ statusText: response.statusText,
1282
+ url: response.url,
1283
+ body: errorText
1284
+ });
1285
+ } catch (readError) {
1286
+ debugLogger.error("GatewayService chat failed to read error response body", { error: readError });
1287
+ errorText = `Request failed with status ${response.status}`;
1288
+ }
1289
+ try {
1290
+ errorData = JSON.parse(errorText);
1291
+ debugLogger.error("GatewayService chat parsed error payload", errorData);
1292
+ } catch (parseError) {
1293
+ debugLogger.error("GatewayService chat error payload was not valid JSON");
1294
+ errorData = { message: errorText };
1295
+ }
1296
+ const error = this._createHttpError(
1297
+ `POST ${url} failed: ${response.status} ${response.statusText ?? ""}`,
1298
+ {
1299
+ status: response.status,
1300
+ statusText: response.statusText ?? "",
1301
+ data: errorData,
1302
+ url
1303
+ }
1304
+ );
1305
+ throw error;
1306
+ }
1307
+ await handleStreamingResponse(response);
1308
+ }).catch((err) => {
1309
+ debugLogger.error("GatewayService chat fetch error", {
1310
+ error: err,
1311
+ url,
1312
+ provider: request.provider
1313
+ });
1314
+ observer.error(err);
1315
+ });
1316
+ };
1317
+ sendRequest(endpoint, true);
1299
1318
  return () => {
1300
1319
  try {
1301
1320
  controller.abort();
@@ -1310,12 +1329,18 @@ var GatewayService = class {
1310
1329
  generate(request) {
1311
1330
  const endpoint = request.provider ? `/api/${request.provider}/generate` : "/api/generate";
1312
1331
  const url = `${this._baseUrl}${endpoint}`;
1313
- debugLogger.debug(`Gateway generate request to ${url} with provider: ${request.provider || "default"}`);
1332
+ const normalizedModel = request.provider === "bandit" ? (() => {
1333
+ const trimmed = (request.model ?? "").replace(/^bandit:/, "").trim();
1334
+ return trimmed !== "" ? trimmed : "bandit-core-1";
1335
+ })() : request.model;
1336
+ debugLogger.debug(`Gateway generate request to ${url} with provider: ${request.provider || "default"}`, {
1337
+ model: normalizedModel
1338
+ });
1314
1339
  return new Observable6((observer) => {
1315
1340
  const task = fetch(url, {
1316
1341
  method: "POST",
1317
1342
  headers: this._getHeaders(),
1318
- body: JSON.stringify({ ...request, stream: request.stream !== false })
1343
+ body: JSON.stringify({ ...request, model: normalizedModel, stream: request.stream !== false })
1319
1344
  });
1320
1345
  task.then(async (response) => {
1321
1346
  if (!response.ok) {
@@ -1422,18 +1447,46 @@ var GatewayService = class {
1422
1447
  );
1423
1448
  }
1424
1449
  _getHeaders() {
1425
- const token = this._tokenFactory();
1450
+ const rawToken = this._tokenFactory();
1426
1451
  const headers = {
1427
1452
  "Content-Type": "application/json"
1428
1453
  };
1429
- if (token && token.trim() !== "") {
1430
- headers["Authorization"] = `Bearer ${token}`;
1431
- debugLogger.debug("Authorization header set with token");
1432
- } else {
1454
+ if (!rawToken) {
1433
1455
  debugLogger.warn("GatewayService: No token found, skipping Authorization header");
1456
+ return headers;
1457
+ }
1458
+ const token = rawToken.trim();
1459
+ if (token === "") {
1460
+ debugLogger.warn("GatewayService: Token factory returned empty string");
1461
+ return headers;
1462
+ }
1463
+ if (/^(Bearer|ApiKey)\s+/i.test(token)) {
1464
+ headers["Authorization"] = token;
1465
+ debugLogger.debug("GatewayService: Authorization header set with explicit scheme");
1466
+ return headers;
1467
+ }
1468
+ if (this._isLikelyBanditApiKey(token)) {
1469
+ headers["Authorization"] = `ApiKey ${token}`;
1470
+ headers["X-Burtson-Api-Key"] = token;
1471
+ debugLogger.debug("GatewayService: Authorization header set using API key");
1472
+ return headers;
1434
1473
  }
1474
+ if (this._isLikelyJwt(token)) {
1475
+ headers["Authorization"] = `Bearer ${token}`;
1476
+ debugLogger.debug("GatewayService: Authorization header set using bearer token");
1477
+ return headers;
1478
+ }
1479
+ headers["Authorization"] = `Bearer ${token}`;
1480
+ debugLogger.debug("GatewayService: Authorization header defaulted to bearer scheme");
1435
1481
  return headers;
1436
1482
  }
1483
+ _isLikelyJwt(token) {
1484
+ const segments = token.split(".");
1485
+ return segments.length === 3 && segments.every((segment) => segment.length > 0);
1486
+ }
1487
+ _isLikelyBanditApiKey(value) {
1488
+ return /^bai_[a-z0-9]{10,}$/i.test(value);
1489
+ }
1437
1490
  /**
1438
1491
  * Submit feedback to the gateway API
1439
1492
  */
@@ -1879,6 +1932,104 @@ var OllamaGatewayService = class {
1879
1932
  }
1880
1933
  };
1881
1934
 
1935
+ // src/services/gateway/bandit-gateway.service.ts
1936
+ import { map as map11 } from "rxjs/operators";
1937
+ var normalizeBanditModel = (model) => {
1938
+ if (typeof model !== "string" || model.trim() === "") {
1939
+ return "bandit-core-1";
1940
+ }
1941
+ const normalized = model.replace(/^bandit:/, "").trim();
1942
+ return normalized === "" ? "bandit-core-1" : normalized;
1943
+ };
1944
+ var isGatewayMessageContent = (value) => {
1945
+ if (!value || typeof value !== "object") return false;
1946
+ const candidate = value;
1947
+ if (candidate.type !== "text" && candidate.type !== "image_url") {
1948
+ return false;
1949
+ }
1950
+ if (candidate.type === "text") {
1951
+ return typeof candidate.text === "string";
1952
+ }
1953
+ if (candidate.type === "image_url") {
1954
+ return !!candidate.image_url && typeof candidate.image_url.url === "string";
1955
+ }
1956
+ return false;
1957
+ };
1958
+ var normalizeBanditMessages = (messages) => messages.map((message) => {
1959
+ const content = message.content;
1960
+ if (typeof content === "string") {
1961
+ return { role: message.role, content };
1962
+ }
1963
+ if (Array.isArray(content)) {
1964
+ const filtered = content.filter(isGatewayMessageContent);
1965
+ if (filtered.length === 0) {
1966
+ return { role: message.role, content: JSON.stringify(content) };
1967
+ }
1968
+ return {
1969
+ role: message.role,
1970
+ content: filtered
1971
+ };
1972
+ }
1973
+ return { role: message.role, content: content != null ? String(content) : "" };
1974
+ });
1975
+ var BanditAIGatewayService = class {
1976
+ _gatewayService;
1977
+ constructor(gatewayUrl, tokenFactory) {
1978
+ this._gatewayService = new GatewayService(gatewayUrl, tokenFactory);
1979
+ debugLogger.info("BanditAIGatewayService initialized", { gatewayUrl });
1980
+ }
1981
+ async validateServiceAvailability(args) {
1982
+ return this._gatewayService.validateServiceAvailability(args);
1983
+ }
1984
+ chat(request) {
1985
+ const model = normalizeBanditModel(request.model);
1986
+ const messages = normalizeBanditMessages(request.messages);
1987
+ const gatewayRequest = {
1988
+ ...request,
1989
+ messages,
1990
+ model,
1991
+ provider: "bandit",
1992
+ stream: request.stream
1993
+ };
1994
+ debugLogger.debug("Bandit Gateway chat request", {
1995
+ model,
1996
+ messageCount: request.messages.length,
1997
+ stream: request.stream
1998
+ });
1999
+ return this._gatewayService.chat(gatewayRequest);
2000
+ }
2001
+ complete(prompt, options) {
2002
+ const model = normalizeBanditModel(options.model);
2003
+ const gatewayRequest = {
2004
+ model,
2005
+ prompt,
2006
+ temperature: options.temperature,
2007
+ max_tokens: options.max_tokens,
2008
+ stream: options.stream,
2009
+ stop: options.stop,
2010
+ provider: "bandit"
2011
+ };
2012
+ debugLogger.debug("Bandit Gateway generate request", {
2013
+ model,
2014
+ promptLength: prompt.length,
2015
+ stream: options.stream
2016
+ });
2017
+ return this._gatewayService.generate(gatewayRequest);
2018
+ }
2019
+ listModels() {
2020
+ debugLogger.debug("Fetching Bandit models through gateway");
2021
+ return this._gatewayService.listModelsByProvider("bandit");
2022
+ }
2023
+ getHealth() {
2024
+ return this._gatewayService.getHealth().pipe(
2025
+ map11((health) => ({
2026
+ ...health,
2027
+ bandit_status: health.providers.find((p) => p.name === "bandit")?.status || "unavailable"
2028
+ }))
2029
+ );
2030
+ }
2031
+ };
2032
+
1882
2033
  // src/services/ai-provider/providers/gateway.provider.ts
1883
2034
  var GatewayProvider = class {
1884
2035
  config;
@@ -1921,6 +2072,9 @@ var GatewayProvider = class {
1921
2072
  case "anthropic":
1922
2073
  this.providerSpecificService = new AnthropicGatewayService(gatewayUrl, tokenFactory);
1923
2074
  break;
2075
+ case "bandit":
2076
+ this.providerSpecificService = new BanditAIGatewayService(gatewayUrl, tokenFactory);
2077
+ break;
1924
2078
  case "ollama":
1925
2079
  this.providerSpecificService = new OllamaGatewayService(gatewayUrl, tokenFactory);
1926
2080
  break;
@@ -1935,6 +2089,16 @@ var GatewayProvider = class {
1935
2089
  role: msg.role,
1936
2090
  content: msg.content
1937
2091
  }));
2092
+ const normalizeImageUrl2 = (value) => {
2093
+ if (!value) {
2094
+ return value;
2095
+ }
2096
+ const trimmed = value.trim();
2097
+ if (/^data:/i.test(trimmed) || /^https?:\/\//i.test(trimmed)) {
2098
+ return trimmed;
2099
+ }
2100
+ return `data:image/jpeg;base64,${trimmed}`;
2101
+ };
1938
2102
  if (request.images && request.images.length > 0) {
1939
2103
  const lastUserMessageIndex = messages.map((m) => m.role).lastIndexOf("user");
1940
2104
  if (this.config.provider === "ollama") {
@@ -1944,7 +2108,7 @@ var GatewayProvider = class {
1944
2108
  images: request.images
1945
2109
  };
1946
2110
  }
1947
- } else if (["openai", "azure-openai", "anthropic"].includes(this.config.provider || "")) {
2111
+ } else if (["openai", "azure-openai", "anthropic", "bandit"].includes(this.config.provider || "")) {
1948
2112
  if (lastUserMessageIndex !== -1) {
1949
2113
  const currentMessage = messages[lastUserMessageIndex];
1950
2114
  const contentArray = [
@@ -1953,11 +2117,11 @@ var GatewayProvider = class {
1953
2117
  text: currentMessage.content
1954
2118
  }
1955
2119
  ];
1956
- request.images.forEach((base64Image) => {
2120
+ request.images.forEach((imageRef) => {
1957
2121
  contentArray.push({
1958
2122
  type: "image_url",
1959
2123
  image_url: {
1960
- url: base64Image.startsWith("data:") ? base64Image : `data:image/jpeg;base64,${base64Image}`,
2124
+ url: normalizeImageUrl2(imageRef),
1961
2125
  detail: "auto"
1962
2126
  }
1963
2127
  });
@@ -1966,6 +2130,11 @@ var GatewayProvider = class {
1966
2130
  ...messages[lastUserMessageIndex],
1967
2131
  content: contentArray
1968
2132
  };
2133
+ debugLogger.debug("Gateway provider injected image attachments", {
2134
+ provider: this.config.provider,
2135
+ imageCount: request.images.length,
2136
+ messageIndex: lastUserMessageIndex
2137
+ });
1969
2138
  }
1970
2139
  }
1971
2140
  }
@@ -1994,7 +2163,7 @@ var GatewayProvider = class {
1994
2163
  }))
1995
2164
  });
1996
2165
  return this.gatewayService.chat(gatewayRequest).pipe(
1997
- map11((response) => ({
2166
+ map12((response) => ({
1998
2167
  message: {
1999
2168
  content: response.choices?.[0]?.message?.content || response.choices?.[0]?.delta?.content || "",
2000
2169
  role: "assistant"
@@ -2017,7 +2186,7 @@ var GatewayProvider = class {
2017
2186
  stream: request.stream
2018
2187
  });
2019
2188
  return this.gatewayService.generate(gatewayRequest).pipe(
2020
- map11((response) => ({
2189
+ map12((response) => ({
2021
2190
  response: response.response || "",
2022
2191
  done: response.done || false
2023
2192
  }))
@@ -2027,7 +2196,7 @@ var GatewayProvider = class {
2027
2196
  debugLogger.debug("Gateway provider listing models", { provider: this.config.provider });
2028
2197
  if (this.config.provider) {
2029
2198
  return this.gatewayService.listModelsByProvider(this.config.provider).pipe(
2030
- map11((models) => models.map((model) => ({
2199
+ map12((models) => models.map((model) => ({
2031
2200
  name: model.id || model.name,
2032
2201
  size: model.size,
2033
2202
  details: model.details,
@@ -2037,7 +2206,7 @@ var GatewayProvider = class {
2037
2206
  );
2038
2207
  } else {
2039
2208
  return this.gatewayService.listModels().pipe(
2040
- map11((models) => models.map((model) => ({
2209
+ map12((models) => models.map((model) => ({
2041
2210
  name: model.id || model.name,
2042
2211
  size: model.size,
2043
2212
  details: model.details,
@@ -2068,7 +2237,7 @@ var GatewayProvider = class {
2068
2237
  */
2069
2238
  getHealth() {
2070
2239
  return this.gatewayService.getHealth().pipe(
2071
- map11((health) => ({
2240
+ map12((health) => ({
2072
2241
  ...health,
2073
2242
  backend_provider: this.config.provider,
2074
2243
  backend_provider_status: health.providers.find((p) => p.name === this.config.provider)?.status || "unavailable"
@@ -2256,7 +2425,7 @@ var PlaygroundProvider = class {
2256
2425
  };
2257
2426
 
2258
2427
  // src/services/ai-provider/providers/xai.provider.ts
2259
- import { Observable as Observable9, from as from6, switchMap as switchMap5, map as map12, throwError as throwError5 } from "rxjs";
2428
+ import { Observable as Observable9, from as from6, switchMap as switchMap5, map as map13, throwError as throwError5 } from "rxjs";
2260
2429
  var XAIProvider = class {
2261
2430
  config;
2262
2431
  baseUrl;
@@ -2287,7 +2456,7 @@ var XAIProvider = class {
2287
2456
  options: request.options
2288
2457
  };
2289
2458
  return this.chat(chatRequest).pipe(
2290
- map12((response) => ({
2459
+ map13((response) => ({
2291
2460
  response: response.message.content,
2292
2461
  done: response.done
2293
2462
  }))
@@ -2304,7 +2473,7 @@ var XAIProvider = class {
2304
2473
  }
2305
2474
  return from6(response.json());
2306
2475
  }),
2307
- map12(
2476
+ map13(
2308
2477
  (data) => data.data.map((model) => ({
2309
2478
  name: model.id,
2310
2479
  details: {
@@ -2439,7 +2608,237 @@ var XAIProvider = class {
2439
2608
  }
2440
2609
  return from6(response.json());
2441
2610
  }),
2442
- map12((data) => ({
2611
+ map13((data) => ({
2612
+ message: {
2613
+ content: data.choices?.[0]?.message?.content ?? "",
2614
+ role: "assistant"
2615
+ },
2616
+ done: true
2617
+ }))
2618
+ );
2619
+ }
2620
+ getHeaders() {
2621
+ const headers = {};
2622
+ if (this.config.apiKey) {
2623
+ headers["Authorization"] = `Bearer ${this.config.apiKey}`;
2624
+ }
2625
+ return headers;
2626
+ }
2627
+ };
2628
+
2629
+ // src/services/ai-provider/providers/bandit-ai.provider.ts
2630
+ import { Observable as Observable10, from as from7, switchMap as switchMap6, map as map14, throwError as throwError6 } from "rxjs";
2631
+ var DEFAULT_BANDIT_BASE = "https://api.burtson.ai";
2632
+ var normalizeImageUrl = (value) => {
2633
+ if (!value) {
2634
+ return null;
2635
+ }
2636
+ const trimmed = value.trim();
2637
+ if (!trimmed) {
2638
+ return null;
2639
+ }
2640
+ if (/^data:/i.test(trimmed) || /^https?:\/\//i.test(trimmed)) {
2641
+ return trimmed;
2642
+ }
2643
+ return `data:image/jpeg;base64,${trimmed}`;
2644
+ };
2645
+ var injectImagesIntoMessages = (messages, images) => {
2646
+ const normalized = messages.map((message) => ({
2647
+ role: message.role,
2648
+ content: message.content
2649
+ }));
2650
+ if (!images || images.length === 0) {
2651
+ return normalized;
2652
+ }
2653
+ const normalizedImages = images.map(normalizeImageUrl).filter((url) => Boolean(url));
2654
+ if (normalizedImages.length === 0) {
2655
+ return normalized;
2656
+ }
2657
+ const lastUserIndex = normalized.map((msg) => msg.role).lastIndexOf("user");
2658
+ if (lastUserIndex === -1) {
2659
+ return normalized;
2660
+ }
2661
+ const target = normalized[lastUserIndex];
2662
+ const baseContent = typeof target.content === "string" && target.content.trim().length > 0 ? [
2663
+ {
2664
+ type: "text",
2665
+ text: target.content
2666
+ }
2667
+ ] : [];
2668
+ const imageContent = normalizedImages.map((url) => ({
2669
+ type: "image_url",
2670
+ image_url: { url, detail: "auto" }
2671
+ }));
2672
+ normalized[lastUserIndex] = {
2673
+ role: target.role,
2674
+ content: [...baseContent, ...imageContent]
2675
+ };
2676
+ return normalized;
2677
+ };
2678
+ var BanditAIProvider = class {
2679
+ config;
2680
+ baseUrl;
2681
+ constructor(config) {
2682
+ this.config = config;
2683
+ this.baseUrl = (config.baseUrl || DEFAULT_BANDIT_BASE).replace(/\/$/, "");
2684
+ }
2685
+ chat(request) {
2686
+ const url = `${this.baseUrl}/chat/completions`;
2687
+ const messages = injectImagesIntoMessages(request.messages, request.images);
2688
+ const payload = {
2689
+ model: request.model,
2690
+ messages,
2691
+ stream: Boolean(request.stream),
2692
+ temperature: request.temperature,
2693
+ max_tokens: request.maxTokens
2694
+ };
2695
+ if (request.stream) {
2696
+ return this.streamChatRequest(url, payload);
2697
+ }
2698
+ return this.nonStreamChatRequest(url, payload);
2699
+ }
2700
+ generate(request) {
2701
+ const chatRequest = {
2702
+ model: request.model,
2703
+ messages: [{ role: "user", content: request.prompt }],
2704
+ stream: request.stream,
2705
+ options: request.options
2706
+ };
2707
+ return this.chat(chatRequest).pipe(
2708
+ map14((response) => ({
2709
+ response: response.message.content,
2710
+ done: response.done
2711
+ }))
2712
+ );
2713
+ }
2714
+ listModels() {
2715
+ const url = `${this.baseUrl}/models`;
2716
+ return from7(fetch(url, { headers: this.getHeaders() })).pipe(
2717
+ switchMap6((response) => {
2718
+ if (!response.ok) {
2719
+ debugLogger.error("BanditAI listModels failed", { status: response.status, url });
2720
+ return throwError6(() => new Error(`Failed to list Bandit models: ${response.status}`));
2721
+ }
2722
+ return from7(response.json());
2723
+ }),
2724
+ map14(
2725
+ (data) => data.data.map((model) => ({
2726
+ name: model.id,
2727
+ details: {
2728
+ format: "bandit",
2729
+ family: model.object
2730
+ }
2731
+ }))
2732
+ )
2733
+ );
2734
+ }
2735
+ async validateServiceAvailability(args) {
2736
+ const attempt = async (url) => {
2737
+ try {
2738
+ const controller = new AbortController();
2739
+ const timeoutId = setTimeout(() => controller.abort(), args.timeoutMs);
2740
+ const response = await fetch(`${url}/models`, {
2741
+ headers: this.getHeaders(),
2742
+ signal: controller.signal
2743
+ });
2744
+ clearTimeout(timeoutId);
2745
+ return response.ok;
2746
+ } catch (error) {
2747
+ debugLogger.warn("BanditAI availability check failed", { url, error });
2748
+ return false;
2749
+ }
2750
+ };
2751
+ const primary = await attempt(this.baseUrl);
2752
+ if (primary) {
2753
+ return { url: this.baseUrl, isAvailable: true };
2754
+ }
2755
+ if (args.fallbackUrl) {
2756
+ const fallback = args.fallbackUrl.replace(/\/$/, "");
2757
+ if (await attempt(fallback)) {
2758
+ this.baseUrl = fallback;
2759
+ return { url: fallback, isAvailable: true };
2760
+ }
2761
+ }
2762
+ return { url: this.baseUrl, isAvailable: false };
2763
+ }
2764
+ getProviderType() {
2765
+ return "bandit" /* BANDIT */;
2766
+ }
2767
+ getConfig() {
2768
+ return this.config;
2769
+ }
2770
+ streamChatRequest(url, payload) {
2771
+ return new Observable10((observer) => {
2772
+ const task = fetch(url, {
2773
+ method: "POST",
2774
+ headers: {
2775
+ ...this.getHeaders(),
2776
+ "Content-Type": "application/json"
2777
+ },
2778
+ body: JSON.stringify(payload)
2779
+ });
2780
+ task.then((response) => {
2781
+ if (!response.ok) {
2782
+ observer.error(new Error(`BanditAI request failed: ${response.status}`));
2783
+ return;
2784
+ }
2785
+ const reader = response.body?.getReader();
2786
+ const decoder = new TextDecoder();
2787
+ let buffer = "";
2788
+ const read = () => {
2789
+ reader?.read().then(({ done, value }) => {
2790
+ if (done) {
2791
+ observer.next({ message: { content: "", role: "assistant" }, done: true });
2792
+ observer.complete();
2793
+ return;
2794
+ }
2795
+ buffer += decoder.decode(value, { stream: true });
2796
+ const lines = buffer.split("\n");
2797
+ buffer = lines.pop() ?? "";
2798
+ for (const rawLine of lines) {
2799
+ const line = rawLine.trim();
2800
+ if (!line.startsWith("data: ")) {
2801
+ continue;
2802
+ }
2803
+ const data = line.slice(6).trim();
2804
+ if (data === "[DONE]") {
2805
+ observer.next({ message: { content: "", role: "assistant" }, done: true });
2806
+ observer.complete();
2807
+ return;
2808
+ }
2809
+ try {
2810
+ const parsed = JSON.parse(data);
2811
+ const content = parsed.choices?.[0]?.delta?.content ?? "";
2812
+ if (content) {
2813
+ observer.next({ message: { content, role: "assistant" }, done: false });
2814
+ }
2815
+ } catch (error) {
2816
+ debugLogger.error("BanditAI stream chunk parse failure", { data, error });
2817
+ }
2818
+ }
2819
+ read();
2820
+ }).catch((err) => observer.error(err));
2821
+ };
2822
+ read();
2823
+ }).catch((err) => observer.error(err));
2824
+ });
2825
+ }
2826
+ nonStreamChatRequest(url, payload) {
2827
+ return from7(fetch(url, {
2828
+ method: "POST",
2829
+ headers: {
2830
+ ...this.getHeaders(),
2831
+ "Content-Type": "application/json"
2832
+ },
2833
+ body: JSON.stringify(payload)
2834
+ })).pipe(
2835
+ switchMap6((response) => {
2836
+ if (!response.ok) {
2837
+ return throwError6(() => new Error(`BanditAI request failed: ${response.status}`));
2838
+ }
2839
+ return from7(response.json());
2840
+ }),
2841
+ map14((data) => ({
2443
2842
  message: {
2444
2843
  content: data.choices?.[0]?.message?.content ?? "",
2445
2844
  role: "assistant"
@@ -2471,6 +2870,8 @@ var AIProviderFactory = class {
2471
2870
  return new AnthropicProvider(config);
2472
2871
  case "xai" /* XAI */:
2473
2872
  return new XAIProvider(config);
2873
+ case "bandit" /* BANDIT */:
2874
+ return new BanditAIProvider(config);
2474
2875
  case "gateway" /* GATEWAY */:
2475
2876
  return new GatewayProvider(config);
2476
2877
  case "playground" /* PLAYGROUND */:
@@ -2485,6 +2886,7 @@ var AIProviderFactory = class {
2485
2886
  "openai" /* OPENAI */,
2486
2887
  "azure-openai" /* AZURE_OPENAI */,
2487
2888
  "xai" /* XAI */,
2889
+ "bandit" /* BANDIT */,
2488
2890
  "gateway" /* GATEWAY */,
2489
2891
  "playground" /* PLAYGROUND */
2490
2892
  ];
@@ -2502,6 +2904,8 @@ var AIProviderFactory = class {
2502
2904
  return !!config.apiKey;
2503
2905
  case "xai" /* XAI */:
2504
2906
  return !!config.apiKey;
2907
+ case "bandit" /* BANDIT */:
2908
+ return !!config.apiKey;
2505
2909
  case "gateway" /* GATEWAY */:
2506
2910
  return !!(config.gatewayUrl && config.provider);
2507
2911
  case "playground" /* PLAYGROUND */:
@@ -2543,4 +2947,4 @@ export {
2543
2947
  AIProviderFactory,
2544
2948
  useAIProviderStore
2545
2949
  };
2546
- //# sourceMappingURL=chunk-QU5S5QQP.mjs.map
2950
+ //# sourceMappingURL=chunk-54ZQ3FSN.mjs.map