@iblai/iblai-js 2.7.1 → 2.7.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/web-containers/source/index.esm.js +90012 -89817
- package/dist/web-containers/source/next/index.esm.js +869 -237
- package/package.json +4 -4
|
@@ -3,7 +3,7 @@ import * as React from 'react';
|
|
|
3
3
|
import React__default, { useState, useEffect, forwardRef, createElement as createElement$3, useLayoutEffect, createContext, useContext, useMemo, useReducer, useRef, useImperativeHandle, useCallback, isValidElement, Children, PureComponent, cloneElement, Component as Component$1, useId as useId$2, useDebugValue, lazy, Suspense } from 'react';
|
|
4
4
|
import * as dataLayer from '@iblai/data-layer';
|
|
5
5
|
import { useGetUserMetadataQuery, useGetUserMetadataEdxQuery, useUpdateUserMetadataMutation, useUpdateUserMetadataEdxMutation, useUploadProfileImageMutation, useResetPasswordMutation, useSelfRetireMutation, useCreateUserInstitutionMutation, useGetUserInstitutionsQuery, useCreateUserEducationMutation, useUpdateUserEducationMutation, useDeleteUserEducationMutation, useGetUserEducationQuery, useCreateUserCompanyMutation, useGetUserCompaniesQuery, useCreateUserExperienceMutation, useUpdateUserExperienceMutation, useDeleteUserExperienceMutation, useGetUserExperienceQuery, useGetUserResumeQuery, useCreateUserResumeMutation, useGetMySubscriptionsQuery, useGetItemSubscriptionQuery, useCancelSubscriptionMutation, useGetLearnerDetailsQuery, useGetUserEnrolledCoursesQuery, useGetUserEnrolledProgramsQuery, useGetUserCatalogPathwaysQuery, useGetUsersAsAssertionsQuery, useGetUserEarnedSkillsQuery, useGetUserReportedSkillsQuery, useGetUserDesiredSkillsQuery, useCreateGlobalMemoryMutation, useUpdateGlobalMemoryMutation, useUpdateMentorMemoryMutation, useGetMemsearchStatusQuery, useGetUserMemorySettingsQuery, useUpdateUserMemorySettingsMutation, useGetGlobalMemoriesQuery, useGetAllMentorMemoriesQuery, useDeleteGlobalMemoryMutation, useDeleteMentorMemoryMutation, useGetAiSearchMentorsQuery, useStarMentorMutation, useUnstarMentorMutation, useGetUserChatHistoryReportsQuery, USER_CHAT_HISTORY_REPORT_ACTIVE_STATES, useCreateUserChatHistoryReportMutation, useLazyGetDownloadReportFromURLQuery, useGetUserChatHistoryReportStatusQuery, userChatHistoryApiSlice, USER_CHAT_HISTORY_QUERY_KEYS, useGetUserChatHistoryQuery, useGetUserChatHistoryFilterQuery, useGetMentorsQuery, useGetUserChatHistorySessionQuery, useInviteUserMutation, usePlatformInvitationsQuery, useCreateCatalogInvitationCourseBulkMutation, useGetCatalogInvitationsCourseQuery, useLazyPlatformUsersQuery, useLazyPlatformUserGroupsQuery, useGetPersonnalizedSearchQuery, useCreateCatalogInvitationProgramBulkMutation, useGetCatalogInvitationsProgramQuery, useUpdateUserRoleMutation, useUpdateUserStatusMutation, useUpdatePlatformUserRoleWithPoliciesMutation, usePlatformUsersQuery, useGetAccessibleUsersQuery, isPoliciesResponse, platformApiSlice, featureTags, useLazyGetRbacTeamsAccessListQuery, useCreateRbacTeamsAccessMutation, useGetRbacGroupsQuery, usePlatformUserGroupsQuery, useCreateRbacGroupMutation, useUpdateRbacGroupMutation, useDeleteRbacGroupMutation, useCreatePlatformUserGroupMutation, useUpdatePlatformUserGroupMutation, useDeletePlatformUserGroupMutation, useGetRbacGroupDetailsQuery, useGetPlatformUserGroupDetailsQuery, useGetRbacPermissionsMutation, useGetRbacRolesQuery, useCreateRbacRoleMutation, useUpdateRbacRoleMutation, useDeleteRbacRoleMutation, useGetRbacRoleDetailsQuery, useGetRbacActionsDefinitionQuery, useGetRbacResourceDiscoveryQuery, useGetRbacResourceListQuery, useGetRbacPoliciesQuery, useCreateRbacPolicyMutation, useUpdateRbacPolicyMutation, useDeleteRbacPolicyMutation, useGetRbacPolicyDetailsQuery, useGetWatchedGroupsQuery, useCreateWatchedGroupMutation, useUpdateWatchedGroupMutation, useDeleteWatchedGroupMutation, useAddWatchedUserMutation, useRemoveWatchedUserMutation, useAddWatcherMutation, useUpdateWatcherMutation, useDeleteWatcherMutation, useGetWatchedUsersQuery, useGetWatchersQuery, WATCHER_NOTIFICATION_EVENTS, WATCHER_NOTIFICATION_EVENT_LABELS, useGetApplicationFormsQuery, useGetApplicationFormDetailQuery, useCreateApplicationFormMutation, useUpdateApplicationFormMutation, useGetApplicationAdminDetailQuery, useInviteApplicationCandidateMutation, useLazyGetApplicationAttachmentFileQuery, useCreateApplicationWaiverMutation, useDeleteApplicationWaiverMutation, useSubmitApplicationOverrideMutation, useUpdateApplicationPlacementMutation, useDeleteApplicationCourseAssignmentMutation, useCreateApplicationNoteMutation, useUpdateApplicationNoteMutation, useDeleteApplicationNoteMutation, useTransitionApplicationMutation, useLinkApplicationCandidateMutation, useCreateApplicationPlacementsMutation, useGetCustomMentorsQuery, useCreateApplicationCourseAssignmentsMutation, useGetApplicationBlocksQuery, useDeleteApplicationBlockMutation, useGetApplicationsPipelineStatsQuery, useGetApplicationsPipelineQuery, useGetTenantMetadataQuery, useUpdateTenantMetadataMutation, useLazyGetTenantMetadataQuery, useDeleteApiKeyMutation, useGetApiKeyQuery, useGetApiKeyFieldPermissionsQuery, useUpdateApiKeyMutation, useGetApiKeysQuery, unwrapApiTokenList, useCreateApiKeyMutation, useCreateLLMCredentialMutation, useGetCredentialsSchemaQuery, useGetMaskedLLMCredentialsQuery, useGetLlmsQuery, useDeleteIntegrationCredentialMutation, useDeleteCredentialMutation, useCreateIntegrationCredentialMutation, useGetIntegrationCredentialsSchemaQuery, useGetMaskedIntegrationCredentialsQuery, useGetAccountBillingInfoQuery, useUpdateAutoRechargeInfoMutation, useTriggerAutoRechargeMutation, useGetAgentSpendCapQuery, useGetTenantSpendCapQuery, useListAgentUserSpendCapsQuery, useUpsertAgentSpendCapMutation, useDeleteAgentSpendCapMutation, useUpsertTenantSpendCapMutation, useDeleteTenantSpendCapMutation, useUpsertUserSpendCapMutation, useDeleteUserSpendCapMutation, useGetFinancialStatsQuery, useListAgentSpendCapsQuery, useGetCreditTransactionsQuery, useCreateStripeCustomerPortalMutation, useSetPlatformConfigurationsMutation, useGetPlatformConfigurationsQuery, useUpdatePlatformMembershipMutation, useGetPlatformMembershipQuery, useGetCustomDomainsQuery, useCreateCustomDomainMutation, useDeleteCustomDomainMutation, useGetStudentMentorCreationStatusQuery, useSetStudentMentorCreationStatusMutation, coreApiSlice, recommendationPromptTypeEnum, useGetRecommendedPromptsListQuery, useCreateRecommendedPromptMutation, useUpdateRecommendedPromptMutation, useDeleteRecommendedPromptMutation, useLazyGetPublicPlatformImageAssetFileUrlQuery, useCreatePlatformImageAssetMutation, useGetProviderConfigQuery, useCreateProviderConfigMutation, useDeleteProviderConfigMutation, useGetExternalMappingQuery, useGetCredentialsListQuery, useCreateExternalMappingMutation, useDeleteExternalMappingMutation, useGetMemsearchConfigQuery, useUpdateMemsearchConfigMutation, useGetStripeConnectStatusQuery, useStartStripeConnectOnboardingMutation, useLazyGetStripeConnectDashboardQuery, useListPaywallsQuery, useListPricesQuery, useCreatePriceMutation, useUpdatePriceMutation, useDeletePriceMutation, useGetPaywallConfigQuery, useEnablePaywallMutation, useUpdatePaywallMutation, useLazyGetCourseMetaDataQuery, useLazyGetCourseCompletionOutlinesQuery, useLazyGetCourseEligibilityQuery, useLazyGetEdxSSOTokenQuery, useCreateCourseEnrollmentMutation, useCreateStripeCheckoutSessionMutation, useLazyGetCourseProgressQuery, useLazyGetCourseCompletionQuery, useUpdateExamAttemptMutation, useStartExamMutation, useLazyGetExamInfoQuery, useGetAgentSkillResourcesQuery, useCreateAgentSkillResourceMutation, useUpdateAgentSkillResourceMutation, useUploadAgentSkillResourceAssetMutation, useDeleteAgentSkillResourceMutation, useGetMentorSkillAssignmentsQuery, useGetAgentSkillsQuery, useCreateMentorSkillAssignmentMutation, useUpdateMentorSkillAssignmentMutation, useDeleteMentorSkillAssignmentMutation, useCreateAgentSkillMutation, useUpdateAgentSkillMutation, useDeleteAgentSkillMutation, filterSlashSkills, useGetMentorSkillAssignmentsInfiniteQuery, MENTOR_SKILL_ASSIGNMENTS_PAGE_SIZE, useUpdateUserProjectMutation, useGetTrainingDocumentsQuery, useGetVectorDocumentsQuery, useGetMentorMemoriesListQuery, useGetMemoryCategoriesAdminQuery, useCreateMentorMemoryMutation, useLazyGetConnectedServiceAuthUrlQuery, useCreateUserProjectMutation, useDeleteUserProjectMutation, useGetUserProjectDetailsQuery, useEditTrainingDocumentMutation, useAddTrainingDocumentMutation, useLazyGetCredentialsQuery, useGetMentorSettingsQuery, useEditMentorMutation, useUploadLightLogoMutation, useUploadDarkLogoMutation, useUpdatePlatformInfoMutation, LOGO_ENDPOINTS, useCreateEvalDatasetMutation, useAddEvalDatasetItemsMutation, useUploadEvalDatasetCsvMutation, useDeleteEvalDatasetItemMutation, useListEvalDatasetItemsQuery, evalTotalPages, useListEvalDatasetsQuery, useGetUserProjectsQuery, useCreateSessionIdMutation, useGetMentorCategoriesQuery, useGetTenantChatPrivacyConfigQuery as useGetTenantChatPrivacyConfigQuery$1, useGetCallConfigurationsQuery, useCreateCallConfigurationMutation, useUpdateCallConfigurationMutation, chatPrivacyApiSlice, useDeleteMentorMutation, useForkMentorMutation, useGetToolsQuery, useLazyGetMCPServersQuery, useOauthFindMutation, useLazyStartOAuthFlowQuery, useCreateMCPServerMutation, usePartialUpdateMCPServerMutation, useCreateMCPServerConnectionMutation, usePatchMCPServerConnectionMutation, useGetConnectedServicesQuery, useGetMCPServersQuery, useGetMCPServerConnectionsQuery, useUpdateMCPServerMutation, useDeleteMCPServerMutation, useEditMentorJsonMutation, useDisconnectServiceMutation, useGetPromptCategoriesQuery, PRIVACY_ACTIONS, PRIVACY_ENTITY_TYPES, useGetVoicesQuery, useGetVoiceQuery, isBaseAgentMentor, useGetGradeResultQuery, useOverrideGradeResultMutation, useGetChatHistoryFilterQuery, useListGradeResultsQuery, useGetMentorGraderConfigurationQuery, useListGraderCriteriaQuery, useCreateMentorGraderConfigurationMutation, useUpdateMentorGraderConfigurationMutation, useCreateGraderCriterionMutation, useUpdateGraderCriterionMutation, useDeleteGraderCriterionMutation, useGetSpendCapStatusQuery, useGetMentorPublicSettingsQuery, useGetChatHistoryQuery, useGetMentorSummariesQuery, useGetConversationMemoriesQuery, useGetSupportTicketsQuery, useGetSupportTicketMessagesQuery, useCreateSupportTicketMessageMutation, usePatchSupportTicketMutation, useCloseSupportTicketMutation, useCreateLtiMentorMutation, useUpdateLtiMentorMutation, useGetLtiMentorsQuery, useDeleteLtiMentorMutation, useCreateLtiKeyMutation, useUpdateLtiKeyMutation, useDeleteLtiKeyMutation, useGetLtiKeysQuery, useCreateLtiToolMutation, useUpdateLtiToolMutation, useGetLtiToolsQuery, useGetPlatformInfoQuery, useCreatePromptMutation, useGetPromptsSearchQuery, useUpdatePromptMutation, useCreateRedirectTokenMutation, useGetShareableLinkQuery, useCreateShareableLinkMutation, useUpdateShareableLinkMutation, useCreateDisclaimerMutation, useUpdateDisclaimerMutation, useGetDisclaimersQuery, useUpdateRbacMentorAccessMutation, useGetRbacMentorAccessListQuery, useStartEvalRunMutation, useEvaluateEvalRunMutation, useGetEvalRunQuery, useDeleteEvalScoreMutation, useListEvalRunJudgesQuery, useCreateEvalScoreMutation, useDeleteEvalRunMutation, useExportEvalRunMutation, useListEvalRunsQuery, useLazyGetEvalRunQuery, evaluationsApiSlice, EVALUATIONS_TAGS, useGetPersonnalizedMentorsQuery, useCreateCallCredentialsMutation, useGetGuidedPromptsQuery, useUpdateChatSessionSharedMutation, useUpdateMessageFeedbackMutation, useLazyGetPromptsSearchQuery, useLazyGetGuidedPromptsQuery, useGetPublicMentorsQuery, useGetPeriodicAgentsQuery, useGetPeriodicAgentLogsListQuery, useCreatePeriodicAgentMutation, useDeletePeriodicAgentMutation, useDeletePromptMutation, useDeleteTrainingDocumentMutation, useGetTrainingDocumentRetrainScheduleQuery, useCreateTrainingDocumentRetrainScheduleMutation, useCreateMemoryCategoryMutation, useUpdateMemoryCategoryMutation, useDeleteMemoryCategoryMutation, useUpdateArtifactMutation, useLazyGetArtifactVersionQuery, useLazyListArtifactVersionsQuery, useSetCurrentVersionMutation, useListArtifactVersionsQuery, useLazyGetArtifactQuery, useLazyListArtifactsQuery, useEditSessionMutation } from '@iblai/data-layer';
|
|
6
|
-
import { getInitials, useTenantMetadata, useCurrentTenant, selectRbacPermissions, isPureWatcher, WithPermissions, canWriteField, useStripeUpgrade, openExternalUrl, CHAT_AREA_SIZE, isAlphaNumeric32, checkRbacPermission, selectNumberOfActiveChatMessages, useUsername, selectStreaming, isLoggedIn, TOOLS, hasRemoteAiConfig, isSafariBrowser, useShowAttachment, useShowVoiceCall, useShowVoiceRecorder, useMentorSettings, selectShowingSharedChat, useShowFreeTrialDialog, useEmbedMode, chatInputSliceSelectors, useResponsive, useAccessingPublicRoute, useVisitingTenant, useModelFileUploadCapabilities, useChatFileUpload, useVoiceChat, selectAttachedFiles, MENTOR_CHAT_DOCUMENTS_EXTENSIONS, removeFile, chatInputSliceActions, chatActions, WithFormPermissions, useTenantContext, TimeTracker, advancedTabsProperties, defaultSessionIds, pickWritableFields, ANONYMOUS_USERNAME as ANONYMOUS_USERNAME$3, redirectToAuthSpaJoinTenant, selectActiveTab, useAxdToken, useWelcomeMessage, markdownToPlainText, useCachedSessionId, use402ErrorCheck, selectTokenEnabled, selectToken, useServiceWorker, useUserAgreement, useAdvancedChat, sendMessageToParentWebsite, useMentorTools, useFileDragDrop, eventBus, RemoteEvents, selectEnableChatActionsPopup, advancedTabs, isInIframe, getAuthSpaJoinUrl, addMessage, clearFiles, MENTOR_VISIBILITY } from '@iblai/web-utils';
|
|
6
|
+
import { removeAuthItem, setAuthItem, getInitials, useTenantMetadata, useCurrentTenant, selectRbacPermissions, isPureWatcher, WithPermissions, canWriteField, useStripeUpgrade, openExternalUrl, CHAT_AREA_SIZE, isAlphaNumeric32, checkRbacPermission, selectNumberOfActiveChatMessages, useUsername, selectStreaming, isLoggedIn, TOOLS, hasRemoteAiConfig, isSafariBrowser, useShowAttachment, useShowVoiceCall, useShowVoiceRecorder, useMentorSettings, selectShowingSharedChat, useShowFreeTrialDialog, useEmbedMode, chatInputSliceSelectors, useResponsive, useAccessingPublicRoute, useVisitingTenant, useModelFileUploadCapabilities, useChatFileUpload, useVoiceChat, selectAttachedFiles, MENTOR_CHAT_DOCUMENTS_EXTENSIONS, removeFile, chatInputSliceActions, chatActions, WithFormPermissions, useTenantContext, TimeTracker, advancedTabsProperties, defaultSessionIds, pickWritableFields, ANONYMOUS_USERNAME as ANONYMOUS_USERNAME$3, redirectToAuthSpaJoinTenant, selectActiveTab, useAxdToken, useWelcomeMessage, markdownToPlainText, useCachedSessionId, use402ErrorCheck, selectTokenEnabled, selectToken, useServiceWorker, useUserAgreement, useAdvancedChat, sendMessageToParentWebsite, useMentorTools, useFileDragDrop, eventBus, RemoteEvents, selectEnableChatActionsPopup, advancedTabs, isInIframe, getAuthSpaJoinUrl, addMessage, clearFiles, MENTOR_VISIBILITY } from '@iblai/web-utils';
|
|
7
7
|
import { useDispatch, useSelector } from 'react-redux';
|
|
8
8
|
import { toast } from 'sonner';
|
|
9
9
|
import { useSearchParams, useParams, useRouter, usePathname } from 'next/navigation';
|
|
@@ -101,17 +101,21 @@ const initializeLocalStorageWithObject = async (data, localStorageKeys) => {
|
|
|
101
101
|
deleteCookie('ibl_tenant');
|
|
102
102
|
// Clear visiting_tenant to prevent stale tenant data from causing redirect issues
|
|
103
103
|
// This is important during tenant switches where old visiting_tenant could redirect back to previous tenant
|
|
104
|
-
|
|
104
|
+
// Route through authStorage: under NEXT_PUBLIC_IBL_PER_TAB_AUTH this installs
|
|
105
|
+
// the session into sessionStorage (this tab's source of truth) AND mirrors the
|
|
106
|
+
// most-recent login into localStorage (the seed); with the flag off it is
|
|
107
|
+
// exactly today's localStorage behavior.
|
|
108
|
+
removeAuthItem('visiting_tenant');
|
|
105
109
|
// Clear current_tenant to ensure a fresh state on tenant switch
|
|
106
110
|
// The new current_tenant will be set by TenantProvider after redirect
|
|
107
|
-
|
|
108
|
-
|
|
111
|
+
removeAuthItem('current_tenant');
|
|
112
|
+
removeAuthItem('tenants');
|
|
109
113
|
Object.entries(data).forEach(([key, value]) => {
|
|
110
|
-
|
|
114
|
+
setAuthItem(key, value);
|
|
111
115
|
});
|
|
112
116
|
// Ensure tenant key is explicitly set from parsedData to prevent stale values
|
|
113
117
|
if (data.tenant) {
|
|
114
|
-
|
|
118
|
+
setAuthItem('tenant', data.tenant);
|
|
115
119
|
}
|
|
116
120
|
// Sync to cookies for cross-SPA synchronization
|
|
117
121
|
// This sets the NEW auth data in cookies after we've cleared the stale ones
|
|
@@ -99502,18 +99506,47 @@ var llmTabLabels$3 = {
|
|
|
99502
99506
|
title: "LLM Configuration",
|
|
99503
99507
|
description: "Configure the language model settings for your agent."
|
|
99504
99508
|
},
|
|
99509
|
+
infoBox: "This is the brain behind your agent — the AI model that powers every reply. Bigger models reason better on tricky questions; lighter ones are faster and cost less. Not sure? The default is a solid all-rounder.",
|
|
99505
99510
|
search: {
|
|
99506
99511
|
placeholder: "Search Providers"
|
|
99507
99512
|
},
|
|
99513
|
+
providerLogoAlt: "{providerName} logo",
|
|
99508
99514
|
toasts: {
|
|
99509
99515
|
updateSuccess: "LLM updated successfully",
|
|
99510
99516
|
updateError: "Failed to update LLM"
|
|
99511
99517
|
},
|
|
99512
99518
|
providerModal: {
|
|
99513
99519
|
title: "LLM Selection",
|
|
99514
|
-
description: "Select one of the
|
|
99520
|
+
description: "Select one of the agents provided by {providerName}",
|
|
99515
99521
|
searchPlaceholder: "Search",
|
|
99516
|
-
helpText: "Choose your preferred LLM from the available provider to tailor your experience."
|
|
99522
|
+
helpText: "Choose your preferred LLM from the available provider to tailor your experience.",
|
|
99523
|
+
providerIconAlt: "{providerName} icon",
|
|
99524
|
+
tooLargeTitle: "This model may be too large for your system",
|
|
99525
|
+
tooLargeDescription: "{modelName} needs about {modelSize}. It may run very slowly or fail to load on this machine. Download anyway?",
|
|
99526
|
+
cancel: "Cancel",
|
|
99527
|
+
downloadAnyway: "Download anyway",
|
|
99528
|
+
alreadyDownloadingTitle: "A model is already downloading",
|
|
99529
|
+
unnamedModel: "A model",
|
|
99530
|
+
alreadyDownloadingDescription: "{modelName} is being downloaded. Only one on-device model downloads at a time — wait for it to finish, or click {modelName} in the list to cancel it.",
|
|
99531
|
+
gotIt: "Got it",
|
|
99532
|
+
announceDownloaded: "{modelName} downloaded",
|
|
99533
|
+
announceCancelled: "Download cancelled",
|
|
99534
|
+
announceFailed: "Download failed",
|
|
99535
|
+
announceStarted: "Started downloading {modelName}",
|
|
99536
|
+
localModel: {
|
|
99537
|
+
onDevice: "On-device",
|
|
99538
|
+
starting: "Starting…",
|
|
99539
|
+
cancel: "Cancel",
|
|
99540
|
+
inUse: "In use",
|
|
99541
|
+
downloadFailedRetry: "Download failed — retry",
|
|
99542
|
+
ariaDownload: "Download {modelName}, {modelSize}, on-device model",
|
|
99543
|
+
ariaStarting: "Starting download of {modelName}",
|
|
99544
|
+
ariaDownloading: "Cancel download of {modelName}, {percent} percent",
|
|
99545
|
+
ariaInstalled: "Use {modelName}, on-device model",
|
|
99546
|
+
ariaSelected: "Selected {modelName}, on-device model, in use",
|
|
99547
|
+
ariaError: "Retry download of {modelName}",
|
|
99548
|
+
ariaErrorWithReason: "Retry download of {modelName}, {reason}"
|
|
99549
|
+
}
|
|
99517
99550
|
}
|
|
99518
99551
|
};
|
|
99519
99552
|
var llmsTabCreateLlmModal$3 = {
|
|
@@ -101834,9 +101867,6 @@ var tabsHumanSupportTab$3 = {
|
|
|
101834
101867
|
ticketListLabel: "Support ticket list",
|
|
101835
101868
|
ticketDetailLabel: "Support ticket details"
|
|
101836
101869
|
};
|
|
101837
|
-
var tabsLlmTab$3 = {
|
|
101838
|
-
providerLogoAlt: "{name} logo"
|
|
101839
|
-
};
|
|
101840
101870
|
var tabsPoliciestab$3 = {
|
|
101841
101871
|
searchPoliciesPlaceholder: "Search policies",
|
|
101842
101872
|
searchPoliciesLabel: "Search policies",
|
|
@@ -103745,7 +103775,6 @@ var en = {
|
|
|
103745
103775
|
tabsGroupstab: tabsGroupstab$3,
|
|
103746
103776
|
tabsHistoryTab: tabsHistoryTab$3,
|
|
103747
103777
|
tabsHumanSupportTab: tabsHumanSupportTab$3,
|
|
103748
|
-
tabsLlmTab: tabsLlmTab$3,
|
|
103749
103778
|
tabsPoliciestab: tabsPoliciestab$3,
|
|
103750
103779
|
tabsPrivacyTab: tabsPrivacyTab$3,
|
|
103751
103780
|
tabsPromptsTab: tabsPromptsTab$3,
|
|
@@ -106640,18 +106669,47 @@ var llmTabLabels$2 = {
|
|
|
106640
106669
|
title: "Configuration LLM",
|
|
106641
106670
|
description: "Configurez les paramètres du modèle de langage pour votre agent."
|
|
106642
106671
|
},
|
|
106672
|
+
infoBox: "C'est le cerveau de votre agent — le modèle d'IA qui alimente chaque réponse. Les modèles plus grands raisonnent mieux sur les questions complexes ; les plus légers sont plus rapides et moins coûteux. Pas sûr ? Le modèle par défaut est un excellent choix polyvalent.",
|
|
106643
106673
|
search: {
|
|
106644
106674
|
placeholder: "Rechercher des fournisseurs"
|
|
106645
106675
|
},
|
|
106676
|
+
providerLogoAlt: "Logo {providerName}",
|
|
106646
106677
|
toasts: {
|
|
106647
106678
|
updateSuccess: "LLM mis à jour avec succès",
|
|
106648
106679
|
updateError: "Échec de la mise à jour du LLM"
|
|
106649
106680
|
},
|
|
106650
106681
|
providerModal: {
|
|
106651
106682
|
title: "Sélection du LLM",
|
|
106652
|
-
description: "Sélectionnez l'un des
|
|
106683
|
+
description: "Sélectionnez l'un des agents proposés par {providerName}",
|
|
106653
106684
|
searchPlaceholder: "Rechercher",
|
|
106654
|
-
helpText: "Choisissez votre LLM préféré parmi les fournisseurs disponibles pour personnaliser votre expérience."
|
|
106685
|
+
helpText: "Choisissez votre LLM préféré parmi les fournisseurs disponibles pour personnaliser votre expérience.",
|
|
106686
|
+
providerIconAlt: "icône {providerName}",
|
|
106687
|
+
tooLargeTitle: "Ce modèle est peut-être trop volumineux pour votre système",
|
|
106688
|
+
tooLargeDescription: "{modelName} nécessite environ {modelSize}. Il peut être très lent ou ne pas se charger sur cette machine. Télécharger quand même ?",
|
|
106689
|
+
cancel: "Annuler",
|
|
106690
|
+
downloadAnyway: "Télécharger quand même",
|
|
106691
|
+
alreadyDownloadingTitle: "Un modèle est déjà en cours de téléchargement",
|
|
106692
|
+
unnamedModel: "Un modèle",
|
|
106693
|
+
alreadyDownloadingDescription: "{modelName} est en cours de téléchargement. Un seul modèle sur l'appareil est téléchargé à la fois : attendez la fin ou cliquez sur {modelName} dans la liste pour l'annuler.",
|
|
106694
|
+
gotIt: "Compris",
|
|
106695
|
+
announceDownloaded: "{modelName} téléchargé",
|
|
106696
|
+
announceCancelled: "Téléchargement annulé",
|
|
106697
|
+
announceFailed: "Échec du téléchargement",
|
|
106698
|
+
announceStarted: "Téléchargement de {modelName} démarré",
|
|
106699
|
+
localModel: {
|
|
106700
|
+
onDevice: "Sur l'appareil",
|
|
106701
|
+
starting: "Démarrage…",
|
|
106702
|
+
cancel: "Annuler",
|
|
106703
|
+
inUse: "Utilisé",
|
|
106704
|
+
downloadFailedRetry: "Échec du téléchargement — réessayer",
|
|
106705
|
+
ariaDownload: "Télécharger {modelName}, {modelSize}, modèle sur l'appareil",
|
|
106706
|
+
ariaStarting: "Démarrage du téléchargement de {modelName}",
|
|
106707
|
+
ariaDownloading: "Annuler le téléchargement de {modelName}, {percent} pour cent",
|
|
106708
|
+
ariaInstalled: "Utiliser {modelName}, modèle sur l'appareil",
|
|
106709
|
+
ariaSelected: "{modelName} sélectionné, modèle sur l'appareil, utilisé",
|
|
106710
|
+
ariaError: "Réessayer le téléchargement de {modelName}",
|
|
106711
|
+
ariaErrorWithReason: "Réessayer le téléchargement de {modelName}, {reason}"
|
|
106712
|
+
}
|
|
106655
106713
|
}
|
|
106656
106714
|
};
|
|
106657
106715
|
var llmsTabCreateLlmModal$2 = {
|
|
@@ -108972,9 +109030,6 @@ var tabsHumanSupportTab$2 = {
|
|
|
108972
109030
|
ticketListLabel: "Liste des tickets de support",
|
|
108973
109031
|
ticketDetailLabel: "Détails du ticket de support"
|
|
108974
109032
|
};
|
|
108975
|
-
var tabsLlmTab$2 = {
|
|
108976
|
-
providerLogoAlt: "Logo {name}"
|
|
108977
|
-
};
|
|
108978
109033
|
var tabsPoliciestab$2 = {
|
|
108979
109034
|
searchPoliciesPlaceholder: "Rechercher des politiques",
|
|
108980
109035
|
searchPoliciesLabel: "Rechercher des politiques",
|
|
@@ -110718,7 +110773,6 @@ var fr = {
|
|
|
110718
110773
|
tabsGroupstab: tabsGroupstab$2,
|
|
110719
110774
|
tabsHistoryTab: tabsHistoryTab$2,
|
|
110720
110775
|
tabsHumanSupportTab: tabsHumanSupportTab$2,
|
|
110721
|
-
tabsLlmTab: tabsLlmTab$2,
|
|
110722
110776
|
tabsPoliciestab: tabsPoliciestab$2,
|
|
110723
110777
|
tabsPrivacyTab: tabsPrivacyTab$2,
|
|
110724
110778
|
tabsPromptsTab: tabsPromptsTab$2,
|
|
@@ -113612,18 +113666,47 @@ var llmTabLabels$1 = {
|
|
|
113612
113666
|
title: "Configuración de LLM",
|
|
113613
113667
|
description: "Configure los ajustes del modelo de lenguaje para su agente."
|
|
113614
113668
|
},
|
|
113669
|
+
infoBox: "Este es el cerebro de tu agente: el modelo de IA que impulsa cada respuesta. Los modelos más grandes razonan mejor las preguntas complejas; los más ligeros son más rápidos y cuestan menos. ¿No estás seguro? El predeterminado es una opción versátil y confiable.",
|
|
113615
113670
|
search: {
|
|
113616
113671
|
placeholder: "Buscar proveedores"
|
|
113617
113672
|
},
|
|
113673
|
+
providerLogoAlt: "Logo de {providerName}",
|
|
113618
113674
|
toasts: {
|
|
113619
113675
|
updateSuccess: "LLM actualizado correctamente",
|
|
113620
113676
|
updateError: "Error al actualizar el LLM"
|
|
113621
113677
|
},
|
|
113622
113678
|
providerModal: {
|
|
113623
113679
|
title: "Selección de LLM",
|
|
113624
|
-
description: "Seleccione uno de los
|
|
113680
|
+
description: "Seleccione uno de los agentes proporcionados por {providerName}",
|
|
113625
113681
|
searchPlaceholder: "Buscar",
|
|
113626
|
-
helpText: "Elija su LLM preferido entre los proveedores disponibles para personalizar su experiencia."
|
|
113682
|
+
helpText: "Elija su LLM preferido entre los proveedores disponibles para personalizar su experiencia.",
|
|
113683
|
+
providerIconAlt: "ícono de {providerName}",
|
|
113684
|
+
tooLargeTitle: "Este modelo puede ser demasiado grande para su sistema",
|
|
113685
|
+
tooLargeDescription: "{modelName} necesita aproximadamente {modelSize}. Puede funcionar muy lentamente o no cargarse en este equipo. ¿Descargar de todos modos?",
|
|
113686
|
+
cancel: "Cancelar",
|
|
113687
|
+
downloadAnyway: "Descargar de todos modos",
|
|
113688
|
+
alreadyDownloadingTitle: "Ya se está descargando un modelo",
|
|
113689
|
+
unnamedModel: "Un modelo",
|
|
113690
|
+
alreadyDownloadingDescription: "{modelName} se está descargando. Solo se descarga un modelo en el dispositivo a la vez: espere a que termine o haga clic en {modelName} en la lista para cancelarlo.",
|
|
113691
|
+
gotIt: "Entendido",
|
|
113692
|
+
announceDownloaded: "{modelName} descargado",
|
|
113693
|
+
announceCancelled: "Descarga cancelada",
|
|
113694
|
+
announceFailed: "Error en la descarga",
|
|
113695
|
+
announceStarted: "Descarga de {modelName} iniciada",
|
|
113696
|
+
localModel: {
|
|
113697
|
+
onDevice: "En el dispositivo",
|
|
113698
|
+
starting: "Iniciando…",
|
|
113699
|
+
cancel: "Cancelar",
|
|
113700
|
+
inUse: "En uso",
|
|
113701
|
+
downloadFailedRetry: "Error en la descarga: reintentar",
|
|
113702
|
+
ariaDownload: "Descargar {modelName}, {modelSize}, modelo en el dispositivo",
|
|
113703
|
+
ariaStarting: "Iniciando la descarga de {modelName}",
|
|
113704
|
+
ariaDownloading: "Cancelar la descarga de {modelName}, {percent} por ciento",
|
|
113705
|
+
ariaInstalled: "Usar {modelName}, modelo en el dispositivo",
|
|
113706
|
+
ariaSelected: "{modelName} seleccionado, modelo en el dispositivo, en uso",
|
|
113707
|
+
ariaError: "Reintentar la descarga de {modelName}",
|
|
113708
|
+
ariaErrorWithReason: "Reintentar la descarga de {modelName}, {reason}"
|
|
113709
|
+
}
|
|
113627
113710
|
}
|
|
113628
113711
|
};
|
|
113629
113712
|
var llmsTabCreateLlmModal$1 = {
|
|
@@ -115944,9 +116027,6 @@ var tabsHumanSupportTab$1 = {
|
|
|
115944
116027
|
ticketListLabel: "Lista de tickets de soporte",
|
|
115945
116028
|
ticketDetailLabel: "Detalles del ticket de soporte"
|
|
115946
116029
|
};
|
|
115947
|
-
var tabsLlmTab$1 = {
|
|
115948
|
-
providerLogoAlt: "Logo de {name}"
|
|
115949
|
-
};
|
|
115950
116030
|
var tabsPoliciestab$1 = {
|
|
115951
116031
|
searchPoliciesPlaceholder: "Buscar políticas",
|
|
115952
116032
|
searchPoliciesLabel: "Buscar políticas",
|
|
@@ -117690,7 +117770,6 @@ var es = {
|
|
|
117690
117770
|
tabsGroupstab: tabsGroupstab$1,
|
|
117691
117771
|
tabsHistoryTab: tabsHistoryTab$1,
|
|
117692
117772
|
tabsHumanSupportTab: tabsHumanSupportTab$1,
|
|
117693
|
-
tabsLlmTab: tabsLlmTab$1,
|
|
117694
117773
|
tabsPoliciestab: tabsPoliciestab$1,
|
|
117695
117774
|
tabsPrivacyTab: tabsPrivacyTab$1,
|
|
117696
117775
|
tabsPromptsTab: tabsPromptsTab$1,
|
|
@@ -120584,18 +120663,47 @@ var llmTabLabels = {
|
|
|
120584
120663
|
title: "LLM 配置",
|
|
120585
120664
|
description: "为您的智能体配置语言模型设置。"
|
|
120586
120665
|
},
|
|
120666
|
+
infoBox: "这是您智能体的大脑——为每次回复提供动力的 AI 模型。更大的模型在处理复杂问题时推理能力更强;更轻量的模型则更快、成本更低。不确定?默认模型是可靠的全能之选。",
|
|
120587
120667
|
search: {
|
|
120588
120668
|
placeholder: "搜索提供商"
|
|
120589
120669
|
},
|
|
120670
|
+
providerLogoAlt: "{providerName} 徽标",
|
|
120590
120671
|
toasts: {
|
|
120591
120672
|
updateSuccess: "LLM 更新成功",
|
|
120592
120673
|
updateError: "LLM 更新失败"
|
|
120593
120674
|
},
|
|
120594
120675
|
providerModal: {
|
|
120595
120676
|
title: "LLM 选择",
|
|
120596
|
-
description: "
|
|
120677
|
+
description: "从 {providerName} 提供的代理中选择一个",
|
|
120597
120678
|
searchPlaceholder: "搜索",
|
|
120598
|
-
helpText: "从可用提供商中选择您偏好的 LLM,以定制您的使用体验。"
|
|
120679
|
+
helpText: "从可用提供商中选择您偏好的 LLM,以定制您的使用体验。",
|
|
120680
|
+
providerIconAlt: "{providerName} 图标",
|
|
120681
|
+
tooLargeTitle: "该模型可能对您的系统而言过大",
|
|
120682
|
+
tooLargeDescription: "{modelName} 约需 {modelSize}。它在此设备上可能运行非常缓慢或无法加载。仍要下载吗?",
|
|
120683
|
+
cancel: "取消",
|
|
120684
|
+
downloadAnyway: "仍要下载",
|
|
120685
|
+
alreadyDownloadingTitle: "已有模型正在下载",
|
|
120686
|
+
unnamedModel: "某个模型",
|
|
120687
|
+
alreadyDownloadingDescription: "{modelName} 正在下载。一次只能下载一个设备端模型——请等待其完成,或点击列表中的 {modelName} 以取消下载。",
|
|
120688
|
+
gotIt: "知道了",
|
|
120689
|
+
announceDownloaded: "{modelName} 已下载",
|
|
120690
|
+
announceCancelled: "下载已取消",
|
|
120691
|
+
announceFailed: "下载失败",
|
|
120692
|
+
announceStarted: "已开始下载 {modelName}",
|
|
120693
|
+
localModel: {
|
|
120694
|
+
onDevice: "设备端",
|
|
120695
|
+
starting: "正在启动…",
|
|
120696
|
+
cancel: "取消",
|
|
120697
|
+
inUse: "使用中",
|
|
120698
|
+
downloadFailedRetry: "下载失败——重试",
|
|
120699
|
+
ariaDownload: "下载 {modelName},{modelSize},设备端模型",
|
|
120700
|
+
ariaStarting: "正在开始下载 {modelName}",
|
|
120701
|
+
ariaDownloading: "取消下载 {modelName},{percent}%",
|
|
120702
|
+
ariaInstalled: "使用 {modelName},设备端模型",
|
|
120703
|
+
ariaSelected: "已选择 {modelName},设备端模型,使用中",
|
|
120704
|
+
ariaError: "重试下载 {modelName}",
|
|
120705
|
+
ariaErrorWithReason: "重试下载 {modelName},{reason}"
|
|
120706
|
+
}
|
|
120599
120707
|
}
|
|
120600
120708
|
};
|
|
120601
120709
|
var llmsTabCreateLlmModal = {
|
|
@@ -122916,9 +123024,6 @@ var tabsHumanSupportTab = {
|
|
|
122916
123024
|
ticketListLabel: "支持工单列表",
|
|
122917
123025
|
ticketDetailLabel: "支持工单详情"
|
|
122918
123026
|
};
|
|
122919
|
-
var tabsLlmTab = {
|
|
122920
|
-
providerLogoAlt: "{name} 标志"
|
|
122921
|
-
};
|
|
122922
123027
|
var tabsPoliciestab = {
|
|
122923
123028
|
searchPoliciesPlaceholder: "搜索策略",
|
|
122924
123029
|
searchPoliciesLabel: "搜索策略",
|
|
@@ -124662,7 +124767,6 @@ var zh = {
|
|
|
124662
124767
|
tabsGroupstab: tabsGroupstab,
|
|
124663
124768
|
tabsHistoryTab: tabsHistoryTab,
|
|
124664
124769
|
tabsHumanSupportTab: tabsHumanSupportTab,
|
|
124665
|
-
tabsLlmTab: tabsLlmTab,
|
|
124666
124770
|
tabsPoliciestab: tabsPoliciestab,
|
|
124667
124771
|
tabsPrivacyTab: tabsPrivacyTab,
|
|
124668
124772
|
tabsPromptsTab: tabsPromptsTab,
|
|
@@ -130883,6 +130987,7 @@ Progress.displayName = Root$9.displayName;
|
|
|
130883
130987
|
|
|
130884
130988
|
const LOCAL_LLM_ENABLED_KEY = 'ibl_local_llm_enabled';
|
|
130885
130989
|
const LOCAL_LLM_MODEL_KEY = 'ibl_local_llm_model';
|
|
130990
|
+
const LOCAL_LLM_TOOL_SUPPORT_KEY = 'ibl_local_llm_tool_support';
|
|
130886
130991
|
/**
|
|
130887
130992
|
* Curated catalog of small, on-device-friendly models from the Ollama library
|
|
130888
130993
|
* (https://ollama.com/library). The model manager itself (Ollama) is not a
|
|
@@ -130940,6 +131045,12 @@ const LOCAL_MODELS = [
|
|
|
130940
131045
|
tool_support: true,
|
|
130941
131046
|
},
|
|
130942
131047
|
];
|
|
131048
|
+
/**
|
|
131049
|
+
* Fraction of the machine's memory above which a model is treated as "too large"
|
|
131050
|
+
* and a confirmation is required before downloading. TESTING VALUE — set low (1%)
|
|
131051
|
+
* so the warning is easy to trigger; raise toward real capacity (e.g. 0.8) later.
|
|
131052
|
+
*/
|
|
131053
|
+
const MODEL_SIZE_WARN_FRACTION = 0.01;
|
|
130943
131054
|
/** Multipliers for the size units used in the catalog (binary / 1024-based). */
|
|
130944
131055
|
const SIZE_UNIT_BYTES = {
|
|
130945
131056
|
B: 1,
|
|
@@ -130961,6 +131072,31 @@ function parseModelSizeBytes(size) {
|
|
|
130961
131072
|
return null;
|
|
130962
131073
|
return value * ((_a = SIZE_UNIT_BYTES[match[2].toUpperCase()]) !== null && _a !== void 0 ? _a : 1);
|
|
130963
131074
|
}
|
|
131075
|
+
/**
|
|
131076
|
+
* The memory a model can use on this machine: the larger of system RAM and GPU
|
|
131077
|
+
* VRAM (a model runs in one or the other). Returns 0 when memory is unknown.
|
|
131078
|
+
*/
|
|
131079
|
+
function usableMemoryBytes(systemMemory) {
|
|
131080
|
+
if (!systemMemory)
|
|
131081
|
+
return 0;
|
|
131082
|
+
return Math.max(systemMemory.ram_total, systemMemory.vram_total);
|
|
131083
|
+
}
|
|
131084
|
+
/**
|
|
131085
|
+
* Whether `model` is large enough relative to this machine's memory to warrant a
|
|
131086
|
+
* "might not run" confirmation. False when memory or the model size is unknown,
|
|
131087
|
+
* so the download simply proceeds rather than warning on incomplete data.
|
|
131088
|
+
*/
|
|
131089
|
+
function modelExceedsCapacity(model, systemMemory) {
|
|
131090
|
+
const capacity = usableMemoryBytes(systemMemory);
|
|
131091
|
+
const modelBytes = parseModelSizeBytes(model.size);
|
|
131092
|
+
if (modelBytes == null)
|
|
131093
|
+
return false;
|
|
131094
|
+
// Unknown memory (capacity 0) → don't warn; the download simply proceeds
|
|
131095
|
+
// rather than blocking on incomplete data.
|
|
131096
|
+
if (capacity <= 0)
|
|
131097
|
+
return false;
|
|
131098
|
+
return modelBytes > capacity * MODEL_SIZE_WARN_FRACTION;
|
|
131099
|
+
}
|
|
130964
131100
|
/**
|
|
130965
131101
|
* Default minimum model size (GB) that can use the Cowork
|
|
130966
131102
|
* (GhostOS MCP/tools). Smaller models don't support tools/MCP well, so the
|
|
@@ -131008,6 +131144,33 @@ function getLocalLLMModel() {
|
|
|
131008
131144
|
return null;
|
|
131009
131145
|
return localStorage.getItem(LOCAL_LLM_MODEL_KEY);
|
|
131010
131146
|
}
|
|
131147
|
+
/**
|
|
131148
|
+
* Set the model the user has chosen to chat with (Ollama pull id).
|
|
131149
|
+
*/
|
|
131150
|
+
function setLocalLLMModel(modelId) {
|
|
131151
|
+
if (typeof window === 'undefined')
|
|
131152
|
+
return;
|
|
131153
|
+
localStorage.setItem(LOCAL_LLM_MODEL_KEY, modelId);
|
|
131154
|
+
}
|
|
131155
|
+
/**
|
|
131156
|
+
* Whether the model the user selected supports tools / function calling
|
|
131157
|
+
* (persisted alongside the model id). The host (e.g. ghost-os) reads this to
|
|
131158
|
+
* route chat to the MCP/tool proxy (:8000) vs plain Ollama (:11434). Defaults to
|
|
131159
|
+
* false when unknown. See {@link LocalModel.tool_support}.
|
|
131160
|
+
*/
|
|
131161
|
+
function getLocalLLMToolSupport() {
|
|
131162
|
+
if (typeof window === 'undefined')
|
|
131163
|
+
return false;
|
|
131164
|
+
return localStorage.getItem(LOCAL_LLM_TOOL_SUPPORT_KEY) === 'true';
|
|
131165
|
+
}
|
|
131166
|
+
/**
|
|
131167
|
+
* Persist whether the selected model supports tools (set when a model is chosen).
|
|
131168
|
+
*/
|
|
131169
|
+
function setLocalLLMToolSupport(toolSupport) {
|
|
131170
|
+
if (typeof window === 'undefined')
|
|
131171
|
+
return;
|
|
131172
|
+
localStorage.setItem(LOCAL_LLM_TOOL_SUPPORT_KEY, String(toolSupport));
|
|
131173
|
+
}
|
|
131011
131174
|
function LocalLLMTab({ isAvailable, state, ollamaStatus, isUsingFoundry = false, foundryModels = [], selectedFoundryModel = null, foundryStatus, onInstallOllama, onStopManager, onInstallFoundry, onCheckStatus, onSelectFoundryModel, }) {
|
|
131012
131175
|
var _a, _b, _c, _d, _e;
|
|
131013
131176
|
const t = useT();
|
|
@@ -225279,62 +225442,215 @@ function CreateApiModal$1({ isOpen, onClose, tenantKey, username }) {
|
|
|
225279
225442
|
: t('apiTabLabels.createModal.submit') })) }) })] })] }) }), apiKey && jsx(ApiKeyModal$1, { isOpen: true, onClose: () => setApiKey(null), apiKey: apiKey })] }));
|
|
225280
225443
|
}
|
|
225281
225444
|
|
|
225445
|
+
/**
|
|
225446
|
+
* Small pure utilities colocated with the LLM tab.
|
|
225447
|
+
* Ported from standalone's `@/lib/utils` and `@/lib/error`.
|
|
225448
|
+
*/
|
|
225449
|
+
// Canonical provider name for any label — a backend key ("azure_openai"), a
|
|
225450
|
+
// human label ("Microsoft"), or a local model's provider ("Meta"). Folds every
|
|
225451
|
+
// alias onto one identity so the same provider from different sources compares
|
|
225452
|
+
// equal. See getProviderName.
|
|
225453
|
+
const PROVIDER_NAME_BY_ALIAS = {
|
|
225454
|
+
microsoft: 'azure_openai',
|
|
225455
|
+
azureopenai: 'azure_openai',
|
|
225456
|
+
openai: 'openai',
|
|
225457
|
+
google: 'google',
|
|
225458
|
+
gemini: 'google',
|
|
225459
|
+
meta: 'llama',
|
|
225460
|
+
metallama: 'llama',
|
|
225461
|
+
llama: 'llama',
|
|
225462
|
+
mistral: 'mistral',
|
|
225463
|
+
mistralai: 'mistral',
|
|
225464
|
+
nvidia: 'nvidia',
|
|
225465
|
+
iblchatnvidia: 'nvidia',
|
|
225466
|
+
deepseek: 'deepseek',
|
|
225467
|
+
anthropic: 'anthropic',
|
|
225468
|
+
iblchatanthropic: 'anthropic',
|
|
225469
|
+
claude: 'anthropic',
|
|
225470
|
+
groq: 'groq',
|
|
225471
|
+
perplexity: 'perplexity',
|
|
225472
|
+
xai: 'xai',
|
|
225473
|
+
bedrock: 'bedrock',
|
|
225474
|
+
amazonbedrock: 'bedrock',
|
|
225475
|
+
amazon: 'bedrock',
|
|
225476
|
+
iblchatbedrock: 'bedrock',
|
|
225477
|
+
alibaba: 'alibaba',
|
|
225478
|
+
qwen: 'alibaba',
|
|
225479
|
+
ibm: 'ibm',
|
|
225480
|
+
granite: 'ibm',
|
|
225481
|
+
// ibl.ai's own hosted provider. The backend key is `iblai` (NameEnum.IBLAI);
|
|
225482
|
+
// the extra aliases cover the `IBLChat<Vendor>` house spelling and the
|
|
225483
|
+
// dotted/spaced brand forms, which all normalize to one of these.
|
|
225484
|
+
iblai: 'iblai',
|
|
225485
|
+
ibl: 'iblai',
|
|
225486
|
+
iblchatibl: 'iblai',
|
|
225487
|
+
iblchatiblai: 'iblai',
|
|
225488
|
+
};
|
|
225489
|
+
/**
|
|
225490
|
+
* Canonical provider name for any label, case- and punctuation-insensitive.
|
|
225491
|
+
* Folds backend keys and human/display labels onto one identity — e.g.
|
|
225492
|
+
* `getProviderName('Microsoft') === 'azure_openai'`,
|
|
225493
|
+
* `getProviderName('Meta') === 'llama'`, `getProviderName('Google') === 'google'`.
|
|
225494
|
+
* Unknown providers return their normalized (lowercased, alphanumeric-only) form.
|
|
225495
|
+
*
|
|
225496
|
+
* This is the single source of truth for provider identity, which matters as
|
|
225497
|
+
* soon as on-device models enter the picture: the local catalog labels its
|
|
225498
|
+
* providers the human way ("Meta", "Microsoft", "Alibaba") while the backend LLM
|
|
225499
|
+
* list uses wire keys ("llama", "azure_openai"). Comparing the two raw would
|
|
225500
|
+
* render a duplicate card for the same vendor.
|
|
225501
|
+
*/
|
|
225502
|
+
function getProviderName(llmProvider) {
|
|
225503
|
+
var _a;
|
|
225504
|
+
const normalized = (llmProvider !== null && llmProvider !== void 0 ? llmProvider : '').toLowerCase().replace(/[^a-z0-9]/g, '');
|
|
225505
|
+
return (_a = PROVIDER_NAME_BY_ALIAS[normalized]) !== null && _a !== void 0 ? _a : normalized;
|
|
225506
|
+
}
|
|
225507
|
+
/**
|
|
225508
|
+
* Whether two Ollama model refs name the same model, comparing by base name and
|
|
225509
|
+
* ignoring a trailing `:tag` (so "llama3.2" ≡ "llama3.2:latest", and
|
|
225510
|
+
* "granite4.1:8b" ≡ "granite4.1"). The download state and Ollama's /api/tags may
|
|
225511
|
+
* carry a tag suffix the catalog id doesn't (or vice-versa); an exact `===` then
|
|
225512
|
+
* fails to recognize a row's own in-flight download — the row shows "Download"
|
|
225513
|
+
* with no progress while the pull runs. Base-name matching is safe here because
|
|
225514
|
+
* every catalog model has a unique base (llama3.2, qwen3, mistral, granite4.1,
|
|
225515
|
+
* gpt-oss, gemma4), so it can never light up the wrong row.
|
|
225516
|
+
*/
|
|
225517
|
+
function sameModel(a, b) {
|
|
225518
|
+
const base = (ref) => (ref !== null && ref !== void 0 ? ref : '').toLowerCase().split(':')[0];
|
|
225519
|
+
const ba = base(a);
|
|
225520
|
+
return ba.length > 0 && ba === base(b);
|
|
225521
|
+
}
|
|
225522
|
+
/**
|
|
225523
|
+
* Whether a catalog model id (e.g. "granite4.1:8b") is among the installed
|
|
225524
|
+
* Ollama tags (e.g. "granite4.1:latest").
|
|
225525
|
+
*
|
|
225526
|
+
* Deliberately NOT the profile tab's `isModelInstalled`, which matches an exact
|
|
225527
|
+
* tag or the `<id>:` prefix: that is one-directional and misses a catalog id
|
|
225528
|
+
* that itself carries a tag ("granite4.1:8b" against an installed
|
|
225529
|
+
* "granite4.1:latest"). The picker needs the symmetric base-name comparison of
|
|
225530
|
+
* {@link sameModel} so a pulled model is recognized whichever side wears the tag.
|
|
225531
|
+
*/
|
|
225532
|
+
function isLocalModelInstalled(modelId, installedTags) {
|
|
225533
|
+
return !!(installedTags === null || installedTags === void 0 ? void 0 : installedTags.some((tag) => sameModel(tag, modelId)));
|
|
225534
|
+
}
|
|
225535
|
+
function canSwitchProvider(providers, name) {
|
|
225536
|
+
const provider = providers.find((p) => p.name === name);
|
|
225537
|
+
return !!(provider === null || provider === void 0 ? void 0 : provider.chat_models) && provider.chat_models.length > 0;
|
|
225538
|
+
}
|
|
225539
|
+
/**
|
|
225540
|
+
* Whether we hold a usable LLM key for this provider — its own credentials, or
|
|
225541
|
+
* the tenant's main keys when it is allowed to borrow them. An *absent*
|
|
225542
|
+
* `main_has_credentials` means the backend did not report on the main keys and
|
|
225543
|
+
* is treated as available (matching standalone's `@/lib/utils` and
|
|
225544
|
+
* `claw-sandbox`'s copy); only an explicit `false` rules the main keys out.
|
|
225545
|
+
*/
|
|
225546
|
+
function canSwitchLLm(llm) {
|
|
225547
|
+
if (llm === null || llm === void 0 ? void 0 : llm.has_credentials) {
|
|
225548
|
+
return true;
|
|
225549
|
+
}
|
|
225550
|
+
return ((llm === null || llm === void 0 ? void 0 : llm.can_use_main_keys) &&
|
|
225551
|
+
((llm === null || llm === void 0 ? void 0 : llm.main_has_credentials) === true || (llm === null || llm === void 0 ? void 0 : llm.main_has_credentials) === undefined));
|
|
225552
|
+
}
|
|
225553
|
+
/**
|
|
225554
|
+
* Compares two raw provider names by the label the user actually sees on the
|
|
225555
|
+
* card, not by the backend key — the two diverge often enough to matter
|
|
225556
|
+
* (`bedrock` renders as "Amazon", `azure_openai` as "Microsoft", `iblai` as
|
|
225557
|
+
* "ibl.ai"). Comparison is case-insensitive (`sensitivity: 'base'`) so casing
|
|
225558
|
+
* never produces a surprising order. `getDetails` is the host-provided resolver
|
|
225559
|
+
* the grid renders with, so the sort always matches what is on screen; unknown
|
|
225560
|
+
* providers fall back to their raw name as the display name.
|
|
225561
|
+
*/
|
|
225562
|
+
function compareLLMProvidersByDisplayName(a, b, getDetails) {
|
|
225563
|
+
return getDetails(a).name.localeCompare(getDetails(b).name, undefined, {
|
|
225564
|
+
sensitivity: 'base',
|
|
225565
|
+
});
|
|
225566
|
+
}
|
|
225567
|
+
/**
|
|
225568
|
+
* Whether the user can reach *any* model of this provider.
|
|
225569
|
+
*
|
|
225570
|
+
* This is exactly the provider-level half of the per-model `isDisabled` rule in
|
|
225571
|
+
* `llm-tab/llm-provider-modal.tsx`: a model row there is disabled when
|
|
225572
|
+
* `!canSwitchLLm(provider) || !canSwitchProvider(llms, provider.name)`, and both
|
|
225573
|
+
* of those depend only on the provider — so when either fails, *every* model row
|
|
225574
|
+
* of that provider is disabled. `canSwitchProvider` is just "has at least one
|
|
225575
|
+
* `chat_models` entry", which is checked inline here to keep the helper pure and
|
|
225576
|
+
* free of the surrounding provider list.
|
|
225577
|
+
*
|
|
225578
|
+
* `canSwitchLLm` can return `undefined` (its `can_use_main_keys` branch), hence
|
|
225579
|
+
* the explicit `Boolean(...)`.
|
|
225580
|
+
*/
|
|
225581
|
+
function canAccessProvider(provider) {
|
|
225582
|
+
var _a, _b;
|
|
225583
|
+
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;
|
|
225584
|
+
}
|
|
225585
|
+
/**
|
|
225586
|
+
* Orders LLM provider cards as two alphabetical groups: providers the user can
|
|
225587
|
+
* actually use (see {@link canAccessProvider}) first, then the ones they can't —
|
|
225588
|
+
* each group sorted alphabetically by display name.
|
|
225589
|
+
*
|
|
225590
|
+
* The grouping predicate is deliberately the same one the grid grays cards with,
|
|
225591
|
+
* so the two always line up: group 1 renders normally, group 2 renders grayed.
|
|
225592
|
+
* Grouping on credentials alone would let a provider that has a key but ships no
|
|
225593
|
+
* chat models sort into the "usable" group while rendering grayed.
|
|
225594
|
+
*
|
|
225595
|
+
* Pure and non-mutating: RTK Query results are frozen, so the input is copied
|
|
225596
|
+
* before sorting. `Array.prototype.sort` is stable, so providers that tie on
|
|
225597
|
+
* both group and display name keep their original relative order.
|
|
225598
|
+
*/
|
|
225599
|
+
function sortLLMProvidersByCredentials(providers, getDetails) {
|
|
225600
|
+
return [...providers].sort((a, b) => {
|
|
225601
|
+
const aUsable = canAccessProvider(a);
|
|
225602
|
+
const bUsable = canAccessProvider(b);
|
|
225603
|
+
if (aUsable !== bUsable)
|
|
225604
|
+
return aUsable ? -1 : 1;
|
|
225605
|
+
return compareLLMProvidersByDisplayName(a.name, b.name, getDetails);
|
|
225606
|
+
});
|
|
225607
|
+
}
|
|
225608
|
+
function extractErrorMessage$1(error, fallback) {
|
|
225609
|
+
var _a, _b;
|
|
225610
|
+
const apiError = error;
|
|
225611
|
+
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;
|
|
225612
|
+
}
|
|
225613
|
+
|
|
225614
|
+
// Logo + display name keyed by canonical provider name (see getProviderName).
|
|
225615
|
+
const PROVIDER_DETAILS_BY_NAME = {
|
|
225616
|
+
groq: { logo: '/llm-groq-provider.png', name: 'Groq' },
|
|
225617
|
+
nvidia: { logo: '/llm-nvidia-provider.webp', name: 'NVIDIA' },
|
|
225618
|
+
azure_openai: { logo: '/llm-microsoft-provider.png', name: 'Microsoft' },
|
|
225619
|
+
openai: { logo: '/llm-openai-provider-2.svg', name: 'OpenAI' },
|
|
225620
|
+
mistral: { logo: '/llm-mistral-provider.jpeg', name: 'Mistral' },
|
|
225621
|
+
google: { logo: '/llm-google-provider.svg', name: 'Google' },
|
|
225622
|
+
llama: { logo: '/llm-llama-provider.jpeg', name: 'Meta' },
|
|
225623
|
+
anthropic: { logo: '/llm-claude-provider.png', name: 'Anthropic' },
|
|
225624
|
+
perplexity: { logo: '/llm-perplexity-provider.webp', name: 'Perplexity' },
|
|
225625
|
+
deepseek: { logo: '/llm-deepseek-provider.png', name: 'DeepSeek' },
|
|
225626
|
+
xai: { logo: '/llm-xai-provider.jpg', name: 'xAI' },
|
|
225627
|
+
bedrock: { logo: '/llm-amazon-provider.png', name: 'Amazon' },
|
|
225628
|
+
alibaba: { logo: '/llm-alibaba-provider.png', name: 'Alibaba' },
|
|
225629
|
+
ibm: { logo: '/llm-ibm-provider.png', name: 'IBM' },
|
|
225630
|
+
iblai: { logo: '/llm-iblai-provider.png', name: 'ibl.ai' },
|
|
225631
|
+
};
|
|
225282
225632
|
/**
|
|
225283
225633
|
* Returns the canonical logo path and display name for an LLM provider.
|
|
225634
|
+
*
|
|
225635
|
+
* Resolution goes through {@link getProviderName}, so a backend wire key
|
|
225636
|
+
* (`azure_openai`), a human label (`Microsoft`) and an on-device catalog's
|
|
225637
|
+
* provider string (`Meta`, `Alibaba`) all land on the same entry — the provider
|
|
225638
|
+
* grid merges cloud and on-device providers into one list, so the two spellings
|
|
225639
|
+
* have to agree.
|
|
225640
|
+
*
|
|
225284
225641
|
* `logo` is a static-root path (e.g. `/llm-amazon-provider.png`); the package
|
|
225285
225642
|
* ships the canonical assets under `@iblai/web-containers/public/` — copy them
|
|
225286
225643
|
* into your app's static-root (`public/` for Next.js) so the paths resolve.
|
|
225287
225644
|
*/
|
|
225288
225645
|
function getLLMProviderDetails(llmProvider, llmName) {
|
|
225289
|
-
|
|
225290
|
-
|
|
225291
|
-
|
|
225292
|
-
|
|
225293
|
-
|
|
225294
|
-
|
|
225295
|
-
|
|
225296
|
-
|
|
225297
|
-
return { logo: '/llm-openai-provider-2.svg', name: 'OpenAI' };
|
|
225298
|
-
case 'mistral':
|
|
225299
|
-
return { logo: '/llm-mistral-provider.jpeg', name: 'Mistral' };
|
|
225300
|
-
case 'google':
|
|
225301
|
-
if (llmName)
|
|
225302
|
-
return { logo: '/llm-gemini-provider.png', name: 'Google' };
|
|
225303
|
-
return { logo: '/llm-google-provider.svg', name: 'Google' };
|
|
225304
|
-
case 'llama':
|
|
225305
|
-
return { logo: '/llm-llama-provider.jpeg', name: 'Meta' };
|
|
225306
|
-
case 'perplexity':
|
|
225307
|
-
return { logo: '/llm-perplexity-provider.webp', name: 'Perplexity' };
|
|
225308
|
-
case 'deepseek':
|
|
225309
|
-
return { logo: '/llm-deepseek-provider.png', name: 'DeepSeek' };
|
|
225310
|
-
case 'xai':
|
|
225311
|
-
return { logo: '/llm-xai-provider.jpg', name: 'xAI' };
|
|
225312
|
-
case 'IBLChatAnthropic':
|
|
225313
|
-
case 'anthropic':
|
|
225314
|
-
return { logo: '/llm-claude-provider.png', name: 'Anthropic' };
|
|
225315
|
-
case 'IBLChatNvidia':
|
|
225316
|
-
case 'nvidia':
|
|
225317
|
-
return { logo: '/llm-nvidia-provider.webp', name: 'NVIDIA' };
|
|
225318
|
-
case 'bedrock':
|
|
225319
|
-
case 'amazon-bedrock':
|
|
225320
|
-
case 'amazon_bedrock':
|
|
225321
|
-
case 'IBLChatBedrock':
|
|
225322
|
-
return { logo: '/llm-amazon-provider.png', name: 'Amazon' };
|
|
225323
|
-
// ibl.ai's own hosted provider. The backend key is `iblai` (NameEnum.IBLAI);
|
|
225324
|
-
// the other cases cover the `IBLChat<Vendor>` house spelling and the
|
|
225325
|
-
// dotted/dashed/underscored brand forms seen in labels and older payloads.
|
|
225326
|
-
case 'iblai':
|
|
225327
|
-
case 'IBLAI':
|
|
225328
|
-
case 'ibl.ai':
|
|
225329
|
-
case 'ibl_ai':
|
|
225330
|
-
case 'ibl-ai':
|
|
225331
|
-
case 'ibl':
|
|
225332
|
-
case 'IBLChatIBL':
|
|
225333
|
-
case 'IBLChatIBLAI':
|
|
225334
|
-
return { logo: '/llm-iblai-provider.png', name: 'ibl.ai' };
|
|
225335
|
-
default:
|
|
225336
|
-
return { logo: '/llm-generic-provider.png', name: llmProvider };
|
|
225337
|
-
}
|
|
225646
|
+
var _a;
|
|
225647
|
+
const name = getProviderName(llmProvider);
|
|
225648
|
+
// OpenAI and Google use a model-specific logo when a concrete model is named.
|
|
225649
|
+
if (name === 'openai' && llmName)
|
|
225650
|
+
return { logo: '/llm-openai-provider.jpg', name: 'OpenAI' };
|
|
225651
|
+
if (name === 'google' && llmName)
|
|
225652
|
+
return { logo: '/llm-gemini-provider.png', name: 'Google' };
|
|
225653
|
+
return (_a = PROVIDER_DETAILS_BY_NAME[name]) !== null && _a !== void 0 ? _a : { logo: '/llm-generic-provider.png', name: llmProvider };
|
|
225338
225654
|
}
|
|
225339
225655
|
|
|
225340
225656
|
// Dynamic schema based on selected provider
|
|
@@ -262271,7 +262587,7 @@ const resourceTypes = [
|
|
|
262271
262587
|
* rate limit.
|
|
262272
262588
|
*/
|
|
262273
262589
|
const SPEND_CAP_EXCEEDED_ERROR_CODE = 'spend_cap_exceeded';
|
|
262274
|
-
const extractErrorMessage
|
|
262590
|
+
const extractErrorMessage = (error, fallback) => {
|
|
262275
262591
|
var _a, _b, _c;
|
|
262276
262592
|
const apiError = error;
|
|
262277
262593
|
if (((_a = apiError === null || apiError === void 0 ? void 0 : apiError.data) === null || _a === void 0 ? void 0 : _a.error_code) === SPEND_CAP_EXCEEDED_ERROR_CODE) {
|
|
@@ -262350,7 +262666,7 @@ function GithubFileUploadModal() {
|
|
|
262350
262666
|
}
|
|
262351
262667
|
catch (error) {
|
|
262352
262668
|
console.error(JSON.stringify(error));
|
|
262353
|
-
const errorMessage = extractErrorMessage
|
|
262669
|
+
const errorMessage = extractErrorMessage(error, t('resourceModalGithubFileUploadModal.errorAddingDocument'));
|
|
262354
262670
|
toast.error(errorMessage);
|
|
262355
262671
|
console.error(JSON.stringify({ tenant: tenantKey, error }));
|
|
262356
262672
|
}
|
|
@@ -262417,7 +262733,7 @@ function LocalFileUploadModal({ resource, maxFileSizeMB = DEFAULT_MAX_FILE_SIZE_
|
|
|
262417
262733
|
}
|
|
262418
262734
|
catch (error) {
|
|
262419
262735
|
console.error(JSON.stringify(error));
|
|
262420
|
-
const errorMessage = extractErrorMessage
|
|
262736
|
+
const errorMessage = extractErrorMessage(error, t('resourceModalLocalFileUploadModal.errorAddingDocument'));
|
|
262421
262737
|
toast.error(errorMessage);
|
|
262422
262738
|
console.error(JSON.stringify({ tenant: tenantKey, error }));
|
|
262423
262739
|
}
|
|
@@ -262520,7 +262836,7 @@ function UrlUploadModal({ resource }) {
|
|
|
262520
262836
|
}
|
|
262521
262837
|
catch (error) {
|
|
262522
262838
|
console.error(JSON.stringify(error));
|
|
262523
|
-
const errorMessage = extractErrorMessage
|
|
262839
|
+
const errorMessage = extractErrorMessage(error, t('resourceModalUrlUploadModal.errorAddingTrainingDocument'));
|
|
262524
262840
|
toast.error(errorMessage);
|
|
262525
262841
|
console.error(JSON.stringify({ tenant: tenantKey, error }));
|
|
262526
262842
|
}
|
|
@@ -262608,7 +262924,7 @@ function useWebsiteCrawlerResource(resource) {
|
|
|
262608
262924
|
}
|
|
262609
262925
|
catch (error) {
|
|
262610
262926
|
console.error(JSON.stringify(error));
|
|
262611
|
-
const errorMessage = extractErrorMessage
|
|
262927
|
+
const errorMessage = extractErrorMessage(error, 'Error submitting web crawl data');
|
|
262612
262928
|
toast.error(errorMessage);
|
|
262613
262929
|
console.error(JSON.stringify({ tenant: tenantKey, error }));
|
|
262614
262930
|
}
|
|
@@ -262798,7 +263114,7 @@ const useGoogleDrivePicker = ({ tenantKey, mentorId }) => {
|
|
|
262798
263114
|
}
|
|
262799
263115
|
catch (error) {
|
|
262800
263116
|
console.error(JSON.stringify(error));
|
|
262801
|
-
const errorMessage = extractErrorMessage
|
|
263117
|
+
const errorMessage = extractErrorMessage(error, 'Error adding training document');
|
|
262802
263118
|
toast.error(errorMessage);
|
|
262803
263119
|
console.error(JSON.stringify({ tenant: tenantKey, error }));
|
|
262804
263120
|
}
|
|
@@ -263103,7 +263419,7 @@ const useDropboxPicker = ({ tenantKey, mentorId, cancel = () => { }, linkType =
|
|
|
263103
263419
|
}
|
|
263104
263420
|
catch (error) {
|
|
263105
263421
|
console.error(JSON.stringify(error));
|
|
263106
|
-
const errorMessage = extractErrorMessage
|
|
263422
|
+
const errorMessage = extractErrorMessage(error, 'Error adding training document');
|
|
263107
263423
|
toast.error(errorMessage);
|
|
263108
263424
|
console.error(JSON.stringify({ tenant: tenantKey, error }));
|
|
263109
263425
|
}
|
|
@@ -263328,7 +263644,7 @@ const useOneDrivePicker = ({ tenantKey, mentorId }) => {
|
|
|
263328
263644
|
}
|
|
263329
263645
|
catch (error) {
|
|
263330
263646
|
console.error(JSON.stringify(error));
|
|
263331
|
-
const errorMessage = extractErrorMessage
|
|
263647
|
+
const errorMessage = extractErrorMessage(error, 'Error adding training document');
|
|
263332
263648
|
toast.error(errorMessage);
|
|
263333
263649
|
console.error(JSON.stringify({ tenant: tenantKey, error }));
|
|
263334
263650
|
}
|
|
@@ -270320,86 +270636,51 @@ var copyMentorModal = /*#__PURE__*/Object.freeze({
|
|
|
270320
270636
|
});
|
|
270321
270637
|
|
|
270322
270638
|
/**
|
|
270323
|
-
*
|
|
270324
|
-
*
|
|
270325
|
-
|
|
270326
|
-
|
|
270327
|
-
|
|
270328
|
-
return !!(provider === null || provider === void 0 ? void 0 : provider.chat_models) && provider.chat_models.length > 0;
|
|
270329
|
-
}
|
|
270330
|
-
/**
|
|
270331
|
-
* Whether we hold a usable LLM key for this provider — its own credentials, or
|
|
270332
|
-
* the tenant's main keys when it is allowed to borrow them. An *absent*
|
|
270333
|
-
* `main_has_credentials` means the backend did not report on the main keys and
|
|
270334
|
-
* is treated as available (matching standalone's `@/lib/utils` and
|
|
270335
|
-
* `claw-sandbox`'s copy); only an explicit `false` rules the main keys out.
|
|
270336
|
-
*/
|
|
270337
|
-
function canSwitchLLm(llm) {
|
|
270338
|
-
if (llm === null || llm === void 0 ? void 0 : llm.has_credentials) {
|
|
270339
|
-
return true;
|
|
270340
|
-
}
|
|
270341
|
-
return ((llm === null || llm === void 0 ? void 0 : llm.can_use_main_keys) &&
|
|
270342
|
-
((llm === null || llm === void 0 ? void 0 : llm.main_has_credentials) === true || (llm === null || llm === void 0 ? void 0 : llm.main_has_credentials) === undefined));
|
|
270343
|
-
}
|
|
270344
|
-
/**
|
|
270345
|
-
* Compares two raw provider names by the label the user actually sees on the
|
|
270346
|
-
* card, not by the backend key — the two diverge often enough to matter
|
|
270347
|
-
* (`bedrock` renders as "Amazon", `azure_openai` as "Microsoft", `iblai` as
|
|
270348
|
-
* "ibl.ai"). Comparison is case-insensitive (`sensitivity: 'base'`) so casing
|
|
270349
|
-
* never produces a surprising order. `getDetails` is the host-provided resolver
|
|
270350
|
-
* the grid renders with, so the sort always matches what is on screen; unknown
|
|
270351
|
-
* providers fall back to their raw name as the display name.
|
|
270352
|
-
*/
|
|
270353
|
-
function compareLLMProvidersByDisplayName(a, b, getDetails) {
|
|
270354
|
-
return getDetails(a).name.localeCompare(getDetails(b).name, undefined, {
|
|
270355
|
-
sensitivity: 'base',
|
|
270356
|
-
});
|
|
270357
|
-
}
|
|
270358
|
-
/**
|
|
270359
|
-
* Whether the user can reach *any* model of this provider.
|
|
270360
|
-
*
|
|
270361
|
-
* This is exactly the provider-level half of the per-model `isDisabled` rule in
|
|
270362
|
-
* `llm-tab/llm-provider-modal.tsx`: a model row there is disabled when
|
|
270363
|
-
* `!canSwitchLLm(provider) || !canSwitchProvider(llms, provider.name)`, and both
|
|
270364
|
-
* of those depend only on the provider — so when either fails, *every* model row
|
|
270365
|
-
* of that provider is disabled. `canSwitchProvider` is just "has at least one
|
|
270366
|
-
* `chat_models` entry", which is checked inline here to keep the helper pure and
|
|
270367
|
-
* free of the surrounding provider list.
|
|
270368
|
-
*
|
|
270369
|
-
* `canSwitchLLm` can return `undefined` (its `can_use_main_keys` branch), hence
|
|
270370
|
-
* the explicit `Boolean(...)`.
|
|
270639
|
+
* Window event any surface dispatches after changing the device-global
|
|
270640
|
+
* on-device selection — a model pick in the LLM tab's provider modal, or the
|
|
270641
|
+
* Local Models master toggle in Profile → Advanced. `storage` events only fire
|
|
270642
|
+
* cross-tab, so this covers the same-tab case (e.g. a nav-bar badge that has to
|
|
270643
|
+
* update the instant a model is picked in a modal above it).
|
|
270371
270644
|
*/
|
|
270372
|
-
|
|
270373
|
-
var _a, _b;
|
|
270374
|
-
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;
|
|
270375
|
-
}
|
|
270645
|
+
const LOCAL_LLM_CHANGED_EVENT = 'ibl:local-llm-changed';
|
|
270376
270646
|
/**
|
|
270377
|
-
*
|
|
270378
|
-
*
|
|
270379
|
-
*
|
|
270380
|
-
*
|
|
270381
|
-
*
|
|
270382
|
-
* so the two always line up: group 1 renders normally, group 2 renders grayed.
|
|
270383
|
-
* Grouping on credentials alone would let a provider that has a key but ships no
|
|
270384
|
-
* chat models sort into the "usable" group while rendering grayed.
|
|
270385
|
-
*
|
|
270386
|
-
* Pure and non-mutating: RTK Query results are frozen, so the input is copied
|
|
270387
|
-
* before sorting. `Array.prototype.sort` is stable, so providers that tie on
|
|
270388
|
-
* both group and display name keep their original relative order.
|
|
270647
|
+
* Reactive view of the on-device model selection (`ibl_local_llm_enabled` +
|
|
270648
|
+
* `ibl_local_llm_model` in localStorage). Reads on mount — so it is
|
|
270649
|
+
* SSR/hydration-safe (nothing resolves until the client mounts) — and re-reads
|
|
270650
|
+
* on cross-tab `storage` events and same-tab {@link LOCAL_LLM_CHANGED_EVENT}
|
|
270651
|
+
* events.
|
|
270389
270652
|
*/
|
|
270390
|
-
function
|
|
270391
|
-
|
|
270392
|
-
|
|
270393
|
-
|
|
270394
|
-
|
|
270395
|
-
return aUsable ? -1 : 1;
|
|
270396
|
-
return compareLLMProvidersByDisplayName(a.name, b.name, getDetails);
|
|
270653
|
+
function useSelectedLocalModel() {
|
|
270654
|
+
const [sel, setSel] = useState({
|
|
270655
|
+
isLocal: false,
|
|
270656
|
+
model: null,
|
|
270657
|
+
modelId: null,
|
|
270397
270658
|
});
|
|
270398
|
-
|
|
270399
|
-
|
|
270400
|
-
|
|
270401
|
-
|
|
270402
|
-
|
|
270659
|
+
useEffect(() => {
|
|
270660
|
+
const read = () => {
|
|
270661
|
+
var _a;
|
|
270662
|
+
const enabled = isLocalLLMEnabled();
|
|
270663
|
+
const id = getLocalLLMModel();
|
|
270664
|
+
const model = id ? ((_a = LOCAL_MODELS.find((m) => m.id === id)) !== null && _a !== void 0 ? _a : null) : null;
|
|
270665
|
+
// Self-heal the derived tool-support cache. Local chat routes on
|
|
270666
|
+
// `ibl_local_llm_tool_support`, but an older selection may have left it
|
|
270667
|
+
// stale/false — which makes streaming reject a genuinely tool-capable
|
|
270668
|
+
// model. The catalog is the source of truth; reconcile here. Write only on
|
|
270669
|
+
// a real mismatch so it can't loop.
|
|
270670
|
+
if (enabled && model && getLocalLLMToolSupport() !== model.tool_support) {
|
|
270671
|
+
setLocalLLMToolSupport(model.tool_support);
|
|
270672
|
+
}
|
|
270673
|
+
setSel({ isLocal: enabled && !!id, model, modelId: id });
|
|
270674
|
+
};
|
|
270675
|
+
read();
|
|
270676
|
+
window.addEventListener('storage', read);
|
|
270677
|
+
window.addEventListener(LOCAL_LLM_CHANGED_EVENT, read);
|
|
270678
|
+
return () => {
|
|
270679
|
+
window.removeEventListener('storage', read);
|
|
270680
|
+
window.removeEventListener(LOCAL_LLM_CHANGED_EVENT, read);
|
|
270681
|
+
};
|
|
270682
|
+
}, []);
|
|
270683
|
+
return sel;
|
|
270403
270684
|
}
|
|
270404
270685
|
|
|
270405
270686
|
const AGENT_LLM_TAB_LABELS = {
|
|
@@ -270407,18 +270688,47 @@ const AGENT_LLM_TAB_LABELS = {
|
|
|
270407
270688
|
title: 'LLM Configuration',
|
|
270408
270689
|
description: 'Configure the language model settings for your agent.',
|
|
270409
270690
|
},
|
|
270691
|
+
infoBox: 'This is the brain behind your agent — the AI model that powers every reply. Bigger models reason better on tricky questions; lighter ones are faster and cost less. Not sure? The default is a solid all-rounder.',
|
|
270410
270692
|
search: {
|
|
270411
270693
|
placeholder: 'Search Providers',
|
|
270412
270694
|
},
|
|
270695
|
+
providerLogoAlt: (providerName) => `${providerName} logo`,
|
|
270413
270696
|
toasts: {
|
|
270414
270697
|
updateSuccess: 'LLM updated successfully',
|
|
270415
270698
|
updateError: 'Failed to update LLM',
|
|
270416
270699
|
},
|
|
270417
270700
|
providerModal: {
|
|
270418
270701
|
title: 'LLM Selection',
|
|
270419
|
-
description:
|
|
270702
|
+
description: (providerName) => `Select one of the agents provided by ${providerName}`,
|
|
270420
270703
|
searchPlaceholder: 'Search',
|
|
270421
270704
|
helpText: 'Choose your preferred LLM from the available provider to tailor your experience.',
|
|
270705
|
+
providerIconAlt: (providerName) => `${providerName} icon`,
|
|
270706
|
+
tooLargeTitle: 'This model may be too large for your system',
|
|
270707
|
+
tooLargeDescription: (modelName, modelSize) => `${modelName} needs about ${modelSize}. It may run very slowly or fail to load on this machine. Download anyway?`,
|
|
270708
|
+
cancel: 'Cancel',
|
|
270709
|
+
downloadAnyway: 'Download anyway',
|
|
270710
|
+
alreadyDownloadingTitle: 'A model is already downloading',
|
|
270711
|
+
unnamedModel: 'A model',
|
|
270712
|
+
alreadyDownloadingDescription: (modelName) => `${modelName} is being downloaded. Only one on-device model downloads at a time — wait for it to finish, or click ${modelName} in the list to cancel it.`,
|
|
270713
|
+
gotIt: 'Got it',
|
|
270714
|
+
announceDownloaded: (modelName) => `${modelName} downloaded`,
|
|
270715
|
+
announceCancelled: 'Download cancelled',
|
|
270716
|
+
announceFailed: 'Download failed',
|
|
270717
|
+
announceStarted: (modelName) => `Started downloading ${modelName}`,
|
|
270718
|
+
localModel: {
|
|
270719
|
+
onDevice: 'On-device',
|
|
270720
|
+
starting: 'Starting…',
|
|
270721
|
+
cancel: 'Cancel',
|
|
270722
|
+
inUse: 'In use',
|
|
270723
|
+
downloadFailedRetry: 'Download failed — retry',
|
|
270724
|
+
ariaDownload: (modelName, modelSize) => `Download ${modelName}, ${modelSize}, on-device model`,
|
|
270725
|
+
ariaStarting: (modelName) => `Starting download of ${modelName}`,
|
|
270726
|
+
ariaDownloading: (modelName, percent) => `Cancel download of ${modelName}, ${percent} percent`,
|
|
270727
|
+
ariaInstalled: (modelName) => `Use ${modelName}, on-device model`,
|
|
270728
|
+
ariaSelected: (modelName) => `Selected ${modelName}, on-device model, in use`,
|
|
270729
|
+
ariaError: (modelName) => `Retry download of ${modelName}`,
|
|
270730
|
+
ariaErrorWithReason: (modelName, reason) => `Retry download of ${modelName}, ${reason}`,
|
|
270731
|
+
},
|
|
270422
270732
|
},
|
|
270423
270733
|
};
|
|
270424
270734
|
function buildLLMTabLabels(t) {
|
|
@@ -270427,18 +270737,47 @@ function buildLLMTabLabels(t) {
|
|
|
270427
270737
|
title: t('llmTabLabels.header.title'),
|
|
270428
270738
|
description: t('llmTabLabels.header.description'),
|
|
270429
270739
|
},
|
|
270740
|
+
infoBox: t('llmTabLabels.infoBox'),
|
|
270430
270741
|
search: {
|
|
270431
270742
|
placeholder: t('llmTabLabels.search.placeholder'),
|
|
270432
270743
|
},
|
|
270744
|
+
providerLogoAlt: (providerName) => t('llmTabLabels.providerLogoAlt', { providerName }),
|
|
270433
270745
|
toasts: {
|
|
270434
270746
|
updateSuccess: t('llmTabLabels.toasts.updateSuccess'),
|
|
270435
270747
|
updateError: t('llmTabLabels.toasts.updateError'),
|
|
270436
270748
|
},
|
|
270437
270749
|
providerModal: {
|
|
270438
270750
|
title: t('llmTabLabels.providerModal.title'),
|
|
270439
|
-
description: t('llmTabLabels.providerModal.description'),
|
|
270751
|
+
description: (providerName) => t('llmTabLabels.providerModal.description', { providerName }),
|
|
270440
270752
|
searchPlaceholder: t('llmTabLabels.providerModal.searchPlaceholder'),
|
|
270441
270753
|
helpText: t('llmTabLabels.providerModal.helpText'),
|
|
270754
|
+
providerIconAlt: (providerName) => t('llmTabLabels.providerModal.providerIconAlt', { providerName }),
|
|
270755
|
+
tooLargeTitle: t('llmTabLabels.providerModal.tooLargeTitle'),
|
|
270756
|
+
tooLargeDescription: (modelName, modelSize) => t('llmTabLabels.providerModal.tooLargeDescription', { modelName, modelSize }),
|
|
270757
|
+
cancel: t('llmTabLabels.providerModal.cancel'),
|
|
270758
|
+
downloadAnyway: t('llmTabLabels.providerModal.downloadAnyway'),
|
|
270759
|
+
alreadyDownloadingTitle: t('llmTabLabels.providerModal.alreadyDownloadingTitle'),
|
|
270760
|
+
unnamedModel: t('llmTabLabels.providerModal.unnamedModel'),
|
|
270761
|
+
alreadyDownloadingDescription: (modelName) => t('llmTabLabels.providerModal.alreadyDownloadingDescription', { modelName }),
|
|
270762
|
+
gotIt: t('llmTabLabels.providerModal.gotIt'),
|
|
270763
|
+
announceDownloaded: (modelName) => t('llmTabLabels.providerModal.announceDownloaded', { modelName }),
|
|
270764
|
+
announceCancelled: t('llmTabLabels.providerModal.announceCancelled'),
|
|
270765
|
+
announceFailed: t('llmTabLabels.providerModal.announceFailed'),
|
|
270766
|
+
announceStarted: (modelName) => t('llmTabLabels.providerModal.announceStarted', { modelName }),
|
|
270767
|
+
localModel: {
|
|
270768
|
+
onDevice: t('llmTabLabels.providerModal.localModel.onDevice'),
|
|
270769
|
+
starting: t('llmTabLabels.providerModal.localModel.starting'),
|
|
270770
|
+
cancel: t('llmTabLabels.providerModal.localModel.cancel'),
|
|
270771
|
+
inUse: t('llmTabLabels.providerModal.localModel.inUse'),
|
|
270772
|
+
downloadFailedRetry: t('llmTabLabels.providerModal.localModel.downloadFailedRetry'),
|
|
270773
|
+
ariaDownload: (modelName, modelSize) => t('llmTabLabels.providerModal.localModel.ariaDownload', { modelName, modelSize }),
|
|
270774
|
+
ariaStarting: (modelName) => t('llmTabLabels.providerModal.localModel.ariaStarting', { modelName }),
|
|
270775
|
+
ariaDownloading: (modelName, percent) => t('llmTabLabels.providerModal.localModel.ariaDownloading', { modelName, percent }),
|
|
270776
|
+
ariaInstalled: (modelName) => t('llmTabLabels.providerModal.localModel.ariaInstalled', { modelName }),
|
|
270777
|
+
ariaSelected: (modelName) => t('llmTabLabels.providerModal.localModel.ariaSelected', { modelName }),
|
|
270778
|
+
ariaError: (modelName) => t('llmTabLabels.providerModal.localModel.ariaError', { modelName }),
|
|
270779
|
+
ariaErrorWithReason: (modelName, reason) => t('llmTabLabels.providerModal.localModel.ariaErrorWithReason', { modelName, reason }),
|
|
270780
|
+
},
|
|
270442
270781
|
},
|
|
270443
270782
|
};
|
|
270444
270783
|
}
|
|
@@ -270450,38 +270789,287 @@ function resolveLLMTabLabels(override, t) {
|
|
|
270450
270789
|
return deepMerge(base, override);
|
|
270451
270790
|
}
|
|
270452
270791
|
|
|
270453
|
-
|
|
270792
|
+
/**
|
|
270793
|
+
* A local (on-device) model presented in the same list as cloud models. It uses
|
|
270794
|
+
* the same provider logo as the cloud rows; the model name gets its own line and
|
|
270795
|
+
* the on-device badge, size, and download/status affordance sit on a subtitle
|
|
270796
|
+
* line beneath it. One click toggles download ⇄ cancel; once installed a click
|
|
270797
|
+
* selects it.
|
|
270798
|
+
*/
|
|
270799
|
+
function LocalModelRow({ name, size, logo, status, progress, disabled = false, disabledReason, errorMessage, onActivate, labels = AGENT_LLM_TAB_LABELS.providerModal.localModel, }) {
|
|
270800
|
+
const selected = status === 'selected';
|
|
270801
|
+
const downloading = status === 'downloading' || status === 'starting';
|
|
270802
|
+
// A resting row (nothing downloading) can still be disabled when another
|
|
270803
|
+
// model is pulling; a row that is itself downloading stays clickable (cancel).
|
|
270804
|
+
const isBusyDisabled = disabled && !downloading && !selected;
|
|
270805
|
+
const inert = isBusyDisabled || selected;
|
|
270806
|
+
const pct = Math.round(Math.max(0, Math.min(100, progress)));
|
|
270807
|
+
const ariaLabel = (() => {
|
|
270808
|
+
switch (status) {
|
|
270809
|
+
case 'not-installed':
|
|
270810
|
+
return labels.ariaDownload(name, size);
|
|
270811
|
+
case 'starting':
|
|
270812
|
+
return labels.ariaStarting(name);
|
|
270813
|
+
case 'downloading':
|
|
270814
|
+
return labels.ariaDownloading(name, pct);
|
|
270815
|
+
case 'installed':
|
|
270816
|
+
return labels.ariaInstalled(name);
|
|
270817
|
+
case 'selected':
|
|
270818
|
+
return labels.ariaSelected(name);
|
|
270819
|
+
case 'error':
|
|
270820
|
+
return errorMessage
|
|
270821
|
+
? labels.ariaErrorWithReason(name, errorMessage)
|
|
270822
|
+
: labels.ariaError(name);
|
|
270823
|
+
}
|
|
270824
|
+
})();
|
|
270825
|
+
return (jsxs("button", { type: "button", "data-testid": "llm-local-model-row", "data-model": name, "data-status": status, disabled: inert, "aria-label": ariaLabel, title: isBusyDisabled ? disabledReason : ariaLabel, onClick: () => {
|
|
270826
|
+
if (!inert)
|
|
270827
|
+
onActivate();
|
|
270828
|
+
}, className: cn(
|
|
270829
|
+
// `isolate` makes the button its own stacking context so the absolute
|
|
270830
|
+
// progress fill (z-0) layers above the button's background and below the
|
|
270831
|
+
// content (z-10) — without it the fill can render behind the background.
|
|
270832
|
+
'group relative isolate flex items-center gap-3 overflow-hidden rounded-lg border p-4 text-left transition-colors', selected
|
|
270833
|
+
? 'cursor-default border-blue-500 bg-blue-50'
|
|
270834
|
+
: isBusyDisabled
|
|
270835
|
+
? 'cursor-not-allowed border-gray-200 opacity-60'
|
|
270836
|
+
: '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",
|
|
270837
|
+
// Minimum width so the bar is visible the moment a download starts
|
|
270838
|
+
// (progress 0 / "starting"), then tracks real progress.
|
|
270839
|
+
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', {
|
|
270840
|
+
grayscale: isBusyDisabled,
|
|
270841
|
+
}), 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" })] }))] })] })] }));
|
|
270842
|
+
}
|
|
270843
|
+
|
|
270844
|
+
function LLMProviderModal({ isOpen, onClose, onSelect, llmProvider, isSelecting, mentorSettings, llms, getLLMProviderDetails: getLLMProviderDetails$1 = getLLMProviderDetails, labels = AGENT_LLM_TAB_LABELS.providerModal, }) {
|
|
270845
|
+
var _a, _b;
|
|
270454
270846
|
const [searchQuery, setSearchQuery] = React__default.useState('');
|
|
270847
|
+
// On-device model download state (Tauri desktop only; `isAvailable` is false
|
|
270848
|
+
// on web, which hides every local row below).
|
|
270849
|
+
const { isAvailable, state: downloadState, ollamaStatus, systemMemory, startDownload, cancelDownload, } = useModelDownload();
|
|
270850
|
+
// Mirror the device-global local selection so picks re-render immediately.
|
|
270851
|
+
const [localSel, setLocalSel] = React__default.useState(() => ({
|
|
270852
|
+
enabled: isLocalLLMEnabled(),
|
|
270853
|
+
model: getLocalLLMModel(),
|
|
270854
|
+
}));
|
|
270855
|
+
// Model awaiting the "may be too large" confirmation before its download
|
|
270856
|
+
// starts; null when no confirmation is pending.
|
|
270857
|
+
const [pendingModel, setPendingModel] = React__default.useState(null);
|
|
270858
|
+
// Name of the model currently downloading, shown in an info dialog when the
|
|
270859
|
+
// user clicks a DIFFERENT model (one on-device download at a time). Null when
|
|
270860
|
+
// no such notice is showing.
|
|
270861
|
+
const [busyDownloadName, setBusyDownloadName] = React__default.useState(null);
|
|
270862
|
+
// The model THIS picker asked to download. Modal-local (not the shared, churny
|
|
270863
|
+
// download state) so it survives the hook's once-per-load reset / remounts /
|
|
270864
|
+
// reloads that wipe `downloadState.activeModel` — the pull's progress events
|
|
270865
|
+
// carry no model id, so once `activeModel` is lost nothing restores it and the
|
|
270866
|
+
// row can't recognize its own in-flight download (bar advances, row still says
|
|
270867
|
+
// "Download"). Cleared only on a terminal status, so the reset's transient
|
|
270868
|
+
// `idle` doesn't drop it mid-pull.
|
|
270869
|
+
const [requestedModel, setRequestedModel] = React__default.useState(null);
|
|
270870
|
+
React__default.useEffect(() => {
|
|
270871
|
+
if (downloadState.status === 'completed' ||
|
|
270872
|
+
downloadState.status === 'cancelled' ||
|
|
270873
|
+
downloadState.status === 'error') {
|
|
270874
|
+
setRequestedModel(null);
|
|
270875
|
+
}
|
|
270876
|
+
}, [downloadState.status]);
|
|
270877
|
+
// Which on-device model is downloading right now: prefer this picker's own
|
|
270878
|
+
// request, fall back to the shared state's id when present.
|
|
270879
|
+
const downloadingModel = requestedModel !== null && requestedModel !== void 0 ? requestedModel : downloadState.activeModel;
|
|
270880
|
+
// The last on-device model whose download reported `completed`. Held sticky so
|
|
270881
|
+
// its row shows "installed" straight through the gap between the terminal
|
|
270882
|
+
// `completed` event and the /api/tags refresh landing — otherwise the completed
|
|
270883
|
+
// handler's checkStatus churns downloadState.status (completed → checking →
|
|
270884
|
+
// idle) first and the row flickers back to a "Download" button.
|
|
270885
|
+
const [lastCompleted, setLastCompleted] = React__default.useState(null);
|
|
270886
|
+
React__default.useEffect(() => {
|
|
270887
|
+
if (downloadState.status === 'completed' && downloadingModel) {
|
|
270888
|
+
setLastCompleted(downloadingModel);
|
|
270889
|
+
}
|
|
270890
|
+
}, [downloadState.status, downloadingModel]);
|
|
270455
270891
|
const filteredLLMs = React__default.useMemo(() => {
|
|
270456
|
-
|
|
270892
|
+
var _a, _b;
|
|
270893
|
+
// Guard `chat_models` (not just `llmProvider`): a provider can come back from
|
|
270894
|
+
// the API without a models array, and an undefined `.filter` here throws in
|
|
270895
|
+
// render and unmounts the whole dialog. Coalesce so the `.map` consumer below
|
|
270896
|
+
// always gets an array.
|
|
270897
|
+
return ((_b = (_a = llmProvider === null || llmProvider === void 0 ? void 0 : llmProvider.chat_models) === null || _a === void 0 ? void 0 : _a.filter((llm) => {
|
|
270898
|
+
var _a;
|
|
270899
|
+
const query = searchQuery.toLowerCase();
|
|
270900
|
+
// Match either label: rows are shown by `display_name`, so searching
|
|
270901
|
+
// what you can read has to work, but the raw key stays searchable for
|
|
270902
|
+
// anyone who knows it (e.g. "gpt-4.1" or "amazon.nova").
|
|
270903
|
+
return (llm.llm_name.toLowerCase().includes(query) ||
|
|
270904
|
+
((_a = llm.display_name) !== null && _a !== void 0 ? _a : '').toLowerCase().includes(query));
|
|
270905
|
+
})) !== null && _b !== void 0 ? _b : []);
|
|
270457
270906
|
}, [searchQuery, llmProvider]);
|
|
270907
|
+
// Local models belonging to THIS provider (merged into the same list).
|
|
270908
|
+
const localModels = React__default.useMemo(() => {
|
|
270909
|
+
if (!isAvailable)
|
|
270910
|
+
return [];
|
|
270911
|
+
const key = getProviderName(llmProvider.name);
|
|
270912
|
+
return LOCAL_MODELS.filter((m) => getProviderName(m.provider) === key &&
|
|
270913
|
+
m.name.toLowerCase().includes(searchQuery.toLowerCase()));
|
|
270914
|
+
}, [isAvailable, llmProvider.name, searchQuery]);
|
|
270458
270915
|
const switchLLMAllowed = canSwitchLLm(llmProvider);
|
|
270459
270916
|
const switchProviderAllowed = canSwitchProvider(llms, llmProvider.name);
|
|
270460
|
-
|
|
270461
|
-
|
|
270462
|
-
|
|
270463
|
-
|
|
270464
|
-
|
|
270465
|
-
|
|
270466
|
-
|
|
270467
|
-
|
|
270468
|
-
|
|
270469
|
-
|
|
270470
|
-
|
|
270471
|
-
|
|
270472
|
-
|
|
270473
|
-
|
|
270474
|
-
|
|
270475
|
-
|
|
270476
|
-
|
|
270477
|
-
|
|
270478
|
-
|
|
270479
|
-
|
|
270480
|
-
|
|
270481
|
-
|
|
270917
|
+
// Only an actual pull counts as "busy". A status refresh ('checking') must NOT
|
|
270918
|
+
// — otherwise a stale `activeModel` makes a row flash a progress bar every time
|
|
270919
|
+
// status is re-checked.
|
|
270920
|
+
const busyDownloading = downloadState.status === 'downloading';
|
|
270921
|
+
const statusFor = (m) => {
|
|
270922
|
+
if (localSel.enabled && localSel.model === m.id)
|
|
270923
|
+
return 'selected';
|
|
270924
|
+
const active = sameModel(downloadingModel, m.id);
|
|
270925
|
+
if (active && downloadState.status === 'error')
|
|
270926
|
+
return 'error';
|
|
270927
|
+
if (active && busyDownloading)
|
|
270928
|
+
return downloadState.progress > 0 ? 'downloading' : 'starting';
|
|
270929
|
+
// `lastCompleted` bridges the just-finished → tags-refreshed gap so the row
|
|
270930
|
+
// stays "installed" instead of flickering back to "Download".
|
|
270931
|
+
if (sameModel(lastCompleted, m.id) ||
|
|
270932
|
+
isLocalModelInstalled(m.id, ollamaStatus === null || ollamaStatus === void 0 ? void 0 : ollamaStatus.installed_models))
|
|
270933
|
+
return 'installed';
|
|
270934
|
+
return 'not-installed';
|
|
270935
|
+
};
|
|
270936
|
+
// A cloud model is "in use" when it's the mentor's selected LLM and local mode
|
|
270937
|
+
// is off.
|
|
270938
|
+
const isCloudActive = (llm) => (mentorSettings === null || mentorSettings === void 0 ? void 0 : mentorSettings.llm_name) === llm.llm_name &&
|
|
270939
|
+
(mentorSettings === null || mentorSettings === void 0 ? void 0 : mentorSettings.llm_provider) === llmProvider.name &&
|
|
270940
|
+
!localSel.enabled;
|
|
270941
|
+
// Order the WHOLE list — cloud + local — available (usable now) first,
|
|
270942
|
+
// unavailable last, with the in-use model leading. "Available" for a cloud
|
|
270943
|
+
// model means the provider is switchable (has credentials + models); for a
|
|
270944
|
+
// local model it's the install state. So an installed on-device model outranks
|
|
270945
|
+
// cloud models whose provider has no credentials. Ranked by state, not the
|
|
270946
|
+
// transient download status, so rows don't jump mid-pull; a stable sort keeps
|
|
270947
|
+
// the original (catalog) order within each rank.
|
|
270948
|
+
const cloudRank = (llm) => isCloudActive(llm) ? 0 : switchLLMAllowed && switchProviderAllowed ? 1 : 2;
|
|
270949
|
+
const localRank = (m) => {
|
|
270950
|
+
const s = statusFor(m);
|
|
270951
|
+
return s === 'selected' ? 0 : s === 'installed' ? 1 : 2;
|
|
270952
|
+
};
|
|
270953
|
+
const sortedModels = [
|
|
270954
|
+
...filteredLLMs.map((llm) => ({ kind: 'cloud', llm, rank: cloudRank(llm) })),
|
|
270955
|
+
...localModels.map((model) => ({ kind: 'local', model, rank: localRank(model) })),
|
|
270956
|
+
].sort((a, b) => a.rank - b.rank);
|
|
270957
|
+
// Preserve the "model may be too large" prompt before a download starts. Opens
|
|
270958
|
+
// the confirmation AlertDialog below (layered on top of the picker), rather
|
|
270959
|
+
// than a toast. `modelExceedsCapacity` compares the model's size to this
|
|
270960
|
+
// machine's RAM/VRAM; when memory is unknown it returns false, so the download
|
|
270961
|
+
// simply proceeds rather than blocking on incomplete data.
|
|
270962
|
+
const startLocalDownload = (m) => {
|
|
270963
|
+
if (modelExceedsCapacity(m, systemMemory)) {
|
|
270964
|
+
setPendingModel(m);
|
|
270965
|
+
return;
|
|
270966
|
+
}
|
|
270967
|
+
setRequestedModel(m.id);
|
|
270968
|
+
startDownload(m.id);
|
|
270969
|
+
};
|
|
270970
|
+
const activateLocal = (m, status) => {
|
|
270971
|
+
var _a;
|
|
270972
|
+
switch (status) {
|
|
270973
|
+
case 'not-installed':
|
|
270974
|
+
case 'error':
|
|
270975
|
+
// Only one on-device model downloads at a time. If ANY pull is already
|
|
270976
|
+
// running, don't start another — starting concurrent pulls floods every
|
|
270977
|
+
// useModelDownload instance with progress events and can hang the app.
|
|
270978
|
+
// Block even when the running model is unknown (activeModel wiped); the
|
|
270979
|
+
// user cancels by clicking the downloading row itself. Selecting an
|
|
270980
|
+
// already-installed model is unaffected (it starts no pull).
|
|
270981
|
+
if (busyDownloading && !sameModel(downloadingModel, m.id)) {
|
|
270982
|
+
const dl = LOCAL_MODELS.find((x) => sameModel(x.id, downloadingModel));
|
|
270983
|
+
setBusyDownloadName((_a = dl === null || dl === void 0 ? void 0 : dl.name) !== null && _a !== void 0 ? _a : labels.unnamedModel);
|
|
270984
|
+
return;
|
|
270985
|
+
}
|
|
270986
|
+
startLocalDownload(m);
|
|
270987
|
+
break;
|
|
270988
|
+
case 'starting':
|
|
270989
|
+
case 'downloading':
|
|
270990
|
+
cancelDownload();
|
|
270991
|
+
break;
|
|
270992
|
+
case 'installed':
|
|
270993
|
+
// Use this on-device model — device-global, mutually exclusive with cloud.
|
|
270994
|
+
setLocalLLMModel(m.id);
|
|
270995
|
+
setLocalLLMEnabled(true);
|
|
270996
|
+
// Persist tool-calling support so local chat routes to the MCP/tool
|
|
270997
|
+
// bridge. Without this the flag stays at its default (false) and
|
|
270998
|
+
// streaming chat rejects the model with "tool_support=false" even though
|
|
270999
|
+
// the catalog marks it tool-capable.
|
|
271000
|
+
setLocalLLMToolSupport(m.tool_support);
|
|
271001
|
+
setLocalSel({ enabled: true, model: m.id });
|
|
271002
|
+
// Notify same-tab listeners (e.g. a nav-bar on-device badge).
|
|
271003
|
+
window.dispatchEvent(new Event(LOCAL_LLM_CHANGED_EVENT));
|
|
271004
|
+
break;
|
|
271005
|
+
}
|
|
271006
|
+
};
|
|
271007
|
+
const selectCloud = (llmName) => {
|
|
271008
|
+
// Picking a cloud model turns local mode off so routing uses the cloud LLM.
|
|
271009
|
+
if (localSel.enabled) {
|
|
271010
|
+
setLocalLLMEnabled(false);
|
|
271011
|
+
setLocalSel((prev) => ({ ...prev, enabled: false }));
|
|
271012
|
+
window.dispatchEvent(new Event(LOCAL_LLM_CHANGED_EVENT));
|
|
271013
|
+
}
|
|
271014
|
+
onSelect(llmProvider.name, llmName);
|
|
271015
|
+
};
|
|
271016
|
+
// Announce download lifecycle changes once (not every %) for screen readers.
|
|
271017
|
+
const activeLocal = LOCAL_MODELS.find((m) => sameModel(m.id, downloadingModel));
|
|
271018
|
+
const liveMessage = !activeLocal
|
|
271019
|
+
? ''
|
|
271020
|
+
: downloadState.status === 'completed'
|
|
271021
|
+
? labels.announceDownloaded(activeLocal.name)
|
|
271022
|
+
: downloadState.status === 'cancelled'
|
|
271023
|
+
? labels.announceCancelled
|
|
271024
|
+
: downloadState.status === 'error'
|
|
271025
|
+
? labels.announceFailed
|
|
271026
|
+
: downloadState.status === 'checking' ||
|
|
271027
|
+
(downloadState.status === 'downloading' && downloadState.progress === 0)
|
|
271028
|
+
? labels.announceStarted(activeLocal.name)
|
|
271029
|
+
: '';
|
|
271030
|
+
return (jsx(Dialog, { open: isOpen, onOpenChange: onClose, children: jsxs(DialogContent, { className: "max-w-4xl space-y-6 p-4", children: [jsx(DialogDescription, { className: "sr-only", children: labels.description(llmProvider.name) }), jsx(DialogHeader, { children: jsx(DialogTitle, { className: "ibl-dialog-title", children: labels.title }) }), jsx("p", { className: "text-sm text-gray-600", children: labels.helpText }), 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: labels.searchPlaceholder, className: "py-6 pl-9", value: searchQuery, onChange: (e) => setSearchQuery(e.target.value), autoComplete: "off", disabled: isSelecting })] }), jsx("div", { className: "sr-only", "aria-live": "polite", role: "status", children: liveMessage }), 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: sortedModels.map((item) => {
|
|
271031
|
+
if (item.kind === 'cloud') {
|
|
271032
|
+
const llm = item.llm;
|
|
271033
|
+
const isActive = isCloudActive(llm);
|
|
271034
|
+
const providerDetails = getLLMProviderDetails$1(llmProvider.name, llm.llm_name);
|
|
271035
|
+
const isDisabled = !switchLLMAllowed || isSelecting || isActive || !switchProviderAllowed;
|
|
271036
|
+
return (jsxs("button", { "data-model": llm.llm_name, disabled: isDisabled, onClick: () => {
|
|
271037
|
+
selectCloud(llm.llm_name);
|
|
271038
|
+
}, className: cn('flex cursor-pointer items-center gap-3 rounded-lg border border-gray-200 p-4 transition-colors', {
|
|
271039
|
+
// Matches the provider grid's inactive treatment: tinted
|
|
271040
|
+
// card, faded logo, full-strength label.
|
|
271041
|
+
'cursor-not-allowed border-gray-100 bg-gray-50': isDisabled && !isActive,
|
|
271042
|
+
'hover:border-blue-500 hover:bg-blue-50': !isDisabled,
|
|
271043
|
+
'cursor-not-allowed border-blue-500 bg-blue-50': isActive,
|
|
271044
|
+
}), 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', {
|
|
271045
|
+
// `grayscale` alone is a no-op on already-black marks,
|
|
271046
|
+
// so fade them as well — same rule as the provider grid.
|
|
271047
|
+
'opacity-40 grayscale': isDisabled && !isActive,
|
|
271048
|
+
}), 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}`));
|
|
271049
|
+
}
|
|
271050
|
+
const m = item.model;
|
|
271051
|
+
const status = statusFor(m);
|
|
271052
|
+
return (jsx(LocalModelRow, { name: m.name, size: m.size, 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}`));
|
|
271053
|
+
}) }), jsx(AlertDialog, { open: !!pendingModel, onOpenChange: (open) => {
|
|
271054
|
+
if (!open)
|
|
271055
|
+
setPendingModel(null);
|
|
271056
|
+
}, children: jsxs(AlertDialogContent, { children: [jsxs(AlertDialogHeader, { children: [jsx(AlertDialogTitle, { children: labels.tooLargeTitle }), jsx(AlertDialogDescription, { children: labels.tooLargeDescription((_a = pendingModel === null || pendingModel === void 0 ? void 0 : pendingModel.name) !== null && _a !== void 0 ? _a : '', (_b = pendingModel === null || pendingModel === void 0 ? void 0 : pendingModel.size) !== null && _b !== void 0 ? _b : '') })] }), jsxs(AlertDialogFooter, { children: [jsx(AlertDialogCancel, { onClick: () => setPendingModel(null), children: labels.cancel }), jsx(AlertDialogAction, { onClick: () => {
|
|
271057
|
+
if (pendingModel) {
|
|
271058
|
+
setRequestedModel(pendingModel.id);
|
|
271059
|
+
startDownload(pendingModel.id);
|
|
271060
|
+
}
|
|
271061
|
+
setPendingModel(null);
|
|
271062
|
+
}, children: labels.downloadAnyway })] })] }) }), jsx(AlertDialog, { open: !!busyDownloadName, onOpenChange: (open) => {
|
|
271063
|
+
if (!open)
|
|
271064
|
+
setBusyDownloadName(null);
|
|
271065
|
+
}, 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 }) })] }) })] }) }));
|
|
271066
|
+
}
|
|
271067
|
+
|
|
271068
|
+
function AgentLLMTab({ showConfigurationHeader = true, mentorId: mentorIdOverride, getLLMProviderDetails: getLLMProviderDetails$1 = getLLMProviderDetails, labels: labelsOverride, }) {
|
|
270482
271069
|
var _a;
|
|
270483
271070
|
const t = useT();
|
|
270484
|
-
const { tenantKey, mentorId, username, enableRBAC } = useAgentSettings();
|
|
271071
|
+
const { tenantKey, mentorId: contextMentorId, username, enableRBAC } = useAgentSettings();
|
|
271072
|
+
const mentorId = mentorIdOverride || contextMentorId;
|
|
270485
271073
|
const labels = React__default.useMemo(() => resolveLLMTabLabels(labelsOverride, t), [labelsOverride, t]);
|
|
270486
271074
|
const [searchQuery, setSearchQuery] = React__default.useState('');
|
|
270487
271075
|
const [selectedLLMProvider, setSelectedLLMProvider] = React__default.useState(null);
|
|
@@ -270499,13 +271087,44 @@ function AgentLLMTab({ showConfigurationHeader = true, getLLMProviderDetails, la
|
|
|
270499
271087
|
}, {
|
|
270500
271088
|
skip: !tenantKey || !username,
|
|
270501
271089
|
});
|
|
271090
|
+
// Providers that only offer on-device (local) models — surfaced as grid cards
|
|
271091
|
+
// (Tauri desktop only) so their downloadable models are reachable even when the
|
|
271092
|
+
// backend LLM list has no matching cloud provider.
|
|
271093
|
+
const localOnlyProviders = React__default.useMemo(() => {
|
|
271094
|
+
if (!isTauriApp$1())
|
|
271095
|
+
return [];
|
|
271096
|
+
const cloudKeys = new Set((llmProviders !== null && llmProviders !== void 0 ? llmProviders : []).map((p) => getProviderName(p.name)));
|
|
271097
|
+
const seen = new Set();
|
|
271098
|
+
const result = [];
|
|
271099
|
+
for (const model of LOCAL_MODELS) {
|
|
271100
|
+
const key = getProviderName(model.provider);
|
|
271101
|
+
if (cloudKeys.has(key) || seen.has(key))
|
|
271102
|
+
continue;
|
|
271103
|
+
seen.add(key);
|
|
271104
|
+
result.push({ key, provider: model.provider });
|
|
271105
|
+
}
|
|
271106
|
+
// Alphabetical among themselves; the whole local-only block still renders
|
|
271107
|
+
// after the API providers.
|
|
271108
|
+
return result.sort((a, b) => compareLLMProvidersByDisplayName(a.provider, b.provider, getLLMProviderDetails$1));
|
|
271109
|
+
}, [llmProviders, getLLMProviderDetails$1]);
|
|
270502
271110
|
// Two alphabetical groups: providers we have a usable LLM key for first, then
|
|
270503
271111
|
// the ones we don't. Ordering is independent of the search filter below, so it
|
|
270504
271112
|
// holds for whatever the filter leaves.
|
|
270505
|
-
const sortedLLMProviders = React__default.useMemo(() => sortLLMProvidersByCredentials(llmProviders !== null && llmProviders !== void 0 ? llmProviders : [], getLLMProviderDetails), [llmProviders, getLLMProviderDetails]);
|
|
271113
|
+
const sortedLLMProviders = React__default.useMemo(() => sortLLMProvidersByCredentials(llmProviders !== null && llmProviders !== void 0 ? llmProviders : [], getLLMProviderDetails$1), [llmProviders, getLLMProviderDetails$1]);
|
|
270506
271114
|
const [editMentor, { isLoading: isEditingMentor }] = useEditMentorMutation();
|
|
270507
271115
|
const isDisabled = isMentorSettingsLoading || isLoadingLLMProviders || isEditingMentor;
|
|
270508
271116
|
const isLoading = isMentorSettingsLoading || isLoadingLLMProviders;
|
|
271117
|
+
// The highlighted (selected) provider card must reflect the model chat
|
|
271118
|
+
// actually uses. When on-device mode is on, that's the selected local model's
|
|
271119
|
+
// provider (e.g. Llama 3.2 → Meta) — NOT the agent's stale cloud
|
|
271120
|
+
// `llm_provider` (which stays e.g. OpenAI). Falls back to the cloud provider
|
|
271121
|
+
// when local mode is off. Reactive so it updates the instant a model is picked.
|
|
271122
|
+
const selectedLocal = useSelectedLocalModel();
|
|
271123
|
+
const activeProviderKey = selectedLocal.isLocal && selectedLocal.model
|
|
271124
|
+
? getProviderName(selectedLocal.model.provider)
|
|
271125
|
+
: (mentorSettings === null || mentorSettings === void 0 ? void 0 : mentorSettings.llm_provider)
|
|
271126
|
+
? getProviderName(mentorSettings.llm_provider)
|
|
271127
|
+
: '';
|
|
270509
271128
|
async function updateMentorLLM(llmProvider, llmName) {
|
|
270510
271129
|
try {
|
|
270511
271130
|
await editMentor({
|
|
@@ -270522,57 +271141,70 @@ function AgentLLMTab({ showConfigurationHeader = true, getLLMProviderDetails, la
|
|
|
270522
271141
|
}
|
|
270523
271142
|
catch (error) {
|
|
270524
271143
|
console.error(JSON.stringify(error));
|
|
270525
|
-
const errorMessage = extractErrorMessage(error, labels.toasts.updateError);
|
|
271144
|
+
const errorMessage = extractErrorMessage$1(error, labels.toasts.updateError);
|
|
270526
271145
|
toast.error(errorMessage);
|
|
270527
271146
|
console.error(JSON.stringify({ tenant: tenantKey, error }));
|
|
270528
271147
|
}
|
|
270529
271148
|
}
|
|
270530
|
-
return (jsxs(Fragment$1, { children: [showConfigurationHeader && (jsx("div", { className: "flex hidden h-[73px] flex-shrink-0 items-center border-b border-gray-200 bg-white p-4 lg:block", children: jsxs("div", { children: [jsx("h3", { className: "mb-1 text-base font-medium text-gray-900", children: labels.header.title }), jsx("p", { className: "text-xs text-gray-700", children: labels.header.description })] }) })),
|
|
271149
|
+
return (jsxs(Fragment$1, { children: [showConfigurationHeader && (jsx("div", { className: "flex hidden h-[73px] flex-shrink-0 items-center border-b border-gray-200 bg-white p-4 lg:block", children: jsxs("div", { children: [jsx("h3", { className: "mb-1 text-base font-medium text-gray-900", children: labels.header.title }), jsx("p", { className: "text-xs text-gray-700", children: labels.header.description })] }) })), jsxs("div", { className: cn('flex-1 space-y-4 pt-3 pb-3 lg:pt-4 lg:pb-4', showConfigurationHeader ? 'px-3 lg:px-4' : 'px-0 lg:px-0'), style: {
|
|
270531
271150
|
overflowY: 'auto',
|
|
270532
271151
|
overflowX: 'hidden',
|
|
270533
|
-
}, children: jsxs("div", { className: "space-y-6", 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, { type: "search", placeholder: labels.search.placeholder, className: "w-full py-6 pl-9 md:w-1/2", value: searchQuery, onChange: (e) => setSearchQuery(e.target.value), disabled: isDisabled })] }), isLoading ? (jsx("div", { className: "flex items-center justify-center py-10", children: jsx(Spinner, {}) })) : (jsx(WithFormPermissions, { name: "llm_provider",
|
|
270534
|
-
|
|
270535
|
-
|
|
270536
|
-
|
|
270537
|
-
|
|
270538
|
-
|
|
270539
|
-
|
|
270540
|
-
|
|
270541
|
-
|
|
270542
|
-
|
|
270543
|
-
|
|
270544
|
-
|
|
270545
|
-
|
|
270546
|
-
|
|
270547
|
-
|
|
270548
|
-
|
|
270549
|
-
|
|
270550
|
-
|
|
270551
|
-
|
|
270552
|
-
|
|
270553
|
-
|
|
270554
|
-
|
|
270555
|
-
|
|
270556
|
-
|
|
270557
|
-
|
|
270558
|
-
|
|
270559
|
-
|
|
270560
|
-
|
|
270561
|
-
|
|
270562
|
-
|
|
270563
|
-
|
|
270564
|
-
|
|
270565
|
-
|
|
270566
|
-
|
|
270567
|
-
|
|
270568
|
-
|
|
270569
|
-
|
|
270570
|
-
|
|
270571
|
-
|
|
270572
|
-
|
|
270573
|
-
|
|
270574
|
-
|
|
270575
|
-
|
|
271152
|
+
}, children: [jsx("div", { className: "rounded-md border border-gray-200 bg-gray-50 p-3 text-xs text-gray-600", "data-testid": "llm-info-box", children: labels.infoBox }), jsxs("div", { className: "space-y-6", 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, { type: "search", placeholder: labels.search.placeholder, className: "w-full py-6 pl-9 md:w-1/2", value: searchQuery, onChange: (e) => setSearchQuery(e.target.value), disabled: isDisabled })] }), isLoading ? (jsx("div", { className: "flex items-center justify-center py-10", children: jsx(Spinner, {}) })) : (jsx(WithFormPermissions, { name: "llm_provider",
|
|
271153
|
+
// @ts-ignore
|
|
271154
|
+
permissions: (_a = mentorSettings === null || mentorSettings === void 0 ? void 0 : mentorSettings.permissions) === null || _a === void 0 ? void 0 : _a.field, enableRBAC: enableRBAC, children: ({ disabled }) => (jsxs("div", { className: "grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4", children: [sortedLLMProviders
|
|
271155
|
+
.filter((model) => model.name.toLowerCase().includes(searchQuery.toLowerCase()))
|
|
271156
|
+
.map((model) => {
|
|
271157
|
+
const providerDetails = getLLMProviderDetails$1(model.name);
|
|
271158
|
+
const isActive = !!activeProviderKey && getProviderName(model.name) === activeProviderKey;
|
|
271159
|
+
// The generated LLM response type doesn't declare the
|
|
271160
|
+
// credential flags the backend actually sends, so read them
|
|
271161
|
+
// through the access-shaped view the helper expects.
|
|
271162
|
+
const isGrayed = !canAccessProvider(model) && !isActive;
|
|
271163
|
+
return (jsxs("div", { "data-testid": "llm-provider-card", "data-provider": model.name, "data-disabled": isGrayed ? 'true' : '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', {
|
|
271164
|
+
'border-blue-500': isActive,
|
|
271165
|
+
// Tint the whole card too: desaturating a logo that
|
|
271166
|
+
// is already black changes nothing, so the card
|
|
271167
|
+
// itself has to carry some of the inactive signal.
|
|
271168
|
+
'border-gray-100 bg-gray-50': isGrayed,
|
|
271169
|
+
}), onClick: () => {
|
|
271170
|
+
if (isDisabled || disabled)
|
|
271171
|
+
return;
|
|
271172
|
+
setSelectedLLMProvider({
|
|
271173
|
+
id: model.id,
|
|
271174
|
+
name: model.name,
|
|
271175
|
+
logo: model.logo,
|
|
271176
|
+
description: model.description,
|
|
271177
|
+
chat_models: model.chat_models,
|
|
271178
|
+
// @ts-ignore - has_credentials property does not exist on LLMResponse type
|
|
271179
|
+
has_credentials: model === null || model === void 0 ? void 0 : model.has_credentials,
|
|
271180
|
+
// @ts-ignore - main_has_credentials property does not exist on LLMResponse type
|
|
271181
|
+
main_has_credentials: model === null || model === void 0 ? void 0 : model.main_has_credentials,
|
|
271182
|
+
// @ts-ignore - can_use_main_keys property does not exist on LLMResponse type
|
|
271183
|
+
can_use_main_keys: model === null || model === void 0 ? void 0 : model.can_use_main_keys,
|
|
271184
|
+
});
|
|
271185
|
+
}, 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', {
|
|
271186
|
+
// `grayscale` only desaturates, so already-black
|
|
271187
|
+
// marks (OpenAI, xAI) looked untouched. Fading is
|
|
271188
|
+
// what actually reads as inactive at any hue.
|
|
271189
|
+
'opacity-40 grayscale': isGrayed,
|
|
271190
|
+
}), width: 32, height: 32, loading: "lazy" }) }), jsx("span", { className: "text-sm font-medium text-gray-900", children: providerDetails.name })] }, model.id));
|
|
271191
|
+
}), localOnlyProviders.map((lp) => {
|
|
271192
|
+
const details = getLLMProviderDetails$1(lp.provider);
|
|
271193
|
+
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: () => {
|
|
271194
|
+
if (isDisabled || disabled)
|
|
271195
|
+
return;
|
|
271196
|
+
setSelectedLLMProvider({
|
|
271197
|
+
id: -1,
|
|
271198
|
+
name: lp.provider,
|
|
271199
|
+
logo: details.logo,
|
|
271200
|
+
description: null,
|
|
271201
|
+
chat_models: [],
|
|
271202
|
+
});
|
|
271203
|
+
}, 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}`));
|
|
271204
|
+
})] })) })), selectedLLMProvider && mentorSettings && (jsx(LLMProviderModal, { isOpen: true, onClose: () => setSelectedLLMProvider(null), onSelect: updateMentorLLM, llmProvider: selectedLLMProvider, isSelecting: isEditingMentor, mentorSettings: {
|
|
271205
|
+
llm_name: mentorSettings === null || mentorSettings === void 0 ? void 0 : mentorSettings.llm_name,
|
|
271206
|
+
llm_provider: mentorSettings === null || mentorSettings === void 0 ? void 0 : mentorSettings.llm_provider,
|
|
271207
|
+
}, llms: (llmProviders !== null && llmProviders !== void 0 ? llmProviders : []), getLLMProviderDetails: getLLMProviderDetails$1, labels: labels.providerModal }))] })] })] }));
|
|
270576
271208
|
}
|
|
270577
271209
|
|
|
270578
271210
|
function useTools({ labels }) {
|
|
@@ -337443,5 +338075,5 @@ var index_es = /*#__PURE__*/Object.freeze({
|
|
|
337443
338075
|
vectorsRatio: vectorsRatio
|
|
337444
338076
|
});
|
|
337445
338077
|
|
|
337446
|
-
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, 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, initializeLocalStorageWithObject, isValidLocale, resolveAgentPickerLabels, resolveAgentSearchLabels, resolveSettingsTabLabels, useAgentSearch, useAgentSearchWithPagination, useAgentSettings, useAgentStar, useGetChatDetails, usePlatformSidebarFooterActions, usePlatformSidebarNavCallback, useSidebar, useT, useTimeTracker, useWebContainersI18n, useWebContainersLocale };
|
|
338078
|
+
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 };
|
|
337447
338079
|
//# sourceMappingURL=index.esm.js.map
|