@burtson-labs/bandit-engine 2.1.7 → 2.1.8

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 (69) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/dist/AdvancedTab-HZTDXFST.mjs +229 -0
  3. package/dist/AdvancedTab-HZTDXFST.mjs.map +1 -0
  4. package/dist/CrewSection-YM26YD2N.mjs +12 -0
  5. package/dist/InstructionsTab-MKOGBQ5L.mjs +195 -0
  6. package/dist/InstructionsTab-MKOGBQ5L.mjs.map +1 -0
  7. package/dist/PersonalizationTab-2XHFTYYW.mjs +179 -0
  8. package/dist/PersonalizationTab-2XHFTYYW.mjs.map +1 -0
  9. package/dist/{ProjectsPane-E5D72PUG.mjs → ProjectsPane-KE5DGFN4.mjs} +9 -6
  10. package/dist/ProjectsPane-KE5DGFN4.mjs.map +1 -0
  11. package/dist/{SchedulesTab-AKJ6WFZV.mjs → SchedulesTab-AM5QP36K.mjs} +7 -5
  12. package/dist/SchedulesTab-AM5QP36K.mjs.map +1 -0
  13. package/dist/{aiProviderStore-ANPQVHGD.mjs → aiProviderStore-GH4JZTMW.mjs} +3 -2
  14. package/dist/chat-D66YM4MB.mjs +26 -0
  15. package/dist/{chunk-4NUQYUZH.mjs → chunk-2FI2IICG.mjs} +7 -569
  16. package/dist/chunk-2FI2IICG.mjs.map +1 -0
  17. package/dist/chunk-2THUYNVU.mjs +1203 -0
  18. package/dist/chunk-2THUYNVU.mjs.map +1 -0
  19. package/dist/{chunk-KWOUXBSF.mjs → chunk-5LXCZJLJ.mjs} +5 -213
  20. package/dist/chunk-5LXCZJLJ.mjs.map +1 -0
  21. package/dist/chunk-5WCKKD3S.mjs +1742 -0
  22. package/dist/chunk-5WCKKD3S.mjs.map +1 -0
  23. package/dist/{chunk-GX4FVZNA.mjs → chunk-DHXSFFLX.mjs} +4 -26
  24. package/dist/chunk-DHXSFFLX.mjs.map +1 -0
  25. package/dist/chunk-EKPPIS3X.mjs +336 -0
  26. package/dist/chunk-EKPPIS3X.mjs.map +1 -0
  27. package/dist/chunk-FBLITVVY.mjs +5845 -0
  28. package/dist/chunk-FBLITVVY.mjs.map +1 -0
  29. package/dist/chunk-H2XUGGKV.mjs +168 -0
  30. package/dist/chunk-H2XUGGKV.mjs.map +1 -0
  31. package/dist/chunk-K4AO466X.mjs +570 -0
  32. package/dist/chunk-K4AO466X.mjs.map +1 -0
  33. package/dist/chunk-KPT7NNNK.mjs +214 -0
  34. package/dist/chunk-KPT7NNNK.mjs.map +1 -0
  35. package/dist/{chunk-SQJ4OKWM.mjs → chunk-LAH6UL3Q.mjs} +10 -1726
  36. package/dist/chunk-LAH6UL3Q.mjs.map +1 -0
  37. package/dist/chunk-LC64VSDS.mjs +541 -0
  38. package/dist/chunk-LC64VSDS.mjs.map +1 -0
  39. package/dist/{chunk-DH5A2HT2.mjs → chunk-LY3O7UWA.mjs} +45 -1
  40. package/dist/chunk-LY3O7UWA.mjs.map +1 -0
  41. package/dist/{chunk-4NHPLHXU.mjs → chunk-WZGDQ4EA.mjs} +5176 -11535
  42. package/dist/chunk-WZGDQ4EA.mjs.map +1 -0
  43. package/dist/chunk-Y272M3D5.mjs +28 -0
  44. package/dist/chunk-Y272M3D5.mjs.map +1 -0
  45. package/dist/index.d.mts +121 -8
  46. package/dist/index.d.ts +121 -8
  47. package/dist/index.js +8732 -7511
  48. package/dist/index.js.map +1 -1
  49. package/dist/index.mjs +1243 -2243
  50. package/dist/index.mjs.map +1 -1
  51. package/dist/{modelStore-YNANSWCN.mjs → modelStore-KO45QAO2.mjs} +3 -2
  52. package/dist/modelStore-KO45QAO2.mjs.map +1 -0
  53. package/dist/{public-Tq2Pmw9m.d.ts → public-_W_Zew3C.d.mts} +14 -0
  54. package/dist/{public-Tq2Pmw9m.d.mts → public-_W_Zew3C.d.ts} +14 -0
  55. package/dist/public-types.d.mts +1 -1
  56. package/dist/public-types.d.ts +1 -1
  57. package/package.json +1 -1
  58. package/dist/ProjectsPane-E5D72PUG.mjs.map +0 -1
  59. package/dist/SchedulesTab-AKJ6WFZV.mjs.map +0 -1
  60. package/dist/chat-MI4IL3WK.mjs +0 -19
  61. package/dist/chunk-4NHPLHXU.mjs.map +0 -1
  62. package/dist/chunk-4NUQYUZH.mjs.map +0 -1
  63. package/dist/chunk-DH5A2HT2.mjs.map +0 -1
  64. package/dist/chunk-GX4FVZNA.mjs.map +0 -1
  65. package/dist/chunk-KWOUXBSF.mjs.map +0 -1
  66. package/dist/chunk-SQJ4OKWM.mjs.map +0 -1
  67. /package/dist/{aiProviderStore-ANPQVHGD.mjs.map → CrewSection-YM26YD2N.mjs.map} +0 -0
  68. /package/dist/{chat-MI4IL3WK.mjs.map → aiProviderStore-GH4JZTMW.mjs.map} +0 -0
  69. /package/dist/{modelStore-YNANSWCN.mjs.map → chat-D66YM4MB.mjs.map} +0 -0
@@ -0,0 +1,1203 @@
1
+ import {
2
+ useVoiceStore
3
+ } from "./chunk-LC64VSDS.mjs";
4
+ import {
5
+ Refresh,
6
+ Settings
7
+ } from "./chunk-K4AO466X.mjs";
8
+ import {
9
+ ManagementCard,
10
+ ManagementEmptyState
11
+ } from "./chunk-LY3O7UWA.mjs";
12
+ import {
13
+ AIProviderFactory,
14
+ GatewayService,
15
+ useAIProviderStore
16
+ } from "./chunk-DHXSFFLX.mjs";
17
+ import {
18
+ indexedDBService_default
19
+ } from "./chunk-KPT7NNNK.mjs";
20
+ import {
21
+ usePackageSettingsStore
22
+ } from "./chunk-K7SQWDNF.mjs";
23
+ import {
24
+ authenticationService
25
+ } from "./chunk-S3DWWUBD.mjs";
26
+ import {
27
+ debugLogger
28
+ } from "./chunk-NI7BKLC7.mjs";
29
+
30
+ // src/hooks/useGatewayQueries.ts
31
+ import { useMemo } from "react";
32
+ import { lastValueFrom } from "rxjs";
33
+ import { useQuery } from "@tanstack/react-query";
34
+ var useGatewayClient = () => {
35
+ const settings = usePackageSettingsStore((state) => state.settings);
36
+ const gatewayUrl = settings?.gatewayApiUrl;
37
+ const feedbackEmail = settings?.feedbackEmail;
38
+ const service = useMemo(() => {
39
+ if (!gatewayUrl) {
40
+ return null;
41
+ }
42
+ return new GatewayService(
43
+ gatewayUrl,
44
+ () => authenticationService.getToken(),
45
+ feedbackEmail
46
+ );
47
+ }, [gatewayUrl, feedbackEmail]);
48
+ return { service, gatewayUrl };
49
+ };
50
+ var useGatewayHealth = (options) => {
51
+ const { service, gatewayUrl } = useGatewayClient();
52
+ const enabled = Boolean(service && gatewayUrl) && (options?.enabled ?? true);
53
+ return useQuery({
54
+ ...options,
55
+ queryKey: ["gateway", "health", gatewayUrl],
56
+ queryFn: async () => {
57
+ if (!service) {
58
+ throw new Error("Gateway service is not configured");
59
+ }
60
+ return lastValueFrom(service.getHealth());
61
+ },
62
+ enabled
63
+ });
64
+ };
65
+ var useGatewayModels = (options) => {
66
+ const { service, gatewayUrl } = useGatewayClient();
67
+ const enabled = Boolean(service && gatewayUrl) && (options?.enabled ?? true);
68
+ return useQuery({
69
+ ...options,
70
+ queryKey: ["gateway", "models", gatewayUrl],
71
+ queryFn: async () => {
72
+ if (!service) {
73
+ throw new Error("Gateway service is not configured");
74
+ }
75
+ return lastValueFrom(service.listModels());
76
+ },
77
+ enabled
78
+ });
79
+ };
80
+ var useGatewayMemory = (options) => {
81
+ const { service, gatewayUrl } = useGatewayClient();
82
+ const enabled = Boolean(service && gatewayUrl) && (options?.enabled ?? true);
83
+ return useQuery({
84
+ ...options,
85
+ queryKey: ["gateway", "memory", gatewayUrl],
86
+ queryFn: async () => {
87
+ if (!service) {
88
+ throw new Error("Gateway service is not configured");
89
+ }
90
+ return lastValueFrom(service.getMemory());
91
+ },
92
+ enabled
93
+ });
94
+ };
95
+
96
+ // src/management/components/ServiceStatusCard.tsx
97
+ import { useCallback } from "react";
98
+ import { Box, Chip, IconButton, Tooltip, Typography } from "@mui/material";
99
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
100
+ var spinSx = {
101
+ animation: "spin 1s linear infinite",
102
+ "@keyframes spin": {
103
+ "0%": { transform: "rotate(0deg)" },
104
+ "100%": { transform: "rotate(360deg)" }
105
+ }
106
+ };
107
+ var ServiceStatusCard = ({
108
+ packageSettings
109
+ }) => {
110
+ const isTTSSTTAvailable = useVoiceStore((state) => state.isServiceAvailable);
111
+ const gatewayConfigured = Boolean(packageSettings?.gatewayApiUrl);
112
+ const {
113
+ data: gatewayHealth,
114
+ error: gatewayHealthError,
115
+ isLoading: gatewayHealthLoading,
116
+ isFetching: gatewayHealthFetching,
117
+ refetch: refetchGatewayHealth
118
+ } = useGatewayHealth({
119
+ enabled: gatewayConfigured,
120
+ refetchOnWindowFocus: false,
121
+ staleTime: 6e4
122
+ });
123
+ const isGatewayLoading = gatewayHealthLoading || gatewayHealthFetching;
124
+ const gatewayChipLabel = gatewayConfigured ? isGatewayLoading ? "Checking\u2026" : gatewayHealthError ? "Offline" : gatewayHealth ? gatewayHealth.status?.toLowerCase() === "healthy" ? "Online" : "Offline" : "Unknown" : "Not configured";
125
+ const gatewayChipColor = (() => {
126
+ if (!gatewayConfigured) return "default";
127
+ if (gatewayHealthError) return "error";
128
+ if (isGatewayLoading) return "default";
129
+ if (!gatewayHealth) return "warning";
130
+ return gatewayHealth.status?.toLowerCase() === "healthy" ? "success" : "error";
131
+ })();
132
+ const handleGatewayStatusRefresh = useCallback(() => {
133
+ if (!gatewayConfigured) return;
134
+ refetchGatewayHealth();
135
+ }, [gatewayConfigured, refetchGatewayHealth]);
136
+ const voiceChip = /* @__PURE__ */ jsx(
137
+ Chip,
138
+ {
139
+ label: isTTSSTTAvailable ? "Available" : packageSettings?.gatewayApiUrl ? "Unavailable" : "Not configured",
140
+ color: isTTSSTTAvailable ? "success" : packageSettings?.gatewayApiUrl ? "warning" : "default",
141
+ size: "small"
142
+ }
143
+ );
144
+ const row = (label, chip) => /* @__PURE__ */ jsxs(
145
+ Box,
146
+ {
147
+ sx: {
148
+ display: "flex",
149
+ alignItems: "center",
150
+ justifyContent: "space-between",
151
+ flexWrap: "wrap",
152
+ gap: 1
153
+ },
154
+ children: [
155
+ /* @__PURE__ */ jsx(Box, { sx: { display: "flex", alignItems: "center", gap: 1 }, children: label }),
156
+ chip
157
+ ]
158
+ }
159
+ );
160
+ return /* @__PURE__ */ jsx(
161
+ ManagementCard,
162
+ {
163
+ title: "Service status",
164
+ description: "The services this deployment is configured with. Your administrator enables or configures them.",
165
+ children: /* @__PURE__ */ jsxs(Box, { sx: { display: "flex", flexDirection: "column", gap: 1.5 }, children: [
166
+ row(
167
+ /* @__PURE__ */ jsx(Typography, { variant: "body2", sx: { fontWeight: 600 }, children: "Text-to-speech (TTS)" }),
168
+ voiceChip
169
+ ),
170
+ row(
171
+ /* @__PURE__ */ jsx(Typography, { variant: "body2", sx: { fontWeight: 600 }, children: "Speech-to-text (STT)" }),
172
+ voiceChip
173
+ ),
174
+ row(
175
+ /* @__PURE__ */ jsxs(Fragment, { children: [
176
+ /* @__PURE__ */ jsx(Typography, { variant: "body2", sx: { fontWeight: 600 }, children: "Gateway/MCP" }),
177
+ gatewayConfigured && /* @__PURE__ */ jsx(Tooltip, { title: "Refresh gateway status", placement: "top", children: /* @__PURE__ */ jsx("span", { children: /* @__PURE__ */ jsx(
178
+ IconButton,
179
+ {
180
+ size: "small",
181
+ onClick: handleGatewayStatusRefresh,
182
+ disabled: isGatewayLoading,
183
+ "aria-label": "Refresh gateway status",
184
+ sx: { width: { xs: 44, sm: 28 }, height: { xs: 44, sm: 28 } },
185
+ children: /* @__PURE__ */ jsx(
186
+ Refresh,
187
+ {
188
+ fontSize: "small",
189
+ sx: isGatewayLoading ? spinSx : void 0
190
+ }
191
+ )
192
+ }
193
+ ) }) })
194
+ ] }),
195
+ /* @__PURE__ */ jsx(Chip, { label: gatewayChipLabel, color: gatewayChipColor, size: "small" })
196
+ ),
197
+ gatewayConfigured && gatewayHealthError && /* @__PURE__ */ jsxs(Box, { sx: { display: "flex", flexDirection: "column", gap: 0.75 }, children: [
198
+ /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "error", children: "Couldn't reach the gateway. Use refresh to check again." }),
199
+ gatewayHealthError.message && /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "text.secondary", children: gatewayHealthError.message })
200
+ ] })
201
+ ] })
202
+ }
203
+ );
204
+ };
205
+
206
+ // src/management/components/ProviderTab.tsx
207
+ import { useState, useEffect, useCallback as useCallback2, useRef } from "react";
208
+ import {
209
+ Box as Box2,
210
+ Typography as Typography2,
211
+ Paper,
212
+ Button,
213
+ TextField,
214
+ MenuItem,
215
+ Chip as Chip2,
216
+ Snackbar,
217
+ Alert,
218
+ CircularProgress,
219
+ Stack
220
+ } from "@mui/material";
221
+
222
+ // src/services/ai-provider-init.service.ts
223
+ var sessionTokenFactory = (label) => () => {
224
+ const token = authenticationService.getToken();
225
+ debugLogger.info(`AIProviderInit: ${label}`, { hasToken: !!token });
226
+ return token;
227
+ };
228
+ var AIProviderInitService = class _AIProviderInitService {
229
+ static instance;
230
+ constructor() {
231
+ }
232
+ static getInstance() {
233
+ if (!_AIProviderInitService.instance) {
234
+ _AIProviderInitService.instance = new _AIProviderInitService();
235
+ }
236
+ return _AIProviderInitService.instance;
237
+ }
238
+ /**
239
+ * Initialize AI provider from package settings
240
+ * Provides backward compatibility with existing Ollama settings
241
+ */
242
+ async initializeFromSettings() {
243
+ const settings = usePackageSettingsStore.getState().settings;
244
+ if (!settings) {
245
+ debugLogger.warn("No package settings found, cannot initialize AI provider");
246
+ return;
247
+ }
248
+ let providerConfig;
249
+ debugLogger.info("AI Provider Init: Starting initialization", {
250
+ hasSettings: !!settings,
251
+ hasAiProvider: !!settings.aiProvider,
252
+ ollamaUrl: settings.ollamaUrl
253
+ });
254
+ const isPlaygroundEnvironment = settings.playgroundMode === true || settings.aiProvider?.type === "playground" /* PLAYGROUND */ || (settings.gatewayApiUrl?.toLowerCase()?.startsWith("playground://") ?? false) || typeof window !== "undefined" && window.location.pathname.includes("/playground");
255
+ if (isPlaygroundEnvironment) {
256
+ debugLogger.info(
257
+ "AI Provider Init: Playground environment detected, bypassing saved provider config"
258
+ );
259
+ } else {
260
+ try {
261
+ const savedConfig = await indexedDBService_default.get(
262
+ "banditConfig",
263
+ 1,
264
+ "config",
265
+ "aiProvider",
266
+ [{ name: "config", keyPath: "id" }]
267
+ );
268
+ if (savedConfig) {
269
+ debugLogger.info("AI Provider Init: Found saved config in IndexedDB", {
270
+ type: savedConfig.type
271
+ });
272
+ const { id: _id, ...configWithoutId } = savedConfig;
273
+ providerConfig = { ...configWithoutId };
274
+ if (providerConfig.type === "anthropic" /* ANTHROPIC */) {
275
+ providerConfig = this.convertAnthropicConfig(
276
+ providerConfig,
277
+ settings?.gatewayApiUrl
278
+ );
279
+ }
280
+ providerConfig = this.ensureTokenFactory(providerConfig);
281
+ try {
282
+ const { createProvider } = useAIProviderStore.getState();
283
+ createProvider(providerConfig);
284
+ const provider = useAIProviderStore.getState().provider;
285
+ if (provider) {
286
+ try {
287
+ await provider.validateServiceAvailability({ timeoutMs: 5e3 });
288
+ debugLogger.info(
289
+ `AI Provider initialized and validated from IndexedDB: ${providerConfig.type}`
290
+ );
291
+ } catch (validationError) {
292
+ debugLogger.warn(`AI Provider created but validation failed`, {
293
+ error: validationError
294
+ });
295
+ }
296
+ }
297
+ return;
298
+ } catch (error) {
299
+ debugLogger.error(
300
+ "Failed to initialize saved provider config, falling back to package settings",
301
+ { error }
302
+ );
303
+ }
304
+ }
305
+ debugLogger.info(
306
+ "AI Provider Init: No saved config found, using package settings"
307
+ );
308
+ } catch (error) {
309
+ debugLogger.warn(
310
+ "AI Provider Init: Failed to load from IndexedDB, using package settings",
311
+ { error }
312
+ );
313
+ }
314
+ }
315
+ if (settings.aiProvider) {
316
+ providerConfig = { ...settings.aiProvider };
317
+ if (providerConfig.type === "anthropic" /* ANTHROPIC */) {
318
+ providerConfig = this.convertAnthropicConfig(
319
+ providerConfig,
320
+ settings.gatewayApiUrl
321
+ );
322
+ }
323
+ providerConfig = this.ensureTokenFactory(providerConfig);
324
+ debugLogger.info("Using explicit AI provider config", providerConfig);
325
+ } else {
326
+ providerConfig = {
327
+ type: "ollama" /* OLLAMA */,
328
+ baseUrl: settings.ollamaUrl,
329
+ tokenFactory: sessionTokenFactory("Token factory called")
330
+ };
331
+ debugLogger.info("Using legacy Ollama config", providerConfig);
332
+ }
333
+ try {
334
+ const { createProvider } = useAIProviderStore.getState();
335
+ createProvider(providerConfig);
336
+ const provider = useAIProviderStore.getState().provider;
337
+ if (provider) {
338
+ try {
339
+ await provider.validateServiceAvailability({ timeoutMs: 5e3 });
340
+ debugLogger.info(
341
+ `AI Provider initialized and validated: ${providerConfig.type}`
342
+ );
343
+ } catch (validationError) {
344
+ debugLogger.warn(`AI Provider created but validation failed:`, {
345
+ error: validationError
346
+ });
347
+ }
348
+ }
349
+ } catch (error) {
350
+ debugLogger.error("Failed to initialize AI provider:", { error });
351
+ if (providerConfig.type !== "ollama" /* OLLAMA */) {
352
+ try {
353
+ const fallbackConfig = {
354
+ type: "ollama" /* OLLAMA */,
355
+ baseUrl: settings.ollamaUrl || "http://localhost:11434",
356
+ tokenFactory: sessionTokenFactory("Fallback tokenFactory")
357
+ };
358
+ const { createProvider } = useAIProviderStore.getState();
359
+ createProvider(fallbackConfig);
360
+ debugLogger.info("Fallback to Ollama provider successful");
361
+ } catch (fallbackError) {
362
+ debugLogger.error("Failed to initialize fallback Ollama provider:", {
363
+ error: fallbackError
364
+ });
365
+ }
366
+ } else {
367
+ try {
368
+ const localFallbackConfig = {
369
+ type: "ollama" /* OLLAMA */,
370
+ baseUrl: "http://localhost:11434",
371
+ tokenFactory: sessionTokenFactory("Local fallback tokenFactory")
372
+ };
373
+ const { createProvider } = useAIProviderStore.getState();
374
+ createProvider(localFallbackConfig);
375
+ debugLogger.info("Fallback to localhost Ollama provider successful");
376
+ } catch (localFallbackError) {
377
+ debugLogger.error("All provider initialization attempts failed:", {
378
+ error: localFallbackError
379
+ });
380
+ }
381
+ }
382
+ }
383
+ }
384
+ /**
385
+ * Switch to a different AI provider
386
+ */
387
+ switchProvider(config) {
388
+ try {
389
+ const normalizedConfig = this.ensureTokenFactory({ ...config });
390
+ const { switchProvider } = useAIProviderStore.getState();
391
+ switchProvider(normalizedConfig);
392
+ debugLogger.info(`Switched to AI provider: ${normalizedConfig.type}`);
393
+ } catch (error) {
394
+ debugLogger.error("Failed to switch AI provider:", { error });
395
+ throw error;
396
+ }
397
+ }
398
+ /**
399
+ * Get the current provider type
400
+ */
401
+ getCurrentProviderType() {
402
+ const provider = useAIProviderStore.getState().provider;
403
+ return provider ? provider.getProviderType() : null;
404
+ }
405
+ /**
406
+ * Check if a provider is initialized
407
+ */
408
+ isProviderInitialized() {
409
+ return useAIProviderStore.getState().provider !== null;
410
+ }
411
+ convertAnthropicConfig(config, gatewayUrl) {
412
+ if (config.type !== "anthropic" /* ANTHROPIC */) {
413
+ return config;
414
+ }
415
+ const defaultModel = typeof config.defaultModel === "string" && config.defaultModel.trim() ? config.defaultModel.trim() : "claude-3-5-sonnet-latest";
416
+ const normalized = {
417
+ type: "gateway" /* GATEWAY */,
418
+ gatewayUrl: gatewayUrl || config.gatewayUrl || "",
419
+ provider: "anthropic",
420
+ defaultModel,
421
+ tokenFactory: config.tokenFactory
422
+ };
423
+ debugLogger.info(
424
+ "AI Provider Init: Converted direct Anthropic provider to gateway configuration"
425
+ );
426
+ return normalized;
427
+ }
428
+ /**
429
+ * Ensure providers that require auth have a token factory configured.
430
+ * Handles both UI auth tokens and API key scenarios.
431
+ */
432
+ ensureTokenFactory(config) {
433
+ if (config.type === "ollama" /* OLLAMA */ || config.type === "gateway" /* GATEWAY */) {
434
+ const existingFactory = config.tokenFactory;
435
+ if (existingFactory) {
436
+ return config;
437
+ }
438
+ if (typeof config.apiKey === "string" && config.apiKey.trim() !== "") {
439
+ const key = config.apiKey.trim();
440
+ config.tokenFactory = () => key;
441
+ debugLogger.info("AIProviderInit: Using API key for token factory", {
442
+ type: config.type,
443
+ hasKey: true
444
+ });
445
+ return config;
446
+ }
447
+ config.tokenFactory = sessionTokenFactory("Token factory resolved auth token");
448
+ }
449
+ return config;
450
+ }
451
+ };
452
+ var aiProviderInitService = AIProviderInitService.getInstance();
453
+
454
+ // src/management/components/ProviderTab.tsx
455
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
456
+ var ProviderTab = () => {
457
+ const { provider: currentProvider, config: currentProviderConfig } = useAIProviderStore();
458
+ const { settings: packageSettings } = usePackageSettingsStore();
459
+ const getSuggestedModel = useCallback2(
460
+ (type) => {
461
+ const configuredDefault = packageSettings?.defaultModel?.trim();
462
+ if (configuredDefault) {
463
+ return configuredDefault;
464
+ }
465
+ switch (type) {
466
+ case "openai" /* OPENAI */:
467
+ return "gpt-4o-mini";
468
+ case "xai" /* XAI */:
469
+ return "grok-beta";
470
+ case "bandit" /* BANDIT */:
471
+ return "bandit-core-1";
472
+ default:
473
+ return "";
474
+ }
475
+ },
476
+ [packageSettings?.defaultModel]
477
+ );
478
+ const applyDefaultModel = useCallback2(
479
+ (config) => {
480
+ const normalized = { ...config };
481
+ const trimmed = typeof normalized.defaultModel === "string" ? normalized.defaultModel.trim() : void 0;
482
+ const requiresModel = normalized.type === "openai" /* OPENAI */ || normalized.type === "xai" /* XAI */ || normalized.type === "bandit" /* BANDIT */;
483
+ if (trimmed) {
484
+ normalized.defaultModel = trimmed;
485
+ return normalized;
486
+ }
487
+ if (!requiresModel) {
488
+ delete normalized.defaultModel;
489
+ return normalized;
490
+ }
491
+ const suggestion = getSuggestedModel(normalized.type);
492
+ if (suggestion) {
493
+ normalized.defaultModel = suggestion;
494
+ } else {
495
+ delete normalized.defaultModel;
496
+ }
497
+ return normalized;
498
+ },
499
+ [getSuggestedModel]
500
+ );
501
+ const sanitizeConfigForSave = useCallback2(
502
+ (config) => {
503
+ const sanitized = { ...config };
504
+ if (typeof sanitized.defaultModel === "string") {
505
+ const trimmed = sanitized.defaultModel.trim();
506
+ sanitized.defaultModel = trimmed || void 0;
507
+ }
508
+ if (typeof sanitized.apiKey === "string") {
509
+ const trimmedKey = sanitized.apiKey.trim();
510
+ sanitized.apiKey = trimmedKey || void 0;
511
+ }
512
+ return sanitized;
513
+ },
514
+ []
515
+ );
516
+ const [providerConfig, setProviderConfig] = useState({
517
+ type: "gateway",
518
+ gatewayUrl: packageSettings?.gatewayApiUrl || "",
519
+ provider: "bandit"
520
+ });
521
+ const [saveResult, setSaveResult] = useState({ state: "idle" });
522
+ const [testResult, setTestResult] = useState({ state: "idle" });
523
+ const providerTypeRef = useRef(null);
524
+ const formRef = useRef(null);
525
+ const revealProviderForm = () => {
526
+ formRef.current?.scrollIntoView({ behavior: "smooth", block: "start" });
527
+ providerTypeRef.current?.focus();
528
+ };
529
+ const [snackbarMessage, setSnackbarMessage] = useState("");
530
+ const [showSnackbar, setShowSnackbar] = useState(false);
531
+ const [snackbarSeverity, setSnackbarSeverity] = useState(
532
+ "success"
533
+ );
534
+ const saveProviderConfigToDB = async (config) => {
535
+ try {
536
+ const { tokenFactory: _tokenFactory, ...persistableConfig } = config;
537
+ const serializableConfig = {
538
+ ...persistableConfig,
539
+ id: "aiProvider"
540
+ };
541
+ await indexedDBService_default.put(
542
+ "banditConfig",
543
+ 1,
544
+ "config",
545
+ serializableConfig,
546
+ [{ name: "config", keyPath: "id" }]
547
+ );
548
+ debugLogger.info("Provider configuration saved to IndexedDB", {
549
+ type: config.type
550
+ });
551
+ } catch (error) {
552
+ debugLogger.error("Failed to save provider config to IndexedDB:", { error });
553
+ throw error;
554
+ }
555
+ };
556
+ const loadProviderConfigFromDB = async () => {
557
+ try {
558
+ const config = await indexedDBService_default.get(
559
+ "banditConfig",
560
+ 1,
561
+ "config",
562
+ "aiProvider",
563
+ [{ name: "config", keyPath: "id" }]
564
+ );
565
+ if (config) {
566
+ debugLogger.info("Provider configuration loaded from IndexedDB", {
567
+ type: config.type
568
+ });
569
+ const { id: _id, ...configWithoutId } = config;
570
+ return configWithoutId;
571
+ }
572
+ } catch (error) {
573
+ debugLogger.error("Failed to load provider config from IndexedDB:", { error });
574
+ }
575
+ return null;
576
+ };
577
+ const convertAnthropicConfig = useCallback2(
578
+ (config) => {
579
+ if (!config) return null;
580
+ if (config.type !== "anthropic" /* ANTHROPIC */) {
581
+ return config;
582
+ }
583
+ const gatewayUrl = config.gatewayUrl || packageSettings?.gatewayApiUrl || "";
584
+ const defaultModel = typeof config.defaultModel === "string" && config.defaultModel.trim() ? config.defaultModel.trim() : "claude-3-5-sonnet-latest";
585
+ const converted = {
586
+ type: "gateway" /* GATEWAY */,
587
+ gatewayUrl,
588
+ provider: "anthropic",
589
+ defaultModel,
590
+ tokenFactory: config.tokenFactory
591
+ };
592
+ return converted;
593
+ },
594
+ [packageSettings?.gatewayApiUrl]
595
+ );
596
+ useEffect(() => {
597
+ const initializeProviderConfig = async () => {
598
+ const savedConfig = await loadProviderConfigFromDB();
599
+ if (savedConfig) {
600
+ const normalized = convertAnthropicConfig(savedConfig) || savedConfig;
601
+ setProviderConfig(applyDefaultModel(normalized));
602
+ } else if (currentProviderConfig) {
603
+ const normalized = convertAnthropicConfig(currentProviderConfig) || currentProviderConfig;
604
+ setProviderConfig(applyDefaultModel(normalized));
605
+ } else if (packageSettings?.aiProvider) {
606
+ const normalized = convertAnthropicConfig(packageSettings.aiProvider) || packageSettings.aiProvider;
607
+ setProviderConfig(applyDefaultModel(normalized));
608
+ }
609
+ };
610
+ initializeProviderConfig();
611
+ }, [applyDefaultModel, convertAnthropicConfig, currentProviderConfig, packageSettings]);
612
+ const showMessage = (message, severity) => {
613
+ setSnackbarMessage(message);
614
+ setSnackbarSeverity(severity);
615
+ setShowSnackbar(true);
616
+ };
617
+ const handleProviderTypeChange = (type) => {
618
+ setSaveResult({ state: "idle" });
619
+ setTestResult({ state: "idle" });
620
+ const baseConfig = { type };
621
+ switch (type) {
622
+ case "ollama" /* OLLAMA */:
623
+ setProviderConfig(
624
+ applyDefaultModel({
625
+ ...baseConfig,
626
+ baseUrl: "http://localhost:11434"
627
+ })
628
+ );
629
+ break;
630
+ case "openai" /* OPENAI */:
631
+ setProviderConfig(
632
+ applyDefaultModel({
633
+ ...baseConfig,
634
+ baseUrl: "https://api.openai.com/v1",
635
+ apiKey: ""
636
+ })
637
+ );
638
+ break;
639
+ case "azure-openai" /* AZURE_OPENAI */:
640
+ setProviderConfig(
641
+ applyDefaultModel({
642
+ ...baseConfig,
643
+ baseUrl: "",
644
+ apiKey: "",
645
+ apiVersion: "2024-02-01",
646
+ deploymentName: ""
647
+ })
648
+ );
649
+ break;
650
+ case "anthropic" /* ANTHROPIC */:
651
+ showMessage(
652
+ "Anthropic is only available via the Gateway provider. Please configure Gateway and choose Anthropic as the backend.",
653
+ "error"
654
+ );
655
+ setProviderConfig(
656
+ applyDefaultModel({
657
+ type: "gateway" /* GATEWAY */,
658
+ gatewayUrl: packageSettings?.gatewayApiUrl || "",
659
+ provider: "anthropic",
660
+ defaultModel: "claude-3-5-sonnet-latest"
661
+ })
662
+ );
663
+ break;
664
+ case "xai" /* XAI */:
665
+ setProviderConfig(
666
+ applyDefaultModel({
667
+ ...baseConfig,
668
+ baseUrl: "https://api.x.ai/v1",
669
+ apiKey: ""
670
+ })
671
+ );
672
+ break;
673
+ case "bandit" /* BANDIT */:
674
+ setProviderConfig(
675
+ applyDefaultModel({
676
+ ...baseConfig,
677
+ baseUrl: "https://api.burtson.ai",
678
+ apiKey: "",
679
+ defaultModel: "bandit-core-1"
680
+ })
681
+ );
682
+ break;
683
+ case "gateway" /* GATEWAY */:
684
+ setProviderConfig(
685
+ applyDefaultModel({
686
+ ...baseConfig,
687
+ gatewayUrl: packageSettings?.gatewayApiUrl || "",
688
+ provider: "bandit"
689
+ })
690
+ );
691
+ break;
692
+ case "playground" /* PLAYGROUND */:
693
+ setProviderConfig(
694
+ applyDefaultModel({
695
+ ...baseConfig
696
+ })
697
+ );
698
+ break;
699
+ }
700
+ };
701
+ const handleSaveProviderConfig = async () => {
702
+ setSaveResult({ state: "pending" });
703
+ try {
704
+ const normalizedConfigIntermediate = sanitizeConfigForSave(providerConfig);
705
+ const normalizedConfig = convertAnthropicConfig(normalizedConfigIntermediate) || normalizedConfigIntermediate;
706
+ const requiresModel = normalizedConfig.type === "openai" /* OPENAI */ || normalizedConfig.type === "xai" /* XAI */ || normalizedConfig.type === "bandit" /* BANDIT */;
707
+ if (requiresModel && !normalizedConfig.defaultModel) {
708
+ setSaveResult({
709
+ state: "error",
710
+ message: "Not saved. Enter a default model ID for this provider."
711
+ });
712
+ return;
713
+ }
714
+ const isValid = AIProviderFactory.validateConfig(normalizedConfig);
715
+ if (!isValid) {
716
+ setSaveResult({
717
+ state: "error",
718
+ message: "Not saved. Some required fields are missing or invalid."
719
+ });
720
+ return;
721
+ }
722
+ await saveProviderConfigToDB(normalizedConfig);
723
+ await aiProviderInitService.switchProvider(normalizedConfig);
724
+ if (packageSettings) {
725
+ const updatedSettings = {
726
+ ...packageSettings,
727
+ aiProvider: normalizedConfig
728
+ };
729
+ if (normalizedConfig.defaultModel) {
730
+ updatedSettings.defaultModel = normalizedConfig.defaultModel;
731
+ }
732
+ usePackageSettingsStore.setState({ settings: updatedSettings });
733
+ }
734
+ setProviderConfig(applyDefaultModel(normalizedConfig));
735
+ setSaveResult({ state: "success", message: "Saved. Chat now uses this provider." });
736
+ debugLogger.info("Provider configuration saved and switched", {
737
+ type: normalizedConfig.type
738
+ });
739
+ } catch (error) {
740
+ debugLogger.error("Failed to save provider configuration:", { error });
741
+ setSaveResult({
742
+ state: "error",
743
+ message: `Couldn't save or switch provider: ${error instanceof Error ? error.message : "unknown error"}`
744
+ });
745
+ }
746
+ };
747
+ const handleTestProviderConnection = async () => {
748
+ setTestResult({ state: "pending" });
749
+ try {
750
+ const testConfig = sanitizeConfigForSave(providerConfig);
751
+ const normalizedTestConfig = convertAnthropicConfig(testConfig) || testConfig;
752
+ const testProvider = AIProviderFactory.createProvider(normalizedTestConfig);
753
+ const result = await testProvider.validateServiceAvailability({ timeoutMs: 1e4 });
754
+ if (result.isAvailable) {
755
+ setTestResult({
756
+ state: "success",
757
+ message: "Connected. The provider responded."
758
+ });
759
+ } else {
760
+ setTestResult({
761
+ state: "error",
762
+ message: "Couldn't reach the provider. Check the URL and key."
763
+ });
764
+ }
765
+ } catch (error) {
766
+ debugLogger.error("Provider connection test failed:", { error });
767
+ setTestResult({
768
+ state: "error",
769
+ message: `Connection test failed${error instanceof Error && error.message ? `: ${error.message}` : "."}`
770
+ });
771
+ }
772
+ };
773
+ const resultColor = (state) => state === "success" ? "success.main" : state === "error" ? "error.main" : "text.secondary";
774
+ return /* @__PURE__ */ jsxs2(Box2, { children: [
775
+ /* @__PURE__ */ jsxs2(Box2, { children: [
776
+ currentProvider ? /* @__PURE__ */ jsxs2(
777
+ Paper,
778
+ {
779
+ variant: "outlined",
780
+ sx: { p: { xs: 1.5, sm: 1.75 }, mb: { xs: 1.5, md: 1.75 } },
781
+ children: [
782
+ /* @__PURE__ */ jsx2(
783
+ Typography2,
784
+ {
785
+ variant: "overline",
786
+ sx: {
787
+ fontWeight: 700,
788
+ color: "text.secondary",
789
+ letterSpacing: 0.6,
790
+ display: "block",
791
+ mb: 1
792
+ },
793
+ children: "Current provider"
794
+ }
795
+ ),
796
+ /* @__PURE__ */ jsxs2(
797
+ Box2,
798
+ {
799
+ sx: {
800
+ display: "flex",
801
+ alignItems: "center",
802
+ gap: 1.5,
803
+ flexWrap: "wrap",
804
+ minWidth: 0
805
+ },
806
+ children: [
807
+ /* @__PURE__ */ jsx2(
808
+ Chip2,
809
+ {
810
+ label: currentProvider.getProviderType().toUpperCase(),
811
+ color: "primary",
812
+ variant: "outlined",
813
+ size: "small"
814
+ }
815
+ ),
816
+ /* @__PURE__ */ jsx2(
817
+ Typography2,
818
+ {
819
+ variant: "body2",
820
+ color: "text.secondary",
821
+ sx: { wordBreak: "break-all", minWidth: 0 },
822
+ children: currentProviderConfig?.baseUrl || currentProviderConfig?.gatewayUrl || "No URL configured"
823
+ }
824
+ )
825
+ ]
826
+ }
827
+ )
828
+ ]
829
+ }
830
+ ) : /* @__PURE__ */ jsx2(Box2, { sx: { mb: { xs: 1.5, md: 1.75 } }, children: /* @__PURE__ */ jsx2(
831
+ ManagementEmptyState,
832
+ {
833
+ compact: true,
834
+ icon: /* @__PURE__ */ jsx2(Settings, {}),
835
+ title: "No provider configured",
836
+ description: "A provider is the model service Bandit sends chat requests to, such as the Bandit gateway, Ollama or OpenAI. Chat won't answer until one is saved.",
837
+ action: /* @__PURE__ */ jsx2(Button, { variant: "contained", onClick: revealProviderForm, children: "Configure a provider" })
838
+ }
839
+ ) }),
840
+ /* @__PURE__ */ jsxs2(
841
+ Paper,
842
+ {
843
+ ref: formRef,
844
+ variant: "outlined",
845
+ sx: {
846
+ p: { xs: 1.5, sm: 1.75 },
847
+ mb: { xs: 1.5, md: 1.75 },
848
+ scrollMarginTop: 16
849
+ },
850
+ children: [
851
+ /* @__PURE__ */ jsx2(
852
+ Typography2,
853
+ {
854
+ variant: "overline",
855
+ sx: {
856
+ fontWeight: 700,
857
+ color: "text.secondary",
858
+ letterSpacing: 0.6,
859
+ display: "block"
860
+ },
861
+ children: "Provider configuration"
862
+ }
863
+ ),
864
+ /* @__PURE__ */ jsx2(Typography2, { variant: "body2", color: "text.secondary", sx: { mb: 2 }, children: "Changes here apply only when you select Save and switch provider." }),
865
+ /* @__PURE__ */ jsxs2(Box2, { children: [
866
+ /* @__PURE__ */ jsxs2(
867
+ TextField,
868
+ {
869
+ label: "Provider type",
870
+ select: true,
871
+ inputRef: providerTypeRef,
872
+ value: providerConfig.type,
873
+ onChange: (e) => handleProviderTypeChange(e.target.value),
874
+ fullWidth: true,
875
+ sx: { mb: 3 },
876
+ children: [
877
+ /* @__PURE__ */ jsx2(MenuItem, { value: "gateway", children: "Gateway (Recommended)" }),
878
+ /* @__PURE__ */ jsx2(MenuItem, { value: "ollama", children: "Ollama" }),
879
+ /* @__PURE__ */ jsx2(MenuItem, { value: "openai", children: "OpenAI" }),
880
+ /* @__PURE__ */ jsx2(MenuItem, { value: "azure-openai", children: "Azure OpenAI" }),
881
+ /* @__PURE__ */ jsx2(MenuItem, { value: "bandit", children: "Bandit AI" }),
882
+ /* @__PURE__ */ jsx2(MenuItem, { value: "xai", children: "xAI" }),
883
+ /* @__PURE__ */ jsx2(MenuItem, { value: "playground", children: "Playground (Mock Demo)" })
884
+ ]
885
+ }
886
+ ),
887
+ providerConfig.type === "gateway" && /* @__PURE__ */ jsxs2(Box2, { children: [
888
+ /* @__PURE__ */ jsx2(
889
+ TextField,
890
+ {
891
+ label: "Gateway URL",
892
+ value: providerConfig.gatewayUrl || "",
893
+ onChange: (e) => setProviderConfig({ ...providerConfig, gatewayUrl: e.target.value }),
894
+ fullWidth: true,
895
+ sx: { mb: 2 },
896
+ placeholder: "https://your-gateway-api.com"
897
+ }
898
+ ),
899
+ /* @__PURE__ */ jsxs2(
900
+ TextField,
901
+ {
902
+ label: "Backend provider",
903
+ select: true,
904
+ value: providerConfig.provider || "openai",
905
+ onChange: (e) => setProviderConfig({
906
+ ...providerConfig,
907
+ provider: e.target.value
908
+ }),
909
+ fullWidth: true,
910
+ sx: { mb: 2 },
911
+ children: [
912
+ /* @__PURE__ */ jsx2(MenuItem, { value: "openai", children: "OpenAI" }),
913
+ /* @__PURE__ */ jsx2(MenuItem, { value: "azure-openai", children: "Azure OpenAI" }),
914
+ /* @__PURE__ */ jsx2(MenuItem, { value: "anthropic", children: "Anthropic" }),
915
+ /* @__PURE__ */ jsx2(MenuItem, { value: "ollama", children: "Ollama" }),
916
+ /* @__PURE__ */ jsx2(MenuItem, { value: "bandit", children: "Bandit AI" }),
917
+ /* @__PURE__ */ jsx2(MenuItem, { value: "xai", children: "xAI" })
918
+ ]
919
+ }
920
+ )
921
+ ] }),
922
+ providerConfig.type === "ollama" && /* @__PURE__ */ jsx2(
923
+ TextField,
924
+ {
925
+ label: "Ollama URL",
926
+ value: providerConfig.baseUrl || "",
927
+ onChange: (e) => setProviderConfig({ ...providerConfig, baseUrl: e.target.value }),
928
+ fullWidth: true,
929
+ placeholder: "http://localhost:11434"
930
+ }
931
+ ),
932
+ providerConfig.type === "bandit" && /* @__PURE__ */ jsxs2(Box2, { children: [
933
+ /* @__PURE__ */ jsx2(
934
+ TextField,
935
+ {
936
+ label: "API base URL",
937
+ value: providerConfig.baseUrl || "",
938
+ onChange: (e) => setProviderConfig((prev) => ({
939
+ ...prev,
940
+ baseUrl: e.target.value
941
+ })),
942
+ fullWidth: true,
943
+ sx: { mb: 2 },
944
+ placeholder: "https://api.burtson.ai",
945
+ helperText: "Defaults to https://api.burtson.ai"
946
+ }
947
+ ),
948
+ /* @__PURE__ */ jsx2(
949
+ TextField,
950
+ {
951
+ label: "API key",
952
+ type: "password",
953
+ value: providerConfig.apiKey || "",
954
+ onChange: (e) => setProviderConfig((prev) => ({
955
+ ...prev,
956
+ apiKey: e.target.value
957
+ })),
958
+ fullWidth: true,
959
+ sx: { mb: 2 },
960
+ placeholder: "bai_..."
961
+ }
962
+ ),
963
+ /* @__PURE__ */ jsx2(
964
+ TextField,
965
+ {
966
+ label: "Default model ID",
967
+ value: providerConfig.defaultModel || "",
968
+ onChange: (e) => setProviderConfig((prev) => ({
969
+ ...prev,
970
+ defaultModel: e.target.value
971
+ })),
972
+ fullWidth: true,
973
+ placeholder: "bandit-core-1",
974
+ helperText: "Example: bandit-core-1 (Bandit Core canonical alias)."
975
+ }
976
+ )
977
+ ] }),
978
+ providerConfig.type === "openai" && /* @__PURE__ */ jsxs2(Box2, { children: [
979
+ /* @__PURE__ */ jsx2(
980
+ TextField,
981
+ {
982
+ label: "API base URL",
983
+ value: providerConfig.baseUrl || "",
984
+ onChange: (e) => setProviderConfig((prev) => ({
985
+ ...prev,
986
+ baseUrl: e.target.value
987
+ })),
988
+ fullWidth: true,
989
+ sx: { mb: 2 },
990
+ placeholder: "https://api.openai.com/v1"
991
+ }
992
+ ),
993
+ /* @__PURE__ */ jsx2(
994
+ TextField,
995
+ {
996
+ label: "API key",
997
+ type: "password",
998
+ value: providerConfig.apiKey || "",
999
+ onChange: (e) => setProviderConfig((prev) => ({
1000
+ ...prev,
1001
+ apiKey: e.target.value
1002
+ })),
1003
+ fullWidth: true,
1004
+ sx: { mb: 2 },
1005
+ placeholder: "sk-..."
1006
+ }
1007
+ ),
1008
+ /* @__PURE__ */ jsx2(
1009
+ TextField,
1010
+ {
1011
+ label: "Default model ID",
1012
+ value: providerConfig.defaultModel || "",
1013
+ onChange: (e) => setProviderConfig((prev) => ({
1014
+ ...prev,
1015
+ defaultModel: e.target.value
1016
+ })),
1017
+ fullWidth: true,
1018
+ placeholder: "gpt-4o-mini",
1019
+ helperText: "Example: gpt-4o-mini, gpt-4.1, gpt-3.5-turbo"
1020
+ }
1021
+ )
1022
+ ] }),
1023
+ providerConfig.type === "azure-openai" && /* @__PURE__ */ jsxs2(Box2, { children: [
1024
+ /* @__PURE__ */ jsx2(
1025
+ TextField,
1026
+ {
1027
+ label: "Azure endpoint",
1028
+ value: providerConfig.baseUrl || "",
1029
+ onChange: (e) => setProviderConfig({ ...providerConfig, baseUrl: e.target.value }),
1030
+ fullWidth: true,
1031
+ sx: { mb: 2 },
1032
+ placeholder: "https://your-resource.openai.azure.com"
1033
+ }
1034
+ ),
1035
+ /* @__PURE__ */ jsx2(
1036
+ TextField,
1037
+ {
1038
+ label: "API key",
1039
+ type: "password",
1040
+ value: providerConfig.apiKey || "",
1041
+ onChange: (e) => setProviderConfig({ ...providerConfig, apiKey: e.target.value }),
1042
+ fullWidth: true,
1043
+ sx: { mb: 2 }
1044
+ }
1045
+ ),
1046
+ /* @__PURE__ */ jsx2(
1047
+ TextField,
1048
+ {
1049
+ label: "API version",
1050
+ value: providerConfig.apiVersion || "",
1051
+ onChange: (e) => setProviderConfig({ ...providerConfig, apiVersion: e.target.value }),
1052
+ fullWidth: true,
1053
+ sx: { mb: 2 },
1054
+ placeholder: "2024-02-01"
1055
+ }
1056
+ ),
1057
+ /* @__PURE__ */ jsx2(
1058
+ TextField,
1059
+ {
1060
+ label: "Deployment name",
1061
+ value: providerConfig.deploymentName || "",
1062
+ onChange: (e) => setProviderConfig({
1063
+ ...providerConfig,
1064
+ deploymentName: e.target.value
1065
+ }),
1066
+ fullWidth: true,
1067
+ placeholder: "gpt-4"
1068
+ }
1069
+ )
1070
+ ] }),
1071
+ providerConfig.type === "xai" && /* @__PURE__ */ jsxs2(Box2, { children: [
1072
+ /* @__PURE__ */ jsx2(
1073
+ TextField,
1074
+ {
1075
+ label: "API base URL",
1076
+ value: providerConfig.baseUrl || "",
1077
+ onChange: (e) => setProviderConfig((prev) => ({
1078
+ ...prev,
1079
+ baseUrl: e.target.value
1080
+ })),
1081
+ fullWidth: true,
1082
+ sx: { mb: 2 },
1083
+ placeholder: "https://api.x.ai/v1"
1084
+ }
1085
+ ),
1086
+ /* @__PURE__ */ jsx2(
1087
+ TextField,
1088
+ {
1089
+ label: "API key",
1090
+ type: "password",
1091
+ value: providerConfig.apiKey || "",
1092
+ onChange: (e) => setProviderConfig((prev) => ({
1093
+ ...prev,
1094
+ apiKey: e.target.value
1095
+ })),
1096
+ fullWidth: true,
1097
+ sx: { mb: 2 },
1098
+ placeholder: "xai-..."
1099
+ }
1100
+ ),
1101
+ /* @__PURE__ */ jsx2(
1102
+ TextField,
1103
+ {
1104
+ label: "Default model ID",
1105
+ value: providerConfig.defaultModel || "",
1106
+ onChange: (e) => setProviderConfig((prev) => ({
1107
+ ...prev,
1108
+ defaultModel: e.target.value
1109
+ })),
1110
+ fullWidth: true,
1111
+ placeholder: "grok-beta",
1112
+ helperText: "Example: grok-beta, grok-2, grok-vision-beta"
1113
+ }
1114
+ )
1115
+ ] }),
1116
+ providerConfig.type === "anthropic" && /* @__PURE__ */ jsx2(Alert, { severity: "warning", sx: { mt: 2 }, children: "Anthropic is only supported through the Bandit Gateway provider. Please switch to Gateway and select Anthropic as the backend service." }),
1117
+ /* @__PURE__ */ jsxs2(Box2, { sx: { display: "flex", flexWrap: "wrap", gap: 1.5, mt: 3 }, children: [
1118
+ /* @__PURE__ */ jsx2(
1119
+ Button,
1120
+ {
1121
+ variant: "outlined",
1122
+ onClick: handleTestProviderConnection,
1123
+ disabled: !providerConfig.type || testResult.state === "pending",
1124
+ startIcon: testResult.state === "pending" ? /* @__PURE__ */ jsx2(CircularProgress, { size: 14, color: "inherit" }) : void 0,
1125
+ children: testResult.state === "pending" ? "Testing\u2026" : "Test connection"
1126
+ }
1127
+ ),
1128
+ /* @__PURE__ */ jsx2(
1129
+ Button,
1130
+ {
1131
+ variant: "contained",
1132
+ onClick: handleSaveProviderConfig,
1133
+ disabled: !providerConfig.type || saveResult.state === "pending",
1134
+ startIcon: saveResult.state === "pending" ? /* @__PURE__ */ jsx2(CircularProgress, { size: 14, color: "inherit" }) : void 0,
1135
+ children: saveResult.state === "pending" ? "Saving\u2026" : "Save & switch provider"
1136
+ }
1137
+ )
1138
+ ] }),
1139
+ (testResult.state === "success" || testResult.state === "error" || saveResult.state === "success" || saveResult.state === "error") && /* @__PURE__ */ jsxs2(Stack, { spacing: 0.5, sx: { mt: 1.5 }, role: "status", "aria-live": "polite", children: [
1140
+ (testResult.state === "success" || testResult.state === "error") && /* @__PURE__ */ jsx2(
1141
+ Typography2,
1142
+ {
1143
+ variant: "body2",
1144
+ sx: { color: resultColor(testResult.state) },
1145
+ "data-testid": "provider-test-result",
1146
+ children: testResult.message
1147
+ }
1148
+ ),
1149
+ (saveResult.state === "success" || saveResult.state === "error") && /* @__PURE__ */ jsx2(
1150
+ Typography2,
1151
+ {
1152
+ variant: "body2",
1153
+ sx: { color: resultColor(saveResult.state) },
1154
+ "data-testid": "provider-save-result",
1155
+ children: saveResult.message
1156
+ }
1157
+ )
1158
+ ] })
1159
+ ] })
1160
+ ]
1161
+ }
1162
+ ),
1163
+ /* @__PURE__ */ jsx2(
1164
+ Typography2,
1165
+ {
1166
+ variant: "caption",
1167
+ color: "text.secondary",
1168
+ sx: { display: "block", mt: 0.5, lineHeight: 1.5 },
1169
+ children: "Gateway is recommended for production. It routes requests through your backend, keeps API keys server-side, and adds rate limiting and monitoring."
1170
+ }
1171
+ )
1172
+ ] }),
1173
+ /* @__PURE__ */ jsx2(
1174
+ Snackbar,
1175
+ {
1176
+ open: showSnackbar,
1177
+ autoHideDuration: 6e3,
1178
+ onClose: () => setShowSnackbar(false),
1179
+ anchorOrigin: { vertical: "bottom", horizontal: "left" },
1180
+ children: /* @__PURE__ */ jsx2(
1181
+ Alert,
1182
+ {
1183
+ onClose: () => setShowSnackbar(false),
1184
+ severity: snackbarSeverity,
1185
+ variant: "filled",
1186
+ sx: { width: "100%" },
1187
+ children: snackbarMessage
1188
+ }
1189
+ )
1190
+ }
1191
+ )
1192
+ ] });
1193
+ };
1194
+
1195
+ export {
1196
+ aiProviderInitService,
1197
+ useGatewayHealth,
1198
+ useGatewayModels,
1199
+ useGatewayMemory,
1200
+ ServiceStatusCard,
1201
+ ProviderTab
1202
+ };
1203
+ //# sourceMappingURL=chunk-2THUYNVU.mjs.map