= ({
}) => {
const { t } = useTranslation()
const { plan } = useProviderContext()
+ const { data: vectorSpace } = useCurrentPlanVectorSpace()
+ const displayPlan = vectorSpace
+ ? {
+ ...plan,
+ usage: {
+ ...plan.usage,
+ vectorSpace: vectorSpace.size,
+ },
+ total: {
+ ...plan.total,
+ vectorSpace: vectorSpace.limit,
+ },
+ }
+ : plan
const {
type,
usage,
total,
- } = plan
+ } = displayPlan
// Determine total based on plan type (in MB), derived from ALL_PLANS config
const getTotalInMB = () => {
diff --git a/web/app/components/billing/utils/__tests__/index.spec.ts b/web/app/components/billing/utils/__tests__/index.spec.ts
index 115da91db7..84818d3175 100644
--- a/web/app/components/billing/utils/__tests__/index.spec.ts
+++ b/web/app/components/billing/utils/__tests__/index.spec.ts
@@ -65,10 +65,6 @@ describe('billing utils', () => {
size: 2,
limit: 5,
},
- vector_space: {
- size: 10,
- limit: 50,
- },
annotation_quota_limit: {
size: 5,
limit: 10,
@@ -108,7 +104,7 @@ describe('billing utils', () => {
const data = createMockPlanData()
const result = parseCurrentPlan(data)
- expect(result.usage.vectorSpace).toBe(10)
+ expect(result.usage.vectorSpace).toBe(0)
expect(result.usage.buildApps).toBe(2)
expect(result.usage.teamMembers).toBe(1)
expect(result.usage.annotatedResponse).toBe(5)
@@ -125,6 +121,29 @@ describe('billing utils', () => {
expect(result.total.annotatedResponse).toBe(10)
})
+ it('should not read vector space usage from current plan info', () => {
+ const data = createMockPlanData()
+ const result = parseCurrentPlan(data)
+
+ expect(result.usage.vectorSpace).toBe(0)
+ expect(result.total.vectorSpace).toBe(50)
+ })
+
+ it('should derive vector space total from plan config', () => {
+ const data = createMockPlanData({
+ billing: {
+ enabled: true,
+ subscription: {
+ plan: Plan.professional,
+ },
+ },
+ })
+ const result = parseCurrentPlan(data)
+
+ expect(result.usage.vectorSpace).toBe(0)
+ expect(result.total.vectorSpace).toBe(5 * 1024)
+ })
+
it('should convert 0 limits to NUM_INFINITE (-1)', () => {
const data = createMockPlanData({
documents_upload_quota: {
diff --git a/web/app/components/billing/utils/index.ts b/web/app/components/billing/utils/index.ts
index 2d37eecbd5..c83c0a6c52 100644
--- a/web/app/components/billing/utils/index.ts
+++ b/web/app/components/billing/utils/index.ts
@@ -79,6 +79,7 @@ const getResetInDaysFromDate = (resetDate?: number | null) => {
export const parseCurrentPlan = (data: CurrentPlanInfoBackend) => {
const planType = data.billing.subscription.plan
const planPreset = ALL_PLANS[planType]
+ const vectorSpaceLimit = getPlanVectorSpaceLimitMB(planType)
const resolveRateLimit = (limit?: number, fallback?: number) => {
const value = limit ?? fallback ?? 0
return parseRateLimit(value)
@@ -93,7 +94,7 @@ export const parseCurrentPlan = (data: CurrentPlanInfoBackend) => {
return {
type: planType,
usage: {
- vectorSpace: data.vector_space.size,
+ vectorSpace: 0,
buildApps: data.apps?.size || 0,
teamMembers: data.members.size,
annotatedResponse: data.annotation_quota_limit.size,
@@ -102,7 +103,7 @@ export const parseCurrentPlan = (data: CurrentPlanInfoBackend) => {
triggerEvents: getQuotaUsage(data.trigger_event),
},
total: {
- vectorSpace: parseLimit(data.vector_space.limit),
+ vectorSpace: vectorSpaceLimit,
buildApps: parseLimit(data.apps?.limit) || 0,
teamMembers: parseLimit(data.members.limit),
annotatedResponse: parseLimit(data.annotation_quota_limit.limit),
diff --git a/web/app/components/billing/vector-space-full/__tests__/index.spec.tsx b/web/app/components/billing/vector-space-full/__tests__/index.spec.tsx
index b1ef0104a0..42054df649 100644
--- a/web/app/components/billing/vector-space-full/__tests__/index.spec.tsx
+++ b/web/app/components/billing/vector-space-full/__tests__/index.spec.tsx
@@ -21,6 +21,12 @@ vi.mock('../../upgrade-btn', () => ({
default: () => ,
}))
+vi.mock('@/service/use-billing', () => ({
+ useCurrentPlanVectorSpace: () => ({
+ data: undefined,
+ }),
+}))
+
// Mock utils to control threshold and plan limits
vi.mock('../../utils', () => ({
getPlanVectorSpaceLimitMB: (planType: string) => {
diff --git a/web/app/components/datasets/create/step-one/__tests__/index.spec.tsx b/web/app/components/datasets/create/step-one/__tests__/index.spec.tsx
index 6c6c60d808..bf44c0f37d 100644
--- a/web/app/components/datasets/create/step-one/__tests__/index.spec.tsx
+++ b/web/app/components/datasets/create/step-one/__tests__/index.spec.tsx
@@ -36,6 +36,16 @@ vi.mock('@/context/provider-context', () => ({
}),
}))
+vi.mock('@/service/use-billing', () => ({
+ useCurrentPlanVectorSpace: () => ({
+ data: {
+ size: mockPlan.usage.vectorSpace,
+ limit: mockPlan.total.vectorSpace,
+ },
+ isFetching: false,
+ }),
+}))
+
vi.mock('../../file-uploader', () => ({
default: ({ onPreview, fileList }: { onPreview: (file: File) => void, fileList: FileItem[] }) => (
diff --git a/web/app/components/datasets/create/step-one/index.tsx b/web/app/components/datasets/create/step-one/index.tsx
index e94a44ebb2..3b57390cce 100644
--- a/web/app/components/datasets/create/step-one/index.tsx
+++ b/web/app/components/datasets/create/step-one/index.tsx
@@ -15,6 +15,7 @@ import VectorSpaceFull from '@/app/components/billing/vector-space-full'
import { useDatasetDetailContextWithSelector } from '@/context/dataset-detail'
import { useProviderContext } from '@/context/provider-context'
import { DataSourceType } from '@/models/datasets'
+import { useCurrentPlanVectorSpace } from '@/service/use-billing'
import EmptyDatasetCreationModal from '../empty-dataset-creation-modal'
import FileUploader from '../file-uploader'
import Website from '../website'
@@ -119,7 +120,15 @@ const StepOne = ({
const allFileLoaded = files.length > 0 && files.every(file => file.file.id)
const hasNotion = notionPages.length > 0
- const isVectorSpaceFull = plan.usage.vectorSpace >= plan.total.vectorSpace
+ const shouldCheckVectorSpace = enableBilling && (allFileLoaded || hasNotion)
+ const {
+ data: vectorSpace,
+ isFetching: isFetchingVectorSpacePlan,
+ } = useCurrentPlanVectorSpace(shouldCheckVectorSpace)
+ const isCheckingVectorSpace = shouldCheckVectorSpace && !vectorSpace && isFetchingVectorSpacePlan
+ const isVectorSpaceFull = !!vectorSpace
+ && vectorSpace.limit > 0
+ && vectorSpace.size >= vectorSpace.limit
const isShowVectorSpaceFull = (allFileLoaded || hasNotion) && isVectorSpaceFull && enableBilling
const supportBatchUpload = !enableBilling || plan.type !== Plan.sandbox
@@ -131,8 +140,10 @@ const StepOne = ({
return true
if (files.some(file => !file.file.id))
return true
+ if (isCheckingVectorSpace)
+ return true
return isShowVectorSpaceFull
- }, [files, isShowVectorSpaceFull])
+ }, [files, isCheckingVectorSpace, isShowVectorSpaceFull])
// Clear previews when switching data source type
const handleClearPreviews = useCallback((newType: DataSourceType) => {
diff --git a/web/app/components/datasets/documents/create-from-pipeline/__tests__/index.spec.tsx b/web/app/components/datasets/documents/create-from-pipeline/__tests__/index.spec.tsx
index 1029714661..ae1eb817b4 100644
--- a/web/app/components/datasets/documents/create-from-pipeline/__tests__/index.spec.tsx
+++ b/web/app/components/datasets/documents/create-from-pipeline/__tests__/index.spec.tsx
@@ -42,6 +42,16 @@ vi.mock('@/context/provider-context', () => ({
selector({ plan: mockPlan, enableBilling: true }),
}))
+vi.mock('@/service/use-billing', () => ({
+ useCurrentPlanVectorSpace: () => ({
+ data: {
+ size: mockPlan.usage.vectorSpace,
+ limit: mockPlan.total.vectorSpace,
+ },
+ isFetching: false,
+ }),
+}))
+
vi.mock('@/context/dataset-detail', () => ({
useDatasetDetailContextWithSelector: (selector: (state: { dataset: { pipeline_id: string } }) => unknown) =>
selector({ dataset: { pipeline_id: 'test-pipeline-id' } }),
diff --git a/web/app/components/datasets/documents/create-from-pipeline/hooks/use-datasource-ui-state.ts b/web/app/components/datasets/documents/create-from-pipeline/hooks/use-datasource-ui-state.ts
index f4c222f652..5cfd557b3d 100644
--- a/web/app/components/datasets/documents/create-from-pipeline/hooks/use-datasource-ui-state.ts
+++ b/web/app/components/datasets/documents/create-from-pipeline/hooks/use-datasource-ui-state.ts
@@ -13,6 +13,7 @@ type DatasourceUIStateParams = {
selectedFileIdsLength: number
onlineDriveFileList: OnlineDriveFile[]
isVectorSpaceFull: boolean
+ isCheckingVectorSpace?: boolean
enableBilling: boolean
currentWorkspacePagesLength: number
fileUploadConfig: { file_size_limit: number, batch_count_limit: number }
@@ -30,6 +31,7 @@ export const useDatasourceUIState = ({
selectedFileIdsLength,
onlineDriveFileList,
isVectorSpaceFull,
+ isCheckingVectorSpace = false,
enableBilling,
currentWorkspacePagesLength,
fileUploadConfig,
@@ -59,14 +61,14 @@ export const useDatasourceUIState = ({
return true
const disabledConditions: Record = {
- [DatasourceType.localFile]: isShowVectorSpaceFull || localFileListLength === 0 || !allFileLoaded,
- [DatasourceType.onlineDocument]: isShowVectorSpaceFull || onlineDocumentsLength === 0,
- [DatasourceType.websiteCrawl]: isShowVectorSpaceFull || websitePagesLength === 0,
- [DatasourceType.onlineDrive]: isShowVectorSpaceFull || selectedFileIdsLength === 0,
+ [DatasourceType.localFile]: isCheckingVectorSpace || isShowVectorSpaceFull || localFileListLength === 0 || !allFileLoaded,
+ [DatasourceType.onlineDocument]: isCheckingVectorSpace || isShowVectorSpaceFull || onlineDocumentsLength === 0,
+ [DatasourceType.websiteCrawl]: isCheckingVectorSpace || isShowVectorSpaceFull || websitePagesLength === 0,
+ [DatasourceType.onlineDrive]: isCheckingVectorSpace || isShowVectorSpaceFull || selectedFileIdsLength === 0,
}
return disabledConditions[datasourceType] ?? true
- }, [datasource, datasourceType, isShowVectorSpaceFull, localFileListLength, allFileLoaded, onlineDocumentsLength, websitePagesLength, selectedFileIdsLength])
+ }, [datasource, datasourceType, isCheckingVectorSpace, isShowVectorSpaceFull, localFileListLength, allFileLoaded, onlineDocumentsLength, websitePagesLength, selectedFileIdsLength])
// Check if select all should be shown
const showSelect = useMemo(() => {
diff --git a/web/app/components/datasets/documents/create-from-pipeline/index.tsx b/web/app/components/datasets/documents/create-from-pipeline/index.tsx
index 799f24fa2a..07843217ef 100644
--- a/web/app/components/datasets/documents/create-from-pipeline/index.tsx
+++ b/web/app/components/datasets/documents/create-from-pipeline/index.tsx
@@ -12,6 +12,7 @@ import { PlanUpgradeModal } from '@/app/components/billing/plan-upgrade-modal'
import { useDatasetDetailContextWithSelector } from '@/context/dataset-detail'
import { useProviderContextSelector } from '@/context/provider-context'
import { DatasourceType } from '@/models/pipeline'
+import { useCurrentPlanVectorSpace } from '@/service/use-billing'
import { useFileUploadConfig } from '@/service/use-common'
import { usePublishedPipelineInfo } from '@/service/use-pipeline'
import { useDataSourceStore } from './data-source/store'
@@ -91,7 +92,20 @@ const CreateFormPipeline = () => {
} = useOnlineDrive()
// Computed values
- const isVectorSpaceFull = plan.usage.vectorSpace >= plan.total.vectorSpace
+ const shouldCheckVectorSpace = enableBilling && (
+ allFileLoaded
+ || onlineDocuments.length > 0
+ || websitePages.length > 0
+ || selectedFileIds.length > 0
+ )
+ const {
+ data: vectorSpace,
+ isFetching: isFetchingVectorSpacePlan,
+ } = useCurrentPlanVectorSpace(shouldCheckVectorSpace)
+ const isCheckingVectorSpace = shouldCheckVectorSpace && !vectorSpace && isFetchingVectorSpacePlan
+ const isVectorSpaceFull = !!vectorSpace
+ && vectorSpace.limit > 0
+ && vectorSpace.size >= vectorSpace.limit
const supportBatchUpload = !enableBilling || plan.type !== 'sandbox'
// UI state
@@ -112,6 +126,7 @@ const CreateFormPipeline = () => {
selectedFileIdsLength: selectedFileIds.length,
onlineDriveFileList,
isVectorSpaceFull,
+ isCheckingVectorSpace,
enableBilling,
currentWorkspacePagesLength: currentWorkspace?.pages.length ?? 0,
fileUploadConfig,
diff --git a/web/service/billing.ts b/web/service/billing.ts
index 075ab71ade..4eaf9ab954 100644
--- a/web/service/billing.ts
+++ b/web/service/billing.ts
@@ -1,10 +1,19 @@
import type { CurrentPlanInfoBackend, SubscriptionUrlsBackend } from '@/app/components/billing/type'
import { get } from './base'
+export type CurrentPlanVectorSpaceBackend = {
+ size: number
+ limit: number
+}
+
export const fetchCurrentPlanInfo = () => {
return get('/features')
}
+export const fetchCurrentPlanVectorSpace = () => {
+ return get('/features/vector-space')
+}
+
export const fetchSubscriptionUrls = (plan: string, interval: string) => {
return get(`/billing/subscription?plan=${plan}&interval=${interval}`)
}
diff --git a/web/service/use-billing.ts b/web/service/use-billing.ts
index 84af077656..29208218db 100644
--- a/web/service/use-billing.ts
+++ b/web/service/use-billing.ts
@@ -1,5 +1,8 @@
import { useMutation, useQuery } from '@tanstack/react-query'
import { consoleClient, consoleQuery } from '@/service/client'
+import { fetchCurrentPlanVectorSpace } from './billing'
+
+const currentPlanVectorSpaceQueryKey = ['billing', 'current-plan-vector-space'] as const
export const useBindPartnerStackInfo = () => {
return useMutation({
@@ -21,3 +24,11 @@ export const useBillingUrl = (enabled: boolean) => {
},
})
}
+
+export const useCurrentPlanVectorSpace = (enabled = true) => {
+ return useQuery({
+ queryKey: currentPlanVectorSpaceQueryKey,
+ queryFn: () => fetchCurrentPlanVectorSpace(),
+ enabled,
+ })
+}