@iblai/iblai-js 2.16.0 → 2.17.1
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 +1439 -293
- package/dist/web-containers/source/next/index.esm.js +1387 -358
- package/package.json +4 -4
|
@@ -2,8 +2,8 @@ import { jsx, jsxs, Fragment as Fragment$1 } from 'react/jsx-runtime';
|
|
|
2
2
|
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
|
-
import { useGetUserMetadataQuery, useGetUserMetadataEdxQuery, useGetUserProfileSummaryQuery, 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, useGetLlmUsageMetricsQuery, 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, useGetMentorMemoriesListQuery, useGetMemoryCategoriesAdminQuery, useCreateMentorMemoryMutation, 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, 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, useCreateMemoryCategoryMutation, useUpdateMemoryCategoryMutation, useDeleteMemoryCategoryMutation, useDeletePromptMutation, useDeleteTrainingDocumentMutation, useGetTrainingDocumentRetrainScheduleQuery, useCreateTrainingDocumentRetrainScheduleMutation, useUpdateArtifactMutation, useLazyGetArtifactVersionQuery, useLazyListArtifactVersionsQuery, useSetCurrentVersionMutation, useListArtifactVersionsQuery, useLazyGetArtifactQuery, useLazyListArtifactsQuery, useEditSessionMutation } from '@iblai/data-layer';
|
|
6
|
-
import { removeAuthItem, setAuthItem, isTauriEmbeddedLLMHost, selectRbacPermissions, useCurrentTenant, canViewOtherUsersChatHistory, getInitials, useTenantMetadata, isPureWatcher, WithPermissions, canWriteField, useStripeUpgrade, openExternalUrl, CHAT_AREA_SIZE, isAlphaNumeric32, checkRbacPermission, selectNumberOfActiveChatMessages,
|
|
5
|
+
import { useGetUserMetadataQuery, useGetUserMetadataEdxQuery, useGetUserProfileSummaryQuery, 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, useGetVectorDocumentsQuery, useGetLlmsQuery, 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, useDeleteIntegrationCredentialMutation, useDeleteCredentialMutation, useCreateIntegrationCredentialMutation, useGetIntegrationCredentialsSchemaQuery, useGetMaskedIntegrationCredentialsQuery, useGetAccountBillingInfoQuery, useUpdateAutoRechargeInfoMutation, useTriggerAutoRechargeMutation, useGetAgentSpendCapQuery, useGetTenantSpendCapQuery, useListAgentUserSpendCapsQuery, useUpsertAgentSpendCapMutation, useDeleteAgentSpendCapMutation, useUpsertTenantSpendCapMutation, useDeleteTenantSpendCapMutation, useUpsertUserSpendCapMutation, useDeleteUserSpendCapMutation, useGetLlmUsageMetricsQuery, 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, useGetMentorMemoriesListQuery, useGetMemoryCategoriesAdminQuery, useCreateMentorMemoryMutation, 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, 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, useCreateMemoryCategoryMutation, useUpdateMemoryCategoryMutation, useDeleteMemoryCategoryMutation, useDeletePromptMutation, useDeleteTrainingDocumentMutation, useGetTrainingDocumentRetrainScheduleQuery, useCreateTrainingDocumentRetrainScheduleMutation, useUpdateArtifactMutation, useLazyGetArtifactVersionQuery, useLazyListArtifactVersionsQuery, useSetCurrentVersionMutation, useListArtifactVersionsQuery, useLazyGetArtifactQuery, useLazyListArtifactsQuery, useEditSessionMutation } from '@iblai/data-layer';
|
|
6
|
+
import { removeAuthItem, setAuthItem, isTauriEmbeddedLLMHost, selectRbacPermissions, useCurrentTenant, canViewOtherUsersChatHistory, getInitials, useTenantMetadata, useUsername, selectStreaming, isLoggedIn, TOOL_NAME_MAP, WRITE_TODOS_TOOL, isPureWatcher, WithPermissions, canWriteField, useStripeUpgrade, openExternalUrl, CHAT_AREA_SIZE, isAlphaNumeric32, checkRbacPermission, selectNumberOfActiveChatMessages, 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, use401TokenRefresh, 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';
|
|
@@ -44326,7 +44326,7 @@ var isArray$h = function isArray(arr) {
|
|
|
44326
44326
|
return toStr.call(arr) === '[object Array]';
|
|
44327
44327
|
};
|
|
44328
44328
|
|
|
44329
|
-
var isPlainObject$
|
|
44329
|
+
var isPlainObject$5 = function isPlainObject(obj) {
|
|
44330
44330
|
if (!obj || toStr.call(obj) !== '[object Object]') {
|
|
44331
44331
|
return false;
|
|
44332
44332
|
}
|
|
@@ -44405,12 +44405,12 @@ var extend$3 = function extend() {
|
|
|
44405
44405
|
// Prevent never-ending loop
|
|
44406
44406
|
if (target !== copy) {
|
|
44407
44407
|
// Recurse if we're merging plain objects or arrays
|
|
44408
|
-
if (deep && copy && (isPlainObject$
|
|
44408
|
+
if (deep && copy && (isPlainObject$5(copy) || (copyIsArray = isArray$h(copy)))) {
|
|
44409
44409
|
if (copyIsArray) {
|
|
44410
44410
|
copyIsArray = false;
|
|
44411
44411
|
clone = src && isArray$h(src) ? src : [];
|
|
44412
44412
|
} else {
|
|
44413
|
-
clone = src && isPlainObject$
|
|
44413
|
+
clone = src && isPlainObject$5(src) ? src : {};
|
|
44414
44414
|
}
|
|
44415
44415
|
|
|
44416
44416
|
// Never move original objects, clone them
|
|
@@ -44431,7 +44431,7 @@ var extend$3 = function extend() {
|
|
|
44431
44431
|
|
|
44432
44432
|
var extend$2 = /*@__PURE__*/getDefaultExportFromCjs(extend$3);
|
|
44433
44433
|
|
|
44434
|
-
function isPlainObject$
|
|
44434
|
+
function isPlainObject$4(value) {
|
|
44435
44435
|
if (typeof value !== 'object' || value === null) {
|
|
44436
44436
|
return false;
|
|
44437
44437
|
}
|
|
@@ -45502,7 +45502,7 @@ class Processor extends CallableInstance {
|
|
|
45502
45502
|
else if (parameters.length > 0) {
|
|
45503
45503
|
let [primary, ...rest] = parameters;
|
|
45504
45504
|
const currentPrimary = attachers[entryIndex][1];
|
|
45505
|
-
if (isPlainObject$
|
|
45505
|
+
if (isPlainObject$4(currentPrimary) && isPlainObject$4(primary)) {
|
|
45506
45506
|
primary = extend$2(true, currentPrimary, primary);
|
|
45507
45507
|
}
|
|
45508
45508
|
|
|
@@ -45594,7 +45594,7 @@ function assertUnfrozen(name, frozen) {
|
|
|
45594
45594
|
function assertNode(node) {
|
|
45595
45595
|
// `isPlainObj` unfortunately uses `any` instead of `unknown`.
|
|
45596
45596
|
// type-coverage:ignore-next-line
|
|
45597
|
-
if (!isPlainObject$
|
|
45597
|
+
if (!isPlainObject$4(node) || typeof node.type !== 'string') {
|
|
45598
45598
|
throw new TypeError('Expected node, got `' + node + '`')
|
|
45599
45599
|
// Fine.
|
|
45600
45600
|
}
|
|
@@ -97047,7 +97047,7 @@ var analyticsAnalyticsTranscriptsStats$3 = {
|
|
|
97047
97047
|
labelUsername: "Username:",
|
|
97048
97048
|
labelAgent: "Agent:",
|
|
97049
97049
|
labelModel: "Model:",
|
|
97050
|
-
labelModelCard: "
|
|
97050
|
+
labelModelCard: "Model:",
|
|
97051
97051
|
labelUser: "User:",
|
|
97052
97052
|
roleUser: "User",
|
|
97053
97053
|
roleAgent: "Agent",
|
|
@@ -97089,6 +97089,37 @@ var analyticsAnalyticsTranscriptsStats$3 = {
|
|
|
97089
97089
|
avgCostPerConversationDescription: "What a typical conversation costs in model usage.",
|
|
97090
97090
|
avgRatingDescription: "Average rating users gave their conversations, on a scale of 1 to 5."
|
|
97091
97091
|
};
|
|
97092
|
+
var analyticsTranscriptTurnDetails$3 = {
|
|
97093
|
+
showDetails: "Show Details",
|
|
97094
|
+
hideDetails: "Hide Details",
|
|
97095
|
+
metadata: "Metadata",
|
|
97096
|
+
requestContext: "Request Context",
|
|
97097
|
+
model: "Model",
|
|
97098
|
+
provider: "Provider",
|
|
97099
|
+
temperature: "Temperature",
|
|
97100
|
+
client: "Client",
|
|
97101
|
+
sourcesBadge: "Documents · {count}",
|
|
97102
|
+
toolsBadge: "Tools · {count}",
|
|
97103
|
+
yes: "Yes",
|
|
97104
|
+
no: "No",
|
|
97105
|
+
redacted: "Redacted",
|
|
97106
|
+
fieldSession: "Session",
|
|
97107
|
+
fieldCallerVerified: "Caller Verified",
|
|
97108
|
+
fieldFiles: "Files",
|
|
97109
|
+
fieldFile: "File",
|
|
97110
|
+
fieldSystemPrompt: "System Prompt",
|
|
97111
|
+
fieldFlow: "Flow",
|
|
97112
|
+
fieldToken: "Token",
|
|
97113
|
+
fieldTenant: "Tenant",
|
|
97114
|
+
fieldPathway: "Pathway",
|
|
97115
|
+
fieldUsername: "Username",
|
|
97116
|
+
fieldName: "Name",
|
|
97117
|
+
fieldCanvas: "Canvas",
|
|
97118
|
+
fieldProactivePrompt: "Proactive Prompt",
|
|
97119
|
+
fieldGrader: "Grader",
|
|
97120
|
+
fieldDatasets: "Datasets",
|
|
97121
|
+
fieldDateTime: "Date & Time"
|
|
97122
|
+
};
|
|
97092
97123
|
var analyticsAnalyticsUsersStats$3 = {
|
|
97093
97124
|
usersLoggedInNow: "Users logged in right now",
|
|
97094
97125
|
usersLoggedInPast30Days: "Users logged in past 30 days",
|
|
@@ -98579,7 +98610,8 @@ var historyTabLabels$3 = {
|
|
|
98579
98610
|
noResponse: "No response available",
|
|
98580
98611
|
selectPrompt: "Select a conversation to view details.",
|
|
98581
98612
|
aiAgent: "AI Agent",
|
|
98582
|
-
noConversations: "No conversations found"
|
|
98613
|
+
noConversations: "No conversations found",
|
|
98614
|
+
documentsChip: "Documents · {count}"
|
|
98583
98615
|
},
|
|
98584
98616
|
summary: {
|
|
98585
98617
|
notAvailable: "Summary not available"
|
|
@@ -102982,6 +103014,7 @@ var en = {
|
|
|
102982
103014
|
analyticsAnalyticsReports: analyticsAnalyticsReports$3,
|
|
102983
103015
|
analyticsAnalyticsTopicsStats: analyticsAnalyticsTopicsStats$3,
|
|
102984
103016
|
analyticsAnalyticsTranscriptsStats: analyticsAnalyticsTranscriptsStats$3,
|
|
103017
|
+
analyticsTranscriptTurnDetails: analyticsTranscriptTurnDetails$3,
|
|
102985
103018
|
analyticsAnalyticsUsersStats: analyticsAnalyticsUsersStats$3,
|
|
102986
103019
|
analyticsGroupsFilterDropdown: analyticsGroupsFilterDropdown$3,
|
|
102987
103020
|
analyticsStatCard: analyticsStatCard$3,
|
|
@@ -104205,7 +104238,7 @@ var analyticsAnalyticsTranscriptsStats$2 = {
|
|
|
104205
104238
|
labelUsername: "Nom d'utilisateur :",
|
|
104206
104239
|
labelAgent: "Agent :",
|
|
104207
104240
|
labelModel: "Modèle :",
|
|
104208
|
-
labelModelCard: "
|
|
104241
|
+
labelModelCard: "Modèle :",
|
|
104209
104242
|
labelUser: "Utilisateur :",
|
|
104210
104243
|
roleUser: "Utilisateur",
|
|
104211
104244
|
roleAgent: "Agent",
|
|
@@ -104247,6 +104280,37 @@ var analyticsAnalyticsTranscriptsStats$2 = {
|
|
|
104247
104280
|
avgCostPerConversationDescription: "Coût d’utilisation des modèles pour une conversation type.",
|
|
104248
104281
|
avgRatingDescription: "Note moyenne attribuée par les utilisateurs à leurs conversations, sur une échelle de 1 à 5."
|
|
104249
104282
|
};
|
|
104283
|
+
var analyticsTranscriptTurnDetails$2 = {
|
|
104284
|
+
showDetails: "Afficher les détails",
|
|
104285
|
+
hideDetails: "Masquer les détails",
|
|
104286
|
+
metadata: "Métadonnées",
|
|
104287
|
+
requestContext: "Contexte de la requête",
|
|
104288
|
+
model: "Modèle",
|
|
104289
|
+
provider: "Fournisseur",
|
|
104290
|
+
temperature: "Température",
|
|
104291
|
+
client: "Client",
|
|
104292
|
+
sourcesBadge: "Documents · {count}",
|
|
104293
|
+
toolsBadge: "Outils · {count}",
|
|
104294
|
+
yes: "Oui",
|
|
104295
|
+
no: "Non",
|
|
104296
|
+
redacted: "Masqué",
|
|
104297
|
+
fieldSession: "Session",
|
|
104298
|
+
fieldCallerVerified: "Appelant vérifié",
|
|
104299
|
+
fieldFiles: "Fichiers",
|
|
104300
|
+
fieldFile: "Fichier",
|
|
104301
|
+
fieldSystemPrompt: "Prompt système",
|
|
104302
|
+
fieldFlow: "Flux",
|
|
104303
|
+
fieldToken: "Jeton",
|
|
104304
|
+
fieldTenant: "Locataire",
|
|
104305
|
+
fieldPathway: "Parcours",
|
|
104306
|
+
fieldUsername: "Nom d’utilisateur",
|
|
104307
|
+
fieldName: "Nom",
|
|
104308
|
+
fieldCanvas: "Canvas",
|
|
104309
|
+
fieldProactivePrompt: "Prompt proactif",
|
|
104310
|
+
fieldGrader: "Correcteur",
|
|
104311
|
+
fieldDatasets: "Jeux de données",
|
|
104312
|
+
fieldDateTime: "Date et heure"
|
|
104313
|
+
};
|
|
104250
104314
|
var analyticsAnalyticsUsersStats$2 = {
|
|
104251
104315
|
usersLoggedInNow: "Utilisateurs connectés en ce moment",
|
|
104252
104316
|
usersLoggedInPast30Days: "Utilisateurs connectés ces 30 derniers jours",
|
|
@@ -105903,7 +105967,8 @@ var historyTabLabels$2 = {
|
|
|
105903
105967
|
noResponse: "Aucune réponse disponible",
|
|
105904
105968
|
selectPrompt: "Sélectionnez une conversation pour afficher les détails.",
|
|
105905
105969
|
aiAgent: "AI Agent",
|
|
105906
|
-
noConversations: "Aucune conversation trouvée"
|
|
105970
|
+
noConversations: "Aucune conversation trouvée",
|
|
105971
|
+
documentsChip: "Documents · {count}"
|
|
105907
105972
|
},
|
|
105908
105973
|
summary: {
|
|
105909
105974
|
notAvailable: "Résumé non disponible"
|
|
@@ -110140,6 +110205,7 @@ var fr = {
|
|
|
110140
110205
|
analyticsAnalyticsReports: analyticsAnalyticsReports$2,
|
|
110141
110206
|
analyticsAnalyticsTopicsStats: analyticsAnalyticsTopicsStats$2,
|
|
110142
110207
|
analyticsAnalyticsTranscriptsStats: analyticsAnalyticsTranscriptsStats$2,
|
|
110208
|
+
analyticsTranscriptTurnDetails: analyticsTranscriptTurnDetails$2,
|
|
110143
110209
|
analyticsAnalyticsUsersStats: analyticsAnalyticsUsersStats$2,
|
|
110144
110210
|
analyticsGroupsFilterDropdown: analyticsGroupsFilterDropdown$2,
|
|
110145
110211
|
analyticsStatCard: analyticsStatCard$2,
|
|
@@ -111363,7 +111429,7 @@ var analyticsAnalyticsTranscriptsStats$1 = {
|
|
|
111363
111429
|
labelUsername: "Nombre de usuario:",
|
|
111364
111430
|
labelAgent: "Agente:",
|
|
111365
111431
|
labelModel: "Modelo:",
|
|
111366
|
-
labelModelCard: "
|
|
111432
|
+
labelModelCard: "Modelo:",
|
|
111367
111433
|
labelUser: "Usuario:",
|
|
111368
111434
|
roleUser: "Usuario",
|
|
111369
111435
|
roleAgent: "Agente",
|
|
@@ -111405,6 +111471,37 @@ var analyticsAnalyticsTranscriptsStats$1 = {
|
|
|
111405
111471
|
avgCostPerConversationDescription: "Lo que cuesta una conversación típica en uso de modelos.",
|
|
111406
111472
|
avgRatingDescription: "Valoración media que los usuarios dieron a sus conversaciones, en una escala de 1 a 5."
|
|
111407
111473
|
};
|
|
111474
|
+
var analyticsTranscriptTurnDetails$1 = {
|
|
111475
|
+
showDetails: "Mostrar detalles",
|
|
111476
|
+
hideDetails: "Ocultar detalles",
|
|
111477
|
+
metadata: "Metadatos",
|
|
111478
|
+
requestContext: "Contexto de la solicitud",
|
|
111479
|
+
model: "Modelo",
|
|
111480
|
+
provider: "Proveedor",
|
|
111481
|
+
temperature: "Temperatura",
|
|
111482
|
+
client: "Cliente",
|
|
111483
|
+
sourcesBadge: "Documentos · {count}",
|
|
111484
|
+
toolsBadge: "Herramientas · {count}",
|
|
111485
|
+
yes: "Sí",
|
|
111486
|
+
no: "No",
|
|
111487
|
+
redacted: "Oculto",
|
|
111488
|
+
fieldSession: "Sesión",
|
|
111489
|
+
fieldCallerVerified: "Llamante verificado",
|
|
111490
|
+
fieldFiles: "Archivos",
|
|
111491
|
+
fieldFile: "Archivo",
|
|
111492
|
+
fieldSystemPrompt: "Prompt del sistema",
|
|
111493
|
+
fieldFlow: "Flujo",
|
|
111494
|
+
fieldToken: "Token",
|
|
111495
|
+
fieldTenant: "Inquilino",
|
|
111496
|
+
fieldPathway: "Ruta",
|
|
111497
|
+
fieldUsername: "Nombre de usuario",
|
|
111498
|
+
fieldName: "Nombre",
|
|
111499
|
+
fieldCanvas: "Canvas",
|
|
111500
|
+
fieldProactivePrompt: "Prompt proactivo",
|
|
111501
|
+
fieldGrader: "Calificador",
|
|
111502
|
+
fieldDatasets: "Conjuntos de datos",
|
|
111503
|
+
fieldDateTime: "Fecha y hora"
|
|
111504
|
+
};
|
|
111408
111505
|
var analyticsAnalyticsUsersStats$1 = {
|
|
111409
111506
|
usersLoggedInNow: "Usuarios conectados ahora mismo",
|
|
111410
111507
|
usersLoggedInPast30Days: "Usuarios conectados en los últimos 30 días",
|
|
@@ -113061,7 +113158,8 @@ var historyTabLabels$1 = {
|
|
|
113061
113158
|
noResponse: "No hay respuesta disponible",
|
|
113062
113159
|
selectPrompt: "Selecciona una conversación para ver los detalles.",
|
|
113063
113160
|
aiAgent: "AI Agent",
|
|
113064
|
-
noConversations: "No se encontraron conversaciones"
|
|
113161
|
+
noConversations: "No se encontraron conversaciones",
|
|
113162
|
+
documentsChip: "Documentos · {count}"
|
|
113065
113163
|
},
|
|
113066
113164
|
summary: {
|
|
113067
113165
|
notAvailable: "Resumen no disponible"
|
|
@@ -117298,6 +117396,7 @@ var es = {
|
|
|
117298
117396
|
analyticsAnalyticsReports: analyticsAnalyticsReports$1,
|
|
117299
117397
|
analyticsAnalyticsTopicsStats: analyticsAnalyticsTopicsStats$1,
|
|
117300
117398
|
analyticsAnalyticsTranscriptsStats: analyticsAnalyticsTranscriptsStats$1,
|
|
117399
|
+
analyticsTranscriptTurnDetails: analyticsTranscriptTurnDetails$1,
|
|
117301
117400
|
analyticsAnalyticsUsersStats: analyticsAnalyticsUsersStats$1,
|
|
117302
117401
|
analyticsGroupsFilterDropdown: analyticsGroupsFilterDropdown$1,
|
|
117303
117402
|
analyticsStatCard: analyticsStatCard$1,
|
|
@@ -118563,6 +118662,37 @@ var analyticsAnalyticsTranscriptsStats = {
|
|
|
118563
118662
|
avgCostPerConversationDescription: "一次典型对话所产生的模型使用成本。",
|
|
118564
118663
|
avgRatingDescription: "用户对其对话给出的平均评分,满分 5 分。"
|
|
118565
118664
|
};
|
|
118665
|
+
var analyticsTranscriptTurnDetails = {
|
|
118666
|
+
showDetails: "显示详情",
|
|
118667
|
+
hideDetails: "隐藏详情",
|
|
118668
|
+
metadata: "元数据",
|
|
118669
|
+
requestContext: "请求上下文",
|
|
118670
|
+
model: "模型",
|
|
118671
|
+
provider: "提供商",
|
|
118672
|
+
temperature: "温度",
|
|
118673
|
+
client: "客户端",
|
|
118674
|
+
sourcesBadge: "文档 · {count}",
|
|
118675
|
+
toolsBadge: "工具 · {count}",
|
|
118676
|
+
yes: "是",
|
|
118677
|
+
no: "否",
|
|
118678
|
+
redacted: "已隐藏",
|
|
118679
|
+
fieldSession: "会话",
|
|
118680
|
+
fieldCallerVerified: "调用者已验证",
|
|
118681
|
+
fieldFiles: "文件",
|
|
118682
|
+
fieldFile: "文件",
|
|
118683
|
+
fieldSystemPrompt: "系统提示",
|
|
118684
|
+
fieldFlow: "流程",
|
|
118685
|
+
fieldToken: "令牌",
|
|
118686
|
+
fieldTenant: "租户",
|
|
118687
|
+
fieldPathway: "路径",
|
|
118688
|
+
fieldUsername: "用户名",
|
|
118689
|
+
fieldName: "名称",
|
|
118690
|
+
fieldCanvas: "画布",
|
|
118691
|
+
fieldProactivePrompt: "主动提示",
|
|
118692
|
+
fieldGrader: "评分器",
|
|
118693
|
+
fieldDatasets: "数据集",
|
|
118694
|
+
fieldDateTime: "日期和时间"
|
|
118695
|
+
};
|
|
118566
118696
|
var analyticsAnalyticsUsersStats = {
|
|
118567
118697
|
usersLoggedInNow: "当前在线用户",
|
|
118568
118698
|
usersLoggedInPast30Days: "过去 30 天登录用户",
|
|
@@ -120219,7 +120349,8 @@ var historyTabLabels = {
|
|
|
120219
120349
|
noResponse: "暂无回复",
|
|
120220
120350
|
selectPrompt: "选择一条对话以查看详情。",
|
|
120221
120351
|
aiAgent: "AI Agent",
|
|
120222
|
-
noConversations: "未找到对话"
|
|
120352
|
+
noConversations: "未找到对话",
|
|
120353
|
+
documentsChip: "文档 · {count}"
|
|
120223
120354
|
},
|
|
120224
120355
|
summary: {
|
|
120225
120356
|
notAvailable: "摘要不可用"
|
|
@@ -124456,6 +124587,7 @@ var zh = {
|
|
|
124456
124587
|
analyticsAnalyticsReports: analyticsAnalyticsReports,
|
|
124457
124588
|
analyticsAnalyticsTopicsStats: analyticsAnalyticsTopicsStats,
|
|
124458
124589
|
analyticsAnalyticsTranscriptsStats: analyticsAnalyticsTranscriptsStats,
|
|
124590
|
+
analyticsTranscriptTurnDetails: analyticsTranscriptTurnDetails,
|
|
124459
124591
|
analyticsAnalyticsUsersStats: analyticsAnalyticsUsersStats,
|
|
124460
124592
|
analyticsGroupsFilterDropdown: analyticsGroupsFilterDropdown,
|
|
124461
124593
|
analyticsStatCard: analyticsStatCard,
|
|
@@ -173316,7 +173448,7 @@ var objectCtorString = funcToString.call(Object);
|
|
|
173316
173448
|
* _.isPlainObject(Object.create(null));
|
|
173317
173449
|
* // => true
|
|
173318
173450
|
*/
|
|
173319
|
-
function isPlainObject$
|
|
173451
|
+
function isPlainObject$3(value) {
|
|
173320
173452
|
if (!isObjectLike$1(value) || baseGetTag$1(value) != objectTag) {
|
|
173321
173453
|
return false;
|
|
173322
173454
|
}
|
|
@@ -173329,9 +173461,9 @@ function isPlainObject$2(value) {
|
|
|
173329
173461
|
funcToString.call(Ctor) == objectCtorString;
|
|
173330
173462
|
}
|
|
173331
173463
|
|
|
173332
|
-
var isPlainObject_1 = isPlainObject$
|
|
173464
|
+
var isPlainObject_1 = isPlainObject$3;
|
|
173333
173465
|
|
|
173334
|
-
var isPlainObject$
|
|
173466
|
+
var isPlainObject$2 = /*@__PURE__*/getDefaultExportFromCjs(isPlainObject_1);
|
|
173335
173467
|
|
|
173336
173468
|
var baseGetTag = _baseGetTag,
|
|
173337
173469
|
isObjectLike = isObjectLike_1;
|
|
@@ -173548,7 +173680,7 @@ function Shape(_ref2) {
|
|
|
173548
173680
|
shape = /*#__PURE__*/cloneElement(option, _objectSpread$e(_objectSpread$e({}, props), getPropsFromShapeOption(option)));
|
|
173549
173681
|
} else if (isFunction$3(option)) {
|
|
173550
173682
|
shape = option(props);
|
|
173551
|
-
} else if (isPlainObject$
|
|
173683
|
+
} else if (isPlainObject$2(option) && !isBoolean(option)) {
|
|
173552
173684
|
var nextProps = propTransformer(option, props);
|
|
173553
173685
|
shape = /*#__PURE__*/React__default.createElement(ShapeSelector, {
|
|
173554
173686
|
shapeType: shapeType,
|
|
@@ -218053,6 +218185,100 @@ function Markdown({ children, className }) {
|
|
|
218053
218185
|
return (jsx("div", { className: className, children: jsx(Markdown$1, { components: components, children: children }) }));
|
|
218054
218186
|
}
|
|
218055
218187
|
|
|
218188
|
+
function FileCard({ fileName, fileType }) {
|
|
218189
|
+
const getFileIcon = (fileName, fileType) => {
|
|
218190
|
+
const type = fileType.toLowerCase();
|
|
218191
|
+
const name = fileName.toLowerCase();
|
|
218192
|
+
const extension = name.split('.').pop() || '';
|
|
218193
|
+
// Check by MIME type first
|
|
218194
|
+
if (type.includes('pdf')) {
|
|
218195
|
+
return jsx(File$1, { className: "h-5 w-5 text-blue-500" });
|
|
218196
|
+
}
|
|
218197
|
+
else if (type.includes('image')) {
|
|
218198
|
+
return jsx(FileImage, { className: "h-5 w-5 text-blue-500" });
|
|
218199
|
+
}
|
|
218200
|
+
else if (type.includes('audio')) {
|
|
218201
|
+
return jsx(FileAudio, { className: "h-5 w-5 text-blue-500" });
|
|
218202
|
+
}
|
|
218203
|
+
else if (type.includes('video')) {
|
|
218204
|
+
return jsx(FilePlay, { className: "h-5 w-5 text-blue-500" });
|
|
218205
|
+
}
|
|
218206
|
+
else if (type.includes('spreadsheet') || type.includes('excel') || type.includes('csv')) {
|
|
218207
|
+
return jsx(FileSpreadsheet, { className: "h-5 w-5 text-blue-500" });
|
|
218208
|
+
}
|
|
218209
|
+
else if (type.includes('zip') || type.includes('compressed') || type.includes('archive')) {
|
|
218210
|
+
return jsx(FileArchive, { className: "h-5 w-5 text-blue-500" });
|
|
218211
|
+
}
|
|
218212
|
+
else if (type.includes('javascript') ||
|
|
218213
|
+
type.includes('typescript') ||
|
|
218214
|
+
type.includes('html') ||
|
|
218215
|
+
type.includes('css') ||
|
|
218216
|
+
type.includes('json')) {
|
|
218217
|
+
return jsx(FileCode, { className: "h-5 w-5 text-blue-500" });
|
|
218218
|
+
}
|
|
218219
|
+
// If MIME type doesn't match, check by extension
|
|
218220
|
+
if (['doc', 'docx', 'txt', 'rtf'].includes(extension)) {
|
|
218221
|
+
return jsx(FileText, { className: "h-5 w-5 text-blue-500" });
|
|
218222
|
+
}
|
|
218223
|
+
else if (['jpg', 'jpeg', 'png', 'gif', 'svg', 'webp'].includes(extension)) {
|
|
218224
|
+
return jsx(FileImage, { className: "h-5 w-5 text-blue-500" });
|
|
218225
|
+
}
|
|
218226
|
+
else if (['pdf'].includes(extension)) {
|
|
218227
|
+
return jsx(File$1, { className: "h-5 w-5 text-blue-500" });
|
|
218228
|
+
}
|
|
218229
|
+
else if (['mp3', 'wav', 'ogg', 'flac'].includes(extension)) {
|
|
218230
|
+
return jsx(FileAudio, { className: "h-5 w-5 text-blue-500" });
|
|
218231
|
+
}
|
|
218232
|
+
else if (['mp4', 'webm', 'avi', 'mov'].includes(extension)) {
|
|
218233
|
+
return jsx(FilePlay, { className: "h-5 w-5 text-blue-500" });
|
|
218234
|
+
}
|
|
218235
|
+
else if (['xls', 'xlsx', 'csv'].includes(extension)) {
|
|
218236
|
+
return jsx(FileSpreadsheet, { className: "h-5 w-5 text-blue-500" });
|
|
218237
|
+
}
|
|
218238
|
+
else if (['zip', 'rar', '7z', 'tar', 'gz'].includes(extension)) {
|
|
218239
|
+
return jsx(FileArchive, { className: "h-5 w-5 text-blue-500" });
|
|
218240
|
+
}
|
|
218241
|
+
else if (['js', 'ts', 'jsx', 'tsx', 'html', 'css', 'json', 'py', 'java', 'c', 'cpp'].includes(extension)) {
|
|
218242
|
+
return jsx(FileCode, { className: "h-5 w-5 text-blue-500" });
|
|
218243
|
+
}
|
|
218244
|
+
// Default icon
|
|
218245
|
+
return jsx(File$1, { className: "h-5 w-5 text-blue-500" });
|
|
218246
|
+
};
|
|
218247
|
+
return (jsxs("div", { className: "mb-2 flex max-w-xs items-center rounded-lg border border-gray-200 bg-white p-3", children: [jsx("div", { className: "mr-3 shrink-0 rounded-lg bg-blue-100 p-2 text-blue-600", children: getFileIcon(fileName, fileType) }), jsxs("div", { className: "min-w-0 flex-1", children: [jsx("h3", { className: "truncate text-sm font-medium text-gray-900", children: fileName }), jsx("p", { className: "truncate text-xs text-gray-500", children: fileType })] })] }));
|
|
218248
|
+
}
|
|
218249
|
+
|
|
218250
|
+
function ImageMessage({ url, fileName, setPreviewImage }) {
|
|
218251
|
+
const [hasError, setHasError] = useState(false);
|
|
218252
|
+
if (hasError || !url) {
|
|
218253
|
+
return jsx(FileCard, { fileName: fileName, fileType: "image/*" });
|
|
218254
|
+
}
|
|
218255
|
+
return (jsx("div", { className: "h-full max-h-64 w-full max-w-96 overflow-hidden rounded-lg", children: jsx("div", { className: "relative", children: jsx("img", { src: url, alt: fileName, className: "h-auto w-full cursor-pointer object-contain p-2", onClick: () => setPreviewImage(url), onError: () => setHasError(true) }) }) }));
|
|
218256
|
+
}
|
|
218257
|
+
|
|
218258
|
+
const openInNewTab = (url) => {
|
|
218259
|
+
window.open(url, '_blank', 'noopener,noreferrer');
|
|
218260
|
+
};
|
|
218261
|
+
/**
|
|
218262
|
+
* Files on an admin transcript, in the chat's own attachment idiom: images
|
|
218263
|
+
* preview like a sent image, everything else is the chat's file card. A card
|
|
218264
|
+
* with a URL opens the file in a new tab (presigned links expire, so nothing
|
|
218265
|
+
* is cached here).
|
|
218266
|
+
*/
|
|
218267
|
+
function TranscriptFileCards({ files, className, }) {
|
|
218268
|
+
const shown = files.filter((file) => file === null || file === void 0 ? void 0 : file.name);
|
|
218269
|
+
if (shown.length === 0)
|
|
218270
|
+
return null;
|
|
218271
|
+
return (jsx("div", { className: className !== null && className !== void 0 ? className : 'mt-2 flex flex-wrap items-start gap-2', "data-testid": "transcript-file-cards", children: shown.map((file, index) => {
|
|
218272
|
+
var _a;
|
|
218273
|
+
const contentType = (_a = file.contentType) !== null && _a !== void 0 ? _a : '';
|
|
218274
|
+
if (file.url && contentType.startsWith('image/')) {
|
|
218275
|
+
return (jsx(ImageMessage, { url: file.url, fileName: file.name, setPreviewImage: openInNewTab }, `${file.name}-${index}`));
|
|
218276
|
+
}
|
|
218277
|
+
const card = jsx(FileCard, { fileName: file.name, fileType: contentType || 'file' });
|
|
218278
|
+
return file.url ? (jsx("a", { href: file.url, target: "_blank", rel: "noopener noreferrer", className: "block hover:opacity-90", "aria-label": `Open ${file.name}`, onClick: (event) => event.stopPropagation(), children: card }, `${file.name}-${index}`)) : (jsx("div", { children: card }, `${file.name}-${index}`));
|
|
218279
|
+
}) }));
|
|
218280
|
+
}
|
|
218281
|
+
|
|
218056
218282
|
/**
|
|
218057
218283
|
* Whether the dropdown is open, closing on a pointer press outside `ref` or on
|
|
218058
218284
|
* Escape.
|
|
@@ -218696,8 +218922,12 @@ function ConversationMessages({ messages, username, agent, fallbackEmail, }) {
|
|
|
218696
218922
|
? (userMetadata === null || userMetadata === void 0 ? void 0 : userMetadata.email) || fallbackEmail || username
|
|
218697
218923
|
: t('chatHistoryChatHistoryTab.you');
|
|
218698
218924
|
return (jsx("div", { className: "space-y-6", children: messages.map((message, index) => {
|
|
218699
|
-
var _a, _b;
|
|
218700
|
-
return (jsxs("div", { className: "space-y-4", children: [jsxs("div", { className: "flex items-start gap-3", children: [jsx(UserMessageAvatar, { username: username }), jsxs("div", { className: "min-w-0 flex-1 overflow-x-hidden", children: [jsx("div", { className: "font-medium break-all text-gray-700", children: humanLabel }), jsx(Markdown, { className: "mt-1 text-sm text-gray-500", children: (_a = message.human) !== null && _a !== void 0 ? _a : '' })
|
|
218925
|
+
var _a, _b, _c;
|
|
218926
|
+
return (jsxs("div", { className: "space-y-4", children: [jsxs("div", { className: "flex items-start gap-3", children: [jsx(UserMessageAvatar, { username: username }), jsxs("div", { className: "min-w-0 flex-1 overflow-x-hidden", children: [jsx("div", { className: "font-medium break-all text-gray-700", children: humanLabel }), jsx(Markdown, { className: "mt-1 text-sm text-gray-500", children: (_a = message.human) !== null && _a !== void 0 ? _a : '' }), jsx(TranscriptFileCards, { files: ((_b = message.human_files) !== null && _b !== void 0 ? _b : []).map((file) => ({
|
|
218927
|
+
name: file.name,
|
|
218928
|
+
contentType: file.content_type,
|
|
218929
|
+
url: file.url,
|
|
218930
|
+
})) })] })] }), jsxs("div", { className: "flex items-start gap-3 border-t border-gray-200 pt-4", children: [jsx(AgentMessageAvatar, { agent: agent, fallback: t('chatHistoryChatHistoryTab.aiAvatarLabel') }), jsxs("div", { className: "min-w-0 flex-1 overflow-x-hidden", children: [jsx("div", { className: "font-medium text-gray-700", children: agent.name }), jsx(Markdown, { className: "mt-1 text-sm text-gray-500", children: (_c = message.ai) !== null && _c !== void 0 ? _c : '' })] })] })] }, index));
|
|
218701
218931
|
}) }));
|
|
218702
218932
|
}
|
|
218703
218933
|
/**
|
|
@@ -220517,6 +220747,624 @@ function InvitedUsersDialog({ tenant, onClose }) {
|
|
|
220517
220747
|
}, children: jsx(DialogPortal, { forceMount: true, children: jsxs(DialogContent, { "aria-label": t('profileInvitedUsers.dialogAriaLabel'), className: "max-w-sm rounded-2xl px-6 py-8 pointer-events-auto", "aria-describedby": "invited-users-dialog", children: [jsx(DialogHeader, { className: "relative flex-row justify-between", children: jsx(DialogTitle, { className: "text-blue-600 text-xl font-semibold", children: t('profileInvitedUsers.title') }) }), jsxs("div", { className: "mt-4 space-y-1", children: [jsx("p", { className: "text-gray-500 text-sm", children: t('profileInvitedUsers.pendingDescription') }), jsx("form", { children: jsx(Input, { value: query, onChange: (event) => setQuery(event.target.value), placeholder: t('profileInvitedUsers.searchPlaceholder'), className: "mt-2" }) })] }), jsxs("div", { children: [jsx("h2", { className: "text-md text-gray-600 font-semibold mb-4", children: t('profileInvitedUsers.emailHeading') }), invitedUsers === null || invitedUsers === void 0 ? void 0 : invitedUsers.results.map((user) => (jsx("div", { className: "border-b-gray-50 pb-2 mb-4 text-sm text-gray-600 dark:text-gray-400", children: user.email }, user.id))), (invitedUsers === null || invitedUsers === void 0 ? void 0 : invitedUsers.results.length) === 0 && (jsx("p", { className: "text-gray", children: t('profileInvitedUsers.noRecords') }))] })] }) }) }));
|
|
220518
220748
|
}
|
|
220519
220749
|
|
|
220750
|
+
function DocumentSidebar({ isModal = false, sessionId = '', documents, }) {
|
|
220751
|
+
const t = useT();
|
|
220752
|
+
const [isCollapsed, setIsCollapsed] = React.useState(false);
|
|
220753
|
+
const params = useParams();
|
|
220754
|
+
const username = useUsername();
|
|
220755
|
+
const isStreaming = useSelector(selectStreaming);
|
|
220756
|
+
const hasOwnDocuments = documents !== undefined;
|
|
220757
|
+
const { data: sessionDocuments, refetch, status, } = useGetVectorDocumentsQuery({
|
|
220758
|
+
org: params.tenantKey,
|
|
220759
|
+
sessionId: sessionId,
|
|
220760
|
+
// @ts-ignore
|
|
220761
|
+
userId: username,
|
|
220762
|
+
}, {
|
|
220763
|
+
skip: hasOwnDocuments || !params.tenantKey || !sessionId || !username || !isLoggedIn(),
|
|
220764
|
+
});
|
|
220765
|
+
const vectorDocuments = hasOwnDocuments
|
|
220766
|
+
? (documents !== null && documents !== void 0 ? documents : undefined)
|
|
220767
|
+
: sessionDocuments;
|
|
220768
|
+
React.useEffect(() => {
|
|
220769
|
+
if (!hasOwnDocuments && !isStreaming && isLoggedIn() && status !== 'uninitialized') {
|
|
220770
|
+
refetch();
|
|
220771
|
+
}
|
|
220772
|
+
}, [hasOwnDocuments, isStreaming, refetch]);
|
|
220773
|
+
const toggleCollapse = () => {
|
|
220774
|
+
setIsCollapsed(!isCollapsed);
|
|
220775
|
+
};
|
|
220776
|
+
return (jsx(Fragment$1, { children: (vectorDocuments === null || vectorDocuments === void 0 ? void 0 : vectorDocuments.length) && vectorDocuments.length > 0 ? (jsx("aside", { className: cn('transition-all duration-300 ease-in-out', isModal
|
|
220777
|
+
? 'h-full max-h-full w-full overflow-auto'
|
|
220778
|
+
: 'hidden rounded-tl-lg rounded-bl-lg md:hidden lg:block', !isModal && (isCollapsed ? 'w-[40px]' : 'w-[380px]')), style: {
|
|
220779
|
+
backgroundColor: isCollapsed && !isModal ? 'white' : '#F3F6FB',
|
|
220780
|
+
// In the dialog the parent bounds the height; the rail caps itself.
|
|
220781
|
+
maxHeight: isModal ? undefined : 'calc(100vh - 80px)',
|
|
220782
|
+
}, children: jsxs("div", { className: "flex h-full min-h-0 flex-col", children: [!isModal && (jsxs("div", { className: cn('flex cursor-pointer items-center gap-2 border-b border-gray-200 p-4 transition-colors duration-200', isCollapsed && 'h-[80px] justify-center rounded-tl-[5px] rounded-bl-[5px] p-2', 'hover:bg-blue-100'), onClick: toggleCollapse, style: { backgroundColor: '#F3F6FB' }, children: [jsx(FileText, { className: "h-5 w-5 shrink-0 text-blue-600" }), !isCollapsed && (jsxs(Fragment$1, { children: [jsx("h2", { className: "text-base font-medium text-[#646464]", children: t('documentSidebar.retrievedDocuments') }), jsx(ChevronRight, { className: cn('ml-auto h-4 w-4 text-gray-400 transition-transform', isCollapsed ? 'rotate-180' : '') })] }))] })), (!isCollapsed || isModal) && (jsx(Fragment$1, { children: jsx("div", { className: "min-h-0 flex-1 overflow-auto", style: { backgroundColor: '#F3F6FB' }, children: jsx("div", { className: "mt-0 space-y-1 p-2", children: vectorDocuments.map((doc, index) => {
|
|
220783
|
+
var _a, _b;
|
|
220784
|
+
return (jsxs(React.Fragment, { children: [jsx("div", { className: "rounded-lg p-2 hover:bg-gray-50", children: jsxs("div", { className: "flex items-start gap-2", children: [jsx(Radio$1, { className: "mt-1 h-4 w-4 text-gray-400" }), jsxs("div", { className: "min-w-0 flex-1 space-y-1", children: [jsxs("div", { className: "flex items-center justify-between", children: [jsx(Link$2, { href: (_a = doc.source) !== null && _a !== void 0 ? _a : '', target: "_blank", className: "min-w-0", children: jsx("span", { className: "text-sm font-semibold break-words text-[#646464] hover:text-blue-600", children: doc.title }) }), jsx("div", { className: "flex items-center gap-2 self-center", children: jsx("span", { className: "text-xs text-gray-500", children: (_b = doc === null || doc === void 0 ? void 0 : doc.score) === null || _b === void 0 ? void 0 : _b.toFixed(2) }) })] }), jsx("p", { className: "line-clamp-6 pt-[10px] text-[14px] text-gray-600", children: doc.snippet })] })] }) }), index < vectorDocuments.length - 1 && (jsx("div", { className: "my-2 border-t border-gray-200" }))] }, index));
|
|
220785
|
+
}) }) }) }))] }) })) : null }));
|
|
220786
|
+
}
|
|
220787
|
+
|
|
220788
|
+
/**
|
|
220789
|
+
* The chat's "Retrieved Documents" control: an outline button opening the
|
|
220790
|
+
* documents dialog (`DocumentSidebar` in modal mode). Fed by the session's
|
|
220791
|
+
* vector results by default, or by `documents` the host already has — the
|
|
220792
|
+
* admin transcript surfaces pass a turn's or a conversation's documents, so
|
|
220793
|
+
* one component shows retrieved documents everywhere.
|
|
220794
|
+
*/
|
|
220795
|
+
function RetrievedDocumentsButton({ sessionId = '', documents, className, label, }) {
|
|
220796
|
+
const t = useT();
|
|
220797
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
220798
|
+
const params = useParams();
|
|
220799
|
+
const username = useUsername();
|
|
220800
|
+
const isStreaming = useSelector(selectStreaming);
|
|
220801
|
+
const hasOwnDocuments = documents !== undefined;
|
|
220802
|
+
const { data: sessionDocuments, refetch, status, } = useGetVectorDocumentsQuery({
|
|
220803
|
+
org: params.tenantKey,
|
|
220804
|
+
sessionId: sessionId,
|
|
220805
|
+
// @ts-ignore
|
|
220806
|
+
userId: username,
|
|
220807
|
+
}, {
|
|
220808
|
+
skip: hasOwnDocuments || !params.tenantKey || !sessionId || !username || !isLoggedIn(),
|
|
220809
|
+
});
|
|
220810
|
+
const vectorDocuments = hasOwnDocuments
|
|
220811
|
+
? (documents !== null && documents !== void 0 ? documents : undefined)
|
|
220812
|
+
: sessionDocuments;
|
|
220813
|
+
useEffect(() => {
|
|
220814
|
+
if (!hasOwnDocuments && !isStreaming && isLoggedIn() && status !== 'uninitialized') {
|
|
220815
|
+
refetch();
|
|
220816
|
+
}
|
|
220817
|
+
}, [hasOwnDocuments, isStreaming, refetch]);
|
|
220818
|
+
if (!(vectorDocuments === null || vectorDocuments === void 0 ? void 0 : vectorDocuments.length)) {
|
|
220819
|
+
return null;
|
|
220820
|
+
}
|
|
220821
|
+
return (jsxs(Fragment$1, { children: [jsxs(Button$1, { variant: "outline", size: "sm", className: cn('flex items-center gap-2 border-gray-200 bg-white text-gray-700 hover:bg-gray-50', className), onClick: (event) => {
|
|
220822
|
+
// Safe inside clickable transcript rows: the row must not react.
|
|
220823
|
+
event.stopPropagation();
|
|
220824
|
+
setIsOpen(true);
|
|
220825
|
+
}, "data-testid": "retrieved-documents-button", children: [jsx(FileText, { className: "h-4 w-4 text-blue-600" }), jsx("span", { children: label !== null && label !== void 0 ? label : t('retrievedDocumentsButton.title') })] }), jsx(Dialog, { open: isOpen, onOpenChange: setIsOpen, children: jsxs(DialogContent, { className: "flex max-h-[80vh] flex-col gap-0 p-0 sm:max-w-[425px] md:max-w-[600px]", children: [jsx(DialogHeader, { className: "border-b px-4 py-2", children: jsxs(DialogTitle, { className: "flex items-center gap-2 text-base font-medium text-[#646464]", children: [jsx(FileText, { className: "h-5 w-5 text-blue-600" }), t('retrievedDocumentsButton.title')] }) }), jsx("div", { className: "min-h-0 flex-1 overflow-y-auto", "data-testid": "retrieved-documents-scroll", children: jsx(DocumentSidebar, { isModal: true, sessionId: sessionId, documents: documents }) })] }) })] }));
|
|
220826
|
+
}
|
|
220827
|
+
|
|
220828
|
+
var COLLAPSIBLE_NAME = "Collapsible";
|
|
220829
|
+
var [createCollapsibleContext] = createContextScope$1(COLLAPSIBLE_NAME);
|
|
220830
|
+
var [CollapsibleProvider, useCollapsibleContext] = createCollapsibleContext(COLLAPSIBLE_NAME);
|
|
220831
|
+
var Collapsible$1 = React.forwardRef(
|
|
220832
|
+
(props, forwardedRef) => {
|
|
220833
|
+
const {
|
|
220834
|
+
__scopeCollapsible,
|
|
220835
|
+
open: openProp,
|
|
220836
|
+
defaultOpen,
|
|
220837
|
+
disabled,
|
|
220838
|
+
onOpenChange,
|
|
220839
|
+
...collapsibleProps
|
|
220840
|
+
} = props;
|
|
220841
|
+
const [open, setOpen] = useControllableState$1({
|
|
220842
|
+
prop: openProp,
|
|
220843
|
+
defaultProp: defaultOpen ?? false,
|
|
220844
|
+
onChange: onOpenChange,
|
|
220845
|
+
caller: COLLAPSIBLE_NAME
|
|
220846
|
+
});
|
|
220847
|
+
return /* @__PURE__ */ jsx(
|
|
220848
|
+
CollapsibleProvider,
|
|
220849
|
+
{
|
|
220850
|
+
scope: __scopeCollapsible,
|
|
220851
|
+
disabled,
|
|
220852
|
+
contentId: useId$1(),
|
|
220853
|
+
open,
|
|
220854
|
+
onOpenToggle: React.useCallback(() => setOpen((prevOpen) => !prevOpen), [setOpen]),
|
|
220855
|
+
children: /* @__PURE__ */ jsx(
|
|
220856
|
+
Primitive$2.div,
|
|
220857
|
+
{
|
|
220858
|
+
"data-state": getState$1(open),
|
|
220859
|
+
"data-disabled": disabled ? "" : void 0,
|
|
220860
|
+
...collapsibleProps,
|
|
220861
|
+
ref: forwardedRef
|
|
220862
|
+
}
|
|
220863
|
+
)
|
|
220864
|
+
}
|
|
220865
|
+
);
|
|
220866
|
+
}
|
|
220867
|
+
);
|
|
220868
|
+
Collapsible$1.displayName = COLLAPSIBLE_NAME;
|
|
220869
|
+
var TRIGGER_NAME$2 = "CollapsibleTrigger";
|
|
220870
|
+
var CollapsibleTrigger$1 = React.forwardRef(
|
|
220871
|
+
(props, forwardedRef) => {
|
|
220872
|
+
const { __scopeCollapsible, ...triggerProps } = props;
|
|
220873
|
+
const context = useCollapsibleContext(TRIGGER_NAME$2, __scopeCollapsible);
|
|
220874
|
+
return /* @__PURE__ */ jsx(
|
|
220875
|
+
Primitive$2.button,
|
|
220876
|
+
{
|
|
220877
|
+
type: "button",
|
|
220878
|
+
"aria-controls": context.contentId,
|
|
220879
|
+
"aria-expanded": context.open || false,
|
|
220880
|
+
"data-state": getState$1(context.open),
|
|
220881
|
+
"data-disabled": context.disabled ? "" : void 0,
|
|
220882
|
+
disabled: context.disabled,
|
|
220883
|
+
...triggerProps,
|
|
220884
|
+
ref: forwardedRef,
|
|
220885
|
+
onClick: composeEventHandlers$1(props.onClick, context.onOpenToggle)
|
|
220886
|
+
}
|
|
220887
|
+
);
|
|
220888
|
+
}
|
|
220889
|
+
);
|
|
220890
|
+
CollapsibleTrigger$1.displayName = TRIGGER_NAME$2;
|
|
220891
|
+
var CONTENT_NAME$3 = "CollapsibleContent";
|
|
220892
|
+
var CollapsibleContent$1 = React.forwardRef(
|
|
220893
|
+
(props, forwardedRef) => {
|
|
220894
|
+
const { forceMount, ...contentProps } = props;
|
|
220895
|
+
const context = useCollapsibleContext(CONTENT_NAME$3, props.__scopeCollapsible);
|
|
220896
|
+
return /* @__PURE__ */ jsx(Presence$2, { present: forceMount || context.open, children: ({ present }) => /* @__PURE__ */ jsx(CollapsibleContentImpl, { ...contentProps, ref: forwardedRef, present }) });
|
|
220897
|
+
}
|
|
220898
|
+
);
|
|
220899
|
+
CollapsibleContent$1.displayName = CONTENT_NAME$3;
|
|
220900
|
+
var CollapsibleContentImpl = React.forwardRef((props, forwardedRef) => {
|
|
220901
|
+
const { __scopeCollapsible, present, children, ...contentProps } = props;
|
|
220902
|
+
const context = useCollapsibleContext(CONTENT_NAME$3, __scopeCollapsible);
|
|
220903
|
+
const [isPresent, setIsPresent] = React.useState(present);
|
|
220904
|
+
const ref = React.useRef(null);
|
|
220905
|
+
const composedRefs = useComposedRefs$1(forwardedRef, ref);
|
|
220906
|
+
const heightRef = React.useRef(0);
|
|
220907
|
+
const height = heightRef.current;
|
|
220908
|
+
const widthRef = React.useRef(0);
|
|
220909
|
+
const width = widthRef.current;
|
|
220910
|
+
const isOpen = context.open || isPresent;
|
|
220911
|
+
const isMountAnimationPreventedRef = React.useRef(isOpen);
|
|
220912
|
+
const originalStylesRef = React.useRef(void 0);
|
|
220913
|
+
React.useEffect(() => {
|
|
220914
|
+
const rAF = requestAnimationFrame(() => isMountAnimationPreventedRef.current = false);
|
|
220915
|
+
return () => cancelAnimationFrame(rAF);
|
|
220916
|
+
}, []);
|
|
220917
|
+
useLayoutEffect2$1(() => {
|
|
220918
|
+
const node = ref.current;
|
|
220919
|
+
if (node) {
|
|
220920
|
+
originalStylesRef.current = originalStylesRef.current || {
|
|
220921
|
+
transitionDuration: node.style.transitionDuration,
|
|
220922
|
+
animationName: node.style.animationName
|
|
220923
|
+
};
|
|
220924
|
+
node.style.transitionDuration = "0s";
|
|
220925
|
+
node.style.animationName = "none";
|
|
220926
|
+
const rect = node.getBoundingClientRect();
|
|
220927
|
+
heightRef.current = rect.height;
|
|
220928
|
+
widthRef.current = rect.width;
|
|
220929
|
+
if (!isMountAnimationPreventedRef.current) {
|
|
220930
|
+
node.style.transitionDuration = originalStylesRef.current.transitionDuration;
|
|
220931
|
+
node.style.animationName = originalStylesRef.current.animationName;
|
|
220932
|
+
}
|
|
220933
|
+
setIsPresent(present);
|
|
220934
|
+
}
|
|
220935
|
+
}, [context.open, present]);
|
|
220936
|
+
return /* @__PURE__ */ jsx(
|
|
220937
|
+
Primitive$2.div,
|
|
220938
|
+
{
|
|
220939
|
+
"data-state": getState$1(context.open),
|
|
220940
|
+
"data-disabled": context.disabled ? "" : void 0,
|
|
220941
|
+
id: context.contentId,
|
|
220942
|
+
hidden: !isOpen,
|
|
220943
|
+
...contentProps,
|
|
220944
|
+
ref: composedRefs,
|
|
220945
|
+
style: {
|
|
220946
|
+
[`--radix-collapsible-content-height`]: height ? `${height}px` : void 0,
|
|
220947
|
+
[`--radix-collapsible-content-width`]: width ? `${width}px` : void 0,
|
|
220948
|
+
...props.style
|
|
220949
|
+
},
|
|
220950
|
+
children: isOpen && children
|
|
220951
|
+
}
|
|
220952
|
+
);
|
|
220953
|
+
});
|
|
220954
|
+
function getState$1(open) {
|
|
220955
|
+
return open ? "open" : "closed";
|
|
220956
|
+
}
|
|
220957
|
+
var Root$5 = Collapsible$1;
|
|
220958
|
+
|
|
220959
|
+
const Collapsible = Root$5;
|
|
220960
|
+
const CollapsibleTrigger = CollapsibleTrigger$1;
|
|
220961
|
+
const CollapsibleContent = CollapsibleContent$1;
|
|
220962
|
+
|
|
220963
|
+
function getFriendlyToolName(toolName) {
|
|
220964
|
+
if (TOOL_NAME_MAP === null || TOOL_NAME_MAP === void 0 ? void 0 : TOOL_NAME_MAP[toolName])
|
|
220965
|
+
return TOOL_NAME_MAP[toolName];
|
|
220966
|
+
const words = toolName.replace(/_/g, ' ').trim();
|
|
220967
|
+
return words.charAt(0).toUpperCase() + words.slice(1);
|
|
220968
|
+
}
|
|
220969
|
+
// Extract a short human-readable query from tool input or log
|
|
220970
|
+
function getQueryLabel(toolCall) {
|
|
220971
|
+
var _a, _b, _c, _d, _e;
|
|
220972
|
+
// Prefer structured input
|
|
220973
|
+
if (toolCall.input && typeof toolCall.input === 'object') {
|
|
220974
|
+
const val = (_c = (_b = (_a = toolCall.input.query) !== null && _a !== void 0 ? _a : toolCall.input.q) !== null && _b !== void 0 ? _b : toolCall.input.input) !== null && _c !== void 0 ? _c : toolCall.input.text;
|
|
220975
|
+
if (val && typeof val === 'string')
|
|
220976
|
+
return val;
|
|
220977
|
+
// Fallback: first string value in input
|
|
220978
|
+
const firstStr = Object.values(toolCall.input).find((v) => typeof v === 'string');
|
|
220979
|
+
if (firstStr)
|
|
220980
|
+
return firstStr;
|
|
220981
|
+
}
|
|
220982
|
+
// Fallback: parse the "… with `{...}`" snippet of the log string. Located
|
|
220983
|
+
// with index scans rather than a lazy regex, which backtracks
|
|
220984
|
+
// polynomially on adversarial logs (CodeQL js/polynomial-redos).
|
|
220985
|
+
if (toolCall.log) {
|
|
220986
|
+
const snippet = extractLogPayload(toolCall.log);
|
|
220987
|
+
if (snippet) {
|
|
220988
|
+
try {
|
|
220989
|
+
const parsed = JSON.parse(snippet.replace(/'/g, '"'));
|
|
220990
|
+
const val = (_e = (_d = parsed.query) !== null && _d !== void 0 ? _d : parsed.q) !== null && _e !== void 0 ? _e : parsed.input;
|
|
220991
|
+
if (val)
|
|
220992
|
+
return val;
|
|
220993
|
+
}
|
|
220994
|
+
catch (_f) {
|
|
220995
|
+
// Not every log snippet is JSON (apostrophes inside the payload break
|
|
220996
|
+
// the quote swap above). That is expected input, not an error — and
|
|
220997
|
+
// this runs inside a memo comparator on every streaming tick, so
|
|
220998
|
+
// logging here flooded the console on phones.
|
|
220999
|
+
}
|
|
221000
|
+
}
|
|
221001
|
+
}
|
|
221002
|
+
return null;
|
|
221003
|
+
}
|
|
221004
|
+
/** The `{...}` inside the first "with `{...}`" of a tool log, or null. */
|
|
221005
|
+
function extractLogPayload(log) {
|
|
221006
|
+
let from = 0;
|
|
221007
|
+
while (from < log.length) {
|
|
221008
|
+
const open = log.indexOf('`{', from);
|
|
221009
|
+
if (open === -1)
|
|
221010
|
+
return null;
|
|
221011
|
+
// Must follow the word "with" plus at least one whitespace character.
|
|
221012
|
+
let i = open - 1;
|
|
221013
|
+
let sawSpace = false;
|
|
221014
|
+
while (i >= 0 && /\s/.test(log[i])) {
|
|
221015
|
+
sawSpace = true;
|
|
221016
|
+
i -= 1;
|
|
221017
|
+
}
|
|
221018
|
+
if (sawSpace && log.slice(Math.max(0, i - 3), i + 1) === 'with') {
|
|
221019
|
+
const close = log.indexOf('}`', open + 1);
|
|
221020
|
+
if (close === -1)
|
|
221021
|
+
return null;
|
|
221022
|
+
return log.slice(open + 1, close + 1);
|
|
221023
|
+
}
|
|
221024
|
+
from = open + 1;
|
|
221025
|
+
}
|
|
221026
|
+
return null;
|
|
221027
|
+
}
|
|
221028
|
+
function formatResult(result) {
|
|
221029
|
+
return result.replace(/\n{3,}/g, '\n\n').trim();
|
|
221030
|
+
}
|
|
221031
|
+
|
|
221032
|
+
const TOOL_ICONS = {
|
|
221033
|
+
web_search_call: Globe,
|
|
221034
|
+
vector_search: Search,
|
|
221035
|
+
code_executor: Code$1,
|
|
221036
|
+
file_reader: FileText,
|
|
221037
|
+
wikipedia: BookOpen,
|
|
221038
|
+
};
|
|
221039
|
+
function getToolIcon(toolName) {
|
|
221040
|
+
return (toolName && TOOL_ICONS[toolName]) || Wrench;
|
|
221041
|
+
}
|
|
221042
|
+
/**
|
|
221043
|
+
* Collapsible record of the tools a turn used — the "Used N tools" line of
|
|
221044
|
+
* the chat bubble.
|
|
221045
|
+
*
|
|
221046
|
+
* The header always reads as the completed record ("Used N tools") and the
|
|
221047
|
+
* individual rows never animate. Only while this row is the live phase does the
|
|
221048
|
+
* header sprout dots — at which point the working line has stood down, so there
|
|
221049
|
+
* is still exactly one animated "agent is working" signal on screen.
|
|
221050
|
+
*/
|
|
221051
|
+
function ToolCallIndicator({ toolCalls, isActive = false, showResults = false, }) {
|
|
221052
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
221053
|
+
// `write_todos` has a dedicated renderer (`AgentTodoList`), so it must never
|
|
221054
|
+
// appear as a generic tool card nor be counted in "Used N tools".
|
|
221055
|
+
const visibleToolCalls = (toolCalls !== null && toolCalls !== void 0 ? toolCalls : []).filter((tc) => (tc === null || tc === void 0 ? void 0 : tc.name) !== WRITE_TODOS_TOOL);
|
|
221056
|
+
if (visibleToolCalls.length === 0) {
|
|
221057
|
+
return null;
|
|
221058
|
+
}
|
|
221059
|
+
const uniqueToolCount = new Set(visibleToolCalls.map((tc) => tc.name)).size;
|
|
221060
|
+
const headerLabel = `Used ${uniqueToolCount} tool${uniqueToolCount === 1 ? '' : 's'}`;
|
|
221061
|
+
return (jsxs(Collapsible, { open: isOpen, onOpenChange: setIsOpen, className: "mb-2", children: [jsxs(CollapsibleTrigger, { className: "flex cursor-pointer items-center gap-1 pt-1 text-xs text-gray-600 transition-colors hover:text-gray-800", children: [jsx(ChevronRight, { className: cn('h-3 w-3 transition-transform duration-200', isOpen && 'rotate-90') }), jsx(Wrench, { className: "h-3 w-3" }), jsx("span", { children: headerLabel }), isActive && (jsxs("span", { className: "inline-flex gap-0.5", "aria-hidden": "true", children: [jsx("span", { className: "inline-block h-1 w-1 animate-bounce rounded-full bg-gray-600 [animation-delay:0ms] motion-reduce:animate-none" }), jsx("span", { className: "inline-block h-1 w-1 animate-bounce rounded-full bg-gray-600 [animation-delay:150ms] motion-reduce:animate-none" }), jsx("span", { className: "inline-block h-1 w-1 animate-bounce rounded-full bg-gray-600 [animation-delay:300ms] motion-reduce:animate-none" })] }))] }), jsx(CollapsibleContent, { className: "pb-2 pl-4", children: jsx("div", { className: "space-y-2.5 border-l-2 border-gray-300 pt-2.5 pl-3 text-xs leading-relaxed text-gray-600", children: visibleToolCalls.map((toolCall, index) => {
|
|
221062
|
+
var _a;
|
|
221063
|
+
const query = getQueryLabel(toolCall);
|
|
221064
|
+
const Icon = getToolIcon(toolCall === null || toolCall === void 0 ? void 0 : toolCall.name);
|
|
221065
|
+
const result = showResults && (toolCall === null || toolCall === void 0 ? void 0 : toolCall.result) ? formatResult(toolCall.result) : null;
|
|
221066
|
+
return (jsxs("div", { children: [jsxs("div", { className: "flex items-center gap-1.5 text-gray-700", children: [jsx(Icon, { className: "h-3 w-3 shrink-0 text-gray-500" }), jsx("span", { className: "font-medium", children: getFriendlyToolName((_a = toolCall === null || toolCall === void 0 ? void 0 : toolCall.name) !== null && _a !== void 0 ? _a : '') })] }), query && (jsx("div", { className: "mt-0.5 ml-[18px] text-gray-600", children: jsx(Markdown, { className: "prose prose-xs max-w-none [&_*]:text-xs [&_*]:text-gray-600 [&_p]:m-0", children: query }) })), result && (jsx("div", { className: "mt-1 ml-[18px] max-h-48 overflow-y-auto rounded-md border border-gray-200 bg-white px-2 py-1.5 leading-relaxed whitespace-pre-wrap text-gray-600", "data-testid": "tool-call-result", children: result }))] }, (toolCall === null || toolCall === void 0 ? void 0 : toolCall.id) || index));
|
|
221067
|
+
}) }) })] }));
|
|
221068
|
+
}
|
|
221069
|
+
|
|
221070
|
+
// Model wire keys whose raw form is not presentable, keyed lowercased and
|
|
221071
|
+
// alphanumerics-only.
|
|
221072
|
+
const IBL_AI_DISPLAY_NAME = 'ibl.ai';
|
|
221073
|
+
const MODEL_DISPLAY_NAME_BY_KEY = {
|
|
221074
|
+
iblai: IBL_AI_DISPLAY_NAME,
|
|
221075
|
+
};
|
|
221076
|
+
/**
|
|
221077
|
+
* The label to show for a model when only its wire key is known.
|
|
221078
|
+
*
|
|
221079
|
+
* The LLM picker gets a `display_name` per model straight from the API, but
|
|
221080
|
+
* mentor settings and transcripts persist only `llm_name` — a wire key — so
|
|
221081
|
+
* surfaces that read from those have no label to fall back on and would
|
|
221082
|
+
* render `iblai`. Most keys are already presentable (`gpt-4.1`), so this maps
|
|
221083
|
+
* only the ones that are not and passes everything else through untouched.
|
|
221084
|
+
*/
|
|
221085
|
+
function getLLMModelDisplayName(llmName) {
|
|
221086
|
+
var _a;
|
|
221087
|
+
const raw = llmName !== null && llmName !== void 0 ? llmName : '';
|
|
221088
|
+
const normalized = raw.toLowerCase().replace(/[^a-z0-9]/g, '');
|
|
221089
|
+
// ibl.ai keys are the brand followed by an optional variant — `iblai`,
|
|
221090
|
+
// `iblai-pro`, `iblai-fast`, `IBL AI Pro`. Rebuild the label the model
|
|
221091
|
+
// picker gets from the API (`ibl.ai Pro`) rather than collapsing every
|
|
221092
|
+
// variant onto a bare `ibl.ai`, which would disagree with the picker.
|
|
221093
|
+
const brand = /^(iblai|ibl)(.*)$/.exec(normalized);
|
|
221094
|
+
const leadsWithBrand = brand &&
|
|
221095
|
+
(brand[1] === 'iblai' ||
|
|
221096
|
+
raw
|
|
221097
|
+
.toLowerCase()
|
|
221098
|
+
.split(/[^a-z0-9]+/)
|
|
221099
|
+
.filter(Boolean)[0] === 'ibl');
|
|
221100
|
+
if (leadsWithBrand) {
|
|
221101
|
+
const variant = brand[2];
|
|
221102
|
+
if (!variant)
|
|
221103
|
+
return IBL_AI_DISPLAY_NAME;
|
|
221104
|
+
// `ai` is part of the brand when the key spells it out (`ibl-ai-pro`).
|
|
221105
|
+
const suffix = variant.startsWith('ai') && brand[1] === 'ibl' ? variant.slice(2) : variant;
|
|
221106
|
+
if (!suffix)
|
|
221107
|
+
return IBL_AI_DISPLAY_NAME;
|
|
221108
|
+
return `${IBL_AI_DISPLAY_NAME} ${suffix.charAt(0).toUpperCase()}${suffix.slice(1)}`;
|
|
221109
|
+
}
|
|
221110
|
+
return (_a = MODEL_DISPLAY_NAME_BY_KEY[normalized]) !== null && _a !== void 0 ? _a : raw;
|
|
221111
|
+
}
|
|
221112
|
+
|
|
221113
|
+
/** Loose key for matching a model across spellings: lower-case, alphanumerics only. */
|
|
221114
|
+
const modelKey = (value) => (value !== null && value !== void 0 ? value : '').toLowerCase().replace(/[^a-z0-9]/g, '');
|
|
221115
|
+
/**
|
|
221116
|
+
* Model families → the provider keys the catalogue may list them under, for
|
|
221117
|
+
* a model the catalogue does not name (a dated or "-latest" wire key, or a
|
|
221118
|
+
* model the tenant enabled elsewhere). First key present in the catalogue
|
|
221119
|
+
* wins; hosted variants (Azure, Bedrock, Groq) come after the vendor.
|
|
221120
|
+
*/
|
|
221121
|
+
const MODEL_FAMILY_PROVIDERS = [
|
|
221122
|
+
[/^(gpt|o[1-9]|chatgpt|davinci|text-embedding)/, ['openai', 'azure_openai', 'azure']],
|
|
221123
|
+
[/^claude/, ['anthropic', 'bedrock']],
|
|
221124
|
+
[/^(gemini|gemma|palm)/, ['google', 'vertex', 'vertexai', 'gemini']],
|
|
221125
|
+
[/^(llama|meta)/, ['llama', 'meta', 'bedrock', 'groq']],
|
|
221126
|
+
[/^(mistral|mixtral|codestral)/, ['mistral']],
|
|
221127
|
+
[/^deepseek/, ['deepseek']],
|
|
221128
|
+
[/^grok/, ['xai', 'grok']],
|
|
221129
|
+
[/^(command|aya)/, ['cohere']],
|
|
221130
|
+
[/^qwen/, ['qwen', 'alibaba']],
|
|
221131
|
+
[/^(nova|titan)/, ['bedrock', 'amazon']],
|
|
221132
|
+
];
|
|
221133
|
+
/** Provider keys a model of this family is listed under, in order of preference. */
|
|
221134
|
+
function inferProviderKeysFromModel(model) {
|
|
221135
|
+
const key = modelKey(model);
|
|
221136
|
+
if (!key)
|
|
221137
|
+
return [];
|
|
221138
|
+
for (const [pattern, providers] of MODEL_FAMILY_PROVIDERS) {
|
|
221139
|
+
if (pattern.test(key))
|
|
221140
|
+
return providers;
|
|
221141
|
+
}
|
|
221142
|
+
return [];
|
|
221143
|
+
}
|
|
221144
|
+
/**
|
|
221145
|
+
* A resolver from a provider key (the `llm_provider` mentor settings persist,
|
|
221146
|
+
* e.g. `openai`) to the name and logo the backend publishes for it. A second,
|
|
221147
|
+
* optional model key finds the provider whose `chat_models` list it, for
|
|
221148
|
+
* payloads that name only the model (analytics transcript rows).
|
|
221149
|
+
*
|
|
221150
|
+
* Provider naming is backend-owned: the mentor-llms catalogue carries each
|
|
221151
|
+
* provider's `logo` (and `display_name` where the API supplies one), so this
|
|
221152
|
+
* reads the same RTK Query cache entry the LLM tab and model picker fill —
|
|
221153
|
+
* matched on `name`. A key the catalogue does not list (the catalogue is
|
|
221154
|
+
* still loading, or an on-device provider that has no backend row) resolves
|
|
221155
|
+
* to the raw key and no logo, so callers degrade to plain text.
|
|
221156
|
+
*
|
|
221157
|
+
* Returns a function rather than a value so list renderers (a mentors table,
|
|
221158
|
+
* a facet dropdown) can resolve many keys from one subscription.
|
|
221159
|
+
*/
|
|
221160
|
+
function useLlmProviderCatalogue({ org, userId, mentorId }) {
|
|
221161
|
+
const skip = !org || !userId || !mentorId;
|
|
221162
|
+
const { data: providers } = useGetLlmsQuery({
|
|
221163
|
+
org: org !== null && org !== void 0 ? org : '',
|
|
221164
|
+
// @ts-expect-error - userId is not in the generated query args but the endpoint scopes by it
|
|
221165
|
+
userId: userId !== null && userId !== void 0 ? userId : '',
|
|
221166
|
+
mentorId: mentorId !== null && mentorId !== void 0 ? mentorId : '',
|
|
221167
|
+
}, { skip });
|
|
221168
|
+
// Indexed once per catalogue load so list renderers resolve each key in
|
|
221169
|
+
// O(1). First row wins on a duplicate name, as `Array.find` would.
|
|
221170
|
+
const rowsByName = useMemo(() => {
|
|
221171
|
+
const byName = new Map();
|
|
221172
|
+
if (!Array.isArray(providers))
|
|
221173
|
+
return byName;
|
|
221174
|
+
for (const row of providers) {
|
|
221175
|
+
if ((row === null || row === void 0 ? void 0 : row.name) && !byName.has(row.name))
|
|
221176
|
+
byName.set(row.name, row);
|
|
221177
|
+
}
|
|
221178
|
+
return byName;
|
|
221179
|
+
}, [providers]);
|
|
221180
|
+
// Model key → the provider row listing it. Keyed loosely (see `modelKey`):
|
|
221181
|
+
// transcripts persist the model as it was displayed ("GPT-5.2") while the
|
|
221182
|
+
// catalogue lists the wire key ("gpt-5.2"), so both spellings, and the
|
|
221183
|
+
// display name, must find the row.
|
|
221184
|
+
const rowsByModel = useMemo(() => {
|
|
221185
|
+
var _a;
|
|
221186
|
+
const byModel = new Map();
|
|
221187
|
+
for (const row of rowsByName.values()) {
|
|
221188
|
+
for (const model of (_a = row.chat_models) !== null && _a !== void 0 ? _a : []) {
|
|
221189
|
+
for (const candidate of [model === null || model === void 0 ? void 0 : model.llm_name, model === null || model === void 0 ? void 0 : model.display_name]) {
|
|
221190
|
+
const key = modelKey(candidate);
|
|
221191
|
+
if (key && !byModel.has(key))
|
|
221192
|
+
byModel.set(key, row);
|
|
221193
|
+
}
|
|
221194
|
+
}
|
|
221195
|
+
}
|
|
221196
|
+
return byModel;
|
|
221197
|
+
}, [rowsByName]);
|
|
221198
|
+
// Provider rows keyed loosely by name and display name ("OpenAI" → openai).
|
|
221199
|
+
const rowsByLooseName = useMemo(() => {
|
|
221200
|
+
const byLoose = new Map();
|
|
221201
|
+
for (const row of rowsByName.values()) {
|
|
221202
|
+
for (const candidate of [row.name, row.display_name]) {
|
|
221203
|
+
const key = modelKey(candidate);
|
|
221204
|
+
if (key && !byLoose.has(key))
|
|
221205
|
+
byLoose.set(key, row);
|
|
221206
|
+
}
|
|
221207
|
+
}
|
|
221208
|
+
return byLoose;
|
|
221209
|
+
}, [rowsByName]);
|
|
221210
|
+
const rowForModel = useCallback((llmName) => {
|
|
221211
|
+
var _a;
|
|
221212
|
+
const wanted = modelKey(llmName);
|
|
221213
|
+
if (!wanted)
|
|
221214
|
+
return undefined;
|
|
221215
|
+
const exact = rowsByModel.get(wanted);
|
|
221216
|
+
if (exact)
|
|
221217
|
+
return exact;
|
|
221218
|
+
// A dated or "-latest" wire key ("gpt-5.2-2026-03-01") still names
|
|
221219
|
+
// the family the catalogue lists ("gpt-5.2"), and vice versa.
|
|
221220
|
+
for (const [listed, row] of rowsByModel) {
|
|
221221
|
+
if (listed.startsWith(wanted) || wanted.startsWith(listed))
|
|
221222
|
+
return row;
|
|
221223
|
+
}
|
|
221224
|
+
for (const provider of inferProviderKeysFromModel(llmName)) {
|
|
221225
|
+
const row = (_a = rowsByName.get(provider)) !== null && _a !== void 0 ? _a : rowsByLooseName.get(modelKey(provider));
|
|
221226
|
+
if (row)
|
|
221227
|
+
return row;
|
|
221228
|
+
}
|
|
221229
|
+
return undefined;
|
|
221230
|
+
}, [rowsByModel, rowsByName, rowsByLooseName]);
|
|
221231
|
+
return useCallback((llmProvider, llmName) => {
|
|
221232
|
+
var _a, _b, _c, _d, _e;
|
|
221233
|
+
const key = llmProvider !== null && llmProvider !== void 0 ? llmProvider : '';
|
|
221234
|
+
const row = (_c = (_b = (_a = rowsByName.get(key)) !== null && _a !== void 0 ? _a : rowsByName.get(key.toLowerCase())) !== null && _b !== void 0 ? _b : rowsByLooseName.get(modelKey(key))) !== null && _c !== void 0 ? _c : rowForModel(llmName);
|
|
221235
|
+
return {
|
|
221236
|
+
logo: (row === null || row === void 0 ? void 0 : row.logo) || null,
|
|
221237
|
+
displayName: ((_d = row === null || row === void 0 ? void 0 : row.display_name) === null || _d === void 0 ? void 0 : _d.trim()) || ((_e = row === null || row === void 0 ? void 0 : row.name) === null || _e === void 0 ? void 0 : _e.trim()) || key,
|
|
221238
|
+
};
|
|
221239
|
+
}, [rowsByName, rowsByLooseName, rowForModel]);
|
|
221240
|
+
}
|
|
221241
|
+
const clean$1 = (value) => (value === null || value === void 0 ? void 0 : value.trim()) || undefined;
|
|
221242
|
+
/**
|
|
221243
|
+
* The label to show for a model.
|
|
221244
|
+
*
|
|
221245
|
+
* Settings persist only `llm_name` — a wire key like `iblai-pro` or
|
|
221246
|
+
* `claude-haiku-4-5-20251001` — while the model picker reads the catalogue,
|
|
221247
|
+
* which carries a `display_name` ("ibl.ai Pro", "Claude Haiku 4.5"). Resolve
|
|
221248
|
+
* the label in three steps:
|
|
221249
|
+
*
|
|
221250
|
+
* 1. `llm_config.display_name` from settings, when the caller has it.
|
|
221251
|
+
* 2. The mentor-llms catalogue, matched on `llm_name`. Only queried when step
|
|
221252
|
+
* 1 came back empty.
|
|
221253
|
+
* 3. `getLLMModelDisplayName`, which tidies the handful of keys whose raw
|
|
221254
|
+
* form is unpresentable — used while the catalogue is loading or failed.
|
|
221255
|
+
*/
|
|
221256
|
+
function useLlmDisplayName({ llmName, llmConfig, org, userId, mentorId, }) {
|
|
221257
|
+
var _a;
|
|
221258
|
+
const fromConfig = clean$1(llmConfig === null || llmConfig === void 0 ? void 0 : llmConfig.display_name);
|
|
221259
|
+
const skip = Boolean(fromConfig) || !llmName || !org || !userId || !mentorId;
|
|
221260
|
+
const { data: providers } = useGetLlmsQuery({
|
|
221261
|
+
org: org !== null && org !== void 0 ? org : '',
|
|
221262
|
+
// @ts-expect-error - userId is not in the generated query args but the endpoint scopes by it
|
|
221263
|
+
userId: userId !== null && userId !== void 0 ? userId : '',
|
|
221264
|
+
mentorId: mentorId !== null && mentorId !== void 0 ? mentorId : '',
|
|
221265
|
+
}, { skip });
|
|
221266
|
+
const fromCatalogue = useMemo(() => {
|
|
221267
|
+
var _a;
|
|
221268
|
+
if (skip || !llmName || !Array.isArray(providers))
|
|
221269
|
+
return undefined;
|
|
221270
|
+
const wanted = modelKey(llmName);
|
|
221271
|
+
for (const provider of providers) {
|
|
221272
|
+
const match = (_a = provider === null || provider === void 0 ? void 0 : provider.chat_models) === null || _a === void 0 ? void 0 : _a.find((model) => modelKey(model === null || model === void 0 ? void 0 : model.llm_name) === wanted || modelKey(model === null || model === void 0 ? void 0 : model.display_name) === wanted);
|
|
221273
|
+
const label = clean$1(match === null || match === void 0 ? void 0 : match.display_name);
|
|
221274
|
+
if (label)
|
|
221275
|
+
return label;
|
|
221276
|
+
}
|
|
221277
|
+
return undefined;
|
|
221278
|
+
}, [skip, providers, llmName]);
|
|
221279
|
+
return (_a = fromConfig !== null && fromConfig !== void 0 ? fromConfig : fromCatalogue) !== null && _a !== void 0 ? _a : getLLMModelDisplayName(llmName);
|
|
221280
|
+
}
|
|
221281
|
+
|
|
221282
|
+
/**
|
|
221283
|
+
* A model the way the nav bar shows the mentor's: the provider logo in a
|
|
221284
|
+
* white circle (the catalogue's artwork, or the bot icon when it has none)
|
|
221285
|
+
* beside the model's display name. Read-only; used on admin transcripts to
|
|
221286
|
+
* name the model a turn ran on.
|
|
221287
|
+
*/
|
|
221288
|
+
function LlmModelBadge({ model, provider, tenantKey, mentorId, className, }) {
|
|
221289
|
+
var _a, _b;
|
|
221290
|
+
const params = useParams();
|
|
221291
|
+
const username = useUsername();
|
|
221292
|
+
const org = (_a = tenantKey !== null && tenantKey !== void 0 ? tenantKey : params === null || params === void 0 ? void 0 : params.tenantKey) !== null && _a !== void 0 ? _a : null;
|
|
221293
|
+
const mentor = (_b = mentorId !== null && mentorId !== void 0 ? mentorId : params === null || params === void 0 ? void 0 : params.mentorId) !== null && _b !== void 0 ? _b : null;
|
|
221294
|
+
const resolveLlmProvider = useLlmProviderCatalogue({ org, userId: username, mentorId: mentor });
|
|
221295
|
+
// With no provider key (transcript rows carry only the model), the
|
|
221296
|
+
// catalogue row that lists this model supplies the logo and name.
|
|
221297
|
+
const providerDetails = resolveLlmProvider(provider, model);
|
|
221298
|
+
const displayName = useLlmDisplayName({
|
|
221299
|
+
llmName: model,
|
|
221300
|
+
org,
|
|
221301
|
+
userId: username,
|
|
221302
|
+
mentorId: mentor,
|
|
221303
|
+
});
|
|
221304
|
+
const label = displayName || providerDetails.displayName;
|
|
221305
|
+
if (!label)
|
|
221306
|
+
return null;
|
|
221307
|
+
return (jsxs("span", { className: cn('inline-flex max-w-full items-center gap-2 text-sm font-medium text-[#646464]', className), title: providerDetails.displayName && providerDetails.displayName !== label
|
|
221308
|
+
? `${label} · ${providerDetails.displayName}`
|
|
221309
|
+
: label, "data-testid": "llm-model-badge", children: [jsx("span", { className: "flex h-6 w-6 flex-shrink-0 items-center justify-center rounded-full bg-white", children: providerDetails.logo ? (jsx("img", { src: providerDetails.logo, alt: `${label} model logo`, className: "h-5 w-5 object-contain", height: 32, width: 32, loading: "lazy" })) : (jsx(Bot, { className: "h-4 w-4", "aria-hidden": "true" })) }), jsx("span", { className: "max-w-[220px] truncate", children: label })] }));
|
|
221310
|
+
}
|
|
221311
|
+
|
|
221312
|
+
/** SPAs whose users have coursework, so the profile leads with a Gradebook tab. */
|
|
221313
|
+
const GRADEBOOK_SPAS = ['lms', 'skills'];
|
|
221314
|
+
/**
|
|
221315
|
+
* The customization `Profile` is opened with: whatever the host passed, with
|
|
221316
|
+
* the Gradebook tab defaulted from the SPA it runs in. An explicit
|
|
221317
|
+
* `showGradebookTab` always wins, so a host can force it either way.
|
|
221318
|
+
*/
|
|
221319
|
+
function profileCustomization(customization, currentSPA) {
|
|
221320
|
+
var _a;
|
|
221321
|
+
return {
|
|
221322
|
+
...customization,
|
|
221323
|
+
showGradebookTab: (_a = customization === null || customization === void 0 ? void 0 : customization.showGradebookTab) !== null && _a !== void 0 ? _a : GRADEBOOK_SPAS.includes((currentSPA !== null && currentSPA !== void 0 ? currentSPA : '').trim().toLowerCase()),
|
|
221324
|
+
};
|
|
221325
|
+
}
|
|
221326
|
+
/**
|
|
221327
|
+
* The shared `Profile` viewer in a dialog — the same modal the tenant
|
|
221328
|
+
* Management tab opens when a user row is clicked. Controlled variant for
|
|
221329
|
+
* hosts that trigger it from a whole table row.
|
|
221330
|
+
*
|
|
221331
|
+
* Radix portals the dialog into `document.body`, but a React portal still
|
|
221332
|
+
* bubbles its events through the React tree — so without the guard below, a
|
|
221333
|
+
* click *inside* the open profile would reach the row this dialog is rendered
|
|
221334
|
+
* from and fire its `onClick` (e.g. opening the enrollment progress modal
|
|
221335
|
+
* behind the profile). The wrapper is `display: contents`, so it adds nothing
|
|
221336
|
+
* to the host's layout.
|
|
221337
|
+
*/
|
|
221338
|
+
function UserProfileDialog({ tenantKey, username, isOpen, onClose, customization, currentSPA, }) {
|
|
221339
|
+
const t = useT();
|
|
221340
|
+
if (!isOpen)
|
|
221341
|
+
return null;
|
|
221342
|
+
return (jsx("span", { className: "contents", onClick: (event) => event.stopPropagation(), onMouseDown: (event) => event.stopPropagation(), onKeyDown: (event) => event.stopPropagation(), children: jsx(Dialog, { open: true, onOpenChange: onClose, children: jsxs(DialogContent, { autoFocus: false, className: "profile-component-dialog max-w-7xl w-[85vw] gap-0 overflow-hidden bg-white p-0", style: { height: '85vh', display: 'flex', flexDirection: 'column' }, "data-testid": "user-profile-link-dialog", children: [jsx(DialogTitle, { className: "sr-only", children: t('tabsUserstab.profileDialogTitle') }), jsx(DialogDescription, { className: "sr-only", children: t('tabsUserstab.profileDialogDescription') }), jsx(Profile, { tenant: tenantKey, tenants: [], username: username, onClose: onClose, customization: profileCustomization(customization, currentSPA), currentSPA: currentSPA })] }) }) }));
|
|
221343
|
+
}
|
|
221344
|
+
/**
|
|
221345
|
+
* Clickable user identity that opens the shared `Profile` viewer in a dialog
|
|
221346
|
+
* — the same modal the tenant Management tab opens when a user row is
|
|
221347
|
+
* clicked. `Profile` drops into preview (read-only) mode on its own for anyone
|
|
221348
|
+
* but the signed-in user. Used by the spend-cap tables and every analytics
|
|
221349
|
+
* surface that names a user; safe inside clickable rows and cards, since
|
|
221350
|
+
* neither its clicks nor its key presses reach the host's handlers.
|
|
221351
|
+
*/
|
|
221352
|
+
function UserProfileLink({ tenantKey, username, display, className, testId, customization, currentSPA, }) {
|
|
221353
|
+
const t = useT();
|
|
221354
|
+
const [isOpen, setIsOpen] = React__default.useState(false);
|
|
221355
|
+
if (!username) {
|
|
221356
|
+
// `inline-block`, or `truncate`'s `overflow: hidden` has nothing to clip:
|
|
221357
|
+
// an inline box takes its width from its own text.
|
|
221358
|
+
return (jsx("span", { className: cn('inline-block max-w-full truncate', className), "data-testid": testId, children: display }));
|
|
221359
|
+
}
|
|
221360
|
+
return (jsxs(Fragment$1, { children: [jsx("button", { type: "button", onClick: (event) => {
|
|
221361
|
+
event.stopPropagation();
|
|
221362
|
+
setIsOpen(true);
|
|
221363
|
+
},
|
|
221364
|
+
// Rows that open on Enter/Space would otherwise swallow the key press.
|
|
221365
|
+
onKeyDown: (event) => event.stopPropagation(), className: cn('max-w-full cursor-pointer truncate text-left hover:text-blue-600 hover:underline focus:text-blue-600 focus:outline-none', className), "aria-label": t('tabsUserstab.viewProfileAriaLabel', { name: display }), "data-testid": testId, children: display }), jsx(UserProfileDialog, { tenantKey: tenantKey, username: username, isOpen: isOpen, onClose: () => setIsOpen(false), customization: customization, currentSPA: currentSPA })] }));
|
|
221366
|
+
}
|
|
221367
|
+
|
|
220520
221368
|
const AdvancedPagination = ({ currentPage, totalPages, onPageChange, totalCount, itemsPerPage, itemLabel = 'items', variant = 'default', className, }) => {
|
|
220521
221369
|
const t = useT();
|
|
220522
221370
|
if (totalPages <= 1) {
|
|
@@ -222427,141 +223275,6 @@ function trimTrailingSlashes(value) {
|
|
|
222427
223275
|
return end === value.length ? value : value.slice(0, end);
|
|
222428
223276
|
}
|
|
222429
223277
|
|
|
222430
|
-
var COLLAPSIBLE_NAME = "Collapsible";
|
|
222431
|
-
var [createCollapsibleContext] = createContextScope$1(COLLAPSIBLE_NAME);
|
|
222432
|
-
var [CollapsibleProvider, useCollapsibleContext] = createCollapsibleContext(COLLAPSIBLE_NAME);
|
|
222433
|
-
var Collapsible$1 = React.forwardRef(
|
|
222434
|
-
(props, forwardedRef) => {
|
|
222435
|
-
const {
|
|
222436
|
-
__scopeCollapsible,
|
|
222437
|
-
open: openProp,
|
|
222438
|
-
defaultOpen,
|
|
222439
|
-
disabled,
|
|
222440
|
-
onOpenChange,
|
|
222441
|
-
...collapsibleProps
|
|
222442
|
-
} = props;
|
|
222443
|
-
const [open, setOpen] = useControllableState$1({
|
|
222444
|
-
prop: openProp,
|
|
222445
|
-
defaultProp: defaultOpen ?? false,
|
|
222446
|
-
onChange: onOpenChange,
|
|
222447
|
-
caller: COLLAPSIBLE_NAME
|
|
222448
|
-
});
|
|
222449
|
-
return /* @__PURE__ */ jsx(
|
|
222450
|
-
CollapsibleProvider,
|
|
222451
|
-
{
|
|
222452
|
-
scope: __scopeCollapsible,
|
|
222453
|
-
disabled,
|
|
222454
|
-
contentId: useId$1(),
|
|
222455
|
-
open,
|
|
222456
|
-
onOpenToggle: React.useCallback(() => setOpen((prevOpen) => !prevOpen), [setOpen]),
|
|
222457
|
-
children: /* @__PURE__ */ jsx(
|
|
222458
|
-
Primitive$2.div,
|
|
222459
|
-
{
|
|
222460
|
-
"data-state": getState$1(open),
|
|
222461
|
-
"data-disabled": disabled ? "" : void 0,
|
|
222462
|
-
...collapsibleProps,
|
|
222463
|
-
ref: forwardedRef
|
|
222464
|
-
}
|
|
222465
|
-
)
|
|
222466
|
-
}
|
|
222467
|
-
);
|
|
222468
|
-
}
|
|
222469
|
-
);
|
|
222470
|
-
Collapsible$1.displayName = COLLAPSIBLE_NAME;
|
|
222471
|
-
var TRIGGER_NAME$2 = "CollapsibleTrigger";
|
|
222472
|
-
var CollapsibleTrigger$1 = React.forwardRef(
|
|
222473
|
-
(props, forwardedRef) => {
|
|
222474
|
-
const { __scopeCollapsible, ...triggerProps } = props;
|
|
222475
|
-
const context = useCollapsibleContext(TRIGGER_NAME$2, __scopeCollapsible);
|
|
222476
|
-
return /* @__PURE__ */ jsx(
|
|
222477
|
-
Primitive$2.button,
|
|
222478
|
-
{
|
|
222479
|
-
type: "button",
|
|
222480
|
-
"aria-controls": context.contentId,
|
|
222481
|
-
"aria-expanded": context.open || false,
|
|
222482
|
-
"data-state": getState$1(context.open),
|
|
222483
|
-
"data-disabled": context.disabled ? "" : void 0,
|
|
222484
|
-
disabled: context.disabled,
|
|
222485
|
-
...triggerProps,
|
|
222486
|
-
ref: forwardedRef,
|
|
222487
|
-
onClick: composeEventHandlers$1(props.onClick, context.onOpenToggle)
|
|
222488
|
-
}
|
|
222489
|
-
);
|
|
222490
|
-
}
|
|
222491
|
-
);
|
|
222492
|
-
CollapsibleTrigger$1.displayName = TRIGGER_NAME$2;
|
|
222493
|
-
var CONTENT_NAME$3 = "CollapsibleContent";
|
|
222494
|
-
var CollapsibleContent$1 = React.forwardRef(
|
|
222495
|
-
(props, forwardedRef) => {
|
|
222496
|
-
const { forceMount, ...contentProps } = props;
|
|
222497
|
-
const context = useCollapsibleContext(CONTENT_NAME$3, props.__scopeCollapsible);
|
|
222498
|
-
return /* @__PURE__ */ jsx(Presence$2, { present: forceMount || context.open, children: ({ present }) => /* @__PURE__ */ jsx(CollapsibleContentImpl, { ...contentProps, ref: forwardedRef, present }) });
|
|
222499
|
-
}
|
|
222500
|
-
);
|
|
222501
|
-
CollapsibleContent$1.displayName = CONTENT_NAME$3;
|
|
222502
|
-
var CollapsibleContentImpl = React.forwardRef((props, forwardedRef) => {
|
|
222503
|
-
const { __scopeCollapsible, present, children, ...contentProps } = props;
|
|
222504
|
-
const context = useCollapsibleContext(CONTENT_NAME$3, __scopeCollapsible);
|
|
222505
|
-
const [isPresent, setIsPresent] = React.useState(present);
|
|
222506
|
-
const ref = React.useRef(null);
|
|
222507
|
-
const composedRefs = useComposedRefs$1(forwardedRef, ref);
|
|
222508
|
-
const heightRef = React.useRef(0);
|
|
222509
|
-
const height = heightRef.current;
|
|
222510
|
-
const widthRef = React.useRef(0);
|
|
222511
|
-
const width = widthRef.current;
|
|
222512
|
-
const isOpen = context.open || isPresent;
|
|
222513
|
-
const isMountAnimationPreventedRef = React.useRef(isOpen);
|
|
222514
|
-
const originalStylesRef = React.useRef(void 0);
|
|
222515
|
-
React.useEffect(() => {
|
|
222516
|
-
const rAF = requestAnimationFrame(() => isMountAnimationPreventedRef.current = false);
|
|
222517
|
-
return () => cancelAnimationFrame(rAF);
|
|
222518
|
-
}, []);
|
|
222519
|
-
useLayoutEffect2$1(() => {
|
|
222520
|
-
const node = ref.current;
|
|
222521
|
-
if (node) {
|
|
222522
|
-
originalStylesRef.current = originalStylesRef.current || {
|
|
222523
|
-
transitionDuration: node.style.transitionDuration,
|
|
222524
|
-
animationName: node.style.animationName
|
|
222525
|
-
};
|
|
222526
|
-
node.style.transitionDuration = "0s";
|
|
222527
|
-
node.style.animationName = "none";
|
|
222528
|
-
const rect = node.getBoundingClientRect();
|
|
222529
|
-
heightRef.current = rect.height;
|
|
222530
|
-
widthRef.current = rect.width;
|
|
222531
|
-
if (!isMountAnimationPreventedRef.current) {
|
|
222532
|
-
node.style.transitionDuration = originalStylesRef.current.transitionDuration;
|
|
222533
|
-
node.style.animationName = originalStylesRef.current.animationName;
|
|
222534
|
-
}
|
|
222535
|
-
setIsPresent(present);
|
|
222536
|
-
}
|
|
222537
|
-
}, [context.open, present]);
|
|
222538
|
-
return /* @__PURE__ */ jsx(
|
|
222539
|
-
Primitive$2.div,
|
|
222540
|
-
{
|
|
222541
|
-
"data-state": getState$1(context.open),
|
|
222542
|
-
"data-disabled": context.disabled ? "" : void 0,
|
|
222543
|
-
id: context.contentId,
|
|
222544
|
-
hidden: !isOpen,
|
|
222545
|
-
...contentProps,
|
|
222546
|
-
ref: composedRefs,
|
|
222547
|
-
style: {
|
|
222548
|
-
[`--radix-collapsible-content-height`]: height ? `${height}px` : void 0,
|
|
222549
|
-
[`--radix-collapsible-content-width`]: width ? `${width}px` : void 0,
|
|
222550
|
-
...props.style
|
|
222551
|
-
},
|
|
222552
|
-
children: isOpen && children
|
|
222553
|
-
}
|
|
222554
|
-
);
|
|
222555
|
-
});
|
|
222556
|
-
function getState$1(open) {
|
|
222557
|
-
return open ? "open" : "closed";
|
|
222558
|
-
}
|
|
222559
|
-
var Root$5 = Collapsible$1;
|
|
222560
|
-
|
|
222561
|
-
const Collapsible = Root$5;
|
|
222562
|
-
const CollapsibleTrigger = CollapsibleTrigger$1;
|
|
222563
|
-
const CollapsibleContent = CollapsibleContent$1;
|
|
222564
|
-
|
|
222565
223278
|
// Pure logic behind the application form (questions) editor. No React here so
|
|
222566
223279
|
// it stays unit testable. The backend validates schemas on save and remains
|
|
222567
223280
|
// authoritative; these helpers only shape the editor's local state.
|
|
@@ -226780,62 +227493,6 @@ function SpendLimitsSection({ tenantKey, labels: labelsOverride }) {
|
|
|
226780
227493
|
return (jsxs(Fragment$1, { children: [jsxs("div", { "data-testid": "spend-limits-workspace-section", className: "rounded-lg border border-[#e5e5e5] bg-white shadow-[0_1px_2px_0_rgba(0,0,0,0.05)] dark:bg-zinc-900 dark:border-zinc-800", children: [jsx("div", { className: "flex flex-col gap-2 p-4 sm:flex-row sm:items-start sm:justify-between", children: jsxs("div", { className: "space-y-0.5", children: [jsx("h3", { className: "text-[16px] font-medium leading-6 text-[#101828] dark:text-gray-100", children: labels.tenantSections.workspace.title }), jsx("p", { className: "text-[13px] font-normal leading-5 text-[#4a5565] dark:text-gray-400", children: labels.tenantSections.workspace.description })] }) }), jsx("div", { className: "border-t border-[#e5e7eb] dark:border-zinc-800 px-4 py-3", children: isTenantCapLoading ? (jsx("div", { className: "flex items-center justify-center py-8", "data-testid": "tenant-cap-loading", children: jsx(Spinner, {}) })) : isTenantForbidden ? (jsxs("div", { className: "flex flex-col items-center gap-2 rounded-lg border border-gray-200 bg-gray-50 px-4 py-10 text-center", "data-testid": "spend-limits-denied", children: [jsx(ShieldAlert, { className: "h-6 w-6 text-gray-400" }), jsx("p", { className: "text-sm font-medium text-gray-900", children: labels.denied.title }), jsx("p", { className: "max-w-sm text-xs text-gray-600", children: labels.denied.description })] })) : (jsxs("div", { className: "space-y-4", children: [!tenantCap && (monthlySpend != null || totalSpend != null) && (jsxs("div", { className: "space-y-1", "data-testid": "workspace-actual-spend-stats", children: [jsxs("div", { className: "grid grid-cols-1 gap-4 sm:grid-cols-2", children: [monthlySpend != null && (jsxs("div", { className: "space-y-0.5", children: [jsx("p", { className: "text-[11px] font-medium uppercase tracking-wide text-[#6a7282] dark:text-gray-400", children: labels.tenantSections.noCapStats.spentThisMonth }), jsx("p", { className: "text-[18px] font-semibold leading-6 text-[#101828] dark:text-gray-100", "data-testid": "workspace-actual-spend-month", children: formatUsd$1(String(monthlySpend)) })] })), totalSpend != null && (jsxs("div", { className: "space-y-0.5", children: [jsx("p", { className: "text-[11px] font-medium uppercase tracking-wide text-[#6a7282] dark:text-gray-400", children: labels.tenantSections.noCapStats.spentAllTime }), jsx("p", { className: "text-[18px] font-semibold leading-6 text-[#101828] dark:text-gray-100", "data-testid": "workspace-actual-spend-total", children: formatUsd$1(String(totalSpend)) })] }))] }), jsx("p", { className: "text-[12px] font-normal leading-4 text-[#6a7282] dark:text-gray-500", children: labels.tenantSections.noCapStats.hint })] })), jsx(SpendCapForm, { labels: labels, cap: tenantCap, isSaving: isSavingTenantCap, isDeleting: isDeletingTenantCap, onSave: saveTenantCap, onDelete: () => setIsDeleteOpen(true), testId: "tenant-cap" })] })) })] }), jsx(Dialog, { open: isDeleteOpen, onOpenChange: () => setIsDeleteOpen(false), children: jsxs(DialogContent, { "aria-describedby": "tenant-cap-delete-description", "data-testid": "tenant-cap-delete-modal", children: [jsx(DialogHeader, { children: jsx(DialogTitle, { className: "ibl-dialog-title", children: labels.deleteModal.title }) }), jsx("div", { className: "my-5", id: "tenant-cap-delete-description", children: jsx("p", { className: "text-sm text-[#646464]", children: labels.deleteModal.description }) }), jsxs(DialogFooter, { className: "gap-3", children: [jsx(Button$1, { variant: "outline", onClick: () => setIsDeleteOpen(false), children: labels.deleteModal.cancel }), jsx(Button$1, { className: "bg-gradient-to-r from-[#2563EB] to-[#93C5FD] text-white hover:opacity-90", onClick: handleConfirmDelete, disabled: isDeletingTenantCap, "data-testid": "tenant-cap-delete-confirm", children: isDeletingTenantCap ? labels.form.removing : labels.deleteModal.confirm })] })] }) })] }));
|
|
226781
227494
|
}
|
|
226782
227495
|
|
|
226783
|
-
/** SPAs whose users have coursework, so the profile leads with a Gradebook tab. */
|
|
226784
|
-
const GRADEBOOK_SPAS = ['lms', 'skills'];
|
|
226785
|
-
/**
|
|
226786
|
-
* The customization `Profile` is opened with: whatever the host passed, with
|
|
226787
|
-
* the Gradebook tab defaulted from the SPA it runs in. An explicit
|
|
226788
|
-
* `showGradebookTab` always wins, so a host can force it either way.
|
|
226789
|
-
*/
|
|
226790
|
-
function profileCustomization(customization, currentSPA) {
|
|
226791
|
-
var _a;
|
|
226792
|
-
return {
|
|
226793
|
-
...customization,
|
|
226794
|
-
showGradebookTab: (_a = customization === null || customization === void 0 ? void 0 : customization.showGradebookTab) !== null && _a !== void 0 ? _a : GRADEBOOK_SPAS.includes((currentSPA !== null && currentSPA !== void 0 ? currentSPA : '').trim().toLowerCase()),
|
|
226795
|
-
};
|
|
226796
|
-
}
|
|
226797
|
-
/**
|
|
226798
|
-
* The shared `Profile` viewer in a dialog — the same modal the tenant
|
|
226799
|
-
* Management tab opens when a user row is clicked. Controlled variant for
|
|
226800
|
-
* hosts that trigger it from a whole table row.
|
|
226801
|
-
*
|
|
226802
|
-
* Radix portals the dialog into `document.body`, but a React portal still
|
|
226803
|
-
* bubbles its events through the React tree — so without the guard below, a
|
|
226804
|
-
* click *inside* the open profile would reach the row this dialog is rendered
|
|
226805
|
-
* from and fire its `onClick` (e.g. opening the enrollment progress modal
|
|
226806
|
-
* behind the profile). The wrapper is `display: contents`, so it adds nothing
|
|
226807
|
-
* to the host's layout.
|
|
226808
|
-
*/
|
|
226809
|
-
function UserProfileDialog({ tenantKey, username, isOpen, onClose, customization, currentSPA, }) {
|
|
226810
|
-
const t = useT();
|
|
226811
|
-
if (!isOpen)
|
|
226812
|
-
return null;
|
|
226813
|
-
return (jsx("span", { className: "contents", onClick: (event) => event.stopPropagation(), onMouseDown: (event) => event.stopPropagation(), onKeyDown: (event) => event.stopPropagation(), children: jsx(Dialog, { open: true, onOpenChange: onClose, children: jsxs(DialogContent, { autoFocus: false, className: "profile-component-dialog max-w-7xl w-[85vw] gap-0 overflow-hidden bg-white p-0", style: { height: '85vh', display: 'flex', flexDirection: 'column' }, "data-testid": "user-profile-link-dialog", children: [jsx(DialogTitle, { className: "sr-only", children: t('tabsUserstab.profileDialogTitle') }), jsx(DialogDescription, { className: "sr-only", children: t('tabsUserstab.profileDialogDescription') }), jsx(Profile, { tenant: tenantKey, tenants: [], username: username, onClose: onClose, customization: profileCustomization(customization, currentSPA), currentSPA: currentSPA })] }) }) }));
|
|
226814
|
-
}
|
|
226815
|
-
/**
|
|
226816
|
-
* Clickable user identity that opens the shared `Profile` viewer in a dialog
|
|
226817
|
-
* — the same modal the tenant Management tab opens when a user row is
|
|
226818
|
-
* clicked. `Profile` drops into preview (read-only) mode on its own for anyone
|
|
226819
|
-
* but the signed-in user. Used by the spend-cap tables and every analytics
|
|
226820
|
-
* surface that names a user; safe inside clickable rows and cards, since
|
|
226821
|
-
* neither its clicks nor its key presses reach the host's handlers.
|
|
226822
|
-
*/
|
|
226823
|
-
function UserProfileLink({ tenantKey, username, display, className, testId, customization, currentSPA, }) {
|
|
226824
|
-
const t = useT();
|
|
226825
|
-
const [isOpen, setIsOpen] = React__default.useState(false);
|
|
226826
|
-
if (!username) {
|
|
226827
|
-
// `inline-block`, or `truncate`'s `overflow: hidden` has nothing to clip:
|
|
226828
|
-
// an inline box takes its width from its own text.
|
|
226829
|
-
return (jsx("span", { className: cn('inline-block max-w-full truncate', className), "data-testid": testId, children: display }));
|
|
226830
|
-
}
|
|
226831
|
-
return (jsxs(Fragment$1, { children: [jsx("button", { type: "button", onClick: (event) => {
|
|
226832
|
-
event.stopPropagation();
|
|
226833
|
-
setIsOpen(true);
|
|
226834
|
-
},
|
|
226835
|
-
// Rows that open on Enter/Space would otherwise swallow the key press.
|
|
226836
|
-
onKeyDown: (event) => event.stopPropagation(), className: cn('max-w-full cursor-pointer truncate text-left hover:text-blue-600 hover:underline focus:text-blue-600 focus:outline-none', className), "aria-label": t('tabsUserstab.viewProfileAriaLabel', { name: display }), "data-testid": testId, children: display }), jsx(UserProfileDialog, { tenantKey: tenantKey, username: username, isOpen: isOpen, onClose: () => setIsOpen(false), customization: customization, currentSPA: currentSPA })] }));
|
|
226837
|
-
}
|
|
226838
|
-
|
|
226839
227496
|
/**
|
|
226840
227497
|
* Add/edit dialog for one per-user spend limit, mirroring the grader
|
|
226841
227498
|
* criterion modal: create mode starts with a searchable platform-user picker
|
|
@@ -247096,7 +247753,7 @@ function inputRulesPlugin(props) {
|
|
|
247096
247753
|
function getType(value) {
|
|
247097
247754
|
return Object.prototype.toString.call(value).slice(8, -1);
|
|
247098
247755
|
}
|
|
247099
|
-
function isPlainObject(value) {
|
|
247756
|
+
function isPlainObject$1(value) {
|
|
247100
247757
|
if (getType(value) !== "Object") {
|
|
247101
247758
|
return false;
|
|
247102
247759
|
}
|
|
@@ -247106,9 +247763,9 @@ function isPlainObject(value) {
|
|
|
247106
247763
|
// src/utilities/mergeDeep.ts
|
|
247107
247764
|
function mergeDeep(target, source) {
|
|
247108
247765
|
const output = { ...target };
|
|
247109
|
-
if (isPlainObject(target) && isPlainObject(source)) {
|
|
247766
|
+
if (isPlainObject$1(target) && isPlainObject$1(source)) {
|
|
247110
247767
|
Object.keys(source).forEach((key) => {
|
|
247111
|
-
if (isPlainObject(source[key]) && isPlainObject(target[key])) {
|
|
247768
|
+
if (isPlainObject$1(source[key]) && isPlainObject$1(target[key])) {
|
|
247112
247769
|
output[key] = mergeDeep(target[key], source[key]);
|
|
247113
247770
|
} else {
|
|
247114
247771
|
output[key] = source[key];
|
|
@@ -258125,6 +258782,492 @@ AnalyticsScopeContext.Provider;
|
|
|
258125
258782
|
|
|
258126
258783
|
Array.from({ length: 24 }, (_, i) => i);
|
|
258127
258784
|
|
|
258785
|
+
const asArray = (value) => (Array.isArray(value) ? value : []);
|
|
258786
|
+
const hasEntries = (value) => !!value && typeof value === 'object' && !Array.isArray(value) && Object.keys(value).length > 0;
|
|
258787
|
+
/** Rollup counts as the conversation-list endpoint reports them: numbers, numeric strings or `null`. */
|
|
258788
|
+
const toRollupCount = (value) => {
|
|
258789
|
+
const count = typeof value === 'string' ? Number(value) : (value !== null && value !== void 0 ? value : 0);
|
|
258790
|
+
return Number.isFinite(count) && count > 0 ? Math.floor(count) : 0;
|
|
258791
|
+
};
|
|
258792
|
+
/**
|
|
258793
|
+
* Sum the retrieved documents and tool calls over a conversation's turns, for
|
|
258794
|
+
* surfaces (the admin chat-history list) that carry the turns inline but have
|
|
258795
|
+
* no server-side rollup fields.
|
|
258796
|
+
*/
|
|
258797
|
+
function summarizeTranscriptTurns(turns) {
|
|
258798
|
+
return asArray(turns).reduce((rollup, turn) => ({
|
|
258799
|
+
documentsCount: rollup.documentsCount + asArray(turn === null || turn === void 0 ? void 0 : turn.documents).length,
|
|
258800
|
+
toolCallsCount: rollup.toolCallsCount + asArray(turn === null || turn === void 0 ? void 0 : turn.tool_calls).length,
|
|
258801
|
+
}), { documentsCount: 0, toolCallsCount: 0 });
|
|
258802
|
+
}
|
|
258803
|
+
/** A transcript's retrieved documents in the shape the chat's documents dialog lists. */
|
|
258804
|
+
function toRetrievedDocuments(documents) {
|
|
258805
|
+
return documents.map((doc) => {
|
|
258806
|
+
var _a, _b, _c, _d;
|
|
258807
|
+
return ({
|
|
258808
|
+
title: (_b = (_a = doc.title) !== null && _a !== void 0 ? _a : doc.source) !== null && _b !== void 0 ? _b : null,
|
|
258809
|
+
snippet: (_c = doc.snippet) !== null && _c !== void 0 ? _c : null,
|
|
258810
|
+
source: (_d = doc.source) !== null && _d !== void 0 ? _d : null,
|
|
258811
|
+
score: typeof doc.score === 'number' ? doc.score : null,
|
|
258812
|
+
});
|
|
258813
|
+
});
|
|
258814
|
+
}
|
|
258815
|
+
/**
|
|
258816
|
+
* Every document a conversation retrieved, across its turns, exactly as the
|
|
258817
|
+
* backend reports them — a source retrieved again on a later turn appears
|
|
258818
|
+
* again, so the count matches the backend's per-conversation totals. Comes
|
|
258819
|
+
* from the admin history payload itself, so it needs no per-user session
|
|
258820
|
+
* lookup.
|
|
258821
|
+
*/
|
|
258822
|
+
function conversationDocuments(turns) {
|
|
258823
|
+
return toRetrievedDocuments(asArray(turns).flatMap((turn) => asArray(turn === null || turn === void 0 ? void 0 : turn.documents)));
|
|
258824
|
+
}
|
|
258825
|
+
const asText = (value) => {
|
|
258826
|
+
if (value === null || value === undefined)
|
|
258827
|
+
return '';
|
|
258828
|
+
if (typeof value === 'string')
|
|
258829
|
+
return value;
|
|
258830
|
+
if (typeof value === 'number' || typeof value === 'boolean')
|
|
258831
|
+
return String(value);
|
|
258832
|
+
try {
|
|
258833
|
+
return JSON.stringify(value, null, 2);
|
|
258834
|
+
}
|
|
258835
|
+
catch (_a) {
|
|
258836
|
+
return String(value);
|
|
258837
|
+
}
|
|
258838
|
+
};
|
|
258839
|
+
/** A transcript's tool calls in the shape the chat's "Used N tools" record renders. */
|
|
258840
|
+
function toToolCallInfos(toolCalls) {
|
|
258841
|
+
return toolCalls.map((call, index) => {
|
|
258842
|
+
var _a;
|
|
258843
|
+
return ({
|
|
258844
|
+
id: `transcript-tool-${index}`,
|
|
258845
|
+
name: (_a = call.name) !== null && _a !== void 0 ? _a : '',
|
|
258846
|
+
input: call.input && typeof call.input === 'object' && !Array.isArray(call.input)
|
|
258847
|
+
? call.input
|
|
258848
|
+
: typeof call.input === 'string'
|
|
258849
|
+
? { input: call.input }
|
|
258850
|
+
: undefined,
|
|
258851
|
+
log: '',
|
|
258852
|
+
result: asText(call.output),
|
|
258853
|
+
});
|
|
258854
|
+
});
|
|
258855
|
+
}
|
|
258856
|
+
/** Whether a turn carries anything worth a details panel. */
|
|
258857
|
+
function hasTranscriptTurnDetails(turn) {
|
|
258858
|
+
if (!turn)
|
|
258859
|
+
return false;
|
|
258860
|
+
return (asArray(turn.documents).length > 0 ||
|
|
258861
|
+
asArray(turn.tool_calls).length > 0 ||
|
|
258862
|
+
hasEntries(turn.metadata) ||
|
|
258863
|
+
hasEntries(turn.request_context));
|
|
258864
|
+
}
|
|
258865
|
+
const isPlainObject = (value) => !!value && typeof value === 'object' && !Array.isArray(value);
|
|
258866
|
+
/** Nothing worth a row: null, blank strings, empty arrays and empty objects. */
|
|
258867
|
+
const isPresent = (value) => {
|
|
258868
|
+
if (value === null || value === undefined)
|
|
258869
|
+
return false;
|
|
258870
|
+
if (typeof value === 'string')
|
|
258871
|
+
return value.trim() !== '';
|
|
258872
|
+
if (Array.isArray(value))
|
|
258873
|
+
return value.length > 0;
|
|
258874
|
+
if (isPlainObject(value))
|
|
258875
|
+
return Object.keys(value).length > 0;
|
|
258876
|
+
return true;
|
|
258877
|
+
};
|
|
258878
|
+
const FIELD_ABBREVIATIONS = new Set([
|
|
258879
|
+
'ai',
|
|
258880
|
+
'api',
|
|
258881
|
+
'css',
|
|
258882
|
+
'csv',
|
|
258883
|
+
'html',
|
|
258884
|
+
'http',
|
|
258885
|
+
'https',
|
|
258886
|
+
'id',
|
|
258887
|
+
'ids',
|
|
258888
|
+
'ip',
|
|
258889
|
+
'json',
|
|
258890
|
+
'llm',
|
|
258891
|
+
'lti',
|
|
258892
|
+
'mcp',
|
|
258893
|
+
'mime',
|
|
258894
|
+
'pdf',
|
|
258895
|
+
'rag',
|
|
258896
|
+
'sso',
|
|
258897
|
+
'stt',
|
|
258898
|
+
'tts',
|
|
258899
|
+
'ui',
|
|
258900
|
+
'uri',
|
|
258901
|
+
'url',
|
|
258902
|
+
'urls',
|
|
258903
|
+
'uuid',
|
|
258904
|
+
]);
|
|
258905
|
+
/**
|
|
258906
|
+
* "file_references" → "File References", "llmModel" → "LLM Model". Backend
|
|
258907
|
+
* keys are never shown raw: every unknown key goes through this, and the
|
|
258908
|
+
* common ones get a translated label on top (see `useFieldLabels`).
|
|
258909
|
+
*/
|
|
258910
|
+
function humanizeFieldName(key) {
|
|
258911
|
+
const words = key
|
|
258912
|
+
.replace(/([a-z0-9])([A-Z])/g, '$1 $2')
|
|
258913
|
+
.replace(/([A-Z]+)([A-Z][a-z])/g, '$1 $2')
|
|
258914
|
+
.toLowerCase()
|
|
258915
|
+
.split(/[^a-z0-9]+/)
|
|
258916
|
+
.filter(Boolean);
|
|
258917
|
+
if (words.length === 0)
|
|
258918
|
+
return key;
|
|
258919
|
+
return words
|
|
258920
|
+
.map((word) => FIELD_ABBREVIATIONS.has(word)
|
|
258921
|
+
? word.toUpperCase()
|
|
258922
|
+
: word.charAt(0).toUpperCase() + word.slice(1))
|
|
258923
|
+
.join(' ');
|
|
258924
|
+
}
|
|
258925
|
+
/** Translated labels for the keys the backend is known to send. */
|
|
258926
|
+
function useFieldLabels() {
|
|
258927
|
+
const t = useT();
|
|
258928
|
+
const known = {
|
|
258929
|
+
llm_model: t('analyticsTranscriptTurnDetails.model'),
|
|
258930
|
+
llm_provider: t('analyticsTranscriptTurnDetails.provider'),
|
|
258931
|
+
llm_temperature: t('analyticsTranscriptTurnDetails.temperature'),
|
|
258932
|
+
model: t('analyticsTranscriptTurnDetails.model'),
|
|
258933
|
+
provider: t('analyticsTranscriptTurnDetails.provider'),
|
|
258934
|
+
temperature: t('analyticsTranscriptTurnDetails.temperature'),
|
|
258935
|
+
client: t('analyticsTranscriptTurnDetails.client'),
|
|
258936
|
+
session_id: t('analyticsTranscriptTurnDetails.fieldSession'),
|
|
258937
|
+
// The product's own names: artifacts are "Canvas" in the chat, proactivity
|
|
258938
|
+
// is the "Proactive Prompt", grading is the "Grader" tab, document filters
|
|
258939
|
+
// are "Datasets".
|
|
258940
|
+
enable_artifacts: t('analyticsTranscriptTurnDetails.fieldCanvas'),
|
|
258941
|
+
artifact: t('analyticsTranscriptTurnDetails.fieldCanvas'),
|
|
258942
|
+
artifacts: t('analyticsTranscriptTurnDetails.fieldCanvas'),
|
|
258943
|
+
is_proactive: t('analyticsTranscriptTurnDetails.fieldProactivePrompt'),
|
|
258944
|
+
grading_context: t('analyticsTranscriptTurnDetails.fieldGrader'),
|
|
258945
|
+
document_filter: t('analyticsTranscriptTurnDetails.fieldDatasets'),
|
|
258946
|
+
current_datetime: t('analyticsTranscriptTurnDetails.fieldDateTime'),
|
|
258947
|
+
caller_verified: t('analyticsTranscriptTurnDetails.fieldCallerVerified'),
|
|
258948
|
+
file_references: t('analyticsTranscriptTurnDetails.fieldFiles'),
|
|
258949
|
+
file_id: t('analyticsTranscriptTurnDetails.fieldFile'),
|
|
258950
|
+
system_prompt: t('analyticsTranscriptTurnDetails.fieldSystemPrompt'),
|
|
258951
|
+
flow: t('analyticsTranscriptTurnDetails.fieldFlow'),
|
|
258952
|
+
token: t('analyticsTranscriptTurnDetails.fieldToken'),
|
|
258953
|
+
tenant: t('analyticsTranscriptTurnDetails.fieldTenant'),
|
|
258954
|
+
pathway: t('analyticsTranscriptTurnDetails.fieldPathway'),
|
|
258955
|
+
username: t('analyticsTranscriptTurnDetails.fieldUsername'),
|
|
258956
|
+
name: t('analyticsTranscriptTurnDetails.fieldName'),
|
|
258957
|
+
};
|
|
258958
|
+
return (key) => { var _a; return (_a = known[key]) !== null && _a !== void 0 ? _a : humanizeFieldName(key); };
|
|
258959
|
+
}
|
|
258960
|
+
const fileName = (value) => {
|
|
258961
|
+
var _a, _b, _c, _d;
|
|
258962
|
+
const name = (_b = (_a = value.file_name) !== null && _a !== void 0 ? _a : value.filename) !== null && _b !== void 0 ? _b : value.name;
|
|
258963
|
+
if (typeof name === 'string' && name.trim())
|
|
258964
|
+
return name.trim();
|
|
258965
|
+
const key = (_d = (_c = value.file_key) !== null && _c !== void 0 ? _c : value.key) !== null && _d !== void 0 ? _d : value.path;
|
|
258966
|
+
if (typeof key === 'string' && key.trim())
|
|
258967
|
+
return key.trim().split('/').pop() || key.trim();
|
|
258968
|
+
return undefined;
|
|
258969
|
+
};
|
|
258970
|
+
/** Backend payloads name files in a few shapes; anything with a name plus a file-ish fact counts. */
|
|
258971
|
+
function isFileReference(value) {
|
|
258972
|
+
if (!isPlainObject(value) || !fileName(value))
|
|
258973
|
+
return false;
|
|
258974
|
+
return ['file_id', 'file_key', 'file_size', 'content_type', 'mime_type', 'url', 'size'].some((key) => key in value && value[key] !== null && value[key] !== undefined);
|
|
258975
|
+
}
|
|
258976
|
+
const attachmentUrl = (name, attachments) => { var _a; return (_a = attachments === null || attachments === void 0 ? void 0 : attachments.find((file) => (file === null || file === void 0 ? void 0 : file.name) === name && !!file.url)) === null || _a === void 0 ? void 0 : _a.url; };
|
|
258977
|
+
/** Normalise a file reference; the turn's attachments lend their presigned `url` to a same-named file. */
|
|
258978
|
+
function toFileReference(value, attachments) {
|
|
258979
|
+
var _a, _b, _c, _d, _e, _f;
|
|
258980
|
+
const name = (_a = fileName(value)) !== null && _a !== void 0 ? _a : '';
|
|
258981
|
+
const contentType = (_c = (_b = value.content_type) !== null && _b !== void 0 ? _b : value.mime_type) !== null && _c !== void 0 ? _c : value.type;
|
|
258982
|
+
const size = (_d = value.file_size) !== null && _d !== void 0 ? _d : value.size;
|
|
258983
|
+
const own = (_f = (_e = value.url) !== null && _e !== void 0 ? _e : value.download_url) !== null && _f !== void 0 ? _f : value.href;
|
|
258984
|
+
const numericSize = typeof size === 'number'
|
|
258985
|
+
? size
|
|
258986
|
+
: typeof size === 'string' && size.trim() && Number.isFinite(Number(size))
|
|
258987
|
+
? Number(size)
|
|
258988
|
+
: undefined;
|
|
258989
|
+
return {
|
|
258990
|
+
name,
|
|
258991
|
+
contentType: typeof contentType === 'string' ? contentType : undefined,
|
|
258992
|
+
size: numericSize,
|
|
258993
|
+
url: typeof own === 'string' && own ? own : attachmentUrl(name, attachments),
|
|
258994
|
+
};
|
|
258995
|
+
}
|
|
258996
|
+
const REDACTED_PATTERN = /^\[?(redacted|hidden|masked|removed)\]?$|^\*{3,}$/i;
|
|
258997
|
+
const UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
258998
|
+
/** Past this depth nested objects flatten to "key: value" text rather than nesting further. */
|
|
258999
|
+
const MAX_NESTING = 4;
|
|
259000
|
+
const formatPrimitive = (value) => {
|
|
259001
|
+
if (value === null || value === undefined)
|
|
259002
|
+
return '';
|
|
259003
|
+
if (typeof value === 'string')
|
|
259004
|
+
return value;
|
|
259005
|
+
if (typeof value === 'number')
|
|
259006
|
+
return Number.isInteger(value) ? String(value) : String(value);
|
|
259007
|
+
return String(value);
|
|
259008
|
+
};
|
|
259009
|
+
/** Deeply nested leftovers as readable "Key: value" text — never JSON. */
|
|
259010
|
+
function compactText(value, labelFor) {
|
|
259011
|
+
if (Array.isArray(value)) {
|
|
259012
|
+
return value
|
|
259013
|
+
.filter(isPresent)
|
|
259014
|
+
.map((item) => compactText(item, labelFor))
|
|
259015
|
+
.join('; ');
|
|
259016
|
+
}
|
|
259017
|
+
if (isPlainObject(value)) {
|
|
259018
|
+
return Object.entries(value)
|
|
259019
|
+
.filter(([, v]) => isPresent(v))
|
|
259020
|
+
.map(([k, v]) => `${labelFor(k)}: ${compactText(v, labelFor)}`)
|
|
259021
|
+
.join(', ');
|
|
259022
|
+
}
|
|
259023
|
+
return formatPrimitive(value);
|
|
259024
|
+
}
|
|
259025
|
+
function BooleanValue({ value }) {
|
|
259026
|
+
const t = useT();
|
|
259027
|
+
return (jsx("span", { className: cn('inline-flex items-center rounded-full px-2 py-0.5 text-xs font-medium', value ? 'bg-green-50 text-green-700' : 'bg-gray-100 text-gray-600'), children: value ? t('analyticsTranscriptTurnDetails.yes') : t('analyticsTranscriptTurnDetails.no') }));
|
|
259028
|
+
}
|
|
259029
|
+
function StringValue({ value }) {
|
|
259030
|
+
const t = useT();
|
|
259031
|
+
const text = value.trim();
|
|
259032
|
+
if (REDACTED_PATTERN.test(text)) {
|
|
259033
|
+
return (jsx("span", { className: "text-gray-400 italic", children: t('analyticsTranscriptTurnDetails.redacted') }));
|
|
259034
|
+
}
|
|
259035
|
+
if (UUID_PATTERN.test(text)) {
|
|
259036
|
+
return jsx("span", { className: "font-mono text-xs break-all text-gray-700", children: text });
|
|
259037
|
+
}
|
|
259038
|
+
return (jsx("span", { className: "block max-h-48 overflow-y-auto break-words whitespace-pre-line", children: text }));
|
|
259039
|
+
}
|
|
259040
|
+
const DetailCtx = React__default.createContext({ labelFor: humanizeFieldName });
|
|
259041
|
+
function DetailValue({ value, depth, labelFor, }) {
|
|
259042
|
+
var _a, _b;
|
|
259043
|
+
const ctx = React__default.useContext(DetailCtx);
|
|
259044
|
+
if (typeof value === 'boolean')
|
|
259045
|
+
return jsx(BooleanValue, { value: value });
|
|
259046
|
+
if (typeof value === 'number')
|
|
259047
|
+
return jsx("span", { className: "tabular-nums", children: formatPrimitive(value) });
|
|
259048
|
+
if (typeof value === 'string')
|
|
259049
|
+
return jsx(StringValue, { value: value });
|
|
259050
|
+
if (depth >= MAX_NESTING) {
|
|
259051
|
+
return jsx("span", { className: "break-words", children: compactText(value, labelFor) });
|
|
259052
|
+
}
|
|
259053
|
+
if (isFileReference(value)) {
|
|
259054
|
+
const files = [toFileReference(value, ctx.attachments)];
|
|
259055
|
+
return jsx(Fragment$1, { children: ((_a = ctx.renderFiles) !== null && _a !== void 0 ? _a : defaultRenderFiles)(files) });
|
|
259056
|
+
}
|
|
259057
|
+
if (Array.isArray(value)) {
|
|
259058
|
+
const items = value.filter(isPresent);
|
|
259059
|
+
if (items.length === 0)
|
|
259060
|
+
return null;
|
|
259061
|
+
if (items.every(isFileReference)) {
|
|
259062
|
+
const files = items.map((item) => toFileReference(item, ctx.attachments));
|
|
259063
|
+
return jsx(Fragment$1, { children: ((_b = ctx.renderFiles) !== null && _b !== void 0 ? _b : defaultRenderFiles)(files) });
|
|
259064
|
+
}
|
|
259065
|
+
const allPrimitive = items.every((item) => !isPlainObject(item) && !Array.isArray(item));
|
|
259066
|
+
if (allPrimitive) {
|
|
259067
|
+
return (jsx("span", { className: "flex flex-wrap gap-1", children: items.map((item, index) => (jsx(Badge, { variant: "secondary", className: "max-w-full font-normal break-all whitespace-normal", children: typeof item === 'boolean' ? jsx(BooleanValue, { value: item }) : formatPrimitive(item) }, index))) }));
|
|
259068
|
+
}
|
|
259069
|
+
return (jsx("ol", { className: "space-y-2", children: items.map((item, index) => (jsxs("li", { className: "flex gap-2", children: [jsxs("span", { className: "mt-1 text-xs font-medium text-gray-400 tabular-nums", children: [index + 1, "."] }), jsx("div", { className: "min-w-0 flex-1 rounded-md border border-gray-200 px-3 py-1", children: isPlainObject(item) ? (jsx(DetailRows, { entries: item, depth: depth + 1, labelFor: labelFor })) : (jsx(DetailValue, { value: item, depth: depth + 1, labelFor: labelFor })) })] }, index))) }));
|
|
259070
|
+
}
|
|
259071
|
+
if (isPlainObject(value)) {
|
|
259072
|
+
return (jsx("div", { className: "border-l-2 border-gray-200 pl-3", children: jsx(DetailRows, { entries: value, depth: depth + 1, labelFor: labelFor }) }));
|
|
259073
|
+
}
|
|
259074
|
+
return jsx("span", { children: formatPrimitive(value) });
|
|
259075
|
+
}
|
|
259076
|
+
/**
|
|
259077
|
+
* Label/value rows for a metadata-style object. Keys are humanized (never
|
|
259078
|
+
* shown raw), blanks are skipped, booleans read Yes/No, lists become chips or
|
|
259079
|
+
* numbered cards, files become the chat's file cards and nested objects
|
|
259080
|
+
* indent — the same read-only detail style as the audit-log sheet, so nothing
|
|
259081
|
+
* here ever looks like a JSON dump.
|
|
259082
|
+
*/
|
|
259083
|
+
function DetailRows({ entries, depth = 0, labelFor, }) {
|
|
259084
|
+
const rows = Object.entries(entries).filter(([, value]) => isPresent(value));
|
|
259085
|
+
if (rows.length === 0)
|
|
259086
|
+
return null;
|
|
259087
|
+
const top = depth === 0;
|
|
259088
|
+
return (jsx("dl", { className: cn(top ? 'divide-y divide-gray-100' : 'space-y-1'), children: rows.map(([key, value]) => (jsxs("div", { className: cn('grid gap-x-3', top
|
|
259089
|
+
? 'grid-cols-[minmax(0,7rem)_minmax(0,1fr)] py-2'
|
|
259090
|
+
: 'grid-cols-[minmax(0,6rem)_minmax(0,1fr)] py-0.5'), children: [jsx("dt", { className: cn('break-words text-gray-500', top ? 'text-xs font-medium tracking-wide uppercase' : 'text-xs font-medium'), children: labelFor(key) }), jsx("dd", { className: "min-w-0 text-sm text-gray-800", children: jsx(DetailValue, { value: value, depth: depth, labelFor: labelFor }) })] }, key))) }));
|
|
259091
|
+
}
|
|
259092
|
+
function SectionHeading({ icon, children }) {
|
|
259093
|
+
return (jsxs("h4", { className: "mb-2 flex items-center gap-1.5 text-xs font-semibold tracking-wide text-gray-700 uppercase", children: [icon, children] }));
|
|
259094
|
+
}
|
|
259095
|
+
const MODEL_KEYS = new Set(['llm_model', 'llm_provider']);
|
|
259096
|
+
function MetadataSection({ metadata, renderModel, }) {
|
|
259097
|
+
var _a, _b;
|
|
259098
|
+
const t = useT();
|
|
259099
|
+
const labelFor = useFieldLabels();
|
|
259100
|
+
const rest = Object.fromEntries(Object.entries(metadata).filter(([key]) => !MODEL_KEYS.has(key)));
|
|
259101
|
+
const model = ((_a = metadata.llm_model) !== null && _a !== void 0 ? _a : '').trim() || undefined;
|
|
259102
|
+
const provider = ((_b = metadata.llm_provider) !== null && _b !== void 0 ? _b : '').trim() || undefined;
|
|
259103
|
+
const hasModel = !!(model || provider);
|
|
259104
|
+
return (jsxs("section", { "data-testid": "transcript-turn-metadata", children: [jsx(SectionHeading, { icon: jsx(Info$3, { className: "h-3.5 w-3.5", "aria-hidden": "true" }), children: t('analyticsTranscriptTurnDetails.metadata') }), hasModel && (jsx("div", { "data-testid": "transcript-turn-model", className: "mb-2", children: (renderModel !== null && renderModel !== void 0 ? renderModel : defaultRenderModel)({ model, provider }) })), jsx(DetailRows, { entries: rest, labelFor: labelFor })] }));
|
|
259105
|
+
}
|
|
259106
|
+
function RequestContextSection({ requestContext }) {
|
|
259107
|
+
const t = useT();
|
|
259108
|
+
const labelFor = useFieldLabels();
|
|
259109
|
+
return (jsxs("section", { "data-testid": "transcript-turn-request-context", children: [jsx(SectionHeading, { icon: jsx(Info$3, { className: "h-3.5 w-3.5", "aria-hidden": "true" }), children: t('analyticsTranscriptTurnDetails.requestContext') }), jsx(DetailRows, { entries: requestContext, labelFor: labelFor })] }));
|
|
259110
|
+
}
|
|
259111
|
+
/**
|
|
259112
|
+
* The panel's defaults are the chat's own components, so admins see exactly
|
|
259113
|
+
* what the conversation shows: the "Retrieved Documents" dialog button, the
|
|
259114
|
+
* "Used N tools" record (with results), the nav-bar model badge and the
|
|
259115
|
+
* attachment file cards.
|
|
259116
|
+
*/
|
|
259117
|
+
const defaultRenderDocuments = (documents) => (jsx(RetrievedDocumentsButton, { documents: toRetrievedDocuments(documents) }));
|
|
259118
|
+
const defaultRenderToolCalls = (toolCalls) => (jsx(ToolCallIndicator, { toolCalls: toToolCallInfos(toolCalls), showResults: true }));
|
|
259119
|
+
const defaultRenderModel = ({ model, provider }) => (jsx(LlmModelBadge, { model: model, provider: provider }));
|
|
259120
|
+
const defaultRenderFiles = (files) => (jsx(TranscriptFileCards, { files: files }));
|
|
259121
|
+
/**
|
|
259122
|
+
* Collapsible "Sources / Tools / Metadata" panel for one AI turn of an admin
|
|
259123
|
+
* transcript (Perplexity/Claude-style citations). Driven by the extended
|
|
259124
|
+
* per-turn context the backend attaches on admin surfaces — the analytics
|
|
259125
|
+
* `messages/details/` endpoint and the admin `chat-history/` list — and
|
|
259126
|
+
* renders nothing at all when every field is null or empty, so it is safe to
|
|
259127
|
+
* drop under every AI bubble.
|
|
259128
|
+
*/
|
|
259129
|
+
function TranscriptTurnDetails({ turn, className, defaultOpen = false, renderDocuments, renderToolCalls, renderModel, renderFiles, }) {
|
|
259130
|
+
const t = useT();
|
|
259131
|
+
const labelFor = useFieldLabels();
|
|
259132
|
+
const [open, setOpen] = React__default.useState(defaultOpen);
|
|
259133
|
+
const attachments = React__default.useMemo(() => [...asArray(turn === null || turn === void 0 ? void 0 : turn.human_files), ...asArray(turn === null || turn === void 0 ? void 0 : turn.ai_files)], [turn === null || turn === void 0 ? void 0 : turn.human_files, turn === null || turn === void 0 ? void 0 : turn.ai_files]);
|
|
259134
|
+
const detailContext = React__default.useMemo(() => ({ labelFor, attachments, renderFiles }), [labelFor, attachments, renderFiles]);
|
|
259135
|
+
if (!hasTranscriptTurnDetails(turn))
|
|
259136
|
+
return null;
|
|
259137
|
+
const documents = asArray(turn === null || turn === void 0 ? void 0 : turn.documents);
|
|
259138
|
+
const toolCalls = asArray(turn === null || turn === void 0 ? void 0 : turn.tool_calls);
|
|
259139
|
+
const metadata = hasEntries(turn === null || turn === void 0 ? void 0 : turn.metadata) ? turn === null || turn === void 0 ? void 0 : turn.metadata : null;
|
|
259140
|
+
const requestContext = hasEntries(turn === null || turn === void 0 ? void 0 : turn.request_context) ? turn === null || turn === void 0 ? void 0 : turn.request_context : null;
|
|
259141
|
+
return (jsx(DetailCtx.Provider, { value: detailContext, children: jsxs(Collapsible, { open: open, onOpenChange: setOpen, className: cn('mt-2', className), "data-testid": "transcript-turn-details", children: [jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [jsx(CollapsibleTrigger, { asChild: true, children: jsxs(Button$1, { type: "button", variant: "outline", size: "sm", className: "flex items-center gap-2 border-gray-200 bg-white text-gray-700 hover:bg-gray-50", "aria-expanded": open, children: [jsx(ChevronDown, { className: cn('h-4 w-4 transition-transform', open && 'rotate-180'), "aria-hidden": "true" }), open
|
|
259142
|
+
? t('analyticsTranscriptTurnDetails.hideDetails')
|
|
259143
|
+
: t('analyticsTranscriptTurnDetails.showDetails')] }) }), documents.length > 0 && (jsx("div", { "data-testid": "transcript-turn-sources", className: "contents", children: (renderDocuments !== null && renderDocuments !== void 0 ? renderDocuments : defaultRenderDocuments)(documents) })), jsx(TranscriptRollupBadges, { documentsCount: 0, toolCallsCount: toolCalls.length })] }), jsxs(CollapsibleContent, { className: "mt-2 space-y-5 rounded-lg border border-gray-200 bg-white p-3", children: [toolCalls.length > 0 && (jsx("div", { "data-testid": "transcript-turn-tools", children: (renderToolCalls !== null && renderToolCalls !== void 0 ? renderToolCalls : defaultRenderToolCalls)(toolCalls) })), metadata && jsx(MetadataSection, { metadata: metadata, renderModel: renderModel }), requestContext && jsx(RequestContextSection, { requestContext: requestContext })] })] }) }));
|
|
259144
|
+
}
|
|
259145
|
+
/**
|
|
259146
|
+
* "Documents · N" / "Tools · N" chips for a conversation row, from the
|
|
259147
|
+
* conversation-list rollup fields (or a client-side `summarizeTranscriptTurns`).
|
|
259148
|
+
* A chip is hidden when its count is zero; nothing renders when both are.
|
|
259149
|
+
*/
|
|
259150
|
+
function TranscriptRollupBadges({ documentsCount, toolCallsCount, className, }) {
|
|
259151
|
+
const t = useT();
|
|
259152
|
+
const documents = toRollupCount(documentsCount);
|
|
259153
|
+
const tools = toRollupCount(toolCallsCount);
|
|
259154
|
+
if (documents === 0 && tools === 0)
|
|
259155
|
+
return null;
|
|
259156
|
+
return (jsxs("span", { className: cn('inline-flex flex-wrap items-center gap-1.5', className), "data-testid": "transcript-rollup-badges", children: [documents > 0 && (jsxs(Badge, { variant: "outline", className: "gap-1 border-transparent bg-blue-50 font-normal text-blue-700", "data-testid": "transcript-sources-badge", children: [jsx(FileText, { className: "h-3 w-3", "aria-hidden": "true" }), t('analyticsTranscriptTurnDetails.sourcesBadge', { count: documents })] })), tools > 0 && (jsxs(Badge, { variant: "outline", className: "gap-1 border-transparent bg-amber-50 font-normal text-amber-800", "data-testid": "transcript-tools-badge", children: [jsx(Wrench, { className: "h-3 w-3", "aria-hidden": "true" }), t('analyticsTranscriptTurnDetails.toolsBadge', { count: tools })] }))] }));
|
|
259157
|
+
}
|
|
259158
|
+
|
|
259159
|
+
/**
|
|
259160
|
+
* One place that decides how a user is shown.
|
|
259161
|
+
*
|
|
259162
|
+
* Backend payloads name their user in a dozen ways — `email` / `lti_email` /
|
|
259163
|
+
* `user_email`, `username` / `student` / `lti_username`, `name` /
|
|
259164
|
+
* `full_name` / `user_full_name`, or a nested `user` object — and most of
|
|
259165
|
+
* them can be empty, `null`, or a placeholder like "None". Every surface used
|
|
259166
|
+
* to pick through those on its own. `resolveUserIdentity()` takes whatever
|
|
259167
|
+
* the payload is and returns what to show, what to open a profile on, and
|
|
259168
|
+
* the avatar initial, so the rule lives here and can be tweaked once.
|
|
259169
|
+
*
|
|
259170
|
+
* Default rule: email → full name → username → the anonymous label.
|
|
259171
|
+
*/
|
|
259172
|
+
const DEFAULT_USER_IDENTITY_ORDER = ['email', 'name', 'username'];
|
|
259173
|
+
const DEFAULT_ANONYMOUS_LABEL = 'Anonymous';
|
|
259174
|
+
/** Values backends emit instead of "nothing". Compared case-insensitively. */
|
|
259175
|
+
const PLACEHOLDERS = new Set([
|
|
259176
|
+
'',
|
|
259177
|
+
'none',
|
|
259178
|
+
'null',
|
|
259179
|
+
'undefined',
|
|
259180
|
+
'n/a',
|
|
259181
|
+
'na',
|
|
259182
|
+
'-',
|
|
259183
|
+
'anonymous',
|
|
259184
|
+
'anonymoususer',
|
|
259185
|
+
]);
|
|
259186
|
+
/**
|
|
259187
|
+
* `local@domain.tld` with no whitespace, checked with a single pass rather than
|
|
259188
|
+
* a backtracking pattern (CodeQL flags `[^@]+@[^@]+\.[^@]+` as polynomial on
|
|
259189
|
+
* adversarial input).
|
|
259190
|
+
*/
|
|
259191
|
+
const isEmailLike = (value) => {
|
|
259192
|
+
const text = value.trim();
|
|
259193
|
+
if (!text || /\s/.test(text))
|
|
259194
|
+
return false;
|
|
259195
|
+
const at = text.indexOf('@');
|
|
259196
|
+
if (at <= 0 || at !== text.lastIndexOf('@') || at === text.length - 1)
|
|
259197
|
+
return false;
|
|
259198
|
+
const domain = text.slice(at + 1);
|
|
259199
|
+
const dot = domain.indexOf('.');
|
|
259200
|
+
// A dot somewhere inside the domain, with something after the last one.
|
|
259201
|
+
return dot > 0 && !domain.endsWith('.');
|
|
259202
|
+
};
|
|
259203
|
+
/** Trimmed string, or undefined for blanks, non-strings and placeholders. */
|
|
259204
|
+
const clean = (value) => {
|
|
259205
|
+
if (typeof value !== 'string')
|
|
259206
|
+
return undefined;
|
|
259207
|
+
const trimmed = value.trim();
|
|
259208
|
+
return PLACEHOLDERS.has(trimmed.toLowerCase()) ? undefined : trimmed;
|
|
259209
|
+
};
|
|
259210
|
+
const firstOf = (...candidates) => {
|
|
259211
|
+
for (const candidate of candidates) {
|
|
259212
|
+
const value = clean(candidate);
|
|
259213
|
+
if (value)
|
|
259214
|
+
return value;
|
|
259215
|
+
}
|
|
259216
|
+
return undefined;
|
|
259217
|
+
};
|
|
259218
|
+
const nested = (source) => source.user && typeof source.user === 'object' ? source.user : undefined;
|
|
259219
|
+
/**
|
|
259220
|
+
* Resolve how a user should be displayed from any payload that names one.
|
|
259221
|
+
*
|
|
259222
|
+
* - `email`: `lti_email`, `email`, `user_email`, the nested `user`'s email —
|
|
259223
|
+
* or a username field that turns out to hold an email address.
|
|
259224
|
+
* - `username`: `lti_username`, `username`, `student`, `user_username`, the
|
|
259225
|
+
* nested `user`'s username, or a bare string/number `user` value.
|
|
259226
|
+
* - `name`: `user_full_name`, `full_name`, `name`, `first_name last_name`, or
|
|
259227
|
+
* the nested `user`'s name.
|
|
259228
|
+
* - `profileUsername`: the first *platform* username (never an LTI claim).
|
|
259229
|
+
*
|
|
259230
|
+
* A string source is treated as an email when it looks like one, else as a
|
|
259231
|
+
* username. Placeholder strings ("None", "-", "anonymous"…) count as absent.
|
|
259232
|
+
*/
|
|
259233
|
+
function resolveUserIdentity(source, options = {}) {
|
|
259234
|
+
var _a;
|
|
259235
|
+
const anonymousLabel = options.anonymousLabel || DEFAULT_ANONYMOUS_LABEL;
|
|
259236
|
+
const order = ((_a = options.order) === null || _a === void 0 ? void 0 : _a.length) ? options.order : DEFAULT_USER_IDENTITY_ORDER;
|
|
259237
|
+
const record = source && typeof source === 'object' ? source : { user: source !== null && source !== void 0 ? source : null };
|
|
259238
|
+
const inner = nested(record);
|
|
259239
|
+
// A bare `user` value is an id or username; numeric ids are still shown.
|
|
259240
|
+
const bareUser = typeof record.user === 'number' && Number.isFinite(record.user)
|
|
259241
|
+
? String(record.user)
|
|
259242
|
+
: typeof record.user === 'string'
|
|
259243
|
+
? clean(record.user)
|
|
259244
|
+
: undefined;
|
|
259245
|
+
let email = firstOf(record.lti_email, record.email, record.user_email, inner === null || inner === void 0 ? void 0 : inner.lti_email, inner === null || inner === void 0 ? void 0 : inner.email, inner === null || inner === void 0 ? void 0 : inner.user_email);
|
|
259246
|
+
const platformUsername = firstOf(record.username, record.student, record.user_username, inner === null || inner === void 0 ? void 0 : inner.username, inner === null || inner === void 0 ? void 0 : inner.student, inner === null || inner === void 0 ? void 0 : inner.user_username, bareUser && !isEmailLike(bareUser) ? bareUser : undefined);
|
|
259247
|
+
let username = firstOf(record.lti_username, inner === null || inner === void 0 ? void 0 : inner.lti_username, platformUsername);
|
|
259248
|
+
const composed = [clean(record.first_name), clean(record.last_name)].filter(Boolean).join(' ');
|
|
259249
|
+
const name = firstOf(record.user_full_name, record.full_name, record.name, composed, inner === null || inner === void 0 ? void 0 : inner.user_full_name, inner === null || inner === void 0 ? void 0 : inner.full_name, inner === null || inner === void 0 ? void 0 : inner.name);
|
|
259250
|
+
// A bare string that looks like an email is an email, and so is a
|
|
259251
|
+
// "username" field that actually carries one.
|
|
259252
|
+
if (!email && bareUser && isEmailLike(bareUser))
|
|
259253
|
+
email = bareUser;
|
|
259254
|
+
if (!email && username && isEmailLike(username)) {
|
|
259255
|
+
email = username;
|
|
259256
|
+
username = undefined;
|
|
259257
|
+
}
|
|
259258
|
+
const facts = { email, username, name };
|
|
259259
|
+
const label = order.map((field) => facts[field]).find(Boolean);
|
|
259260
|
+
return {
|
|
259261
|
+
label: label !== null && label !== void 0 ? label : anonymousLabel,
|
|
259262
|
+
email,
|
|
259263
|
+
username,
|
|
259264
|
+
name,
|
|
259265
|
+
profileUsername: platformUsername,
|
|
259266
|
+
initial: (label !== null && label !== void 0 ? label : anonymousLabel).charAt(0).toUpperCase() || 'A',
|
|
259267
|
+
isAnonymous: !label,
|
|
259268
|
+
};
|
|
259269
|
+
}
|
|
259270
|
+
|
|
258128
259271
|
const AnalyticsSettingsContext = createContext({});
|
|
258129
259272
|
AnalyticsSettingsContext.Provider;
|
|
258130
259273
|
|
|
@@ -260865,64 +262008,6 @@ const AutoResizeTextarea = ({ allowEmptySubmit = false, value, onChange, onSubmi
|
|
|
260865
262008
|
disabled, placeholder: placeholder, style: combinedStyle, className: `flex max-h-32 min-h-[36px] w-full resize-none rounded-2xl border-0 border-gray-200 bg-transparent px-4 py-2 pt-2 pb-1 text-base ring-offset-white placeholder:text-gray-400 focus:outline-none focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50 dark:border-gray-800 dark:bg-gray-950 dark:ring-offset-gray-950 dark:placeholder:text-gray-400 ${className}`, ...props }));
|
|
260866
262009
|
};
|
|
260867
262010
|
|
|
260868
|
-
function DocumentSidebar({ isModal = false, sessionId }) {
|
|
260869
|
-
const t = useT();
|
|
260870
|
-
const [isCollapsed, setIsCollapsed] = React.useState(false);
|
|
260871
|
-
const params = useParams();
|
|
260872
|
-
const username = useUsername();
|
|
260873
|
-
const isStreaming = useSelector(selectStreaming);
|
|
260874
|
-
const { data: vectorDocuments, refetch, status, } = useGetVectorDocumentsQuery({
|
|
260875
|
-
org: params.tenantKey,
|
|
260876
|
-
sessionId: sessionId,
|
|
260877
|
-
// @ts-ignore
|
|
260878
|
-
userId: username,
|
|
260879
|
-
}, {
|
|
260880
|
-
skip: !params.tenantKey || !sessionId || !username || !isLoggedIn(),
|
|
260881
|
-
});
|
|
260882
|
-
React.useEffect(() => {
|
|
260883
|
-
if (!isStreaming && isLoggedIn() && status !== 'uninitialized') {
|
|
260884
|
-
refetch();
|
|
260885
|
-
}
|
|
260886
|
-
}, [isStreaming, refetch]);
|
|
260887
|
-
const toggleCollapse = () => {
|
|
260888
|
-
setIsCollapsed(!isCollapsed);
|
|
260889
|
-
};
|
|
260890
|
-
return (jsx(Fragment$1, { children: (vectorDocuments === null || vectorDocuments === void 0 ? void 0 : vectorDocuments.length) && vectorDocuments.length > 0 ? (jsx("aside", { className: cn('transition-all duration-300 ease-in-out', isModal
|
|
260891
|
-
? 'max-h-full w-full overflow-auto'
|
|
260892
|
-
: 'hidden rounded-tl-lg rounded-bl-lg md:hidden lg:block', !isModal && (isCollapsed ? 'w-[40px]' : 'w-[380px]')), style: {
|
|
260893
|
-
backgroundColor: isCollapsed && !isModal ? 'white' : '#F3F6FB',
|
|
260894
|
-
maxHeight: 'calc(100vh - 80px)',
|
|
260895
|
-
}, children: jsxs("div", { className: "flex h-full flex-col", children: [!isModal && (jsxs("div", { className: cn('flex cursor-pointer items-center gap-2 border-b border-gray-200 p-4 transition-colors duration-200', isCollapsed && 'h-[80px] justify-center rounded-tl-[5px] rounded-bl-[5px] p-2', 'hover:bg-blue-100'), onClick: toggleCollapse, style: { backgroundColor: '#F3F6FB' }, children: [jsx(FileText, { className: "h-5 w-5 shrink-0 text-blue-600" }), !isCollapsed && (jsxs(Fragment$1, { children: [jsx("h2", { className: "text-base font-medium text-[#646464]", children: t('documentSidebar.retrievedDocuments') }), jsx(ChevronRight, { className: cn('ml-auto h-4 w-4 text-gray-400 transition-transform', isCollapsed ? 'rotate-180' : '') })] }))] })), (!isCollapsed || isModal) && (jsx(Fragment$1, { children: jsx("div", { className: "flex-1 overflow-auto", style: { backgroundColor: '#F3F6FB' }, children: jsx("div", { className: "mt-0 space-y-1 p-2", children: vectorDocuments.map((doc, index) => {
|
|
260896
|
-
var _a, _b;
|
|
260897
|
-
return (jsxs(React.Fragment, { children: [jsx("div", { className: "rounded-lg p-2 hover:bg-gray-50", children: jsxs("div", { className: "flex items-start gap-2", children: [jsx(Radio$1, { className: "mt-1 h-4 w-4 text-gray-400" }), jsxs("div", { className: "min-w-0 flex-1 space-y-1", children: [jsxs("div", { className: "flex items-center justify-between", children: [jsx(Link$2, { href: (_a = doc.source) !== null && _a !== void 0 ? _a : '', target: "_blank", className: "min-w-0", children: jsx("span", { className: "text-sm font-semibold break-words text-[#646464] hover:text-blue-600", children: doc.title }) }), jsx("div", { className: "flex items-center gap-2 self-center", children: jsx("span", { className: "text-xs text-gray-500", children: (_b = doc === null || doc === void 0 ? void 0 : doc.score) === null || _b === void 0 ? void 0 : _b.toFixed(2) }) })] }), jsx("p", { className: "line-clamp-6 pt-[10px] text-[14px] text-gray-600", children: doc.snippet })] })] }) }), index < vectorDocuments.length - 1 && (jsx("div", { className: "my-2 border-t border-gray-200" }))] }, index));
|
|
260898
|
-
}) }) }) }))] }) })) : null }));
|
|
260899
|
-
}
|
|
260900
|
-
|
|
260901
|
-
function RetrievedDocumentsButton({ sessionId }) {
|
|
260902
|
-
const t = useT();
|
|
260903
|
-
const [isOpen, setIsOpen] = useState(false);
|
|
260904
|
-
const params = useParams();
|
|
260905
|
-
const username = useUsername();
|
|
260906
|
-
const isStreaming = useSelector(selectStreaming);
|
|
260907
|
-
const { data: vectorDocuments, refetch, status, } = useGetVectorDocumentsQuery({
|
|
260908
|
-
org: params.tenantKey,
|
|
260909
|
-
sessionId: sessionId,
|
|
260910
|
-
// @ts-ignore
|
|
260911
|
-
userId: username,
|
|
260912
|
-
}, {
|
|
260913
|
-
skip: !params.tenantKey || !sessionId || !username || !isLoggedIn(),
|
|
260914
|
-
});
|
|
260915
|
-
useEffect(() => {
|
|
260916
|
-
if (!isStreaming && isLoggedIn() && status !== 'uninitialized') {
|
|
260917
|
-
refetch();
|
|
260918
|
-
}
|
|
260919
|
-
}, [isStreaming, refetch]);
|
|
260920
|
-
if (!(vectorDocuments === null || vectorDocuments === void 0 ? void 0 : vectorDocuments.length)) {
|
|
260921
|
-
return null;
|
|
260922
|
-
}
|
|
260923
|
-
return (jsxs(Fragment$1, { children: [jsxs(Button$1, { variant: "outline", size: "sm", className: "flex items-center gap-2 border-gray-200 bg-white text-gray-700 hover:bg-gray-50", onClick: () => setIsOpen(true), children: [jsx(FileText, { className: "h-4 w-4 text-blue-600" }), jsx("span", { children: t('retrievedDocumentsButton.title') })] }), jsx(Dialog, { open: isOpen, onOpenChange: setIsOpen, children: jsxs(DialogContent, { className: "flex max-h-[80vh] flex-col gap-0 p-0 sm:max-w-[425px] md:max-w-[600px]", children: [jsx(DialogHeader, { className: "border-b px-4 py-2", children: jsxs(DialogTitle, { className: "flex items-center gap-2 text-base font-medium text-[#646464]", children: [jsx(FileText, { className: "h-5 w-5 text-blue-600" }), t('retrievedDocumentsButton.title')] }) }), jsx("div", { className: "flex-1 overflow-hidden", children: jsx(DocumentSidebar, { isModal: true, sessionId: sessionId }) })] }) })] }));
|
|
260924
|
-
}
|
|
260925
|
-
|
|
260926
262011
|
const PAGE_SIZE$5 = 10;
|
|
260927
262012
|
const MemoryMenu = ({ onClose, mentorId, tenantKey, username }) => {
|
|
260928
262013
|
const t = useT();
|
|
@@ -276866,6 +277951,7 @@ const AGENT_HISTORY_TAB_LABELS = {
|
|
|
276866
277951
|
selectPrompt: 'Select a conversation to view details.',
|
|
276867
277952
|
aiAgent: 'AI Agent',
|
|
276868
277953
|
noConversations: 'No conversations found',
|
|
277954
|
+
documentsChip: (count) => `Documents · ${count}`,
|
|
276869
277955
|
},
|
|
276870
277956
|
summary: {
|
|
276871
277957
|
notAvailable: 'Summary not available',
|
|
@@ -276904,6 +277990,7 @@ function buildHistoryTabLabels(t) {
|
|
|
276904
277990
|
selectPrompt: t('historyTabLabels.conversation.selectPrompt'),
|
|
276905
277991
|
aiAgent: t('historyTabLabels.conversation.aiAgent'),
|
|
276906
277992
|
noConversations: t('historyTabLabels.conversation.noConversations'),
|
|
277993
|
+
documentsChip: (count) => t('historyTabLabels.conversation.documentsChip', { count }),
|
|
276907
277994
|
},
|
|
276908
277995
|
summary: {
|
|
276909
277996
|
notAvailable: t('historyTabLabels.summary.notAvailable'),
|
|
@@ -276923,6 +278010,8 @@ function resolveHistoryTabLabels(override, t) {
|
|
|
276923
278010
|
}
|
|
276924
278011
|
|
|
276925
278012
|
const ANONYMOUS_USERNAME$1 = 'anonymous';
|
|
278013
|
+
/** A turn's attachments in the shape the chat's file cards take. */
|
|
278014
|
+
const attachmentCards = (files) => (files !== null && files !== void 0 ? files : []).map((file) => ({ name: file.name, contentType: file.content_type, url: file.url }));
|
|
276926
278015
|
function textTruncate$1(str, length, ending) {
|
|
276927
278016
|
if (length == null) {
|
|
276928
278017
|
length = 50;
|
|
@@ -276935,7 +278024,7 @@ function textTruncate$1(str, length, ending) {
|
|
|
276935
278024
|
}
|
|
276936
278025
|
return str;
|
|
276937
278026
|
}
|
|
276938
|
-
function AgentHistoryTab({ renderMessageContent, PaginationComponent, onExport, isExporting = false, labels: labelsOverride, }) {
|
|
278027
|
+
function AgentHistoryTab({ renderMessageContent, PaginationComponent, onExport, isExporting = false, labels: labelsOverride, currentSPA, }) {
|
|
276939
278028
|
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p;
|
|
276940
278029
|
const t = useT();
|
|
276941
278030
|
const { tenantKey, mentorId, username } = useAgentSettings();
|
|
@@ -276985,6 +278074,10 @@ function AgentHistoryTab({ renderMessageContent, PaginationComponent, onExport,
|
|
|
276985
278074
|
// Use mentor summaries data for rating and tags
|
|
276986
278075
|
const averageRating = (mentorSummaries === null || mentorSummaries === void 0 ? void 0 : mentorSummaries.rating) || 0;
|
|
276987
278076
|
const topicTags = (mentorSummaries === null || mentorSummaries === void 0 ? void 0 : mentorSummaries.tags) || [];
|
|
278077
|
+
// Who the previewed conversation belongs to (email → full name → username → Anonymous).
|
|
278078
|
+
const identityOptions = { anonymousLabel: labels.conversation.anonymous };
|
|
278079
|
+
const selectedOwner = resolveUserIdentity(selectedConversation, identityOptions);
|
|
278080
|
+
const previewOwner = resolveUserIdentity(previewConversationContent, identityOptions);
|
|
276988
278081
|
const renderAiContent = (content) => {
|
|
276989
278082
|
if (renderMessageContent) {
|
|
276990
278083
|
return renderMessageContent(content);
|
|
@@ -277016,7 +278109,10 @@ function AgentHistoryTab({ renderMessageContent, PaginationComponent, onExport,
|
|
|
277016
278109
|
const conversation = _conversation;
|
|
277017
278110
|
const messages = conversation.messages;
|
|
277018
278111
|
const firstMessage = messages[0];
|
|
277019
|
-
|
|
278112
|
+
// Email → full name → username → Anonymous, decided in one place.
|
|
278113
|
+
const owner = resolveUserIdentity(conversation, {
|
|
278114
|
+
anonymousLabel: labels.conversation.anonymous,
|
|
278115
|
+
});
|
|
277020
278116
|
const timeAgo = formatDistanceToNow(new Date(conversation.inserted_at), {
|
|
277021
278117
|
addSuffix: true,
|
|
277022
278118
|
});
|
|
@@ -277026,22 +278122,25 @@ function AgentHistoryTab({ renderMessageContent, PaginationComponent, onExport,
|
|
|
277026
278122
|
const preview = (firstMessage === null || firstMessage === void 0 ? void 0 : firstMessage.ai)
|
|
277027
278123
|
? textTruncate$1(firstMessage.ai, 60)
|
|
277028
278124
|
: labels.conversation.noResponse;
|
|
278125
|
+
// The admin list carries the turns inline (no server-side
|
|
278126
|
+
// rollups), so badge rows from the turns themselves.
|
|
278127
|
+
// The chip counts what the dialog lists: every document the
|
|
278128
|
+
// turns carry, as the backend reports them.
|
|
278129
|
+
const documents = conversationDocuments(messages);
|
|
278130
|
+
const rollup = summarizeTranscriptTurns(messages);
|
|
277029
278131
|
return (jsxs("div", { className: cn('flex cursor-pointer items-center justify-between border-b p-4 last:border-b-0', (selectedConversation === null || selectedConversation === void 0 ? void 0 : selectedConversation.id) === conversation.id
|
|
277030
278132
|
? 'bg-gray-100'
|
|
277031
|
-
: 'hover:bg-gray-50'), onClick: () => handleConversationClick(conversation), children: [jsxs("div", { className: "w-full", children: [jsxs("div", { className: "mb-2 flex items-center justify-between", children: [jsx("span", { className: "text-sm text-gray-600", children: timeAgo }), jsx(
|
|
277032
|
-
|
|
277033
|
-
|
|
277034
|
-
|
|
277035
|
-
|
|
277036
|
-
|
|
277037
|
-
|
|
278133
|
+
: 'hover:bg-gray-50'), onClick: () => handleConversationClick(conversation), children: [jsxs("div", { className: "w-full", children: [jsxs("div", { className: "mb-2 flex items-center justify-between", children: [jsx("span", { className: "text-sm text-gray-600", children: timeAgo }), jsx(UserProfileLink, { tenantKey: tenantKey, username: owner.profileUsername, display: owner.label, currentSPA: currentSPA, className: "max-w-[100px] text-sm text-gray-900 sm:max-w-[200px]" })] }), jsx("div", { className: "font-medium text-gray-900", children: title }), jsx("p", { className: "line-clamp-1 text-sm text-gray-600", children: preview }), (rollup.documentsCount > 0 || rollup.toolCallsCount > 0) && (jsxs("div", { className: "mt-2 flex flex-wrap items-center gap-1.5", children: [rollup.documentsCount > 0 && (
|
|
278134
|
+
// The chat's documents dialog, as a chip: one
|
|
278135
|
+
// click shows what the conversation retrieved.
|
|
278136
|
+
jsx(RetrievedDocumentsButton, { documents: documents, label: labels.conversation.documentsChip(rollup.documentsCount), className: "h-6 gap-1 rounded-md border-transparent bg-blue-50 px-2 text-xs font-normal text-blue-700 hover:bg-blue-100 [&_svg]:h-3 [&_svg]:w-3" })), jsx(TranscriptRollupBadges, { documentsCount: 0, toolCallsCount: rollup.toolCallsCount })] }))] }), jsx("div", { className: "flex items-center gap-2 text-sm text-gray-600", children: jsx(ChevronRight, { className: "h-4 w-4" }) })] }, conversation.id));
|
|
278137
|
+
}) }), totalPages > 1 && PaginationComponent && (jsx("div", { className: "my-2", children: jsx(PaginationComponent, { currentPage: currentPage, totalPages: totalPages, onPageChange: handlePageChange, disabled: isChatHistoryFetching || isChatHistoryLoading, disableNumberedButtons: isMobile }) }))] }), jsx("div", { className: "scrollbar-hide hidden max-h-[500px] flex-col overflow-y-auto rounded-lg border bg-white p-6 shadow-sm md:flex", tabIndex: 0, role: "region", "aria-label": t('tabsHistoryTab.conversationPreviewLabel'), children: selectedConversation && previewConversationContent ? (jsxs(Fragment$1, { children: [jsxs("div", { className: "mb-4", children: [jsx("h3", { className: "mb-1 text-base font-semibold text-gray-700", children: ((_m = selectedConversation.messages[0]) === null || _m === void 0 ? void 0 : _m.human) || labels.conversation.title }), jsx("span", { className: "text-sm text-gray-500", children: format$1(new Date(selectedConversation.inserted_at), "MMM dd, yyyy 'at' h:mm a") }), jsx(RetrievedDocumentsButton, { sessionId: selectedConversation.id, documents: conversationDocuments(selectedConversation.messages).length > 0
|
|
278138
|
+
? conversationDocuments(selectedConversation.messages)
|
|
278139
|
+
: undefined, className: "mt-2" })] }), jsxs("div", { className: "space-y-6", children: [selectedConversation.messages.map((message, index) => (jsxs("div", { className: "space-y-4", children: [jsxs("div", { className: "flex items-start gap-3", children: [jsx("div", { className: "flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-full bg-blue-100", children: jsx("span", { className: "text-sm font-medium text-blue-600", children: selectedOwner.initial }) }), jsxs("div", { className: "flex-1", children: [jsx("div", { className: "font-medium text-gray-700", children: jsx(UserProfileLink, { tenantKey: tenantKey, username: selectedOwner.profileUsername, display: selectedOwner.label, currentSPA: currentSPA }) }), jsx("p", { className: "mt-1 text-sm whitespace-pre-line text-gray-500", children: message.human }), jsx(TranscriptFileCards, { files: attachmentCards(message.human_files) })] })] }), jsxs("div", { className: "flex items-start gap-3 border-t border-gray-200 pt-4", children: [jsx("div", { className: "flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-full bg-gray-100", children: jsx("span", { className: "text-sm font-medium text-gray-600", children: t('tabsHistoryTab.aiAvatarLabel') }) }), jsxs("div", { className: "flex-1", children: [jsx("div", { className: "font-medium text-gray-700", children: labels.conversation.aiAgent }), jsx("div", { className: "mt-1 text-sm text-gray-500", children: renderAiContent(message.ai) }), jsx(TranscriptFileCards, { files: attachmentCards(message.ai_files) }), jsx(TranscriptTurnDetails, { turn: message })] })] })] }, index))), (selectedConversation === null || selectedConversation === void 0 ? void 0 : selectedConversation.memory_tracked) && (jsxs("div", { className: "border-t border-gray-200 pt-4", children: [jsxs(Button$1, { variant: "outline", onClick: () => setShowConversationMemory(!showConversationMemory), className: "flex w-full items-center justify-center gap-2 border-gray-200 text-gray-700 hover:bg-gray-50", children: [jsx(Brain, { className: "h-4 w-4" }), showConversationMemory ? labels.memory.hide : labels.memory.show] }), showConversationMemory && (jsx("div", { className: "mt-4 space-y-6", children: isConversationMemoryLoading ? (jsx("div", { className: "flex items-center justify-center py-4", children: jsx(Spinner, {}) })) : ((_o = conversationMemory === null || conversationMemory === void 0 ? void 0 : conversationMemory.entries) === null || _o === void 0 ? void 0 : _o.length) ? (jsx(Fragment$1, { children: conversationMemory.entries.map((entry, index) => (jsxs("div", { children: [jsx("h4", { className: "mb-3 font-semibold text-gray-900", children: entry.key }), jsx("ul", { className: "space-y-2", children: jsx("li", { className: "flex items-start gap-2 text-sm text-gray-600", children: entry.value }) })] }, index))) })) : (jsx("div", { className: "text-sm text-gray-500", children: labels.memory.notAvailable })) }))] }))] })] })) : (jsx("div", { className: "flex h-full items-center justify-center text-gray-500", children: labels.conversation.selectPrompt })) })] }))] }) }), jsx(Dialog, { open: isConversationPreviewModalOpen, onOpenChange: setIsConversationPreviewModalOpen, children: jsxs(DialogContent, { className: "scrollbar-hide mx-auto max-h-[80vh] max-w-[90vw] overflow-x-hidden overflow-y-auto", children: [jsx(DialogHeader, { children: jsx(DialogTitle, { className: "text-lg font-semibold text-gray-900", children: previewConversationContent
|
|
277038
278140
|
? ((_p = previewConversationContent.messages[0]) === null || _p === void 0 ? void 0 : _p.human) || labels.conversation.title
|
|
277039
|
-
: labels.conversation.title }) }), jsx("div", { className: "mx-auto max-w-full overflow-x-hidden", children: previewConversationContent && (jsxs("div", { className: "scrollbar-hide space-y-4 overflow-y-auto", children: [jsx("div", { className: "text-sm text-gray-500", children: format$1(new Date(previewConversationContent.inserted_at), "MMM dd, yyyy 'at' h:mm a") }), previewConversationContent.
|
|
277040
|
-
|
|
277041
|
-
|
|
277042
|
-
previewConversationContent.email ||
|
|
277043
|
-
labels.conversation.anonymous }), jsx("p", { className: "mt-1 text-sm whitespace-pre-line text-gray-900", children: message.human })] })] }), jsxs("div", { className: "flex items-start gap-3 border-t border-gray-200 pt-4", children: [jsx("div", { className: "flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-full bg-gray-100", children: jsx("span", { className: "text-sm font-medium text-gray-600", children: t('tabsHistoryTab.aiAvatarLabel') }) }), jsxs("div", { className: "overflow-x-hidden", children: [jsx("div", { className: "font-medium text-gray-900", children: labels.conversation.aiAgent }), jsx("div", { className: "mt-1 text-sm text-gray-900", children: renderAiContent(message.ai) })] })] })] }, index));
|
|
277044
|
-
})] })) })] }) })] }));
|
|
278141
|
+
: labels.conversation.title }) }), jsx("div", { className: "mx-auto max-w-full overflow-x-hidden", children: previewConversationContent && (jsxs("div", { className: "scrollbar-hide space-y-4 overflow-y-auto", children: [jsx("div", { className: "text-sm text-gray-500", children: format$1(new Date(previewConversationContent.inserted_at), "MMM dd, yyyy 'at' h:mm a") }), jsx(RetrievedDocumentsButton, { sessionId: previewConversationContent.id, documents: conversationDocuments(previewConversationContent.messages).length > 0
|
|
278142
|
+
? conversationDocuments(previewConversationContent.messages)
|
|
278143
|
+
: undefined }), previewConversationContent.messages.map((message, index) => (jsxs("div", { className: "space-y-4", children: [jsxs("div", { className: "flex items-start gap-3", children: [jsx("div", { className: "flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-full bg-blue-100", children: jsx("span", { className: "text-sm font-medium text-blue-600", children: previewOwner.initial }) }), jsxs("div", { className: "overflow-x-hidden", children: [jsx("div", { className: "truncate font-medium text-gray-900", children: jsx(UserProfileLink, { tenantKey: tenantKey, username: previewOwner.profileUsername, display: previewOwner.label, currentSPA: currentSPA }) }), jsx("p", { className: "mt-1 text-sm whitespace-pre-line text-gray-900", children: message.human }), jsx(TranscriptFileCards, { files: attachmentCards(message.human_files) })] })] }), jsxs("div", { className: "flex items-start gap-3 border-t border-gray-200 pt-4", children: [jsx("div", { className: "flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-full bg-gray-100", children: jsx("span", { className: "text-sm font-medium text-gray-600", children: t('tabsHistoryTab.aiAvatarLabel') }) }), jsxs("div", { className: "overflow-x-hidden", children: [jsx("div", { className: "font-medium text-gray-900", children: labels.conversation.aiAgent }), jsx("div", { className: "mt-1 text-sm text-gray-900", children: renderAiContent(message.ai) }), jsx(TranscriptFileCards, { files: attachmentCards(message.ai_files) }), jsx(TranscriptTurnDetails, { turn: message })] })] })] }, index)))] })) })] }) })] }));
|
|
277045
278144
|
}
|
|
277046
278145
|
|
|
277047
278146
|
const ANONYMOUS_USERNAME = 'anonymous';
|
|
@@ -283839,76 +284938,6 @@ const AIMessageBubble = forwardRef(function AIMessageBubble({ content, profileIm
|
|
|
283839
284938
|
}, className: "-ml-1 text-gray-500 hover:text-gray-700", children: [jsx("span", { className: "sr-only", children: t('chatAiMessageBubble.retryScreenReaderLabel') }), jsx(RefreshCcw, { className: "h-4 w-4" })] }) }), jsx(TooltipContent, { className: "ibl-tooltip-content", children: t('chatAiMessageBubble.retryTooltip') })] }))] })] })] }) }) }));
|
|
283840
284939
|
});
|
|
283841
284940
|
|
|
283842
|
-
function FileCard({ fileName, fileType }) {
|
|
283843
|
-
const getFileIcon = (fileName, fileType) => {
|
|
283844
|
-
const type = fileType.toLowerCase();
|
|
283845
|
-
const name = fileName.toLowerCase();
|
|
283846
|
-
const extension = name.split('.').pop() || '';
|
|
283847
|
-
// Check by MIME type first
|
|
283848
|
-
if (type.includes('pdf')) {
|
|
283849
|
-
return jsx(File$1, { className: "h-5 w-5 text-blue-500" });
|
|
283850
|
-
}
|
|
283851
|
-
else if (type.includes('image')) {
|
|
283852
|
-
return jsx(FileImage, { className: "h-5 w-5 text-blue-500" });
|
|
283853
|
-
}
|
|
283854
|
-
else if (type.includes('audio')) {
|
|
283855
|
-
return jsx(FileAudio, { className: "h-5 w-5 text-blue-500" });
|
|
283856
|
-
}
|
|
283857
|
-
else if (type.includes('video')) {
|
|
283858
|
-
return jsx(FilePlay, { className: "h-5 w-5 text-blue-500" });
|
|
283859
|
-
}
|
|
283860
|
-
else if (type.includes('spreadsheet') || type.includes('excel') || type.includes('csv')) {
|
|
283861
|
-
return jsx(FileSpreadsheet, { className: "h-5 w-5 text-blue-500" });
|
|
283862
|
-
}
|
|
283863
|
-
else if (type.includes('zip') || type.includes('compressed') || type.includes('archive')) {
|
|
283864
|
-
return jsx(FileArchive, { className: "h-5 w-5 text-blue-500" });
|
|
283865
|
-
}
|
|
283866
|
-
else if (type.includes('javascript') ||
|
|
283867
|
-
type.includes('typescript') ||
|
|
283868
|
-
type.includes('html') ||
|
|
283869
|
-
type.includes('css') ||
|
|
283870
|
-
type.includes('json')) {
|
|
283871
|
-
return jsx(FileCode, { className: "h-5 w-5 text-blue-500" });
|
|
283872
|
-
}
|
|
283873
|
-
// If MIME type doesn't match, check by extension
|
|
283874
|
-
if (['doc', 'docx', 'txt', 'rtf'].includes(extension)) {
|
|
283875
|
-
return jsx(FileText, { className: "h-5 w-5 text-blue-500" });
|
|
283876
|
-
}
|
|
283877
|
-
else if (['jpg', 'jpeg', 'png', 'gif', 'svg', 'webp'].includes(extension)) {
|
|
283878
|
-
return jsx(FileImage, { className: "h-5 w-5 text-blue-500" });
|
|
283879
|
-
}
|
|
283880
|
-
else if (['pdf'].includes(extension)) {
|
|
283881
|
-
return jsx(File$1, { className: "h-5 w-5 text-blue-500" });
|
|
283882
|
-
}
|
|
283883
|
-
else if (['mp3', 'wav', 'ogg', 'flac'].includes(extension)) {
|
|
283884
|
-
return jsx(FileAudio, { className: "h-5 w-5 text-blue-500" });
|
|
283885
|
-
}
|
|
283886
|
-
else if (['mp4', 'webm', 'avi', 'mov'].includes(extension)) {
|
|
283887
|
-
return jsx(FilePlay, { className: "h-5 w-5 text-blue-500" });
|
|
283888
|
-
}
|
|
283889
|
-
else if (['xls', 'xlsx', 'csv'].includes(extension)) {
|
|
283890
|
-
return jsx(FileSpreadsheet, { className: "h-5 w-5 text-blue-500" });
|
|
283891
|
-
}
|
|
283892
|
-
else if (['zip', 'rar', '7z', 'tar', 'gz'].includes(extension)) {
|
|
283893
|
-
return jsx(FileArchive, { className: "h-5 w-5 text-blue-500" });
|
|
283894
|
-
}
|
|
283895
|
-
else if (['js', 'ts', 'jsx', 'tsx', 'html', 'css', 'json', 'py', 'java', 'c', 'cpp'].includes(extension)) {
|
|
283896
|
-
return jsx(FileCode, { className: "h-5 w-5 text-blue-500" });
|
|
283897
|
-
}
|
|
283898
|
-
// Default icon
|
|
283899
|
-
return jsx(File$1, { className: "h-5 w-5 text-blue-500" });
|
|
283900
|
-
};
|
|
283901
|
-
return (jsxs("div", { className: "mb-2 flex max-w-xs items-center rounded-lg border border-gray-200 bg-white p-3", children: [jsx("div", { className: "mr-3 shrink-0 rounded-lg bg-blue-100 p-2 text-blue-600", children: getFileIcon(fileName, fileType) }), jsxs("div", { className: "min-w-0 flex-1", children: [jsx("h3", { className: "truncate text-sm font-medium text-gray-900", children: fileName }), jsx("p", { className: "truncate text-xs text-gray-500", children: fileType })] })] }));
|
|
283902
|
-
}
|
|
283903
|
-
|
|
283904
|
-
function ImageMessage({ url, fileName, setPreviewImage }) {
|
|
283905
|
-
const [hasError, setHasError] = useState(false);
|
|
283906
|
-
if (hasError || !url) {
|
|
283907
|
-
return jsx(FileCard, { fileName: fileName, fileType: "image/*" });
|
|
283908
|
-
}
|
|
283909
|
-
return (jsx("div", { className: "h-full max-h-64 w-full max-w-96 overflow-hidden rounded-lg", children: jsx("div", { className: "relative", children: jsx("img", { src: url, alt: fileName, className: "h-auto w-full cursor-pointer object-contain p-2", onClick: () => setPreviewImage(url), onError: () => setHasError(true) }) }) }));
|
|
283910
|
-
}
|
|
283911
|
-
|
|
283912
284941
|
function UserMessageBubble({ message, isHighlighted, profileImage, mentorName, messages, onHighlightMessage, onPreviewImage, }) {
|
|
283913
284942
|
return (jsxs("div", { className: `message-container mb-4 flex flex-col items-end transition-all duration-300 ${isHighlighted ? 'rounded-2xl bg-blue-100' : ''}`, children: [message.fileAttachments && message.fileAttachments.length > 0 && (jsx("div", { className: "mb-2 flex flex-col items-end gap-2", children: message.fileAttachments.map((attachment, idx) => {
|
|
283914
284943
|
if (attachment.fileType.startsWith('image/')) {
|