@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.
- package/README.md +14 -11
- package/dist/{aiProviderStore-XN7GCBHJ.mjs → aiProviderStore-UQI33C5E.mjs} +2 -2
- package/dist/{chat-5QJNWB7I.mjs → chat-T5ANWWYQ.mjs} +5 -5
- package/dist/chat-provider.js +571 -122
- package/dist/chat-provider.js.map +1 -1
- package/dist/chat-provider.mjs +4 -4
- package/dist/{chunk-3A2527TE.mjs → chunk-22EY3ZDC.mjs} +3 -3
- package/dist/{chunk-ECRNIAG6.mjs → chunk-3E57HLDV.mjs} +4 -4
- package/dist/{chunk-QU5S5QQP.mjs → chunk-54ZQ3FSN.mjs} +481 -77
- package/dist/chunk-54ZQ3FSN.mjs.map +1 -0
- package/dist/{chunk-JRCDANLN.mjs → chunk-A6OBEF72.mjs} +75 -12
- package/dist/{chunk-JRCDANLN.mjs.map → chunk-A6OBEF72.mjs.map} +1 -1
- package/dist/{chunk-CDQYBO3Q.mjs → chunk-CX3INLYJ.mjs} +27 -5
- package/dist/chunk-CX3INLYJ.mjs.map +1 -0
- package/dist/{chunk-QYH2T4L5.mjs → chunk-LYWVYBKU.mjs} +3 -3
- package/dist/{chunk-WO5KFNNW.mjs → chunk-QFNEHSY4.mjs} +62 -24
- package/dist/chunk-QFNEHSY4.mjs.map +1 -0
- package/dist/{chunk-EOKIE5HZ.mjs → chunk-WPWWWUD7.mjs} +51 -46
- package/dist/chunk-WPWWWUD7.mjs.map +1 -0
- package/dist/{cli/cli.js → cli.js} +423 -10
- package/dist/cli.js.map +1 -0
- package/dist/{gateway-B0LJ3-jT.d.ts → gateway-5yt_3QDP.d.mts} +4 -4
- package/dist/{gateway-B0LJ3-jT.d.mts → gateway-5yt_3QDP.d.ts} +4 -4
- package/dist/index.d.mts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +756 -206
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +8 -8
- package/dist/management/management.js +754 -204
- package/dist/management/management.js.map +1 -1
- package/dist/management/management.mjs +6 -6
- package/dist/modals/chat-modal/chat-modal.js +532 -88
- package/dist/modals/chat-modal/chat-modal.js.map +1 -1
- package/dist/modals/chat-modal/chat-modal.mjs +4 -4
- package/dist/public-types.d.mts +1 -1
- package/dist/public-types.d.ts +1 -1
- package/docs/01_quickstart.md +10 -4
- package/docs/02_gateway_api.md +19 -3
- package/docs/03_provider_integration.md +5 -4
- package/docs/api_reference/media/02_gateway_api.md +19 -3
- package/docs/api_reference/media/README.md +3 -1
- package/package.json +1 -1
- package/dist/chunk-CDQYBO3Q.mjs.map +0 -1
- package/dist/chunk-EOKIE5HZ.mjs.map +0 -1
- package/dist/chunk-QU5S5QQP.mjs.map +0 -1
- package/dist/chunk-WO5KFNNW.mjs.map +0 -1
- package/dist/cli/cli.js.map +0 -1
- /package/dist/{aiProviderStore-XN7GCBHJ.mjs.map → aiProviderStore-UQI33C5E.mjs.map} +0 -0
- /package/dist/{chat-5QJNWB7I.mjs.map → chat-T5ANWWYQ.mjs.map} +0 -0
- /package/dist/{chunk-3A2527TE.mjs.map → chunk-22EY3ZDC.mjs.map} +0 -0
- /package/dist/{chunk-ECRNIAG6.mjs.map → chunk-3E57HLDV.mjs.map} +0 -0
- /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
|
|
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
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
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
|
|
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
|
-
}
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
1450
|
+
const rawToken = this._tokenFactory();
|
|
1426
1451
|
const headers = {
|
|
1427
1452
|
"Content-Type": "application/json"
|
|
1428
1453
|
};
|
|
1429
|
-
if (
|
|
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((
|
|
2120
|
+
request.images.forEach((imageRef) => {
|
|
1957
2121
|
contentArray.push({
|
|
1958
2122
|
type: "image_url",
|
|
1959
2123
|
image_url: {
|
|
1960
|
-
url:
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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-
|
|
2950
|
+
//# sourceMappingURL=chunk-54ZQ3FSN.mjs.map
|