@iblai/iblai-js 2.11.1 → 2.11.2

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.
@@ -192840,111 +192840,6 @@ function CreateApiModal({ isOpen, onClose, tenantKey, username }) {
192840
192840
  : t('apiTabLabels.createModal.submit') })) }) })] })] }) }), apiKey && jsx(ApiKeyModal, { isOpen: true, onClose: () => setApiKey(null), apiKey: apiKey })] }));
192841
192841
  }
192842
192842
 
192843
- /**
192844
- * Small pure utilities colocated with the LLM tab.
192845
- * Ported from standalone's `@/lib/utils` and `@/lib/error`.
192846
- */
192847
- // Canonical provider name for any label — a backend key ("azure_openai"), a
192848
- // human label ("Microsoft"), or a local model's provider ("Meta"). Folds every
192849
- // alias onto one identity so the same provider from different sources compares
192850
- // equal. See getProviderName.
192851
- const PROVIDER_NAME_BY_ALIAS = {
192852
- microsoft: 'azure_openai',
192853
- azureopenai: 'azure_openai',
192854
- openai: 'openai',
192855
- google: 'google',
192856
- gemini: 'google',
192857
- meta: 'llama',
192858
- metallama: 'llama',
192859
- llama: 'llama',
192860
- mistral: 'mistral',
192861
- mistralai: 'mistral',
192862
- nvidia: 'nvidia',
192863
- iblchatnvidia: 'nvidia',
192864
- deepseek: 'deepseek',
192865
- anthropic: 'anthropic',
192866
- iblchatanthropic: 'anthropic',
192867
- claude: 'anthropic',
192868
- groq: 'groq',
192869
- perplexity: 'perplexity',
192870
- xai: 'xai',
192871
- bedrock: 'bedrock',
192872
- amazonbedrock: 'bedrock',
192873
- amazon: 'bedrock',
192874
- iblchatbedrock: 'bedrock',
192875
- alibaba: 'alibaba',
192876
- qwen: 'alibaba',
192877
- ibm: 'ibm',
192878
- granite: 'ibm',
192879
- // ibl.ai's own hosted provider. The backend key is `iblai` (NameEnum.IBLAI);
192880
- // the extra aliases cover the `IBLChat<Vendor>` house spelling and the
192881
- // dotted/spaced brand forms, which all normalize to one of these.
192882
- iblai: 'iblai',
192883
- ibl: 'iblai',
192884
- iblchatibl: 'iblai',
192885
- iblchatiblai: 'iblai',
192886
- };
192887
- /**
192888
- * Canonical provider name for any label, case- and punctuation-insensitive.
192889
- * Folds backend keys and human/display labels onto one identity — e.g.
192890
- * `getProviderName('Microsoft') === 'azure_openai'`,
192891
- * `getProviderName('Meta') === 'llama'`, `getProviderName('Google') === 'google'`.
192892
- * Unknown providers return their normalized (lowercased, alphanumeric-only) form.
192893
- *
192894
- * This is the single source of truth for provider identity, which matters as
192895
- * soon as on-device models enter the picture: the local catalog labels its
192896
- * providers the human way ("Meta", "Microsoft", "Alibaba") while the backend LLM
192897
- * list uses wire keys ("llama", "azure_openai"). Comparing the two raw would
192898
- * render a duplicate card for the same vendor.
192899
- */
192900
- function getProviderName(llmProvider) {
192901
- var _a;
192902
- const normalized = (llmProvider !== null && llmProvider !== void 0 ? llmProvider : '').toLowerCase().replace(/[^a-z0-9]/g, '');
192903
- return (_a = PROVIDER_NAME_BY_ALIAS[normalized]) !== null && _a !== void 0 ? _a : normalized;
192904
- }
192905
-
192906
- // Logo + display name keyed by canonical provider name (see getProviderName).
192907
- const PROVIDER_DETAILS_BY_NAME = {
192908
- groq: { logo: '/llm-groq-provider.png', name: 'Groq' },
192909
- nvidia: { logo: '/llm-nvidia-provider.webp', name: 'NVIDIA' },
192910
- azure_openai: { logo: '/llm-microsoft-provider.png', name: 'Microsoft' },
192911
- openai: { logo: '/llm-openai-provider-2.svg', name: 'OpenAI' },
192912
- mistral: { logo: '/llm-mistral-provider.jpeg', name: 'Mistral' },
192913
- google: { logo: '/llm-google-provider.svg', name: 'Google' },
192914
- llama: { logo: '/llm-llama-provider.jpeg', name: 'Meta' },
192915
- anthropic: { logo: '/llm-claude-provider.png', name: 'Anthropic' },
192916
- perplexity: { logo: '/llm-perplexity-provider.webp', name: 'Perplexity' },
192917
- deepseek: { logo: '/llm-deepseek-provider.png', name: 'DeepSeek' },
192918
- xai: { logo: '/llm-xai-provider.jpg', name: 'xAI' },
192919
- bedrock: { logo: '/llm-amazon-provider.png', name: 'Amazon' },
192920
- alibaba: { logo: '/llm-alibaba-provider.png', name: 'Alibaba' },
192921
- ibm: { logo: '/llm-ibm-provider.png', name: 'IBM' },
192922
- iblai: { logo: '/llm-iblai-provider.png', name: 'ibl.ai' },
192923
- };
192924
- /**
192925
- * Returns the canonical logo path and display name for an LLM provider.
192926
- *
192927
- * Resolution goes through {@link getProviderName}, so a backend wire key
192928
- * (`azure_openai`), a human label (`Microsoft`) and an on-device catalog's
192929
- * provider string (`Meta`, `Alibaba`) all land on the same entry — the provider
192930
- * grid merges cloud and on-device providers into one list, so the two spellings
192931
- * have to agree.
192932
- *
192933
- * `logo` is a static-root path (e.g. `/llm-amazon-provider.png`); the package
192934
- * ships the canonical assets under `@iblai/web-containers/public/` — copy them
192935
- * into your app's static-root (`public/` for Next.js) so the paths resolve.
192936
- */
192937
- function getLLMProviderDetails(llmProvider, llmName) {
192938
- var _a;
192939
- const name = getProviderName(llmProvider);
192940
- // OpenAI and Google use a model-specific logo when a concrete model is named.
192941
- if (name === 'openai' && llmName)
192942
- return { logo: '/llm-openai-provider.jpg', name: 'OpenAI' };
192943
- if (name === 'google' && llmName)
192944
- return { logo: '/llm-gemini-provider.png', name: 'Google' };
192945
- return (_a = PROVIDER_DETAILS_BY_NAME[name]) !== null && _a !== void 0 ? _a : { logo: '/llm-generic-provider.png', name: llmProvider };
192946
- }
192947
-
192948
192843
  // Dynamic schema based on selected provider
192949
192844
  const createDynamicSchema$1 = (schemaFields) => {
192950
192845
  const schemaObj = {
@@ -193173,14 +193068,9 @@ function CreateLLMModal({ isOpen, onClose, tenantKey, onSuccess }) {
193173
193068
  }
193174
193069
  }, children: [jsx(SelectTrigger, { children: jsx(SelectValue, { placeholder: t('llmsTabCreateLlmModal.selectProviderPlaceholder') }) }), jsx(SelectContent, { children: availableProviders.map((provider) => {
193175
193070
  var _a, _b;
193176
- // Prefer the logo the API ships; fall back to the
193177
- // bundled provider artwork so known providers (ibl.ai
193178
- // included) never degrade to a generic key emoji.
193179
- const providerLogo = ((_a = provider === null || provider === void 0 ? void 0 : provider.service_info) === null || _a === void 0 ? void 0 : _a.logo) ||
193180
- getLLMProviderDetails(provider.name).logo;
193181
- const providerName = ((_b = provider === null || provider === void 0 ? void 0 : provider.service_info) === null || _b === void 0 ? void 0 : _b.display_name) ||
193182
- provider.name;
193183
- return (jsx(SelectItem, { value: provider.name, children: jsxs("div", { className: "flex items-center gap-2", children: [jsx("img", { src: providerLogo, alt: provider.name, className: "w-4 h-4 rounded-sm object-contain" }), providerName || provider.name] }) }, provider.name));
193071
+ const providerLogo = (_a = provider.service_info) === null || _a === void 0 ? void 0 : _a.logo;
193072
+ const providerName = ((_b = provider.service_info) === null || _b === void 0 ? void 0 : _b.display_name) || provider.name;
193073
+ return (jsx(SelectItem, { value: provider.name, children: jsxs("div", { className: "flex items-center gap-2", children: [providerLogo ? (jsx("img", { src: providerLogo, alt: providerName, className: "w-4 h-4 rounded-sm object-contain" })) : (jsx("span", { children: "\uD83D\uDD11" })), providerName] }) }, provider.name));
193184
193074
  }) })] }), shouldShowError && (jsx("p", { className: "text-red-500 text-xs", children: (_b = field.state.meta.errors[0]) === null || _b === void 0 ? void 0 : _b.message }))] }));
193185
193075
  } })), isCredentialsSchemaLoading && (jsxs("div", { className: "flex items-center justify-center py-4", children: [jsx(LoaderCircle, { className: "h-4 w-4 animate-spin mr-2" }), jsx("span", { className: "text-sm text-gray-600", children: t('llmsTabCreateLlmModal.loadingProviderConfigurations') })] })), showProviderForm && !isCredentialsSchemaLoading && !isAzureOpenAI && (jsx(Fragment$1, { children: schemaFields.map((fieldName) => (jsx(form.Field, { name: fieldName, children: (field) => {
193186
193076
  var _a, _b;
@@ -193267,26 +193157,18 @@ function IntegrationLLMsTab({ tenantKey }) {
193267
193157
  }
193268
193158
  };
193269
193159
  return (jsxs("div", { className: "space-y-4", children: [jsx(InfoBanner, { icon: jsx(Bot, { className: "w-5 h-5" }), children: t('llmsTabIntegrationLlmsTab.infoBannerText') }), jsx("div", { className: "bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg overflow-x-auto", children: jsxs("table", { className: "w-full text-sm min-w-[600px]", children: [jsx("thead", { className: "bg-gray-50 dark:bg-gray-700 border-b border-gray-200 dark:border-gray-600", children: jsxs("tr", { children: [jsx("th", { className: "px-6 py-3 text-left font-medium text-gray-500 dark:text-gray-400", children: t('llmsTabIntegrationLlmsTab.colName') }), jsx("th", { className: "px-6 py-3 text-left font-medium text-gray-500 dark:text-gray-400", children: t('llmsTabIntegrationLlmsTab.colKey') }), jsx("th", { className: "px-6 py-3 text-left font-medium text-gray-500 dark:text-gray-400 w-[50px]" })] }) }), jsx("tbody", { className: "divide-y divide-gray-200 dark:divide-gray-600", children: isMaskedCredentialsLoading ? (jsx("tr", { children: jsx("td", { colSpan: 3, className: "px-6 py-8 text-center text-sm text-gray-500", children: t('llmsTabIntegrationLlmsTab.loading') }) })) : maskedCredentials.length === 0 ? (jsx("tr", { children: jsx("td", { colSpan: 3, className: "px-6 py-8 text-center text-sm text-gray-500", children: t('llmsTabIntegrationLlmsTab.noLlmKeysFound') }) })) : (maskedCredentials.map((cred, idx) => {
193270
- var _a, _b, _c;
193160
+ var _a, _b, _c, _d, _e;
193271
193161
  const provider = ((_a = cred.service_info) === null || _a === void 0 ? void 0 : _a.display_name) || cred.name;
193272
- // Prefer the logo the API ships; fall back to the bundled
193273
- // provider artwork so known providers (ibl.ai included) never
193274
- // degrade to a generic key emoji.
193275
- let logoUrl;
193276
- let logoAlt;
193277
- if ((_b = cred.service_info) === null || _b === void 0 ? void 0 : _b.logo) {
193278
- logoUrl = cred.service_info.logo;
193279
- logoAlt = cred.service_info.display_name || cred.service_info.name;
193280
- }
193281
- else {
193282
- logoUrl = getLLMProviderDetails(cred.name).logo;
193283
- }
193162
+ // Logos are backend-owned (`service_info.logo`); no bundled fallback.
193163
+ const logoUrl = (_b = cred.service_info) === null || _b === void 0 ? void 0 : _b.logo;
193284
193164
  const isAzure = isAzureOpenAI(cred);
193285
193165
  const azureModels = isAzure ? getAzureModels(cred) : [];
193286
193166
  const isExpanded = expandedRows.has(cred.name);
193287
- return (jsxs(React__default.Fragment, { children: [jsxs("tr", { children: [jsx("td", { className: "px-6 py-4", children: jsxs("div", { className: "flex items-center gap-2", children: [jsx("img", { src: logoUrl, alt: logoAlt || provider || cred.name, className: "w-6 h-6 rounded-sm object-contain" }), jsx("span", { className: "font-normal text-gray-900 dark:text-gray-100", children: provider || cred.name }), isAzure && azureModels.length > 0 && (jsx("button", { type: "button", onClick: () => toggleRowExpansion(cred.name), className: "p-0.5 hover:bg-gray-100 rounded", children: isExpanded ? (jsx(ChevronUp, { className: "h-4 w-4 text-gray-500" })) : (jsx(ChevronDown, { className: "h-4 w-4 text-gray-500" })) }))] }) }), jsx("td", { className: "px-6 py-4 font-mono text-sm text-gray-900 dark:text-gray-100", children: isAzure ? (jsx("span", { className: "text-xs text-gray-500", children: t('llmsTabIntegrationLlmsTab.azureModelCount', {
193167
+ return (jsxs(React__default.Fragment, { children: [jsxs("tr", { children: [jsx("td", { className: "px-6 py-4", children: jsxs("div", { className: "flex items-center gap-2", children: [logoUrl ? (jsx("img", { src: logoUrl, alt: ((_c = cred.service_info) === null || _c === void 0 ? void 0 : _c.display_name) ||
193168
+ ((_d = cred.service_info) === null || _d === void 0 ? void 0 : _d.name) ||
193169
+ cred.name, className: "w-6 h-6 rounded-sm object-contain" })) : (jsx("span", { className: "text-lg", children: '🔑' })), jsx("span", { className: "font-normal text-gray-900 dark:text-gray-100", children: provider || cred.name }), isAzure && azureModels.length > 0 && (jsx("button", { type: "button", onClick: () => toggleRowExpansion(cred.name), className: "p-0.5 hover:bg-gray-100 rounded", children: isExpanded ? (jsx(ChevronUp, { className: "h-4 w-4 text-gray-500" })) : (jsx(ChevronDown, { className: "h-4 w-4 text-gray-500" })) }))] }) }), jsx("td", { className: "px-6 py-4 font-mono text-sm text-gray-900 dark:text-gray-100", children: isAzure ? (jsx("span", { className: "text-xs text-gray-500", children: t('llmsTabIntegrationLlmsTab.azureModelCount', {
193288
193170
  count: azureModels.length,
193289
- }) })) : (String((_c = cred === null || cred === void 0 ? void 0 : cred.value) === null || _c === void 0 ? void 0 : _c.key).slice(0, 15) || '') }), jsx("td", { className: "px-6 py-4", children: jsx(Button$1, { variant: "ghost", size: "sm", className: "cursor-pointer mr-2", disabled: isDeletingIntegration || isDeletingCredential, onClick: () => handleDeleteCredential(cred.name, cred.platform, false), children: jsx(Trash, { className: "h-4 w-4" }) }) })] }), isAzure &&
193171
+ }) })) : (String((_e = cred === null || cred === void 0 ? void 0 : cred.value) === null || _e === void 0 ? void 0 : _e.key).slice(0, 15) || '') }), jsx("td", { className: "px-6 py-4", children: jsx(Button$1, { variant: "ghost", size: "sm", className: "cursor-pointer mr-2", disabled: isDeletingIntegration || isDeletingCredential, onClick: () => handleDeleteCredential(cred.name, cred.platform, false), children: jsx(Trash, { className: "h-4 w-4" }) }) })] }), isAzure &&
193290
193172
  isExpanded &&
193291
193173
  azureModels.map((model, modelIdx) => (jsxs("tr", { children: [jsx("td", { className: "px-6 py-4 pl-12", children: jsx("span", { className: "text-sm text-gray-700", children: model.modelName.toLowerCase() }) }), jsx("td", { className: "px-6 py-4 font-mono text-sm text-gray-600", children: model.maskedKey.slice(0, 15) || '' }), jsx("td", { className: "px-6 py-4", children: jsx(Button$1, { variant: "ghost", size: "sm", className: "cursor-pointer", disabled: isDeletingIntegration || isDeletingCredential, onClick: () => handleDeleteCredential(cred.name, cred.platform, false, model.modelName), children: jsx(Trash, { className: "h-4 w-4" }) }) })] }, `${cred.name}-${model.modelName}-${modelIdx}`)))] }, cred.name + idx));
193292
193174
  })) })] }) })] }));
@@ -237407,6 +237289,41 @@ function SignupButton({ authUrl, tenantKey, redirectTo, label = 'Sign Up', onCli
237407
237289
  }
237408
237290
  SignupButton.displayName = 'SignupButton';
237409
237291
 
237292
+ /**
237293
+ * A provider's logo as the backend serves it. Provider naming and artwork are
237294
+ * owned by the API (`LLMResponse.logo`, `ExternalServiceInfo.logo`), so there
237295
+ * is no bundled fallback image: a provider without a logo renders a neutral
237296
+ * initial instead, keeping the card layout intact.
237297
+ */
237298
+ function LLMProviderLogo({ logo, label, alt = label, size = 32, className, }) {
237299
+ if (logo) {
237300
+ return (jsx(Image$4, { src: logo, alt: alt, className: cn('h-full w-full object-contain', className), width: size, height: size, loading: "lazy" }));
237301
+ }
237302
+ return (jsx("span", { role: "img", "aria-label": alt, "data-testid": "llm-provider-logo-placeholder", className: cn('flex h-full w-full items-center justify-center rounded bg-gray-100 text-xs font-semibold text-gray-500 uppercase', className), children: label.charAt(0) }));
237303
+ }
237304
+
237305
+ /**
237306
+ * Small pure utilities colocated with the LLM tab.
237307
+ * Ported from standalone's `@/lib/utils` and `@/lib/error`.
237308
+ */
237309
+ /**
237310
+ * The label a provider renders with: the backend's `display_name` when it
237311
+ * ships one, else its raw `name`. There is deliberately no frontend map behind
237312
+ * this — naming (and the logo next to it) is owned by the backend.
237313
+ */
237314
+ function llmProviderLabel(provider) {
237315
+ return provider.display_name || provider.name;
237316
+ }
237317
+ /**
237318
+ * The label a chat model renders with: the backend's `display_name` (`GPT-4.1
237319
+ * Mini`) when it ships one, else the raw wire key (`gpt-4.1-mini`). Same rule
237320
+ * as {@link llmProviderLabel}; every model list (LLM tab, sandbox) goes through
237321
+ * here so they never drift apart.
237322
+ */
237323
+ function llmModelLabel(model) {
237324
+ return model.display_name || model.llm_name;
237325
+ }
237326
+
237410
237327
  function OverlayModal({ open, onClose, title, description, className, children, }) {
237411
237328
  const t = useT();
237412
237329
  const [mounted, setMounted] = useState(false);
@@ -237442,15 +237359,22 @@ function canSwitchProvider(providers, name) {
237442
237359
  function LLMProviderModal({ isOpen, onClose, onSelect, llmProvider, isSelecting, mentorSettings, llms, }) {
237443
237360
  const t = useT();
237444
237361
  const [searchQuery, setSearchQuery] = useState('');
237362
+ const providerLabel = llmProviderLabel(llmProvider);
237445
237363
  const filteredLLMs = useMemo(() => {
237446
- return llmProvider === null || llmProvider === void 0 ? void 0 : llmProvider.chat_models.filter((llm) => llm.llm_name.toLowerCase().includes(searchQuery.toLowerCase()));
237364
+ const query = searchQuery.toLowerCase();
237365
+ // Match either label, same as the LLM tab's picker: rows show
237366
+ // `display_name`, but the raw key stays searchable.
237367
+ return llmProvider === null || llmProvider === void 0 ? void 0 : llmProvider.chat_models.filter((llm) => {
237368
+ var _a;
237369
+ return llm.llm_name.toLowerCase().includes(query) ||
237370
+ ((_a = llm.display_name) !== null && _a !== void 0 ? _a : '').toLowerCase().includes(query);
237371
+ });
237447
237372
  }, [searchQuery, llmProvider]);
237448
237373
  const switchLLMAllowed = canSwitchLLm(llmProvider);
237449
237374
  const switchProviderAllowed = canSwitchProvider(llms, llmProvider.name);
237450
237375
  return (jsxs(OverlayModal, { open: isOpen, onClose: onClose, title: t('clawSandboxLlmProviderModal.title'), description: t('clawSandboxLlmProviderModal.description', { providerName: llmProvider.name }), className: "max-w-4xl", children: [jsx("p", { className: "text-sm text-gray-600", children: t('clawSandboxLlmProviderModal.chooseLlmHint') }), jsxs("div", { className: "relative", children: [jsx(Search, { className: "absolute top-1/2 left-3 h-4 w-4 -translate-y-1/2 text-gray-400" }), jsx(Input, { placeholder: t('clawSandboxLlmProviderModal.searchPlaceholder'), className: "py-6 pl-9", value: searchQuery, onChange: (e) => setSearchQuery(e.target.value), autoComplete: "off", disabled: isSelecting })] }), jsx("div", { className: "grid max-h-[60vh] grid-cols-1 gap-4 overflow-y-auto pr-2 sm:grid-cols-2 lg:grid-cols-3", children: filteredLLMs.map((llm) => {
237451
237376
  const isActive = (mentorSettings === null || mentorSettings === void 0 ? void 0 : mentorSettings.llm_name) === llm.llm_name &&
237452
237377
  (mentorSettings === null || mentorSettings === void 0 ? void 0 : mentorSettings.llm_provider) === llmProvider.name;
237453
- const providerDetails = getLLMProviderDetails(llmProvider.name, llm.llm_name);
237454
237378
  const isDisabled = !switchLLMAllowed || isSelecting || isActive || !switchProviderAllowed;
237455
237379
  return (jsxs("button", { disabled: isDisabled, onClick: () => {
237456
237380
  onSelect(llmProvider.name, llm.llm_name);
@@ -237458,11 +237382,9 @@ function LLMProviderModal({ isOpen, onClose, onSelect, llmProvider, isSelecting,
237458
237382
  'cursor-not-allowed border-gray-200 bg-white': isDisabled,
237459
237383
  'hover:border-blue-500 hover:bg-blue-50': !isDisabled,
237460
237384
  'cursor-not-allowed border-blue-500 bg-blue-50': isActive,
237461
- }), children: [jsx("span", { className: "flex h-8 w-8 items-center justify-center overflow-hidden rounded-lg", children: jsx("img", { src: providerDetails.logo, alt: t('clawSandboxLlmProviderModal.providerIconAlt', {
237462
- providerName: providerDetails.name,
237463
- }), className: cn('h-full w-full object-contain', {
237464
- grayscale: isDisabled && !isActive,
237465
- }), width: 32, height: 32, loading: "lazy" }) }), jsx("span", { className: "text-left text-sm font-medium text-[#646464]", children: llm.llm_name })] }, llm.llm_name));
237385
+ }), children: [jsx("span", { className: "flex h-8 w-8 items-center justify-center overflow-hidden rounded-lg", children: jsx(LLMProviderLogo, { logo: llmProvider.logo, label: providerLabel, alt: t('clawSandboxLlmProviderModal.providerIconAlt', {
237386
+ providerName: providerLabel,
237387
+ }), className: cn({ grayscale: isDisabled && !isActive }) }) }), jsx("span", { className: "text-left text-sm font-medium text-[#646464]", children: llmModelLabel(llm) })] }, llm.llm_name));
237466
237388
  }) })] }));
237467
237389
  }
237468
237390
 
@@ -237633,6 +237555,13 @@ function SandboxConfig({ platformKey, mentorUniqueId, username }) {
237633
237555
  const availableLLMProviders = useMemo(() => (llmProviders !== null && llmProviders !== void 0 ? llmProviders : []), [llmProviders]);
237634
237556
  const filteredLLMProviders = useMemo(() => availableLLMProviders.filter((provider) => provider.name.toLowerCase().includes(llmSearchQuery.toLowerCase())), [availableLLMProviders, llmSearchQuery]);
237635
237557
  const currentModel = parseModelIdentifier(agentConfig === null || agentConfig === void 0 ? void 0 : agentConfig.model);
237558
+ const currentModelProvider = availableLLMProviders.find((provider) => provider.name === currentModel.provider);
237559
+ // Same backend `display_name` the pickers render, so the trigger reads
237560
+ // `GPT-4.1 Mini` rather than the `gpt-4.1-mini` wire key stored in `model`.
237561
+ const currentChatModel = currentModelProvider === null || currentModelProvider === void 0 ? void 0 : currentModelProvider.chat_models.find((llm) => llm.llm_name === currentModel.llmName);
237562
+ const currentModelLabel = currentChatModel
237563
+ ? llmModelLabel(currentChatModel)
237564
+ : currentModel.llmName || (agentConfig === null || agentConfig === void 0 ? void 0 : agentConfig.model);
237636
237565
  const isUpdatingLLM = isUpdatingAgent;
237637
237566
  // Push fails server-side with "No configuration to push" when every AgentConfig
237638
237567
  // field is empty. Detect that locally so we can deny the click and tell the user
@@ -237920,17 +237849,17 @@ function SandboxConfig({ platformKey, mentorUniqueId, username }) {
237920
237849
  }), children: ((_f = mentorConfig === null || mentorConfig === void 0 ? void 0 : mentorConfig.last_config_push_status) === null || _f === void 0 ? void 0 : _f.toLocaleLowerCase()) === 'success' ? (jsx(CircleCheck, { className: "h-4 w-4 text-green-600" })) : (jsx(CircleX, { className: "h-4 w-4 text-red-600" })) }) }), jsx(TooltipContent, { className: "ibl-tooltip-content", children: jsx("p", { className: "capitalize", children: mentorConfig.last_config_push_status }) })] }) }))] }), jsx(TooltipProvider, { children: jsxs(Tooltip$1, { children: [jsx(TooltipTrigger, { asChild: true, children: jsx("span", { className: "inline-flex", children: jsxs(Button$1, { variant: "outline", size: "sm", onClick: handlePush, disabled: isPushing || !hasAgentConfiguration, "aria-disabled": isPushing || !hasAgentConfiguration, children: [isPushing ? (jsx(LoaderCircle, { className: "h-4 w-4 animate-spin mr-2" })) : (jsx(RefreshCw, { className: "h-4 w-4 mr-2" })), t('clawSandboxSandboxConfig.push')] }) }) }), !hasAgentConfiguration && (jsx(TooltipContent, { className: "ibl-tooltip-content", children: jsx("p", { children: t('clawSandboxSandboxConfig.setAgentConfigBeforePushing') }) }))] }) })] }), jsxs("div", { className: "flex items-center justify-between rounded-lg border p-6", children: [jsxs("div", { className: "flex items-center gap-2", children: [jsx("span", { className: "text-sm font-medium text-gray-700", children: t('clawSandboxSandboxConfig.model') }), jsx(TooltipProvider, { children: jsxs(Tooltip$1, { children: [jsx(TooltipTrigger, { "aria-label": t('clawSandboxSandboxConfig.modelInfoAriaLabel'), children: jsx(Info$3, { className: "h-4 w-4 text-gray-400" }) }), jsx(TooltipContent, { className: "ibl-tooltip-content", children: jsx("p", { children: t('clawSandboxSandboxConfig.modelTooltip') }) })] }) })] }), jsx("div", { className: "flex items-center gap-3", children: (agentConfig === null || agentConfig === void 0 ? void 0 : agentConfig.model) ? (jsxs("button", { type: "button", className: "flex items-center gap-2 text-sm font-medium text-[#646464] transition-colors hover:text-[#484848] disabled:opacity-50", onClick: () => {
237921
237850
  setLlmSearchQuery('');
237922
237851
  setShowLLMPicker(true);
237923
- }, disabled: isUpdatingLLM, "aria-label": t('clawSandboxSandboxConfig.changeModel'), children: [jsx("span", { className: "flex h-6 w-6 flex-shrink-0 items-center justify-center rounded-full bg-white border", children: jsx("img", { src: getLLMProviderDetails(currentModel.provider, currentModel.llmName).logo, alt: t('clawSandboxSandboxConfig.providerLogoAlt', {
237852
+ }, disabled: isUpdatingLLM, "aria-label": t('clawSandboxSandboxConfig.changeModel'), children: [jsx("span", { className: "flex h-6 w-6 flex-shrink-0 items-center justify-center rounded-full bg-white border", children: jsx(LLMProviderLogo, { logo: currentModelProvider === null || currentModelProvider === void 0 ? void 0 : currentModelProvider.logo, label: currentModel.llmName || currentModel.provider, alt: t('clawSandboxSandboxConfig.providerLogoAlt', {
237924
237853
  name: currentModel.llmName || currentModel.provider,
237925
- }), className: "h-5 w-5 object-contain", width: 20, height: 20, loading: "lazy" }) }), jsx("span", { className: "max-w-[200px] truncate", children: currentModel.llmName || agentConfig.model }), jsx(ChevronDown, { className: "h-4 w-4 text-gray-500" })] })) : (jsx(Button$1, { variant: "outline", size: "sm", onClick: () => {
237854
+ }), size: 20, className: "h-5 w-5" }) }), jsx("span", { className: "max-w-[200px] truncate", children: currentModelLabel }), jsx(ChevronDown, { className: "h-4 w-4 text-gray-500" })] })) : (jsx(Button$1, { variant: "outline", size: "sm", onClick: () => {
237926
237855
  setLlmSearchQuery('');
237927
237856
  setShowLLMPicker(true);
237928
237857
  }, disabled: isUpdatingLLM, children: t('clawSandboxSandboxConfig.selectModel') })) })] }), jsxs(OverlayModal, { open: showDisconnectConfirm, onClose: () => setShowDisconnectConfirm(false), title: t('clawSandboxSandboxConfig.disconnectInstanceTitle'), className: "max-w-md", children: [jsx("p", { className: "text-sm text-gray-600", children: t('clawSandboxSandboxConfig.disconnectInstanceConfirm', {
237929
237858
  name: (_g = connectedInstance === null || connectedInstance === void 0 ? void 0 : connectedInstance.name) !== null && _g !== void 0 ? _g : mentorConfig.server_name,
237930
237859
  }) }), jsxs("div", { className: "flex justify-end gap-2 pt-2", children: [jsx(Button$1, { variant: "outline", onClick: () => setShowDisconnectConfirm(false), children: t('clawSandboxSandboxConfig.cancel') }), jsx(Button$1, { onClick: handleDisconnect, disabled: isDeleting, className: "bg-red-600 text-white hover:bg-red-700", children: isDeleting
237931
237860
  ? t('clawSandboxSandboxConfig.disconnecting')
237932
- : t('clawSandboxSandboxConfig.disconnect') })] })] }), showLLMPicker && !selectedLLMProvider && (jsx(OverlayModal, { open: !selectedLLMProvider, onClose: closeLLMPicker, title: t('clawSandboxSandboxConfig.selectProviderTitle'), description: t('clawSandboxSandboxConfig.selectProviderDescription'), className: "max-w-3xl", children: !resolvedUsername ? (jsx("p", { className: "py-6 text-center text-sm text-gray-500", children: t('clawSandboxSandboxConfig.noUsernameForProviders') })) : (jsxs(Fragment$1, { children: [jsxs("div", { className: "relative", children: [jsx(Search, { className: "absolute top-1/2 left-3 h-4 w-4 -translate-y-1/2 text-gray-500" }), jsx(Input, { id: "llm-search", name: "llm-search", type: "search", placeholder: t('clawSandboxSandboxConfig.searchProvidersPlaceholder'), className: "w-full py-6 pl-9", value: llmSearchQuery, onChange: (e) => setLlmSearchQuery(e.target.value) })] }), isLoadingLLMProviders ? (jsx("div", { className: "flex items-center justify-center py-10", children: jsx(LoaderCircle, { className: "h-5 w-5 animate-spin text-blue-500" }) })) : isLLMProvidersError ? (jsx("p", { className: "py-6 text-center text-sm text-red-500", children: t('clawSandboxSandboxConfig.failedToLoadProviders') })) : filteredLLMProviders.length === 0 ? (jsx("p", { className: "py-6 text-center text-sm text-gray-500", children: t('clawSandboxSandboxConfig.noProvidersAvailable') })) : (jsx("div", { className: "mt-2 grid max-h-[50vh] grid-cols-1 gap-4 overflow-y-auto pr-2 sm:grid-cols-2 lg:grid-cols-3", children: filteredLLMProviders.map((provider) => {
237933
- const providerDetails = getLLMProviderDetails(provider.name);
237861
+ : t('clawSandboxSandboxConfig.disconnect') })] })] }), showLLMPicker && (jsx(OverlayModal, { open: true, onClose: closeLLMPicker, title: t('clawSandboxSandboxConfig.selectProviderTitle'), description: t('clawSandboxSandboxConfig.selectProviderDescription'), className: "max-w-3xl", children: !resolvedUsername ? (jsx("p", { className: "py-6 text-center text-sm text-gray-500", children: t('clawSandboxSandboxConfig.noUsernameForProviders') })) : (jsxs(Fragment$1, { children: [jsxs("div", { className: "relative", children: [jsx(Search, { className: "absolute top-1/2 left-3 h-4 w-4 -translate-y-1/2 text-gray-500" }), jsx(Input, { id: "llm-search", name: "llm-search", type: "search", placeholder: t('clawSandboxSandboxConfig.searchProvidersPlaceholder'), className: "w-full py-6 pl-9", value: llmSearchQuery, onChange: (e) => setLlmSearchQuery(e.target.value) })] }), isLoadingLLMProviders ? (jsx("div", { className: "flex items-center justify-center py-10", children: jsx(LoaderCircle, { className: "h-5 w-5 animate-spin text-blue-500" }) })) : isLLMProvidersError ? (jsx("p", { className: "py-6 text-center text-sm text-red-500", children: t('clawSandboxSandboxConfig.failedToLoadProviders') })) : filteredLLMProviders.length === 0 ? (jsx("p", { className: "py-6 text-center text-sm text-gray-500", children: t('clawSandboxSandboxConfig.noProvidersAvailable') })) : (jsx("div", { className: "mt-2 grid max-h-[50vh] grid-cols-1 gap-4 overflow-y-auto pr-2 sm:grid-cols-2 lg:grid-cols-3", children: filteredLLMProviders.map((provider) => {
237862
+ const providerLabel = llmProviderLabel(provider);
237934
237863
  const isSelected = currentModel.provider === provider.name;
237935
237864
  return (jsxs("button", { type: "button", className: cn('flex cursor-pointer items-center gap-3 rounded-lg border border-gray-200 bg-white p-4 text-left shadow-sm transition-shadow hover:shadow-md', { 'border-blue-500': isSelected }), onClick: () => {
237936
237865
  setSelectedLLMProvider({
@@ -237943,10 +237872,10 @@ function SandboxConfig({ platformKey, mentorUniqueId, username }) {
237943
237872
  main_has_credentials: provider.main_has_credentials,
237944
237873
  can_use_main_keys: provider.can_use_main_keys,
237945
237874
  });
237946
- }, children: [jsx("div", { className: "h-8 w-8 flex-shrink-0", children: jsx("img", { src: providerDetails.logo, alt: t('clawSandboxSandboxConfig.providerLogoAlt', {
237947
- name: providerDetails.name,
237948
- }), className: "h-full w-full object-contain", width: 32, height: 32, loading: "lazy" }) }), jsx("span", { className: "text-sm font-medium text-gray-900", children: providerDetails.name })] }, provider.id));
237949
- }) }))] })) })), selectedLLMProvider && (jsx(LLMProviderModal, { isOpen: true, onClose: closeLLMPicker, onSelect: handleSelectLLM, llmProvider: selectedLLMProvider, isSelecting: isUpdatingLLM, mentorSettings: {
237875
+ }, children: [jsx("div", { className: "h-8 w-8 flex-shrink-0", children: jsx(LLMProviderLogo, { logo: provider.logo, label: providerLabel, alt: t('clawSandboxSandboxConfig.providerLogoAlt', {
237876
+ name: providerLabel,
237877
+ }) }) }), jsx("span", { className: "text-sm font-medium text-gray-900", children: providerLabel })] }, provider.id));
237878
+ }) }))] })) })), selectedLLMProvider && (jsx(LLMProviderModal, { isOpen: true, onClose: () => setSelectedLLMProvider(null), onSelect: handleSelectLLM, llmProvider: selectedLLMProvider, isSelecting: isUpdatingLLM, mentorSettings: {
237950
237879
  llm_name: currentModel.llmName,
237951
237880
  llm_provider: currentModel.provider,
237952
237881
  }, llms: availableLLMProviders }))] })) : null;
@@ -225376,217 +225376,6 @@ function CreateApiModal$1({ isOpen, onClose, tenantKey, username }) {
225376
225376
  : t('apiTabLabels.createModal.submit') })) }) })] })] }) }), apiKey && jsx(ApiKeyModal$1, { isOpen: true, onClose: () => setApiKey(null), apiKey: apiKey })] }));
225377
225377
  }
225378
225378
 
225379
- /**
225380
- * Small pure utilities colocated with the LLM tab.
225381
- * Ported from standalone's `@/lib/utils` and `@/lib/error`.
225382
- */
225383
- // Canonical provider name for any label — a backend key ("azure_openai"), a
225384
- // human label ("Microsoft"), or a local model's provider ("Meta"). Folds every
225385
- // alias onto one identity so the same provider from different sources compares
225386
- // equal. See getProviderName.
225387
- const PROVIDER_NAME_BY_ALIAS = {
225388
- microsoft: 'azure_openai',
225389
- azureopenai: 'azure_openai',
225390
- openai: 'openai',
225391
- google: 'google',
225392
- gemini: 'google',
225393
- meta: 'llama',
225394
- metallama: 'llama',
225395
- llama: 'llama',
225396
- mistral: 'mistral',
225397
- mistralai: 'mistral',
225398
- nvidia: 'nvidia',
225399
- iblchatnvidia: 'nvidia',
225400
- deepseek: 'deepseek',
225401
- anthropic: 'anthropic',
225402
- iblchatanthropic: 'anthropic',
225403
- claude: 'anthropic',
225404
- groq: 'groq',
225405
- perplexity: 'perplexity',
225406
- xai: 'xai',
225407
- bedrock: 'bedrock',
225408
- amazonbedrock: 'bedrock',
225409
- amazon: 'bedrock',
225410
- iblchatbedrock: 'bedrock',
225411
- alibaba: 'alibaba',
225412
- qwen: 'alibaba',
225413
- ibm: 'ibm',
225414
- granite: 'ibm',
225415
- // ibl.ai's own hosted provider. The backend key is `iblai` (NameEnum.IBLAI);
225416
- // the extra aliases cover the `IBLChat<Vendor>` house spelling and the
225417
- // dotted/spaced brand forms, which all normalize to one of these.
225418
- iblai: 'iblai',
225419
- ibl: 'iblai',
225420
- iblchatibl: 'iblai',
225421
- iblchatiblai: 'iblai',
225422
- };
225423
- /**
225424
- * Canonical provider name for any label, case- and punctuation-insensitive.
225425
- * Folds backend keys and human/display labels onto one identity — e.g.
225426
- * `getProviderName('Microsoft') === 'azure_openai'`,
225427
- * `getProviderName('Meta') === 'llama'`, `getProviderName('Google') === 'google'`.
225428
- * Unknown providers return their normalized (lowercased, alphanumeric-only) form.
225429
- *
225430
- * This is the single source of truth for provider identity, which matters as
225431
- * soon as on-device models enter the picture: the local catalog labels its
225432
- * providers the human way ("Meta", "Microsoft", "Alibaba") while the backend LLM
225433
- * list uses wire keys ("llama", "azure_openai"). Comparing the two raw would
225434
- * render a duplicate card for the same vendor.
225435
- */
225436
- function getProviderName(llmProvider) {
225437
- var _a;
225438
- const normalized = (llmProvider !== null && llmProvider !== void 0 ? llmProvider : '').toLowerCase().replace(/[^a-z0-9]/g, '');
225439
- return (_a = PROVIDER_NAME_BY_ALIAS[normalized]) !== null && _a !== void 0 ? _a : normalized;
225440
- }
225441
- /**
225442
- * Whether two Ollama model refs name the same model, comparing by base name and
225443
- * ignoring a trailing `:tag` (so "llama3.2" ≡ "llama3.2:latest", and
225444
- * "granite4.1:8b" ≡ "granite4.1"). The download state and Ollama's /api/tags may
225445
- * carry a tag suffix the catalog id doesn't (or vice-versa); an exact `===` then
225446
- * fails to recognize a row's own in-flight download — the row shows "Download"
225447
- * with no progress while the pull runs. Base-name matching is safe here because
225448
- * every catalog model has a unique base (llama3.2, qwen3, mistral, granite4.1,
225449
- * gpt-oss, gemma4), so it can never light up the wrong row.
225450
- */
225451
- function sameModel(a, b) {
225452
- const base = (ref) => (ref !== null && ref !== void 0 ? ref : '').toLowerCase().split(':')[0];
225453
- const ba = base(a);
225454
- return ba.length > 0 && ba === base(b);
225455
- }
225456
- /**
225457
- * Whether a catalog model id (e.g. "granite4.1:8b") is among the installed
225458
- * Ollama tags (e.g. "granite4.1:latest").
225459
- *
225460
- * Deliberately NOT the profile tab's `isModelInstalled`, which matches an exact
225461
- * tag or the `<id>:` prefix: that is one-directional and misses a catalog id
225462
- * that itself carries a tag ("granite4.1:8b" against an installed
225463
- * "granite4.1:latest"). The picker needs the symmetric base-name comparison of
225464
- * {@link sameModel} so a pulled model is recognized whichever side wears the tag.
225465
- */
225466
- function isLocalModelInstalled(modelId, installedTags) {
225467
- return !!(installedTags === null || installedTags === void 0 ? void 0 : installedTags.some((tag) => sameModel(tag, modelId)));
225468
- }
225469
- function canSwitchProvider(providers, name) {
225470
- const provider = providers.find((p) => p.name === name);
225471
- return !!(provider === null || provider === void 0 ? void 0 : provider.chat_models) && provider.chat_models.length > 0;
225472
- }
225473
- /**
225474
- * Whether we hold a usable LLM key for this provider — its own credentials, or
225475
- * the tenant's main keys when it is allowed to borrow them. An *absent*
225476
- * `main_has_credentials` means the backend did not report on the main keys and
225477
- * is treated as available (matching standalone's `@/lib/utils` and
225478
- * `claw-sandbox`'s copy); only an explicit `false` rules the main keys out.
225479
- */
225480
- function canSwitchLLm(llm) {
225481
- if (llm === null || llm === void 0 ? void 0 : llm.has_credentials) {
225482
- return true;
225483
- }
225484
- return ((llm === null || llm === void 0 ? void 0 : llm.can_use_main_keys) &&
225485
- ((llm === null || llm === void 0 ? void 0 : llm.main_has_credentials) === true || (llm === null || llm === void 0 ? void 0 : llm.main_has_credentials) === undefined));
225486
- }
225487
- /**
225488
- * Compares two raw provider names by the label the user actually sees on the
225489
- * card, not by the backend key — the two diverge often enough to matter
225490
- * (`bedrock` renders as "Amazon", `azure_openai` as "Microsoft", `iblai` as
225491
- * "ibl.ai"). Comparison is case-insensitive (`sensitivity: 'base'`) so casing
225492
- * never produces a surprising order. `getDetails` is the host-provided resolver
225493
- * the grid renders with, so the sort always matches what is on screen; unknown
225494
- * providers fall back to their raw name as the display name.
225495
- */
225496
- function compareLLMProvidersByDisplayName(a, b, getDetails) {
225497
- return getDetails(a).name.localeCompare(getDetails(b).name, undefined, {
225498
- sensitivity: 'base',
225499
- });
225500
- }
225501
- /**
225502
- * Whether the user can reach *any* model of this provider.
225503
- *
225504
- * This is exactly the provider-level half of the per-model `isDisabled` rule in
225505
- * `llm-tab/llm-provider-modal.tsx`: a model row there is disabled when
225506
- * `!canSwitchLLm(provider) || !canSwitchProvider(llms, provider.name)`, and both
225507
- * of those depend only on the provider — so when either fails, *every* model row
225508
- * of that provider is disabled. `canSwitchProvider` is just "has at least one
225509
- * `chat_models` entry", which is checked inline here to keep the helper pure and
225510
- * free of the surrounding provider list.
225511
- *
225512
- * `canSwitchLLm` can return `undefined` (its `can_use_main_keys` branch), hence
225513
- * the explicit `Boolean(...)`.
225514
- */
225515
- function canAccessProvider(provider) {
225516
- var _a, _b;
225517
- return Boolean(canSwitchLLm(provider !== null && provider !== void 0 ? provider : {})) && ((_b = (_a = provider === null || provider === void 0 ? void 0 : provider.chat_models) === null || _a === void 0 ? void 0 : _a.length) !== null && _b !== void 0 ? _b : 0) > 0;
225518
- }
225519
- /**
225520
- * Orders LLM provider cards as two alphabetical groups: providers the user can
225521
- * actually use (see {@link canAccessProvider}) first, then the ones they can't —
225522
- * each group sorted alphabetically by display name.
225523
- *
225524
- * The grouping predicate is deliberately the same one the grid grays cards with,
225525
- * so the two always line up: group 1 renders normally, group 2 renders grayed.
225526
- * Grouping on credentials alone would let a provider that has a key but ships no
225527
- * chat models sort into the "usable" group while rendering grayed.
225528
- *
225529
- * Pure and non-mutating: RTK Query results are frozen, so the input is copied
225530
- * before sorting. `Array.prototype.sort` is stable, so providers that tie on
225531
- * both group and display name keep their original relative order.
225532
- */
225533
- function sortLLMProvidersByCredentials(providers, getDetails) {
225534
- return [...providers].sort((a, b) => {
225535
- const aUsable = canAccessProvider(a);
225536
- const bUsable = canAccessProvider(b);
225537
- if (aUsable !== bUsable)
225538
- return aUsable ? -1 : 1;
225539
- return compareLLMProvidersByDisplayName(a.name, b.name, getDetails);
225540
- });
225541
- }
225542
- function extractErrorMessage$1(error, fallback) {
225543
- var _a, _b;
225544
- const apiError = error;
225545
- return ((_a = apiError === null || apiError === void 0 ? void 0 : apiError.data) === null || _a === void 0 ? void 0 : _a.error) || ((_b = apiError === null || apiError === void 0 ? void 0 : apiError.error) === null || _b === void 0 ? void 0 : _b.error) || fallback;
225546
- }
225547
-
225548
- // Logo + display name keyed by canonical provider name (see getProviderName).
225549
- const PROVIDER_DETAILS_BY_NAME = {
225550
- groq: { logo: '/llm-groq-provider.png', name: 'Groq' },
225551
- nvidia: { logo: '/llm-nvidia-provider.webp', name: 'NVIDIA' },
225552
- azure_openai: { logo: '/llm-microsoft-provider.png', name: 'Microsoft' },
225553
- openai: { logo: '/llm-openai-provider-2.svg', name: 'OpenAI' },
225554
- mistral: { logo: '/llm-mistral-provider.jpeg', name: 'Mistral' },
225555
- google: { logo: '/llm-google-provider.svg', name: 'Google' },
225556
- llama: { logo: '/llm-llama-provider.jpeg', name: 'Meta' },
225557
- anthropic: { logo: '/llm-claude-provider.png', name: 'Anthropic' },
225558
- perplexity: { logo: '/llm-perplexity-provider.webp', name: 'Perplexity' },
225559
- deepseek: { logo: '/llm-deepseek-provider.png', name: 'DeepSeek' },
225560
- xai: { logo: '/llm-xai-provider.jpg', name: 'xAI' },
225561
- bedrock: { logo: '/llm-amazon-provider.png', name: 'Amazon' },
225562
- alibaba: { logo: '/llm-alibaba-provider.png', name: 'Alibaba' },
225563
- ibm: { logo: '/llm-ibm-provider.png', name: 'IBM' },
225564
- iblai: { logo: '/llm-iblai-provider.png', name: 'ibl.ai' },
225565
- };
225566
- /**
225567
- * Returns the canonical logo path and display name for an LLM provider.
225568
- *
225569
- * Resolution goes through {@link getProviderName}, so a backend wire key
225570
- * (`azure_openai`), a human label (`Microsoft`) and an on-device catalog's
225571
- * provider string (`Meta`, `Alibaba`) all land on the same entry — the provider
225572
- * grid merges cloud and on-device providers into one list, so the two spellings
225573
- * have to agree.
225574
- *
225575
- * `logo` is a static-root path (e.g. `/llm-amazon-provider.png`); the package
225576
- * ships the canonical assets under `@iblai/web-containers/public/` — copy them
225577
- * into your app's static-root (`public/` for Next.js) so the paths resolve.
225578
- */
225579
- function getLLMProviderDetails(llmProvider, llmName) {
225580
- var _a;
225581
- const name = getProviderName(llmProvider);
225582
- // OpenAI and Google use a model-specific logo when a concrete model is named.
225583
- if (name === 'openai' && llmName)
225584
- return { logo: '/llm-openai-provider.jpg', name: 'OpenAI' };
225585
- if (name === 'google' && llmName)
225586
- return { logo: '/llm-gemini-provider.png', name: 'Google' };
225587
- return (_a = PROVIDER_DETAILS_BY_NAME[name]) !== null && _a !== void 0 ? _a : { logo: '/llm-generic-provider.png', name: llmProvider };
225588
- }
225589
-
225590
225379
  // Dynamic schema based on selected provider
225591
225380
  const createDynamicSchema$1 = (schemaFields) => {
225592
225381
  const schemaObj = {
@@ -225815,14 +225604,9 @@ function CreateLLMModal({ isOpen, onClose, tenantKey, onSuccess }) {
225815
225604
  }
225816
225605
  }, children: [jsx(SelectTrigger, { children: jsx(SelectValue, { placeholder: t('llmsTabCreateLlmModal.selectProviderPlaceholder') }) }), jsx(SelectContent, { children: availableProviders.map((provider) => {
225817
225606
  var _a, _b;
225818
- // Prefer the logo the API ships; fall back to the
225819
- // bundled provider artwork so known providers (ibl.ai
225820
- // included) never degrade to a generic key emoji.
225821
- const providerLogo = ((_a = provider === null || provider === void 0 ? void 0 : provider.service_info) === null || _a === void 0 ? void 0 : _a.logo) ||
225822
- getLLMProviderDetails(provider.name).logo;
225823
- const providerName = ((_b = provider === null || provider === void 0 ? void 0 : provider.service_info) === null || _b === void 0 ? void 0 : _b.display_name) ||
225824
- provider.name;
225825
- return (jsx(SelectItem, { value: provider.name, children: jsxs("div", { className: "flex items-center gap-2", children: [jsx("img", { src: providerLogo, alt: provider.name, className: "w-4 h-4 rounded-sm object-contain" }), providerName || provider.name] }) }, provider.name));
225607
+ const providerLogo = (_a = provider.service_info) === null || _a === void 0 ? void 0 : _a.logo;
225608
+ const providerName = ((_b = provider.service_info) === null || _b === void 0 ? void 0 : _b.display_name) || provider.name;
225609
+ return (jsx(SelectItem, { value: provider.name, children: jsxs("div", { className: "flex items-center gap-2", children: [providerLogo ? (jsx("img", { src: providerLogo, alt: providerName, className: "w-4 h-4 rounded-sm object-contain" })) : (jsx("span", { children: "\uD83D\uDD11" })), providerName] }) }, provider.name));
225826
225610
  }) })] }), shouldShowError && (jsx("p", { className: "text-red-500 text-xs", children: (_b = field.state.meta.errors[0]) === null || _b === void 0 ? void 0 : _b.message }))] }));
225827
225611
  } })), isCredentialsSchemaLoading && (jsxs("div", { className: "flex items-center justify-center py-4", children: [jsx(LoaderCircle, { className: "h-4 w-4 animate-spin mr-2" }), jsx("span", { className: "text-sm text-gray-600", children: t('llmsTabCreateLlmModal.loadingProviderConfigurations') })] })), showProviderForm && !isCredentialsSchemaLoading && !isAzureOpenAI && (jsx(Fragment$1, { children: schemaFields.map((fieldName) => (jsx(form.Field, { name: fieldName, children: (field) => {
225828
225612
  var _a, _b;
@@ -225909,26 +225693,18 @@ function IntegrationLLMsTab({ tenantKey }) {
225909
225693
  }
225910
225694
  };
225911
225695
  return (jsxs("div", { className: "space-y-4", children: [jsx(InfoBanner, { icon: jsx(Bot, { className: "w-5 h-5" }), children: t('llmsTabIntegrationLlmsTab.infoBannerText') }), jsx("div", { className: "bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg overflow-x-auto", children: jsxs("table", { className: "w-full text-sm min-w-[600px]", children: [jsx("thead", { className: "bg-gray-50 dark:bg-gray-700 border-b border-gray-200 dark:border-gray-600", children: jsxs("tr", { children: [jsx("th", { className: "px-6 py-3 text-left font-medium text-gray-500 dark:text-gray-400", children: t('llmsTabIntegrationLlmsTab.colName') }), jsx("th", { className: "px-6 py-3 text-left font-medium text-gray-500 dark:text-gray-400", children: t('llmsTabIntegrationLlmsTab.colKey') }), jsx("th", { className: "px-6 py-3 text-left font-medium text-gray-500 dark:text-gray-400 w-[50px]" })] }) }), jsx("tbody", { className: "divide-y divide-gray-200 dark:divide-gray-600", children: isMaskedCredentialsLoading ? (jsx("tr", { children: jsx("td", { colSpan: 3, className: "px-6 py-8 text-center text-sm text-gray-500", children: t('llmsTabIntegrationLlmsTab.loading') }) })) : maskedCredentials.length === 0 ? (jsx("tr", { children: jsx("td", { colSpan: 3, className: "px-6 py-8 text-center text-sm text-gray-500", children: t('llmsTabIntegrationLlmsTab.noLlmKeysFound') }) })) : (maskedCredentials.map((cred, idx) => {
225912
- var _a, _b, _c;
225696
+ var _a, _b, _c, _d, _e;
225913
225697
  const provider = ((_a = cred.service_info) === null || _a === void 0 ? void 0 : _a.display_name) || cred.name;
225914
- // Prefer the logo the API ships; fall back to the bundled
225915
- // provider artwork so known providers (ibl.ai included) never
225916
- // degrade to a generic key emoji.
225917
- let logoUrl;
225918
- let logoAlt;
225919
- if ((_b = cred.service_info) === null || _b === void 0 ? void 0 : _b.logo) {
225920
- logoUrl = cred.service_info.logo;
225921
- logoAlt = cred.service_info.display_name || cred.service_info.name;
225922
- }
225923
- else {
225924
- logoUrl = getLLMProviderDetails(cred.name).logo;
225925
- }
225698
+ // Logos are backend-owned (`service_info.logo`); no bundled fallback.
225699
+ const logoUrl = (_b = cred.service_info) === null || _b === void 0 ? void 0 : _b.logo;
225926
225700
  const isAzure = isAzureOpenAI(cred);
225927
225701
  const azureModels = isAzure ? getAzureModels(cred) : [];
225928
225702
  const isExpanded = expandedRows.has(cred.name);
225929
- return (jsxs(React__default.Fragment, { children: [jsxs("tr", { children: [jsx("td", { className: "px-6 py-4", children: jsxs("div", { className: "flex items-center gap-2", children: [jsx("img", { src: logoUrl, alt: logoAlt || provider || cred.name, className: "w-6 h-6 rounded-sm object-contain" }), jsx("span", { className: "font-normal text-gray-900 dark:text-gray-100", children: provider || cred.name }), isAzure && azureModels.length > 0 && (jsx("button", { type: "button", onClick: () => toggleRowExpansion(cred.name), className: "p-0.5 hover:bg-gray-100 rounded", children: isExpanded ? (jsx(ChevronUp, { className: "h-4 w-4 text-gray-500" })) : (jsx(ChevronDown, { className: "h-4 w-4 text-gray-500" })) }))] }) }), jsx("td", { className: "px-6 py-4 font-mono text-sm text-gray-900 dark:text-gray-100", children: isAzure ? (jsx("span", { className: "text-xs text-gray-500", children: t('llmsTabIntegrationLlmsTab.azureModelCount', {
225703
+ return (jsxs(React__default.Fragment, { children: [jsxs("tr", { children: [jsx("td", { className: "px-6 py-4", children: jsxs("div", { className: "flex items-center gap-2", children: [logoUrl ? (jsx("img", { src: logoUrl, alt: ((_c = cred.service_info) === null || _c === void 0 ? void 0 : _c.display_name) ||
225704
+ ((_d = cred.service_info) === null || _d === void 0 ? void 0 : _d.name) ||
225705
+ cred.name, className: "w-6 h-6 rounded-sm object-contain" })) : (jsx("span", { className: "text-lg", children: '🔑' })), jsx("span", { className: "font-normal text-gray-900 dark:text-gray-100", children: provider || cred.name }), isAzure && azureModels.length > 0 && (jsx("button", { type: "button", onClick: () => toggleRowExpansion(cred.name), className: "p-0.5 hover:bg-gray-100 rounded", children: isExpanded ? (jsx(ChevronUp, { className: "h-4 w-4 text-gray-500" })) : (jsx(ChevronDown, { className: "h-4 w-4 text-gray-500" })) }))] }) }), jsx("td", { className: "px-6 py-4 font-mono text-sm text-gray-900 dark:text-gray-100", children: isAzure ? (jsx("span", { className: "text-xs text-gray-500", children: t('llmsTabIntegrationLlmsTab.azureModelCount', {
225930
225706
  count: azureModels.length,
225931
- }) })) : (String((_c = cred === null || cred === void 0 ? void 0 : cred.value) === null || _c === void 0 ? void 0 : _c.key).slice(0, 15) || '') }), jsx("td", { className: "px-6 py-4", children: jsx(Button$1, { variant: "ghost", size: "sm", className: "cursor-pointer mr-2", disabled: isDeletingIntegration || isDeletingCredential, onClick: () => handleDeleteCredential(cred.name, cred.platform, false), children: jsx(Trash, { className: "h-4 w-4" }) }) })] }), isAzure &&
225707
+ }) })) : (String((_e = cred === null || cred === void 0 ? void 0 : cred.value) === null || _e === void 0 ? void 0 : _e.key).slice(0, 15) || '') }), jsx("td", { className: "px-6 py-4", children: jsx(Button$1, { variant: "ghost", size: "sm", className: "cursor-pointer mr-2", disabled: isDeletingIntegration || isDeletingCredential, onClick: () => handleDeleteCredential(cred.name, cred.platform, false), children: jsx(Trash, { className: "h-4 w-4" }) }) })] }), isAzure &&
225932
225708
  isExpanded &&
225933
225709
  azureModels.map((model, modelIdx) => (jsxs("tr", { children: [jsx("td", { className: "px-6 py-4 pl-12", children: jsx("span", { className: "text-sm text-gray-700", children: model.modelName.toLowerCase() }) }), jsx("td", { className: "px-6 py-4 font-mono text-sm text-gray-600", children: model.maskedKey.slice(0, 15) || '' }), jsx("td", { className: "px-6 py-4", children: jsx(Button$1, { variant: "ghost", size: "sm", className: "cursor-pointer", disabled: isDeletingIntegration || isDeletingCredential, onClick: () => handleDeleteCredential(cred.name, cred.platform, false, model.modelName), children: jsx(Trash, { className: "h-4 w-4" }) }) })] }, `${cred.name}-${model.modelName}-${modelIdx}`)))] }, cred.name + idx));
225934
225710
  })) })] }) })] }));
@@ -259377,6 +259153,202 @@ var reactPaginate = {exports: {}};
259377
259153
 
259378
259154
  } (reactPaginate));
259379
259155
 
259156
+ /**
259157
+ * A provider's logo as the backend serves it. Provider naming and artwork are
259158
+ * owned by the API (`LLMResponse.logo`, `ExternalServiceInfo.logo`), so there
259159
+ * is no bundled fallback image: a provider without a logo renders a neutral
259160
+ * initial instead, keeping the card layout intact.
259161
+ */
259162
+ function LLMProviderLogo({ logo, label, alt = label, size = 32, className, }) {
259163
+ if (logo) {
259164
+ return (jsx(Image$4, { src: logo, alt: alt, className: cn('h-full w-full object-contain', className), width: size, height: size, loading: "lazy" }));
259165
+ }
259166
+ return (jsx("span", { role: "img", "aria-label": alt, "data-testid": "llm-provider-logo-placeholder", className: cn('flex h-full w-full items-center justify-center rounded bg-gray-100 text-xs font-semibold text-gray-500 uppercase', className), children: label.charAt(0) }));
259167
+ }
259168
+
259169
+ /**
259170
+ * Small pure utilities colocated with the LLM tab.
259171
+ * Ported from standalone's `@/lib/utils` and `@/lib/error`.
259172
+ */
259173
+ /**
259174
+ * The label a provider renders with: the backend's `display_name` when it
259175
+ * ships one, else its raw `name`. There is deliberately no frontend map behind
259176
+ * this — naming (and the logo next to it) is owned by the backend.
259177
+ */
259178
+ function llmProviderLabel(provider) {
259179
+ return provider.display_name || provider.name;
259180
+ }
259181
+ /**
259182
+ * The label a chat model renders with: the backend's `display_name` (`GPT-4.1
259183
+ * Mini`) when it ships one, else the raw wire key (`gpt-4.1-mini`). Same rule
259184
+ * as {@link llmProviderLabel}; every model list (LLM tab, sandbox) goes through
259185
+ * here so they never drift apart.
259186
+ */
259187
+ function llmModelLabel(model) {
259188
+ return model.display_name || model.llm_name;
259189
+ }
259190
+ // Canonical provider name for any label — a backend key ("azure_openai"), a
259191
+ // human label ("Microsoft"), or a local model's provider ("Meta"). Folds every
259192
+ // alias onto one identity so the same provider from different sources compares
259193
+ // equal. See getProviderName.
259194
+ const PROVIDER_NAME_BY_ALIAS = {
259195
+ microsoft: 'azure_openai',
259196
+ azureopenai: 'azure_openai',
259197
+ openai: 'openai',
259198
+ google: 'google',
259199
+ gemini: 'google',
259200
+ meta: 'llama',
259201
+ metallama: 'llama',
259202
+ llama: 'llama',
259203
+ mistral: 'mistral',
259204
+ mistralai: 'mistral',
259205
+ nvidia: 'nvidia',
259206
+ iblchatnvidia: 'nvidia',
259207
+ deepseek: 'deepseek',
259208
+ anthropic: 'anthropic',
259209
+ iblchatanthropic: 'anthropic',
259210
+ claude: 'anthropic',
259211
+ groq: 'groq',
259212
+ perplexity: 'perplexity',
259213
+ xai: 'xai',
259214
+ bedrock: 'bedrock',
259215
+ amazonbedrock: 'bedrock',
259216
+ amazon: 'bedrock',
259217
+ iblchatbedrock: 'bedrock',
259218
+ alibaba: 'alibaba',
259219
+ qwen: 'alibaba',
259220
+ ibm: 'ibm',
259221
+ granite: 'ibm',
259222
+ // ibl.ai's own hosted provider. The backend key is `iblai` (NameEnum.IBLAI);
259223
+ // the extra aliases cover the `IBLChat<Vendor>` house spelling and the
259224
+ // dotted/spaced brand forms, which all normalize to one of these.
259225
+ iblai: 'iblai',
259226
+ ibl: 'iblai',
259227
+ iblchatibl: 'iblai',
259228
+ iblchatiblai: 'iblai',
259229
+ };
259230
+ /**
259231
+ * Canonical provider name for any label, case- and punctuation-insensitive.
259232
+ * Folds backend keys and human/display labels onto one identity — e.g.
259233
+ * `getProviderName('Microsoft') === 'azure_openai'`,
259234
+ * `getProviderName('Meta') === 'llama'`, `getProviderName('Google') === 'google'`.
259235
+ * Unknown providers return their normalized (lowercased, alphanumeric-only) form.
259236
+ *
259237
+ * This is the single source of truth for provider identity, which matters as
259238
+ * soon as on-device models enter the picture: the local catalog labels its
259239
+ * providers the human way ("Meta", "Microsoft", "Alibaba") while the backend LLM
259240
+ * list uses wire keys ("llama", "azure_openai"). Comparing the two raw would
259241
+ * render a duplicate card for the same vendor.
259242
+ */
259243
+ function getProviderName(llmProvider) {
259244
+ var _a;
259245
+ const normalized = (llmProvider !== null && llmProvider !== void 0 ? llmProvider : '').toLowerCase().replace(/[^a-z0-9]/g, '');
259246
+ return (_a = PROVIDER_NAME_BY_ALIAS[normalized]) !== null && _a !== void 0 ? _a : normalized;
259247
+ }
259248
+ /**
259249
+ * Whether two Ollama model refs name the same model, comparing by base name and
259250
+ * ignoring a trailing `:tag` (so "llama3.2" ≡ "llama3.2:latest", and
259251
+ * "granite4.1:8b" ≡ "granite4.1"). The download state and Ollama's /api/tags may
259252
+ * carry a tag suffix the catalog id doesn't (or vice-versa); an exact `===` then
259253
+ * fails to recognize a row's own in-flight download — the row shows "Download"
259254
+ * with no progress while the pull runs. Base-name matching is safe here because
259255
+ * every catalog model has a unique base (llama3.2, qwen3, mistral, granite4.1,
259256
+ * gpt-oss, gemma4), so it can never light up the wrong row.
259257
+ */
259258
+ function sameModel(a, b) {
259259
+ const base = (ref) => (ref !== null && ref !== void 0 ? ref : '').toLowerCase().split(':')[0];
259260
+ const ba = base(a);
259261
+ return ba.length > 0 && ba === base(b);
259262
+ }
259263
+ /**
259264
+ * Whether a catalog model id (e.g. "granite4.1:8b") is among the installed
259265
+ * Ollama tags (e.g. "granite4.1:latest").
259266
+ *
259267
+ * Deliberately NOT the profile tab's `isModelInstalled`, which matches an exact
259268
+ * tag or the `<id>:` prefix: that is one-directional and misses a catalog id
259269
+ * that itself carries a tag ("granite4.1:8b" against an installed
259270
+ * "granite4.1:latest"). The picker needs the symmetric base-name comparison of
259271
+ * {@link sameModel} so a pulled model is recognized whichever side wears the tag.
259272
+ */
259273
+ function isLocalModelInstalled(modelId, installedTags) {
259274
+ return !!(installedTags === null || installedTags === void 0 ? void 0 : installedTags.some((tag) => sameModel(tag, modelId)));
259275
+ }
259276
+ function canSwitchProvider(providers, name) {
259277
+ const provider = providers.find((p) => p.name === name);
259278
+ return !!(provider === null || provider === void 0 ? void 0 : provider.chat_models) && provider.chat_models.length > 0;
259279
+ }
259280
+ /**
259281
+ * Whether we hold a usable LLM key for this provider — its own credentials, or
259282
+ * the tenant's main keys when it is allowed to borrow them. An *absent*
259283
+ * `main_has_credentials` means the backend did not report on the main keys and
259284
+ * is treated as available (matching standalone's `@/lib/utils` and
259285
+ * `claw-sandbox`'s copy); only an explicit `false` rules the main keys out.
259286
+ */
259287
+ function canSwitchLLm(llm) {
259288
+ if (llm === null || llm === void 0 ? void 0 : llm.has_credentials) {
259289
+ return true;
259290
+ }
259291
+ return ((llm === null || llm === void 0 ? void 0 : llm.can_use_main_keys) &&
259292
+ ((llm === null || llm === void 0 ? void 0 : llm.main_has_credentials) === true || (llm === null || llm === void 0 ? void 0 : llm.main_has_credentials) === undefined));
259293
+ }
259294
+ /**
259295
+ * Compares two providers by the label the user actually sees on the card (see
259296
+ * {@link llmProviderLabel}), not by the backend key. Comparison is
259297
+ * case-insensitive (`sensitivity: 'base'`) so casing never produces a
259298
+ * surprising order.
259299
+ */
259300
+ function compareLLMProvidersByDisplayName(a, b) {
259301
+ return llmProviderLabel(a).localeCompare(llmProviderLabel(b), undefined, {
259302
+ sensitivity: 'base',
259303
+ });
259304
+ }
259305
+ /**
259306
+ * Whether the user can reach *any* model of this provider.
259307
+ *
259308
+ * This is exactly the provider-level half of the per-model `isDisabled` rule in
259309
+ * `llm-tab/llm-provider-modal.tsx`: a model row there is disabled when
259310
+ * `!canSwitchLLm(provider) || !canSwitchProvider(llms, provider.name)`, and both
259311
+ * of those depend only on the provider — so when either fails, *every* model row
259312
+ * of that provider is disabled. `canSwitchProvider` is just "has at least one
259313
+ * `chat_models` entry", which is checked inline here to keep the helper pure and
259314
+ * free of the surrounding provider list.
259315
+ *
259316
+ * `canSwitchLLm` can return `undefined` (its `can_use_main_keys` branch), hence
259317
+ * the explicit `Boolean(...)`.
259318
+ */
259319
+ function canAccessProvider(provider) {
259320
+ var _a, _b;
259321
+ return Boolean(canSwitchLLm(provider !== null && provider !== void 0 ? provider : {})) && ((_b = (_a = provider === null || provider === void 0 ? void 0 : provider.chat_models) === null || _a === void 0 ? void 0 : _a.length) !== null && _b !== void 0 ? _b : 0) > 0;
259322
+ }
259323
+ /**
259324
+ * Orders LLM provider cards as two alphabetical groups: providers the user can
259325
+ * actually use (see {@link canAccessProvider}) first, then the ones they can't —
259326
+ * each group sorted alphabetically by display name.
259327
+ *
259328
+ * The grouping predicate is deliberately the same one the grid grays cards with,
259329
+ * so the two always line up: group 1 renders normally, group 2 renders grayed.
259330
+ * Grouping on credentials alone would let a provider that has a key but ships no
259331
+ * chat models sort into the "usable" group while rendering grayed.
259332
+ *
259333
+ * Pure and non-mutating: RTK Query results are frozen, so the input is copied
259334
+ * before sorting. `Array.prototype.sort` is stable, so providers that tie on
259335
+ * both group and display name keep their original relative order.
259336
+ */
259337
+ function sortLLMProvidersByCredentials(providers) {
259338
+ return [...providers].sort((a, b) => {
259339
+ const aUsable = canAccessProvider(a);
259340
+ const bUsable = canAccessProvider(b);
259341
+ if (aUsable !== bUsable)
259342
+ return aUsable ? -1 : 1;
259343
+ return compareLLMProvidersByDisplayName(a, b);
259344
+ });
259345
+ }
259346
+ function extractErrorMessage$1(error, fallback) {
259347
+ var _a, _b;
259348
+ const apiError = error;
259349
+ return ((_a = apiError === null || apiError === void 0 ? void 0 : apiError.data) === null || _a === void 0 ? void 0 : _a.error) || ((_b = apiError === null || apiError === void 0 ? void 0 : apiError.error) === null || _b === void 0 ? void 0 : _b.error) || fallback;
259350
+ }
259351
+
259380
259352
  function OverlayModal({ open, onClose, title, description, className, children, }) {
259381
259353
  const t = useT();
259382
259354
  const [mounted, setMounted] = useState(false);
@@ -270957,7 +270929,7 @@ function resolveLLMTabLabels(override, t) {
270957
270929
  * line beneath it. One click toggles download ⇄ cancel; once installed a click
270958
270930
  * selects it.
270959
270931
  */
270960
- function LocalModelRow({ name, size, logo, status, progress, disabled = false, disabledReason, errorMessage, onActivate, labels: labelsProp, }) {
270932
+ function LocalModelRow({ name, size, logo, providerLabel, status, progress, disabled = false, disabledReason, errorMessage, onActivate, labels: labelsProp, }) {
270961
270933
  const t = useT();
270962
270934
  const labels = useMemo(() => labelsProp !== null && labelsProp !== void 0 ? labelsProp : buildLLMTabLabels(t).providerModal.localModel, [labelsProp, t]);
270963
270935
  const selected = status === 'selected';
@@ -270999,16 +270971,15 @@ function LocalModelRow({ name, size, logo, status, progress, disabled = false, d
270999
270971
  : 'cursor-pointer border-gray-200 hover:border-blue-500 hover:bg-blue-50'), children: [downloading && (jsx("span", { "aria-hidden": "true", "data-testid": "llm-local-model-progress", className: "pointer-events-none absolute inset-y-0 left-0 z-0 bg-blue-500/40 motion-safe:transition-[width] motion-safe:duration-300",
271000
270972
  // Minimum width so the bar is visible the moment a download starts
271001
270973
  // (progress 0 / "starting"), then tracks real progress.
271002
- style: { width: `${status === 'starting' ? 6 : Math.max(pct, 6)}%` } })), jsx("span", { className: "relative z-10 flex h-8 w-8 flex-shrink-0 items-center justify-center overflow-hidden rounded-lg", children: jsx(Image$4, { src: logo, alt: "", "aria-hidden": "true", width: 32, height: 32, className: cn('h-full w-full object-contain', {
271003
- grayscale: isBusyDisabled,
271004
- }), loading: "lazy" }) }), jsxs("span", { className: "relative z-10 flex min-w-0 flex-1 flex-col gap-1.5", children: [jsx("span", { className: "truncate text-sm font-medium text-[#646464] lowercase", children: name }), jsxs("span", { className: "flex min-w-0 items-center gap-1.5 text-sm text-gray-500", children: [jsx("span", { className: "flex-shrink-0 rounded bg-gray-100 px-2 py-0.5 text-sm font-medium text-gray-500", children: labels.onDevice }), status === 'not-installed' && (jsxs(Fragment$1, { children: [jsx("span", { "aria-hidden": "true", children: "\u00B7" }), jsx("span", { children: size }), jsx(Download, { className: "h-4 w-4", "aria-hidden": "true" })] })), status === 'starting' && (jsxs(Fragment$1, { children: [jsx("span", { "aria-hidden": "true", children: "\u00B7" }), jsx("span", { children: labels.starting })] })), status === 'downloading' && (jsxs(Fragment$1, { children: [jsx("span", { "aria-hidden": "true", children: "\u00B7" }), jsxs("span", { className: "tabular-nums group-hover:hidden group-focus-visible:hidden", children: [pct, "%"] }), jsxs("span", { className: "hidden items-center gap-1 text-blue-600 group-hover:flex group-focus-visible:flex", children: [jsx(X$2, { className: "h-3.5 w-3.5", "aria-hidden": "true" }), labels.cancel] })] })), status === 'selected' && (jsxs("span", { className: "flex items-center gap-1 text-blue-600", children: [jsx(Check, { className: "h-4 w-4", "aria-hidden": "true" }), labels.inUse] })), status === 'error' && (jsxs(Fragment$1, { children: [jsx("span", { "aria-hidden": "true", children: "\u00B7" }), jsx("span", { className: "truncate text-red-500", children: errorMessage || labels.downloadFailedRetry }), jsx(RotateCcw, { className: "h-4 w-4 flex-shrink-0 text-red-500", "aria-hidden": "true" })] }))] })] })] }));
270974
+ style: { width: `${status === 'starting' ? 6 : Math.max(pct, 6)}%` } })), jsx("span", { "aria-hidden": "true", className: "relative z-10 flex h-8 w-8 flex-shrink-0 items-center justify-center overflow-hidden rounded-lg", children: jsx(LLMProviderLogo, { logo: logo, label: providerLabel !== null && providerLabel !== void 0 ? providerLabel : name, alt: "", className: cn({ grayscale: isBusyDisabled }) }) }), jsxs("span", { className: "relative z-10 flex min-w-0 flex-1 flex-col gap-1.5", children: [jsx("span", { className: "truncate text-sm font-medium text-[#646464] lowercase", children: name }), jsxs("span", { className: "flex min-w-0 items-center gap-1.5 text-sm text-gray-500", children: [jsx("span", { className: "flex-shrink-0 rounded bg-gray-100 px-2 py-0.5 text-sm font-medium text-gray-500", children: labels.onDevice }), status === 'not-installed' && (jsxs(Fragment$1, { children: [jsx("span", { "aria-hidden": "true", children: "\u00B7" }), jsx("span", { children: size }), jsx(Download, { className: "h-4 w-4", "aria-hidden": "true" })] })), status === 'starting' && (jsxs(Fragment$1, { children: [jsx("span", { "aria-hidden": "true", children: "\u00B7" }), jsx("span", { children: labels.starting })] })), status === 'downloading' && (jsxs(Fragment$1, { children: [jsx("span", { "aria-hidden": "true", children: "\u00B7" }), jsxs("span", { className: "tabular-nums group-hover:hidden group-focus-visible:hidden", children: [pct, "%"] }), jsxs("span", { className: "hidden items-center gap-1 text-blue-600 group-hover:flex group-focus-visible:flex", children: [jsx(X$2, { className: "h-3.5 w-3.5", "aria-hidden": "true" }), labels.cancel] })] })), status === 'selected' && (jsxs("span", { className: "flex items-center gap-1 text-blue-600", children: [jsx(Check, { className: "h-4 w-4", "aria-hidden": "true" }), labels.inUse] })), status === 'error' && (jsxs(Fragment$1, { children: [jsx("span", { "aria-hidden": "true", children: "\u00B7" }), jsx("span", { className: "truncate text-red-500", children: errorMessage || labels.downloadFailedRetry }), jsx(RotateCcw, { className: "h-4 w-4 flex-shrink-0 text-red-500", "aria-hidden": "true" })] }))] })] })] }));
271005
270975
  }
271006
270976
 
271007
- function LLMProviderModal({ isOpen, onClose, onSelect, llmProvider, isSelecting, mentorSettings, llms, getLLMProviderDetails: getLLMProviderDetails$1 = getLLMProviderDetails, labels: labelsProp, }) {
270977
+ function LLMProviderModal({ isOpen, onClose, onSelect, llmProvider, isSelecting, mentorSettings, llms, labels: labelsProp, }) {
271008
270978
  var _a;
271009
270979
  const t = useT();
271010
270980
  const labels = React__default.useMemo(() => labelsProp !== null && labelsProp !== void 0 ? labelsProp : buildLLMTabLabels(t).providerModal, [labelsProp, t]);
271011
270981
  const [searchQuery, setSearchQuery] = React__default.useState('');
270982
+ const providerLabel = llmProviderLabel(llmProvider);
271012
270983
  // On-device model download state (Tauri desktop only; `isAvailable` is false
271013
270984
  // on web, which hides every local row below).
271014
270985
  const { isAvailable, state: downloadState, ollamaStatus, systemMemory, startDownload, cancelDownload, } = useModelDownload();
@@ -271197,7 +271168,6 @@ function LLMProviderModal({ isOpen, onClose, onSelect, llmProvider, isSelecting,
271197
271168
  if (item.kind === 'cloud') {
271198
271169
  const llm = item.llm;
271199
271170
  const isActive = isCloudActive(llm);
271200
- const providerDetails = getLLMProviderDetails$1(llmProvider.name, llm.llm_name);
271201
271171
  const isDisabled = !switchLLMAllowed || isSelecting || isActive || !switchProviderAllowed;
271202
271172
  return (jsxs("button", { "data-model": llm.llm_name, disabled: isDisabled, onClick: () => {
271203
271173
  selectCloud(llm.llm_name);
@@ -271207,15 +271177,15 @@ function LLMProviderModal({ isOpen, onClose, onSelect, llmProvider, isSelecting,
271207
271177
  'cursor-not-allowed border-gray-100 bg-gray-50': isDisabled && !isActive,
271208
271178
  'hover:border-blue-500 hover:bg-blue-50': !isDisabled,
271209
271179
  'cursor-not-allowed border-blue-500 bg-blue-50': isActive,
271210
- }), children: [jsx("span", { className: "flex h-8 w-8 items-center justify-center overflow-hidden rounded-lg", children: jsx(Image$4, { src: providerDetails.logo, alt: labels.providerIconAlt(providerDetails.name), className: cn('h-full w-full object-contain', {
271180
+ }), children: [jsx("span", { className: "flex h-8 w-8 items-center justify-center overflow-hidden rounded-lg", children: jsx(LLMProviderLogo, { logo: llmProvider.logo, label: providerLabel, alt: labels.providerIconAlt(providerLabel), className: cn({
271211
271181
  // `grayscale` alone is a no-op on already-black marks,
271212
271182
  // so fade them as well — same rule as the provider grid.
271213
271183
  'opacity-40 grayscale': isDisabled && !isActive,
271214
- }), width: 32, height: 32, loading: "lazy" }) }), jsx("span", { className: "text-left text-sm font-medium text-gray-900", children: llm.display_name || llm.llm_name })] }, `cloud-${llm.llm_name}`));
271184
+ }) }) }), jsx("span", { className: "text-left text-sm font-medium text-gray-900", children: llmModelLabel(llm) })] }, `cloud-${llm.llm_name}`));
271215
271185
  }
271216
271186
  const m = item.model;
271217
271187
  const status = statusFor(m);
271218
- return (jsx(LocalModelRow, { name: m.name, size: localModelDisplaySize(m), logo: getLLMProviderDetails$1(llmProvider.name, m.name).logo, status: status, progress: sameModel(downloadingModel, m.id) ? downloadState.progress : 0, onActivate: () => activateLocal(m, status), labels: labels.localModel }, `local-${m.id}`));
271188
+ return (jsx(LocalModelRow, { name: m.name, size: localModelDisplaySize(m), logo: llmProvider.logo, providerLabel: providerLabel, status: status, progress: sameModel(downloadingModel, m.id) ? downloadState.progress : 0, onActivate: () => activateLocal(m, status), labels: labels.localModel }, `local-${m.id}`));
271219
271189
  }) }), jsx(AlertDialog, { open: !!pendingModel, onOpenChange: (open) => {
271220
271190
  if (!open)
271221
271191
  setPendingModel(null);
@@ -271231,7 +271201,7 @@ function LLMProviderModal({ isOpen, onClose, onSelect, llmProvider, isSelecting,
271231
271201
  }, children: jsxs(AlertDialogContent, { children: [jsxs(AlertDialogHeader, { children: [jsx(AlertDialogTitle, { children: labels.alreadyDownloadingTitle }), jsx(AlertDialogDescription, { children: labels.alreadyDownloadingDescription(busyDownloadName !== null && busyDownloadName !== void 0 ? busyDownloadName : '') })] }), jsx(AlertDialogFooter, { children: jsx(AlertDialogAction, { onClick: () => setBusyDownloadName(null), children: labels.gotIt }) })] }) })] }) }));
271232
271202
  }
271233
271203
 
271234
- function AgentLLMTab({ showConfigurationHeader = true, mentorId: mentorIdOverride, getLLMProviderDetails: getLLMProviderDetails$1 = getLLMProviderDetails, labels: labelsOverride, }) {
271204
+ function AgentLLMTab({ showConfigurationHeader = true, mentorId: mentorIdOverride, labels: labelsOverride, }) {
271235
271205
  var _a;
271236
271206
  const t = useT();
271237
271207
  const { tenantKey, mentorId: contextMentorId, username, enableRBAC } = useAgentSettings();
@@ -271274,8 +271244,8 @@ function AgentLLMTab({ showConfigurationHeader = true, mentorId: mentorIdOverrid
271274
271244
  }
271275
271245
  // Alphabetical among themselves; the whole local-only block still renders
271276
271246
  // after the API providers.
271277
- return result.sort((a, b) => compareLLMProvidersByDisplayName(a.provider, b.provider, getLLMProviderDetails$1));
271278
- }, [llmProviders, getLLMProviderDetails$1]);
271247
+ return result.sort((a, b) => compareLLMProvidersByDisplayName({ name: a.provider }, { name: b.provider }));
271248
+ }, [llmProviders]);
271279
271249
  // Providers this device can serve on-device models for, by normalized key.
271280
271250
  // Empty off Tauri on purpose: the web has no local runtime, so a zero-model
271281
271251
  // server provider must not stay visible just because its NAME happens to
@@ -271289,7 +271259,7 @@ function AgentLLMTab({ showConfigurationHeader = true, mentorId: mentorIdOverrid
271289
271259
  // Two alphabetical groups: providers we have a usable LLM key for first, then
271290
271260
  // the ones we don't. Ordering is independent of the search filter below, so it
271291
271261
  // holds for whatever the filter leaves.
271292
- const sortedLLMProviders = React__default.useMemo(() => sortLLMProvidersByCredentials(llmProviders !== null && llmProviders !== void 0 ? llmProviders : [], getLLMProviderDetails$1), [llmProviders, getLLMProviderDetails$1]);
271262
+ const sortedLLMProviders = React__default.useMemo(() => sortLLMProvidersByCredentials(llmProviders !== null && llmProviders !== void 0 ? llmProviders : []), [llmProviders]);
271293
271263
  const [editMentor, { isLoading: isEditingMentor }] = useEditMentorMutation();
271294
271264
  const isDisabled = isMentorSettingsLoading || isLoadingLLMProviders || isEditingMentor;
271295
271265
  const isLoading = isMentorSettingsLoading || isLoadingLLMProviders;
@@ -271344,7 +271314,7 @@ function AgentLLMTab({ showConfigurationHeader = true, mentorId: mentorIdOverrid
271344
271314
  localProviderKeys.has(getProviderName(model.name));
271345
271315
  })
271346
271316
  .map((model) => {
271347
- const providerDetails = getLLMProviderDetails$1(model.name);
271317
+ const providerLabel = llmProviderLabel(model);
271348
271318
  const isActive = !!activeProviderKey && getProviderName(model.name) === activeProviderKey;
271349
271319
  // The generated LLM response type doesn't declare the
271350
271320
  // credential flags the backend actually sends, so read them
@@ -271372,29 +271342,28 @@ function AgentLLMTab({ showConfigurationHeader = true, mentorId: mentorIdOverrid
271372
271342
  // @ts-ignore - can_use_main_keys property does not exist on LLMResponse type
271373
271343
  can_use_main_keys: model === null || model === void 0 ? void 0 : model.can_use_main_keys,
271374
271344
  });
271375
- }, children: [jsx("div", { className: "h-8 w-8 flex-shrink-0", children: jsx(Image$4, { src: providerDetails.logo, alt: labels.providerLogoAlt(providerDetails.name), className: cn('h-full w-full object-contain', {
271345
+ }, children: [jsx("div", { className: "h-8 w-8 flex-shrink-0", children: jsx(LLMProviderLogo, { logo: model.logo, label: providerLabel, alt: labels.providerLogoAlt(providerLabel), className: cn({
271376
271346
  // `grayscale` only desaturates, so already-black
271377
271347
  // marks (OpenAI, xAI) looked untouched. Fading is
271378
271348
  // what actually reads as inactive at any hue.
271379
271349
  'opacity-40 grayscale': isGrayed,
271380
- }), width: 32, height: 32, loading: "lazy" }) }), jsx("span", { className: "text-sm font-medium text-gray-900", children: providerDetails.name })] }, model.id));
271350
+ }) }) }), jsx("span", { className: "text-sm font-medium text-gray-900", children: providerLabel })] }, model.id));
271381
271351
  }), localOnlyProviders.map((lp) => {
271382
- const details = getLLMProviderDetails$1(lp.provider);
271383
271352
  return (jsxs("div", { "data-testid": "llm-provider-card", "data-provider": lp.provider, "data-disabled": "false", className: cn('flex cursor-pointer items-center gap-3 rounded-lg border border-gray-200 bg-white p-4 shadow-sm transition-shadow hover:shadow-md', { 'border-blue-500': lp.key === activeProviderKey }), onClick: () => {
271384
271353
  if (isDisabled || disabled)
271385
271354
  return;
271386
271355
  setSelectedLLMProvider({
271387
271356
  id: -1,
271388
271357
  name: lp.provider,
271389
- logo: details.logo,
271358
+ logo: null,
271390
271359
  description: null,
271391
271360
  chat_models: [],
271392
271361
  });
271393
- }, children: [jsx("div", { className: "h-8 w-8 flex-shrink-0", children: jsx(Image$4, { src: details.logo, alt: labels.providerLogoAlt(details.name), className: "h-full w-full object-contain", width: 32, height: 32, loading: "lazy" }) }), jsx("span", { className: "text-sm font-medium text-gray-900", children: details.name })] }, `local-${lp.key}`));
271362
+ }, children: [jsx("div", { className: "h-8 w-8 flex-shrink-0", children: jsx(LLMProviderLogo, { logo: null, label: lp.provider, alt: labels.providerLogoAlt(lp.provider) }) }), jsx("span", { className: "text-sm font-medium text-gray-900", children: lp.provider })] }, `local-${lp.key}`));
271394
271363
  })] })) })), selectedLLMProvider && mentorSettings && (jsx(LLMProviderModal, { isOpen: true, onClose: () => setSelectedLLMProvider(null), onSelect: updateMentorLLM, llmProvider: selectedLLMProvider, isSelecting: isEditingMentor, mentorSettings: {
271395
271364
  llm_name: mentorSettings === null || mentorSettings === void 0 ? void 0 : mentorSettings.llm_name,
271396
271365
  llm_provider: mentorSettings === null || mentorSettings === void 0 ? void 0 : mentorSettings.llm_provider,
271397
- }, llms: (llmProviders !== null && llmProviders !== void 0 ? llmProviders : []), getLLMProviderDetails: getLLMProviderDetails$1, labels: labels.providerModal }))] })] })] }));
271366
+ }, llms: (llmProviders !== null && llmProviders !== void 0 ? llmProviders : []), labels: labels.providerModal }))] })] })] }));
271398
271367
  }
271399
271368
 
271400
271369
  function useTools({ labels }) {
@@ -275045,8 +275014,8 @@ function AgentVoiceTab({ labels: labelsOverride, defaultSubTab = 'voice', render
275045
275014
  /**
275046
275015
  * Provider chips, ordered by lowercasing each label and then sorting
275047
275016
  * alphabetically, so a newly added provider slots itself in without a
275048
- * hand-edit. We reuse the vendor logos already shipped in the host's
275049
- * static-root (see `getLLMProviderDetails` for the canonical paths).
275017
+ * hand-edit. The vendor logos are shipped under this package's `public/`
275018
+ * and must be copied into the host's static-root.
275050
275019
  * Browser falls back to a Lucide globe — there's no vendor logo for
275051
275020
  * "device speech engine".
275052
275021
  */
@@ -281151,8 +281120,8 @@ function StartExperimentModal({ isOpen, onClose, tenantKey, username, mentorUniq
281151
281120
  return (jsx(Dialog, { open: isOpen, onOpenChange: (open) => !open && handleClose(), children: jsxs(DialogContent, { "aria-label": "start-evaluation", className: "w-full max-w-md", children: [jsxs(DialogHeader, { children: [jsx(DialogTitle, { children: labels.title }), jsx(DialogDescription, { children: labels.description })] }), jsxs("div", { className: "grid gap-4 mt-3", children: [jsxs("div", { className: "grid gap-1.5", children: [jsx(Label$1, { children: labels.benchmarkLabel }), jsx("div", { className: "rounded-md border border-gray-200 bg-gray-50 px-3 py-2 text-sm font-medium text-gray-800", children: datasetName })] }), jsxs("div", { className: "grid gap-1.5", children: [jsx(Label$1, { htmlFor: "experiment-name", children: labels.experimentNameLabel }), jsx(Input, { id: "experiment-name", value: runName, onChange: (event) => setRunName(event.target.value), placeholder: labels.experimentNamePlaceholder, maxLength: 255, disabled: isStarting })] })] }), jsxs(DialogFooter, { className: "mt-4", children: [jsx(Button$1, { variant: "outline", onClick: handleClose, disabled: isStarting, children: labels.cancel }), jsxs(Button$1, { onClick: handleStart, disabled: isStarting, className: "bg-gradient-to-r from-[#2563EB] to-[#93C5FD] text-white hover:opacity-90", children: [isStarting && jsx(LoaderCircle, { className: "h-4 w-4 mr-1 animate-spin" }), labels.start] })] })] }) }));
281152
281121
  }
281153
281122
 
281154
- function EvaluateLlmPicker({ tenantKey, username, mentorId, value, onChange, getLLMProviderDetails, placeholder, pickerTitle, pickerDescription, selectorAriaLabel, searchPlaceholder, emptyMessage, logoAlt, disabled, }) {
281155
- var _a, _b;
281123
+ function EvaluateLlmPicker({ tenantKey, username, mentorId, value, onChange, placeholder, pickerTitle, pickerDescription, selectorAriaLabel, searchPlaceholder, emptyMessage, logoAlt, disabled, }) {
281124
+ var _a, _b, _c;
281156
281125
  const [showProviders, setShowProviders] = React__default.useState(false);
281157
281126
  const [selectedProvider, setSelectedProvider] = React__default.useState(null);
281158
281127
  const [providerSearch, setProviderSearch] = React__default.useState('');
@@ -281163,7 +281132,14 @@ function EvaluateLlmPicker({ tenantKey, username, mentorId, value, onChange, get
281163
281132
  mentorId,
281164
281133
  }, { skip: !tenantKey || !username || !mentorId });
281165
281134
  const filteredProviders = React__default.useMemo(() => (llmProviders !== null && llmProviders !== void 0 ? llmProviders : []).filter((model) => model.name.toLowerCase().includes(providerSearch.toLowerCase())), [llmProviders, providerSearch]);
281166
- const currentDetails = value ? getLLMProviderDetails(value.llm_provider, value.llm_name) : null;
281135
+ // The selected provider's backend row, for its logo and label; `undefined`
281136
+ // until the catalogue loads or when the stored key no longer matches one.
281137
+ const currentProvider = value
281138
+ ? llmProviders === null || llmProviders === void 0 ? void 0 : llmProviders.find((model) => model.name === value.llm_provider)
281139
+ : undefined;
281140
+ const currentLabel = currentProvider
281141
+ ? llmProviderLabel(currentProvider)
281142
+ : ((_a = value === null || value === void 0 ? void 0 : value.llm_provider) !== null && _a !== void 0 ? _a : '');
281167
281143
  const handleProviderClick = (model) => {
281168
281144
  setSelectedProvider({
281169
281145
  id: model.id,
@@ -281184,17 +281160,17 @@ function EvaluateLlmPicker({ tenantKey, username, mentorId, value, onChange, get
281184
281160
  setSelectedProvider(null);
281185
281161
  setShowProviders(false);
281186
281162
  };
281187
- return (jsxs(Fragment$1, { children: [jsxs(Button$1, { type: "button", variant: "ghost", disabled: disabled, onClick: () => setShowProviders(true), "aria-label": selectorAriaLabel, className: "flex h-9 cursor-pointer items-center gap-2 px-2 text-sm font-medium text-[#646464] transition-colors hover:text-[#484848] disabled:opacity-50", children: [(currentDetails === null || currentDetails === void 0 ? void 0 : currentDetails.logo) ? (jsx(Image$4, { src: currentDetails.logo, alt: logoAlt.replace('{name}', currentDetails.name), width: 20, height: 20, className: "h-5 w-5 flex-shrink-0 object-contain", loading: "lazy" })) : (jsx(Bot, { className: "h-5 w-5 flex-shrink-0 text-gray-400" })), jsx("span", { className: "max-w-[220px] truncate", children: (value === null || value === void 0 ? void 0 : value.llm_name) || (currentDetails === null || currentDetails === void 0 ? void 0 : currentDetails.name) || placeholder }), jsx(ChevronDown, { className: "h-4 w-4 text-gray-500" })] }), jsx(Dialog, { open: showProviders, onOpenChange: (open) => !open && setShowProviders(false), children: jsxs(DialogContent, { "aria-label": "evaluate-llm-picker", className: "max-h-[90vh] w-full max-w-3xl overflow-y-auto", children: [jsxs(DialogHeader, { children: [jsx(DialogTitle, { children: pickerTitle }), jsx(DialogDescription, { children: pickerDescription })] }), jsxs("div", { className: "relative", children: [jsx(Search, { className: "absolute top-1/2 left-3 h-4 w-4 -translate-y-1/2 text-gray-400" }), jsx(Input, { placeholder: searchPlaceholder, className: "pl-9", value: providerSearch, onChange: (event) => setProviderSearch(event.target.value) })] }), isLoading ? (jsx("div", { className: "flex items-center justify-center py-10", children: jsx(Spinner, {}) })) : (jsxs("div", { className: "grid grid-cols-2 gap-3 sm:grid-cols-3", children: [filteredProviders.map((model) => {
281188
- const details = getLLMProviderDetails(model.name);
281163
+ return (jsxs(Fragment$1, { children: [jsxs(Button$1, { type: "button", variant: "ghost", disabled: disabled, onClick: () => setShowProviders(true), "aria-label": selectorAriaLabel, className: "flex h-9 cursor-pointer items-center gap-2 px-2 text-sm font-medium text-[#646464] transition-colors hover:text-[#484848] disabled:opacity-50", children: [(currentProvider === null || currentProvider === void 0 ? void 0 : currentProvider.logo) ? (jsx(LLMProviderLogo, { logo: currentProvider.logo, label: currentLabel, alt: logoAlt.replace('{name}', currentLabel), size: 20, className: "h-5 w-5 flex-shrink-0" })) : (jsx(Bot, { className: "h-5 w-5 flex-shrink-0 text-gray-400" })), jsx("span", { className: "max-w-[220px] truncate", children: (value === null || value === void 0 ? void 0 : value.llm_name) || currentLabel || placeholder }), jsx(ChevronDown, { className: "h-4 w-4 text-gray-500" })] }), jsx(Dialog, { open: showProviders, onOpenChange: (open) => !open && setShowProviders(false), children: jsxs(DialogContent, { "aria-label": "evaluate-llm-picker", className: "max-h-[90vh] w-full max-w-3xl overflow-y-auto", children: [jsxs(DialogHeader, { children: [jsx(DialogTitle, { children: pickerTitle }), jsx(DialogDescription, { children: pickerDescription })] }), jsxs("div", { className: "relative", children: [jsx(Search, { className: "absolute top-1/2 left-3 h-4 w-4 -translate-y-1/2 text-gray-400" }), jsx(Input, { placeholder: searchPlaceholder, className: "pl-9", value: providerSearch, onChange: (event) => setProviderSearch(event.target.value) })] }), isLoading ? (jsx("div", { className: "flex items-center justify-center py-10", children: jsx(Spinner, {}) })) : (jsxs("div", { className: "grid grid-cols-2 gap-3 sm:grid-cols-3", children: [filteredProviders.map((model) => {
281164
+ const label = llmProviderLabel(model);
281189
281165
  const isCurrent = (value === null || value === void 0 ? void 0 : value.llm_provider) === model.name;
281190
- return (jsxs("button", { type: "button", onClick: () => handleProviderClick(model), className: cn('flex cursor-pointer items-center gap-3 rounded-lg border border-gray-200 bg-white p-3 text-left shadow-sm transition-shadow hover:shadow-md', isCurrent && 'border-blue-500'), children: [jsx("div", { className: "h-8 w-8 flex-shrink-0", children: details.logo && (jsx(Image$4, { src: details.logo, alt: logoAlt.replace('{name}', details.name), className: "h-full w-full object-contain", width: 32, height: 32, loading: "lazy" })) }), jsx("span", { className: "truncate text-sm font-medium text-gray-900", children: details.name })] }, model.id));
281166
+ return (jsxs("button", { type: "button", onClick: () => handleProviderClick(model), className: cn('flex cursor-pointer items-center gap-3 rounded-lg border border-gray-200 bg-white p-3 text-left shadow-sm transition-shadow hover:shadow-md', isCurrent && 'border-blue-500'), children: [jsx("div", { className: "h-8 w-8 flex-shrink-0", children: jsx(LLMProviderLogo, { logo: model.logo, label: label, alt: logoAlt.replace('{name}', label) }) }), jsx("span", { className: "truncate text-sm font-medium text-gray-900", children: label })] }, model.id));
281191
281167
  }), filteredProviders.length === 0 && (jsx("p", { className: "col-span-full py-6 text-center text-sm text-gray-500", children: emptyMessage }))] }))] }) }), selectedProvider && (jsx(LLMProviderModal, { isOpen: true, onClose: () => setSelectedProvider(null), onSelect: handleSelect, llmProvider: selectedProvider, isSelecting: false, mentorSettings: {
281192
- llm_name: (_a = value === null || value === void 0 ? void 0 : value.llm_name) !== null && _a !== void 0 ? _a : '',
281193
- llm_provider: (_b = value === null || value === void 0 ? void 0 : value.llm_provider) !== null && _b !== void 0 ? _b : '',
281194
- }, llms: (llmProviders !== null && llmProviders !== void 0 ? llmProviders : []), getLLMProviderDetails: getLLMProviderDetails }))] }));
281168
+ llm_name: (_b = value === null || value === void 0 ? void 0 : value.llm_name) !== null && _b !== void 0 ? _b : '',
281169
+ llm_provider: (_c = value === null || value === void 0 ? void 0 : value.llm_provider) !== null && _c !== void 0 ? _c : '',
281170
+ }, llms: (llmProviders !== null && llmProviders !== void 0 ? llmProviders : []) }))] }));
281195
281171
  }
281196
281172
 
281197
- function LlmJudgeModal({ isOpen, onClose, tenantKey, username, mentorId, datasetName, runName, labels, getLLMProviderDetails, }) {
281173
+ function LlmJudgeModal({ isOpen, onClose, tenantKey, username, mentorId, datasetName, runName, labels, }) {
281198
281174
  const [criteria, setCriteria] = React__default.useState('');
281199
281175
  const [scoreName, setScoreName] = React__default.useState('');
281200
281176
  const [llm, setLlm] = React__default.useState(null);
@@ -281246,7 +281222,7 @@ function LlmJudgeModal({ isOpen, onClose, tenantKey, username, mentorId, dataset
281246
281222
  if (!Number.isNaN(parsed)) {
281247
281223
  setMaxConcurrency(Math.min(20, Math.max(1, parsed)));
281248
281224
  }
281249
- }, disabled: isSubmitting, className: "mt-2" })] })] }), jsxs("div", { className: "flex items-center gap-2", children: [jsx(Label$1, { className: "m-0", children: labels.llmLabel }), jsx(EvaluateLlmPicker, { tenantKey: tenantKey, username: username, mentorId: mentorId, value: llm, onChange: setLlm, getLLMProviderDetails: getLLMProviderDetails, placeholder: labels.selectLlmPlaceholder, pickerTitle: labels.pickerTitle, pickerDescription: labels.pickerDescription, selectorAriaLabel: labels.pickerSelectorAriaLabel, searchPlaceholder: labels.pickerSearchPlaceholder, emptyMessage: labels.pickerEmpty, logoAlt: labels.pickerLogoAlt, disabled: isSubmitting })] })] }), jsxs(DialogFooter, { className: "mt-4", children: [jsx(Button$1, { variant: "outline", onClick: handleClose, disabled: isSubmitting, children: labels.cancel }), jsxs(Button$1, { onClick: handleSubmit, disabled: isSubmitting || !criteria.trim() || !scoreName.trim() || !llm, className: "bg-gradient-to-r from-[#2563EB] to-[#93C5FD] text-white hover:opacity-90", children: [isSubmitting && jsx(LoaderCircle, { className: "h-4 w-4 mr-1 animate-spin" }), labels.submit] })] })] }) }));
281225
+ }, disabled: isSubmitting, className: "mt-2" })] })] }), jsxs("div", { className: "flex items-center gap-2", children: [jsx(Label$1, { className: "m-0", children: labels.llmLabel }), jsx(EvaluateLlmPicker, { tenantKey: tenantKey, username: username, mentorId: mentorId, value: llm, onChange: setLlm, placeholder: labels.selectLlmPlaceholder, pickerTitle: labels.pickerTitle, pickerDescription: labels.pickerDescription, selectorAriaLabel: labels.pickerSelectorAriaLabel, searchPlaceholder: labels.pickerSearchPlaceholder, emptyMessage: labels.pickerEmpty, logoAlt: labels.pickerLogoAlt, disabled: isSubmitting })] })] }), jsxs(DialogFooter, { className: "mt-4", children: [jsx(Button$1, { variant: "outline", onClick: handleClose, disabled: isSubmitting, children: labels.cancel }), jsxs(Button$1, { onClick: handleSubmit, disabled: isSubmitting || !criteria.trim() || !scoreName.trim() || !llm, className: "bg-gradient-to-r from-[#2563EB] to-[#93C5FD] text-white hover:opacity-90", children: [isSubmitting && jsx(LoaderCircle, { className: "h-4 w-4 mr-1 animate-spin" }), labels.submit] })] })] }) }));
281250
281226
  }
281251
281227
 
281252
281228
  function renderItemText(value) {
@@ -281366,7 +281342,7 @@ function ManualScoreRow({ tenantKey, username, datasetName, runName, traceId, ru
281366
281342
  }
281367
281343
  /** Poll cadence while the run (or one of its reviews) is still producing data. */
281368
281344
  const ACTIVE_POLL_INTERVAL_MS = 10000;
281369
- function ExperimentDetailModal({ isOpen, onClose, tenantKey, username, mentorId, datasetName, runName, labels, getLLMProviderDetails, }) {
281345
+ function ExperimentDetailModal({ isOpen, onClose, tenantKey, username, mentorId, datasetName, runName, labels, }) {
281370
281346
  var _a;
281371
281347
  const [showJudge, setShowJudge] = React__default.useState(false);
281372
281348
  const [expandedTrace, setExpandedTrace] = React__default.useState(null);
@@ -281393,6 +281369,15 @@ function ExperimentDetailModal({ isOpen, onClose, tenantKey, username, mentorId,
281393
281369
  page: 1,
281394
281370
  limit: 50,
281395
281371
  }, { refetchOnMountOrArgChange: true, pollingInterval });
281372
+ // Provider rows, for the logo/label next to each review's judging LLM. Same
281373
+ // args as the judge picker's query, so RTK Query serves both from one cache
281374
+ // entry.
281375
+ const { data: llmProviders } = useGetLlmsQuery({
281376
+ org: tenantKey,
281377
+ // @ts-ignore - userId not on generated signature
281378
+ userId: username,
281379
+ mentorId,
281380
+ }, { skip: !tenantKey || !username || !mentorId });
281396
281381
  const runItems = (_a = run === null || run === void 0 ? void 0 : run.dataset_run_items) !== null && _a !== void 0 ? _a : [];
281397
281382
  // The backend returns run items in nondeterministic order (the eval store
281398
281383
  // is eventually consistent), so the raw order can shift between refetches.
@@ -281485,7 +281470,7 @@ function ExperimentDetailModal({ isOpen, onClose, tenantKey, username, mentorId,
281485
281470
  : labels.detailModal.tracesSuffixPlural, reviewCounts.pending > 0 && (jsxs("span", { className: "ml-2 inline-flex items-center gap-1 text-xs text-blue-600", children: [jsx(LoaderCircle, { className: "h-3 w-3 animate-spin" }), reviewCounts.pending, " ", labels.detailModal.reviewsRunningLabel] })), reviewCounts.completed > 0 && (jsxs("span", { className: "ml-2 text-xs text-gray-500", children: ["\u00B7 ", reviewCounts.completed, " ", labels.detailModal.reviewsCompletedLabel] })), reviewCounts.failed > 0 && (jsxs("span", { className: "ml-2 text-xs text-red-500", children: ["\u00B7 ", reviewCounts.failed, " ", labels.detailModal.reviewsFailedLabel] }))] }), jsxs("div", { className: "flex items-center gap-2", children: [jsx(Button$1, { size: "sm", variant: "outline", onClick: () => {
281486
281471
  refetchRun();
281487
281472
  refetchReviews();
281488
- }, disabled: isRunFetching || isReviewsFetching, "aria-label": labels.detailModal.refresh, title: labels.detailModal.refresh, children: jsx(RefreshCw, { className: isRunFetching || isReviewsFetching ? 'h-4 w-4 animate-spin' : 'h-4 w-4' }) }), jsxs(Button$1, { size: "sm", onClick: () => setShowJudge(true), disabled: runItems.length === 0, className: "bg-gradient-to-r from-[#2563EB] to-[#93C5FD] text-white hover:opacity-90", children: [jsx(Sparkles, { className: "h-4 w-4 mr-1" }), labels.detailModal.newReview] })] })] }), jsx("p", { className: "text-xs text-gray-500", children: labels.detailModal.reviewsHint })] }), allReviews.length > 0 && (jsx(ReviewsList, { reviews: allReviews, runItems: runItems, labels: labels, getLLMProviderDetails: getLLMProviderDetails })), isRunLoading ? (jsx("div", { className: "flex justify-center items-center h-40", children: jsx(Spinner, {}) })) : (jsxs("div", { className: "space-y-3", children: [runItems.length === 0 && (jsx("div", { className: "rounded-md border border-gray-200 bg-gray-50 p-6 text-center text-sm text-gray-600", children: labels.detailModal.waitingHint })), sortedRunItems.map((item) => {
281473
+ }, disabled: isRunFetching || isReviewsFetching, "aria-label": labels.detailModal.refresh, title: labels.detailModal.refresh, children: jsx(RefreshCw, { className: isRunFetching || isReviewsFetching ? 'h-4 w-4 animate-spin' : 'h-4 w-4' }) }), jsxs(Button$1, { size: "sm", onClick: () => setShowJudge(true), disabled: runItems.length === 0, className: "bg-gradient-to-r from-[#2563EB] to-[#93C5FD] text-white hover:opacity-90", children: [jsx(Sparkles, { className: "h-4 w-4 mr-1" }), labels.detailModal.newReview] })] })] }), jsx("p", { className: "text-xs text-gray-500", children: labels.detailModal.reviewsHint })] }), allReviews.length > 0 && (jsx(ReviewsList, { reviews: allReviews, runItems: runItems, labels: labels, llmProviders: llmProviders !== null && llmProviders !== void 0 ? llmProviders : [] })), isRunLoading ? (jsx("div", { className: "flex justify-center items-center h-40", children: jsx(Spinner, {}) })) : (jsxs("div", { className: "space-y-3", children: [runItems.length === 0 && (jsx("div", { className: "rounded-md border border-gray-200 bg-gray-50 p-6 text-center text-sm text-gray-600", children: labels.detailModal.waitingHint })), sortedRunItems.map((item) => {
281489
281474
  var _a;
281490
281475
  // Sort scores by created_at so the order is stable across refetches.
281491
281476
  const scores = [...((_a = item.scores) !== null && _a !== void 0 ? _a : [])].sort((a, b) => {
@@ -281502,7 +281487,7 @@ function ExperimentDetailModal({ isOpen, onClose, tenantKey, username, mentorId,
281502
281487
  var _a, _b;
281503
281488
  return (jsxs("div", { className: "group inline-flex items-center gap-1.5 rounded-full border border-gray-200 bg-gray-50 px-2 py-1 text-xs", title: (_a = score.comment) !== null && _a !== void 0 ? _a : undefined, children: [jsx("span", { className: "font-medium text-gray-700", children: score.name }), jsx("span", { className: "text-gray-600", children: (_b = score.string_value) !== null && _b !== void 0 ? _b : score.value }), jsx("button", { type: "button", "aria-label": labels.detailModal.removeScoreAriaLabel, onClick: () => handleDeleteScore(score.id), className: "opacity-50 transition-opacity group-hover:opacity-100", children: jsx(Trash2, { className: "h-3 w-3 text-red-500" }) })] }, score.id));
281504
281489
  }) })) : (jsx("p", { className: "text-xs text-gray-500", children: labels.detailModal.noScoresHint }))] }), jsxs("div", { className: "space-y-1 pt-2 border-t border-gray-100", children: [jsx("p", { className: "text-xs font-medium text-gray-500", children: labels.detailModal.addScoreHeading }), jsx(ManualScoreRow, { tenantKey: tenantKey, username: username, datasetName: datasetName, runName: runName, traceId: item.trace_id, runId: (run === null || run === void 0 ? void 0 : run.id) || undefined, labels: labels.detailModal })] })] }))] }, item.id));
281505
- })] })), showJudge && run && (jsx(LlmJudgeModal, { isOpen: showJudge, onClose: () => setShowJudge(false), tenantKey: tenantKey, username: username, mentorId: mentorId, datasetName: datasetName, runName: runName, labels: labels.evaluateModal, getLLMProviderDetails: getLLMProviderDetails }))] }) }));
281490
+ })] })), showJudge && run && (jsx(LlmJudgeModal, { isOpen: showJudge, onClose: () => setShowJudge(false), tenantKey: tenantKey, username: username, mentorId: mentorId, datasetName: datasetName, runName: runName, labels: labels.evaluateModal }))] }) }));
281506
281491
  }
281507
281492
  /**
281508
281493
  * Reviews list — surfaces every LLM-as-Judge task recorded against this
@@ -281514,14 +281499,14 @@ function ExperimentDetailModal({ isOpen, onClose, tenantKey, username, mentorId,
281514
281499
  * Per row: status badge, the score this review is producing, the LLM
281515
281500
  * doing the judging, when it started, and (on failure) the error.
281516
281501
  */
281517
- function ReviewsList({ reviews, runItems, labels, getLLMProviderDetails, }) {
281502
+ function ReviewsList({ reviews, runItems, labels, llmProviders, }) {
281518
281503
  const [expandedReview, setExpandedReview] = React__default.useState(null);
281519
281504
  return (jsxs("div", { className: "mb-4 rounded-md border border-gray-200", children: [jsx("div", { className: "px-4 py-2 border-b border-gray-200 bg-gray-50", children: jsx("h4", { className: "text-xs font-semibold uppercase tracking-wide text-gray-600", children: labels.detailModal.reviewsHeading }) }), jsx("ul", { className: "divide-y divide-gray-100", children: reviews.map((review) => {
281520
- var _a, _b, _c, _d, _e, _f, _g;
281505
+ var _a, _b, _c, _d;
281521
281506
  const provider = (_a = review.request_metadata) === null || _a === void 0 ? void 0 : _a.llm_provider;
281522
281507
  const llmName = (_b = review.request_metadata) === null || _b === void 0 ? void 0 : _b.llm_name;
281523
281508
  const criteria = (_c = review.request_metadata) === null || _c === void 0 ? void 0 : _c.criteria;
281524
- const providerDetails = provider ? getLLMProviderDetails(provider, llmName) : undefined;
281509
+ const providerRow = provider ? llmProviders.find((p) => p.name === provider) : undefined;
281525
281510
  const started = parseEvalDate((_d = review.started_at) !== null && _d !== void 0 ? _d : review.created_at);
281526
281511
  const completed = parseEvalDate(review.completed_at);
281527
281512
  const rowKey = review.task_id || `${review.id}`;
@@ -281538,7 +281523,7 @@ function ReviewsList({ reviews, runItems, labels, getLLMProviderDetails, }) {
281538
281523
  .map((score) => ({ score, questionText }));
281539
281524
  })
281540
281525
  : [];
281541
- return (jsxs("li", { children: [jsxs("button", { type: "button", onClick: () => setExpandedReview(isExpanded ? null : rowKey), "aria-expanded": isExpanded, "aria-label": labels.detailModal.reviewDetailsAriaLabel, className: "w-full px-4 py-3 flex flex-col gap-2 text-left hover:bg-gray-50 sm:flex-row sm:items-start sm:justify-between", children: [jsxs("div", { className: "min-w-0 flex-1", children: [jsxs("div", { className: "flex items-center gap-2 flex-wrap", children: [jsx(ChevronDown, { className: cn('h-4 w-4 flex-shrink-0 text-gray-400 transition-transform', isExpanded && 'rotate-180') }), jsx(ReviewStatusBadge, { status: review.status, labels: labels.detailModal.reviewStatus }), jsx("span", { className: "text-sm font-medium text-gray-900 truncate", children: review.score_name || labels.detailModal.reviewUnnamedScore })] }), criteria && !isExpanded && (jsx("p", { className: "text-xs text-gray-600 mt-1 line-clamp-2", children: criteria })), review.error_message && !isExpanded && (jsx("p", { className: "text-xs text-red-600 mt-1 line-clamp-2", children: review.error_message }))] }), jsxs("div", { className: "flex flex-col items-end gap-1 text-xs text-gray-500 sm:min-w-[12rem]", children: [(providerDetails === null || providerDetails === void 0 ? void 0 : providerDetails.logo) ? (jsxs("span", { className: "inline-flex items-center gap-1", children: [jsx("img", { src: providerDetails.logo, alt: (_g = (_f = (_e = providerDetails.name) !== null && _e !== void 0 ? _e : llmName) !== null && _f !== void 0 ? _f : provider) !== null && _g !== void 0 ? _g : '', className: "h-3 w-3" }), llmName !== null && llmName !== void 0 ? llmName : provider] })) : llmName || provider ? (jsx("span", { children: llmName !== null && llmName !== void 0 ? llmName : provider })) : null, completed
281526
+ return (jsxs("li", { children: [jsxs("button", { type: "button", onClick: () => setExpandedReview(isExpanded ? null : rowKey), "aria-expanded": isExpanded, "aria-label": labels.detailModal.reviewDetailsAriaLabel, className: "w-full px-4 py-3 flex flex-col gap-2 text-left hover:bg-gray-50 sm:flex-row sm:items-start sm:justify-between", children: [jsxs("div", { className: "min-w-0 flex-1", children: [jsxs("div", { className: "flex items-center gap-2 flex-wrap", children: [jsx(ChevronDown, { className: cn('h-4 w-4 flex-shrink-0 text-gray-400 transition-transform', isExpanded && 'rotate-180') }), jsx(ReviewStatusBadge, { status: review.status, labels: labels.detailModal.reviewStatus }), jsx("span", { className: "text-sm font-medium text-gray-900 truncate", children: review.score_name || labels.detailModal.reviewUnnamedScore })] }), criteria && !isExpanded && (jsx("p", { className: "text-xs text-gray-600 mt-1 line-clamp-2", children: criteria })), review.error_message && !isExpanded && (jsx("p", { className: "text-xs text-red-600 mt-1 line-clamp-2", children: review.error_message }))] }), jsxs("div", { className: "flex flex-col items-end gap-1 text-xs text-gray-500 sm:min-w-[12rem]", children: [(providerRow === null || providerRow === void 0 ? void 0 : providerRow.logo) ? (jsxs("span", { className: "inline-flex items-center gap-1", children: [jsx("img", { src: providerRow.logo, alt: llmProviderLabel(providerRow), className: "h-3 w-3" }), llmName !== null && llmName !== void 0 ? llmName : provider] })) : llmName || provider ? (jsx("span", { children: llmName !== null && llmName !== void 0 ? llmName : provider })) : null, completed
281542
281527
  ? `${labels.detailModal.reviewFinishedPrefix} ${formatDistanceToNow(completed, { addSuffix: true })}`
281543
281528
  : started
281544
281529
  ? `${labels.detailModal.reviewStartedPrefix} ${formatDistanceToNow(started, { addSuffix: true })}`
@@ -281900,7 +281885,7 @@ function AgentEvaluationTab({ userIsStudent: userIsStudentProp, ...props }) {
281900
281885
  return null;
281901
281886
  return jsx(AgentEvaluationTabContent, { ...props });
281902
281887
  }
281903
- function AgentEvaluationTabContent({ getLLMProviderDetails, labels: labelsOverride, PaginationComponent, }) {
281888
+ function AgentEvaluationTabContent({ labels: labelsOverride, PaginationComponent, }) {
281904
281889
  var _a, _b;
281905
281890
  const { tenantKey, mentorId, username } = useAgentSettings();
281906
281891
  const dispatch = useDispatch();
@@ -282051,7 +282036,7 @@ function AgentEvaluationTabContent({ getLLMProviderDetails, labels: labelsOverri
282051
282036
  }), children: [jsx(Trash2, { className: "mr-2 h-4 w-4" }), labels.actions.delete] })] })] }) })] }, (_b = (_a = run.task_record_id) !== null && _a !== void 0 ? _a : run.id) !== null && _b !== void 0 ? _b : run.name));
282052
282037
  }), runs.length === 0 && (jsx(TableRow$1, { children: jsx(TableCell$1, { colSpan: 5, className: "p-6 text-center text-sm text-gray-500", children: labels.table.emptyState.replace('{dataset}', selectedBenchmark) }) }))] })] })) }) })), selectedBenchmark &&
282053
282038
  totalPages > 1 &&
282054
- (PaginationComponent ? (jsx(PaginationComponent, { currentPage: page, totalPages: totalPages, onPageChange: setPage, disabled: isRunsLoading })) : (jsx(AdvancedPagination, { currentPage: page, totalPages: totalPages, onPageChange: setPage }))), showManageBenchmarks && (jsx(Dialog, { open: showManageBenchmarks, onOpenChange: setShowManageBenchmarks, children: jsxs(DialogContent, { "aria-label": "manage-benchmarks", className: "max-w-4xl max-h-[85vh] overflow-y-auto", children: [jsxs(DialogHeader, { children: [jsx(DialogTitle, { children: labels.manageBenchmarksModal.title }), jsx(DialogDescription, { children: labels.manageBenchmarksModal.description })] }), jsx(BenchmarksTab, { tenantKey: tenantKey, username: username })] }) })), showStart && (jsx(StartExperimentModal, { isOpen: showStart, onClose: () => setShowStart(false), tenantKey: tenantKey, username: username, mentorUniqueId: mentorId, datasetName: selectedBenchmark, onStarted: () => setPage(1), labels: labels.startExperimentModal })), selectedRun && (jsx(ExperimentDetailModal, { isOpen: !!selectedRun, onClose: () => setSelectedRun(null), tenantKey: tenantKey, username: username, mentorId: mentorId, datasetName: selectedRun.datasetName, runName: selectedRun.runName, labels: labels, getLLMProviderDetails: getLLMProviderDetails })), judgeRun && (jsx(LlmJudgeModal, { isOpen: !!judgeRun, onClose: () => setJudgeRun(null), tenantKey: tenantKey, username: username, mentorId: mentorId, datasetName: judgeRun.datasetName, runName: judgeRun.runName, labels: labels.evaluateModal, getLLMProviderDetails: getLLMProviderDetails })), deleteRun && (jsx(DeleteExperimentModal, { isOpen: !!deleteRun, onClose: () => setDeleteRun(null), tenantKey: tenantKey, username: username, datasetName: deleteRun.datasetName, runName: deleteRun.runName, labels: labels.deleteModal }))] })] }));
282039
+ (PaginationComponent ? (jsx(PaginationComponent, { currentPage: page, totalPages: totalPages, onPageChange: setPage, disabled: isRunsLoading })) : (jsx(AdvancedPagination, { currentPage: page, totalPages: totalPages, onPageChange: setPage }))), showManageBenchmarks && (jsx(Dialog, { open: showManageBenchmarks, onOpenChange: setShowManageBenchmarks, children: jsxs(DialogContent, { "aria-label": "manage-benchmarks", className: "max-w-4xl max-h-[85vh] overflow-y-auto", children: [jsxs(DialogHeader, { children: [jsx(DialogTitle, { children: labels.manageBenchmarksModal.title }), jsx(DialogDescription, { children: labels.manageBenchmarksModal.description })] }), jsx(BenchmarksTab, { tenantKey: tenantKey, username: username })] }) })), showStart && (jsx(StartExperimentModal, { isOpen: showStart, onClose: () => setShowStart(false), tenantKey: tenantKey, username: username, mentorUniqueId: mentorId, datasetName: selectedBenchmark, onStarted: () => setPage(1), labels: labels.startExperimentModal })), selectedRun && (jsx(ExperimentDetailModal, { isOpen: !!selectedRun, onClose: () => setSelectedRun(null), tenantKey: tenantKey, username: username, mentorId: mentorId, datasetName: selectedRun.datasetName, runName: selectedRun.runName, labels: labels })), judgeRun && (jsx(LlmJudgeModal, { isOpen: !!judgeRun, onClose: () => setJudgeRun(null), tenantKey: tenantKey, username: username, mentorId: mentorId, datasetName: judgeRun.datasetName, runName: judgeRun.runName, labels: labels.evaluateModal })), deleteRun && (jsx(DeleteExperimentModal, { isOpen: !!deleteRun, onClose: () => setDeleteRun(null), tenantKey: tenantKey, username: username, datasetName: deleteRun.datasetName, runName: deleteRun.runName, labels: labels.deleteModal }))] })] }));
282055
282040
  }
282056
282041
 
282057
282042
  const CUSTOM_AGENTS_LIMIT = 6;
@@ -338443,5 +338428,5 @@ var index_es = /*#__PURE__*/Object.freeze({
338443
338428
  vectorsRatio: vectorsRatio
338444
338429
  });
338445
338430
 
338446
- export { AGENT_ACCESS_TAB_LABELS, AGENT_ANALYTICS_TAB_LABELS, AGENT_API_TAB_LABELS, AGENT_DATASETS_TAB_LABELS, AGENT_DISCLAIMERS_TAB_LABELS, AGENT_EMBED_TAB_LABELS, AGENT_EVALUATION_TAB_LABELS, AGENT_GRADER_TAB_LABELS, AGENT_HISTORY_TAB_LABELS, AGENT_HUMAN_SUPPORT_TAB_LABELS, AGENT_LLM_TAB_LABELS, AGENT_LTI_TAB_LABELS, AGENT_MEMORY_TAB_LABELS, AGENT_PICKER_LABELS, AGENT_PRIVACY_TAB_LABELS, AGENT_PROMPTS_TAB_LABELS, AGENT_SAFETY_TAB_LABELS, AGENT_SCREENSHARE_TAB_LABELS, AGENT_SEARCH_LABELS, AGENT_SETTINGS_TAB_LABELS, AGENT_SKILLS_TAB_LABELS, AGENT_SPEND_CAPS_TAB_LABELS, AGENT_TASKS_TAB_LABELS, AGENT_TOOLS_TAB_LABELS, AGENT_VOICE_TAB_LABELS, Account, AgentAccessTab, AgentAnalyticsTab, AgentApiTab, AgentCard, AgentDatasetsTab, AgentDisclaimersTab, AgentEmbedTab, AgentEmptyState, AgentEvaluationTab, AgentGraderTab, AgentHistoryTab, AgentHumanSupportTab, AgentLLMTab, AgentLtiTab, AgentMemoryTab, AgentPrivacyTab, AgentPromptsTab, AgentSafetyTab, AgentScreenShareTab, AgentSearch, AgentSearchFilters, AgentSearchInput, AgentSettingsProvider, AgentSettingsTab, AgentSkillsTab, AgentSpendCapsTab, AgentTasksTab, AgentToolsTab, AgentVoiceTab, AppSidebar, CATALOGS, Chat, ClientErrorPage, ConversationStarters$1 as ConversationStarters, CopyButton, CopyMentorModal, CourseBox, CourseContentLayout, CourseContentTabPage, CreatePathwayModal, CredentialDetailModal, CredentialMiniBox, DEFAULT_ANALYTICS_HUB_TABS, DEFAULT_LOCALE, DeleteMentorModal, EMPTY_PROGRAM_SETTINGS_FORM, EdxIframe, ErrorPage, GRADING_TOOL_NAME, GreetingMethod, LLMProviderModal, LocalModelRow, McpTab, MediaBox, MentorSelectionGrid, NavBar, OrganizationTab, PLATFORM_SIDEBAR_FLYOUT_ITEM_COLOR, PLATFORM_SIDEBAR_FLYOUT_TITLE_COLOR, PLATFORM_SIDEBAR_NAV_ACTIVE_BG_OPEN, PLATFORM_SIDEBAR_NAV_MUTED, PathwayDetailModal, PlatformAccountSheet, PlatformSidebar, PlatformSidebarCollapsedLabelFlyout, PlatformSidebarCollapsedNavFlyout, PlatformSidebarFooter, PlatformSidebarNavDivider, PlatformSidebarNavSection, PlatformSidebarSubNavItem, ProfileInfoCards, ProfileTabs, ProgramDetailModal, ProjectsSidebarDropdown, SPEND_CAPS_SUB_TABS, SUPPORTED_LOCALES, SidebarInset, SidebarProvider, SidebarTrigger, SpendCapUsage, SsoLogin, StarButton, UserProfileDropdown, UserProfileModal, WebContainersI18nProvider, formatDateString, getCatalog, getLLMProviderDetails, getProviderName, initializeLocalStorageWithObject, isValidLocale, resolveAgentPickerLabels, resolveAgentSearchLabels, resolveLLMTabLabels, resolveSettingsTabLabels, useAgentSearch, useAgentSearchWithPagination, useAgentSettings, useAgentStar, useGetChatDetails, usePlatformSidebarFooterActions, usePlatformSidebarNavCallback, useSidebar, useT, useTimeTracker, useWebContainersI18n, useWebContainersLocale };
338431
+ export { AGENT_ACCESS_TAB_LABELS, AGENT_ANALYTICS_TAB_LABELS, AGENT_API_TAB_LABELS, AGENT_DATASETS_TAB_LABELS, AGENT_DISCLAIMERS_TAB_LABELS, AGENT_EMBED_TAB_LABELS, AGENT_EVALUATION_TAB_LABELS, AGENT_GRADER_TAB_LABELS, AGENT_HISTORY_TAB_LABELS, AGENT_HUMAN_SUPPORT_TAB_LABELS, AGENT_LLM_TAB_LABELS, AGENT_LTI_TAB_LABELS, AGENT_MEMORY_TAB_LABELS, AGENT_PICKER_LABELS, AGENT_PRIVACY_TAB_LABELS, AGENT_PROMPTS_TAB_LABELS, AGENT_SAFETY_TAB_LABELS, AGENT_SCREENSHARE_TAB_LABELS, AGENT_SEARCH_LABELS, AGENT_SETTINGS_TAB_LABELS, AGENT_SKILLS_TAB_LABELS, AGENT_SPEND_CAPS_TAB_LABELS, AGENT_TASKS_TAB_LABELS, AGENT_TOOLS_TAB_LABELS, AGENT_VOICE_TAB_LABELS, Account, AgentAccessTab, AgentAnalyticsTab, AgentApiTab, AgentCard, AgentDatasetsTab, AgentDisclaimersTab, AgentEmbedTab, AgentEmptyState, AgentEvaluationTab, AgentGraderTab, AgentHistoryTab, AgentHumanSupportTab, AgentLLMTab, AgentLtiTab, AgentMemoryTab, AgentPrivacyTab, AgentPromptsTab, AgentSafetyTab, AgentScreenShareTab, AgentSearch, AgentSearchFilters, AgentSearchInput, AgentSettingsProvider, AgentSettingsTab, AgentSkillsTab, AgentSpendCapsTab, AgentTasksTab, AgentToolsTab, AgentVoiceTab, AppSidebar, CATALOGS, Chat, ClientErrorPage, ConversationStarters$1 as ConversationStarters, CopyButton, CopyMentorModal, CourseBox, CourseContentLayout, CourseContentTabPage, CreatePathwayModal, CredentialDetailModal, CredentialMiniBox, DEFAULT_ANALYTICS_HUB_TABS, DEFAULT_LOCALE, DeleteMentorModal, EMPTY_PROGRAM_SETTINGS_FORM, EdxIframe, ErrorPage, GRADING_TOOL_NAME, GreetingMethod, LLMProviderModal, LocalModelRow, McpTab, MediaBox, MentorSelectionGrid, NavBar, OrganizationTab, PLATFORM_SIDEBAR_FLYOUT_ITEM_COLOR, PLATFORM_SIDEBAR_FLYOUT_TITLE_COLOR, PLATFORM_SIDEBAR_NAV_ACTIVE_BG_OPEN, PLATFORM_SIDEBAR_NAV_MUTED, PathwayDetailModal, PlatformAccountSheet, PlatformSidebar, PlatformSidebarCollapsedLabelFlyout, PlatformSidebarCollapsedNavFlyout, PlatformSidebarFooter, PlatformSidebarNavDivider, PlatformSidebarNavSection, PlatformSidebarSubNavItem, ProfileInfoCards, ProfileTabs, ProgramDetailModal, ProjectsSidebarDropdown, SPEND_CAPS_SUB_TABS, SUPPORTED_LOCALES, SidebarInset, SidebarProvider, SidebarTrigger, SpendCapUsage, SsoLogin, StarButton, UserProfileDropdown, UserProfileModal, WebContainersI18nProvider, formatDateString, getCatalog, getProviderName, initializeLocalStorageWithObject, isValidLocale, resolveAgentPickerLabels, resolveAgentSearchLabels, resolveLLMTabLabels, resolveSettingsTabLabels, useAgentSearch, useAgentSearchWithPagination, useAgentSettings, useAgentStar, useGetChatDetails, usePlatformSidebarFooterActions, usePlatformSidebarNavCallback, useSidebar, useT, useTimeTracker, useWebContainersI18n, useWebContainersLocale };
338447
338432
  //# sourceMappingURL=index.esm.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@iblai/iblai-js",
3
- "version": "2.11.1",
3
+ "version": "2.11.2",
4
4
  "description": "Unified JavaScript SDK for IBL.ai — re-exports data-layer, web-containers, and web-utils under a single package",
5
5
  "type": "module",
6
6
  "engines": {
@@ -76,9 +76,9 @@
76
76
  "axios": "1.13.6",
77
77
  "dotenv": "16.6.1",
78
78
  "winston": "3.19.0",
79
- "@iblai/mcp": "1.12.13",
80
- "@iblai/data-layer": "1.13.3",
81
- "@iblai/web-containers": "1.20.1",
79
+ "@iblai/data-layer": "1.13.4",
80
+ "@iblai/mcp": "1.12.14",
81
+ "@iblai/web-containers": "1.20.2",
82
82
  "@iblai/web-utils": "2.4.6"
83
83
  },
84
84
  "peerDependencies": {