Merge commit '92bde350' into sandboxed-agent-rebase

Made-with: Cursor

# Conflicts:
#	api/controllers/console/app/workflow_draft_variable.py
#	api/core/agent/cot_agent_runner.py
#	api/core/agent/cot_chat_agent_runner.py
#	api/core/agent/cot_completion_agent_runner.py
#	api/core/agent/fc_agent_runner.py
#	api/core/app/apps/advanced_chat/app_generator.py
#	api/core/app/apps/advanced_chat/app_runner.py
#	api/core/app/apps/agent_chat/app_runner.py
#	api/core/app/apps/workflow/app_generator.py
#	api/core/app/apps/workflow/app_runner.py
#	api/core/app/entities/app_invoke_entities.py
#	api/core/app/entities/queue_entities.py
#	api/core/llm_generator/output_parser/structured_output.py
#	api/core/workflow/workflow_entry.py
#	api/dify_graph/context/__init__.py
#	api/dify_graph/entities/tool_entities.py
#	api/dify_graph/file/file_manager.py
#	api/dify_graph/graph_engine/response_coordinator/coordinator.py
#	api/dify_graph/graph_events/node.py
#	api/dify_graph/node_events/node.py
#	api/dify_graph/nodes/agent/agent_node.py
#	api/dify_graph/nodes/llm/entities.py
#	api/dify_graph/nodes/llm/llm_utils.py
#	api/dify_graph/nodes/llm/node.py
#	api/dify_graph/nodes/question_classifier/question_classifier_node.py
#	api/dify_graph/runtime/graph_runtime_state.py
#	api/dify_graph/variables/segments.py
#	api/factories/variable_factory.py
#	api/services/variable_truncator.py
#	api/tests/unit_tests/utils/structured_output_parser/test_structured_output_parser.py
#	api/uv.lock
#	web/app/components/app-sidebar/app-info.tsx
#	web/app/components/app-sidebar/app-sidebar-dropdown.tsx
#	web/app/components/app/create-app-modal/index.spec.tsx
#	web/app/components/apps/__tests__/list.spec.tsx
#	web/app/components/apps/app-card.tsx
#	web/app/components/apps/list.tsx
#	web/app/components/header/account-dropdown/compliance.tsx
#	web/app/components/header/account-dropdown/index.tsx
#	web/app/components/header/account-dropdown/support.tsx
#	web/app/components/workflow-app/components/workflow-onboarding-modal/index.tsx
#	web/app/components/workflow/panel/debug-and-preview/hooks.ts
#	web/contract/console/apps.ts
#	web/contract/router.ts
#	web/eslint-suppressions.json
#	web/next.config.ts
#	web/pnpm-lock.yaml
This commit is contained in:
Novice
2026-03-23 09:39:49 +08:00
1252 changed files with 27184 additions and 13336 deletions

View File

@ -3,13 +3,18 @@
import type { FC, ReactNode } from 'react'
import type { ICurrentWorkspace, LangGeniusVersionResponse, UserProfileResponse } from '@/models/common'
import { useQueryClient } from '@tanstack/react-query'
import { noop } from 'es-toolkit/function'
import { useCallback, useEffect, useMemo } from 'react'
import { createContext, useContext, useContextSelector } from 'use-context-selector'
import { setUserId, setUserProperties } from '@/app/components/base/amplitude'
import { setZendeskConversationFields } from '@/app/components/base/zendesk/utils'
import MaintenanceNotice from '@/app/components/header/maintenance-notice'
import { ZENDESK_FIELD_IDS } from '@/config'
import {
AppContext,
initialLangGeniusVersionInfo,
initialWorkspaceInfo,
userProfilePlaceholder,
useSelector,
} from '@/context/app-context'
import { env } from '@/env'
import {
useCurrentWorkspace,
@ -18,72 +23,6 @@ import {
} from '@/service/use-common'
import { useGlobalPublicStore } from './global-public-context'
export type AppContextValue = {
userProfile: UserProfileResponse
mutateUserProfile: VoidFunction
currentWorkspace: ICurrentWorkspace
isCurrentWorkspaceManager: boolean
isCurrentWorkspaceOwner: boolean
isCurrentWorkspaceEditor: boolean
isCurrentWorkspaceDatasetOperator: boolean
mutateCurrentWorkspace: VoidFunction
langGeniusVersionInfo: LangGeniusVersionResponse
useSelector: typeof useSelector
isLoadingCurrentWorkspace: boolean
isValidatingCurrentWorkspace: boolean
}
const userProfilePlaceholder = {
id: '',
name: '',
email: '',
avatar: '',
avatar_url: '',
is_password_set: false,
}
const initialLangGeniusVersionInfo = {
current_env: '',
current_version: '',
latest_version: '',
release_date: '',
release_notes: '',
version: '',
can_auto_update: false,
}
const initialWorkspaceInfo: ICurrentWorkspace = {
id: '',
name: '',
plan: '',
status: '',
created_at: 0,
role: 'normal',
providers: [],
trial_credits: 200,
trial_credits_used: 0,
next_credit_reset_date: 0,
}
const AppContext = createContext<AppContextValue>({
userProfile: userProfilePlaceholder,
currentWorkspace: initialWorkspaceInfo,
isCurrentWorkspaceManager: false,
isCurrentWorkspaceOwner: false,
isCurrentWorkspaceEditor: false,
isCurrentWorkspaceDatasetOperator: false,
mutateUserProfile: noop,
mutateCurrentWorkspace: noop,
langGeniusVersionInfo: initialLangGeniusVersionInfo,
useSelector,
isLoadingCurrentWorkspace: false,
isValidatingCurrentWorkspace: false,
})
export function useSelector<T>(selector: (value: AppContextValue) => T): T {
return useContextSelector(AppContext, selector)
}
export type AppContextProviderProps = {
children: ReactNode
}
@ -170,7 +109,7 @@ export const AppContextProvider: FC<AppContextProviderProps> = ({ children }) =>
// Report user and workspace info to Amplitude when loaded
if (userProfile?.id) {
setUserId(userProfile.email)
const properties: Record<string, any> = {
const properties: Record<string, string | number | boolean> = {
email: userProfile.email,
name: userProfile.name,
has_password: userProfile.is_password_set,
@ -213,7 +152,3 @@ export const AppContextProvider: FC<AppContextProviderProps> = ({ children }) =>
</AppContext.Provider>
)
}
export const useAppContext = () => useContext(AppContext)
export default AppContext

View File

@ -0,0 +1,73 @@
'use client'
import type { ICurrentWorkspace, LangGeniusVersionResponse, UserProfileResponse } from '@/models/common'
import { noop } from 'es-toolkit/function'
import { createContext, useContext, useContextSelector } from 'use-context-selector'
export type AppContextValue = {
userProfile: UserProfileResponse
mutateUserProfile: VoidFunction
currentWorkspace: ICurrentWorkspace
isCurrentWorkspaceManager: boolean
isCurrentWorkspaceOwner: boolean
isCurrentWorkspaceEditor: boolean
isCurrentWorkspaceDatasetOperator: boolean
mutateCurrentWorkspace: VoidFunction
langGeniusVersionInfo: LangGeniusVersionResponse
useSelector: typeof useSelector
isLoadingCurrentWorkspace: boolean
isValidatingCurrentWorkspace: boolean
}
export const userProfilePlaceholder = {
id: '',
name: '',
email: '',
avatar: '',
avatar_url: '',
is_password_set: false,
}
export const initialLangGeniusVersionInfo = {
current_env: '',
current_version: '',
latest_version: '',
release_date: '',
release_notes: '',
version: '',
can_auto_update: false,
}
export const initialWorkspaceInfo: ICurrentWorkspace = {
id: '',
name: '',
plan: '',
status: '',
created_at: 0,
role: 'normal',
providers: [],
trial_credits: 200,
trial_credits_used: 0,
next_credit_reset_date: 0,
}
export const AppContext = createContext<AppContextValue>({
userProfile: userProfilePlaceholder,
currentWorkspace: initialWorkspaceInfo,
isCurrentWorkspaceManager: false,
isCurrentWorkspaceOwner: false,
isCurrentWorkspaceEditor: false,
isCurrentWorkspaceDatasetOperator: false,
mutateUserProfile: noop,
mutateCurrentWorkspace: noop,
langGeniusVersionInfo: initialLangGeniusVersionInfo,
useSelector,
isLoadingCurrentWorkspace: false,
isValidatingCurrentWorkspace: false,
})
export function useSelector<T>(selector: (value: AppContextValue) => T): T {
return useContextSelector(AppContext, selector)
}
export const useAppContext = () => useContext(AppContext)

View File

@ -0,0 +1,22 @@
'use client'
import type { ReactNode } from 'react'
import type { EventEmitterValue } from './event-emitter'
import { useEventEmitter } from 'ahooks'
import { EventEmitterContext } from './event-emitter'
type EventEmitterContextProviderProps = {
children: ReactNode
}
export const EventEmitterContextProvider = ({
children,
}: EventEmitterContextProviderProps) => {
const eventEmitter = useEventEmitter<EventEmitterValue>()
return (
<EventEmitterContext.Provider value={{ eventEmitter }}>
{children}
</EventEmitterContext.Provider>
)
}

View File

@ -1,7 +1,6 @@
'use client'
import type { EventEmitter } from 'ahooks/lib/useEventEmitter'
import { useEventEmitter } from 'ahooks'
import { createContext, useContext } from 'use-context-selector'
/**
@ -17,25 +16,10 @@ export type EventEmitterMessage = {
export type EventEmitterValue = string | EventEmitterMessage
export type EventPayload = EventEmitterValue
const EventEmitterContext = createContext<{ eventEmitter: EventEmitter<EventEmitterValue> | null }>({
export const EventEmitterContext = createContext<{ eventEmitter: EventEmitter<EventEmitterValue> | null }>({
eventEmitter: null,
})
export const useEventEmitterContextContext = () => useContext(EventEmitterContext)
type EventEmitterContextProviderProps = {
children: React.ReactNode
}
export const EventEmitterContextProvider = ({
children,
}: EventEmitterContextProviderProps) => {
const eventEmitter = useEventEmitter<EventEmitterValue>()
return (
<EventEmitterContext.Provider value={{ eventEmitter }}>
{children}
</EventEmitterContext.Provider>
)
}
export default EventEmitterContext

View File

@ -0,0 +1,19 @@
'use client'
import type { ReactNode } from 'react'
import { useMitt } from '@/hooks/use-mitt'
import { MittContext } from './mitt-context'
type MittProviderProps = {
children: ReactNode
}
export const MittProvider = ({ children }: MittProviderProps) => {
const mitt = useMitt()
return (
<MittContext.Provider value={mitt}>
{children}
</MittContext.Provider>
)
}

View File

@ -1,6 +1,8 @@
'use client'
import type { useMitt } from '@/hooks/use-mitt'
import { noop } from 'es-toolkit/function'
import { createContext, useContext, useContextSelector } from 'use-context-selector'
import { useMitt } from '@/hooks/use-mitt'
type ContextValueType = ReturnType<typeof useMitt>
export const MittContext = createContext<ContextValueType>({
@ -8,16 +10,6 @@ export const MittContext = createContext<ContextValueType>({
useSubscribe: noop,
})
export const MittProvider = ({ children }: { children: React.ReactNode }) => {
const mitt = useMitt()
return (
<MittContext.Provider value={mitt}>
{children}
</MittContext.Provider>
)
}
export const useMittContext = () => {
return useContext(MittContext)
}

View File

@ -1,32 +1,20 @@
'use client'
import type { Dispatch, SetStateAction } from 'react'
import type { TriggerEventsLimitModalPayload } from './hooks/use-trigger-events-limit-modal'
import type { ReactNode, SetStateAction } from 'react'
import type { ModalState, ModelModalType } from './modal-context'
import type { OpeningStatement } from '@/app/components/base/features/types'
import type { CreateExternalAPIReq } from '@/app/components/datasets/external-api/declarations'
import type { AccountSettingTab } from '@/app/components/header/account-setting/constants'
import type {
ConfigurationMethodEnum,
Credential,
CustomConfigurationModelFixedFields,
CustomModel,
ModelModalModeEnum,
ModelProvider,
} from '@/app/components/header/account-setting/model-provider-page/declarations'
import type { ModelLoadBalancingModalProps } from '@/app/components/header/account-setting/model-provider-page/provider-added-card/model-load-balancing-modal'
import type { UpdatePluginPayload } from '@/app/components/plugins/types'
import type { InputVar } from '@/app/components/workflow/types'
import type { ExpireNoticeModalPayloadProps } from '@/app/education-apply/expire-notice-modal'
import type {
ApiBasedExtension,
ExternalDataTool,
} from '@/models/common'
import type { ApiBasedExtension, ExternalDataTool } from '@/models/common'
import type { ModerationConfig, PromptVariable } from '@/models/debug'
import { noop } from 'es-toolkit/function'
import dynamic from 'next/dynamic'
import { useCallback, useEffect, useRef, useState } from 'react'
import { createContext, useContext, useContextSelector } from 'use-context-selector'
import {
DEFAULT_ACCOUNT_SETTING_TAB,
isValidAccountSettingTab,
} from '@/app/components/header/account-setting/constants'
@ -39,11 +27,10 @@ import {
useAccountSettingModal,
usePricingModal,
} from '@/hooks/use-query-params'
import { useTriggerEventsLimitModal } from './hooks/use-trigger-events-limit-modal'
import {
useTriggerEventsLimitModal,
} from './hooks/use-trigger-events-limit-modal'
ModalContext,
} from './modal-context'
const AccountSetting = dynamic(() => import('@/app/components/header/account-setting'), {
ssr: false,
@ -86,79 +73,15 @@ const TriggerEventsLimitModal = dynamic(() => import('@/app/components/billing/t
ssr: false,
})
export type ModalState<T> = {
payload: T
onCancelCallback?: () => void
onSaveCallback?: (newPayload?: T, formValues?: Record<string, any>) => void
onRemoveCallback?: (newPayload?: T, formValues?: Record<string, any>) => void
onEditCallback?: (newPayload: T) => void
onValidateBeforeSaveCallback?: (newPayload: T) => boolean
isEditMode?: boolean
datasetBindings?: { id: string, name: string }[]
}
export type ModelModalType = {
currentProvider: ModelProvider
currentConfigurationMethod: ConfigurationMethodEnum
currentCustomConfigurationModelFixedFields?: CustomConfigurationModelFixedFields
isModelCredential?: boolean
credential?: Credential
model?: CustomModel
mode?: ModelModalModeEnum
}
export type ModalContextState = {
setShowAccountSettingModal: Dispatch<SetStateAction<ModalState<AccountSettingTab> | null>>
setShowApiBasedExtensionModal: Dispatch<SetStateAction<ModalState<ApiBasedExtension> | null>>
setShowModerationSettingModal: Dispatch<SetStateAction<ModalState<ModerationConfig> | null>>
setShowExternalDataToolModal: Dispatch<SetStateAction<ModalState<ExternalDataTool> | null>>
setShowPricingModal: () => void
setShowAnnotationFullModal: () => void
setShowModelModal: Dispatch<SetStateAction<ModalState<ModelModalType> | null>>
setShowExternalKnowledgeAPIModal: Dispatch<SetStateAction<ModalState<CreateExternalAPIReq> | null>>
setShowModelLoadBalancingModal: Dispatch<SetStateAction<ModelLoadBalancingModalProps | null>>
setShowOpeningModal: Dispatch<SetStateAction<ModalState<OpeningStatement & {
promptVariables?: PromptVariable[]
workflowVariables?: InputVar[]
onAutoAddPromptVariable?: (variable: PromptVariable[]) => void
}> | null>>
setShowUpdatePluginModal: Dispatch<SetStateAction<ModalState<UpdatePluginPayload> | null>>
setShowEducationExpireNoticeModal: Dispatch<SetStateAction<ModalState<ExpireNoticeModalPayloadProps> | null>>
setShowTriggerEventsLimitModal: Dispatch<SetStateAction<ModalState<TriggerEventsLimitModalPayload> | null>>
}
const ModalContext = createContext<ModalContextState>({
setShowAccountSettingModal: noop,
setShowApiBasedExtensionModal: noop,
setShowModerationSettingModal: noop,
setShowExternalDataToolModal: noop,
setShowPricingModal: noop,
setShowAnnotationFullModal: noop,
setShowModelModal: noop,
setShowExternalKnowledgeAPIModal: noop,
setShowModelLoadBalancingModal: noop,
setShowOpeningModal: noop,
setShowUpdatePluginModal: noop,
setShowEducationExpireNoticeModal: noop,
setShowTriggerEventsLimitModal: noop,
})
export const useModalContext = () => useContext(ModalContext)
// Adding a dangling comma to avoid the generic parsing issue in tsx, see:
// https://github.com/microsoft/TypeScript/issues/15713
export const useModalContextSelector = <T,>(selector: (state: ModalContextState) => T): T =>
useContextSelector(ModalContext, selector)
type ModalContextProviderProps = {
children: React.ReactNode
children: ReactNode
}
export const ModalContextProvider = ({
children,
}: ModalContextProviderProps) => {
// Use nuqs hooks for URL-based modal state management
const [showPricingModal, setPricingModalOpen] = usePricingModal()
const [urlAccountModalState, setUrlAccountModalState] = useAccountSettingModal<AccountSettingTab>()
const [urlAccountModalState, setUrlAccountModalState] = useAccountSettingModal()
const accountSettingCallbacksRef = useRef<Omit<ModalState<AccountSettingTab>, 'payload'> | null>(null)
const accountSettingTab = urlAccountModalState.isOpen

View File

@ -1,9 +1,9 @@
import { act, render, screen, waitFor } from '@testing-library/react'
import { NuqsTestingAdapter } from 'nuqs/adapters/testing'
import { act, screen, waitFor } from '@testing-library/react'
import * as React from 'react'
import { defaultPlan } from '@/app/components/billing/config'
import { Plan } from '@/app/components/billing/type'
import { ModalContextProvider } from '@/context/modal-context'
import { ModalContextProvider } from '@/context/modal-context-provider'
import { renderWithNuqs } from '@/test/nuqs-testing'
vi.mock('@/config', async (importOriginal) => {
const actual = await importOriginal<typeof import('@/config')>()
@ -71,12 +71,10 @@ const createPlan = (overrides: PlanOverrides = {}): PlanShape => ({
},
})
const renderProvider = () => render(
<NuqsTestingAdapter>
<ModalContextProvider>
<div data-testid="modal-context-test-child" />
</ModalContextProvider>
</NuqsTestingAdapter>,
const renderProvider = () => renderWithNuqs(
<ModalContextProvider>
<div data-testid="modal-context-test-child" />
</ModalContextProvider>,
)
describe('ModalContextProvider trigger events limit modal', () => {

View File

@ -0,0 +1,92 @@
'use client'
import type { Dispatch, SetStateAction } from 'react'
import type { TriggerEventsLimitModalPayload } from './hooks/use-trigger-events-limit-modal'
import type { OpeningStatement } from '@/app/components/base/features/types'
import type { CreateExternalAPIReq } from '@/app/components/datasets/external-api/declarations'
import type { AccountSettingTab } from '@/app/components/header/account-setting/constants'
import type {
ConfigurationMethodEnum,
Credential,
CustomConfigurationModelFixedFields,
CustomModel,
ModelModalModeEnum,
ModelProvider,
} from '@/app/components/header/account-setting/model-provider-page/declarations'
import type { ModelLoadBalancingModalProps } from '@/app/components/header/account-setting/model-provider-page/provider-added-card/model-load-balancing-modal'
import type { UpdatePluginPayload } from '@/app/components/plugins/types'
import type { InputVar } from '@/app/components/workflow/types'
import type { ExpireNoticeModalPayloadProps } from '@/app/education-apply/expire-notice-modal'
import type {
ApiBasedExtension,
ExternalDataTool,
} from '@/models/common'
import type { ModerationConfig, PromptVariable } from '@/models/debug'
import { noop } from 'es-toolkit/function'
import { createContext, useContext, useContextSelector } from 'use-context-selector'
export type ModalState<T> = {
payload: T
onCancelCallback?: () => void
onSaveCallback?: (newPayload?: T, formValues?: Record<string, any>) => void
onRemoveCallback?: (newPayload?: T, formValues?: Record<string, any>) => void
onEditCallback?: (newPayload: T) => void
onValidateBeforeSaveCallback?: (newPayload: T) => boolean
isEditMode?: boolean
datasetBindings?: { id: string, name: string }[]
}
export type ModelModalType = {
currentProvider: ModelProvider
currentConfigurationMethod: ConfigurationMethodEnum
currentCustomConfigurationModelFixedFields?: CustomConfigurationModelFixedFields
isModelCredential?: boolean
credential?: Credential
model?: CustomModel
mode?: ModelModalModeEnum
}
export type ModalContextState = {
setShowAccountSettingModal: Dispatch<SetStateAction<ModalState<AccountSettingTab> | null>>
setShowApiBasedExtensionModal: Dispatch<SetStateAction<ModalState<ApiBasedExtension> | null>>
setShowModerationSettingModal: Dispatch<SetStateAction<ModalState<ModerationConfig> | null>>
setShowExternalDataToolModal: Dispatch<SetStateAction<ModalState<ExternalDataTool> | null>>
setShowPricingModal: () => void
setShowAnnotationFullModal: () => void
setShowModelModal: Dispatch<SetStateAction<ModalState<ModelModalType> | null>>
setShowExternalKnowledgeAPIModal: Dispatch<SetStateAction<ModalState<CreateExternalAPIReq> | null>>
setShowModelLoadBalancingModal: Dispatch<SetStateAction<ModelLoadBalancingModalProps | null>>
setShowOpeningModal: Dispatch<SetStateAction<ModalState<OpeningStatement & {
promptVariables?: PromptVariable[]
workflowVariables?: InputVar[]
onAutoAddPromptVariable?: (variable: PromptVariable[]) => void
}> | null>>
setShowUpdatePluginModal: Dispatch<SetStateAction<ModalState<UpdatePluginPayload> | null>>
setShowEducationExpireNoticeModal: Dispatch<SetStateAction<ModalState<ExpireNoticeModalPayloadProps> | null>>
setShowTriggerEventsLimitModal: Dispatch<SetStateAction<ModalState<TriggerEventsLimitModalPayload> | null>>
}
export const ModalContext = createContext<ModalContextState>({
setShowAccountSettingModal: noop,
setShowApiBasedExtensionModal: noop,
setShowModerationSettingModal: noop,
setShowExternalDataToolModal: noop,
setShowPricingModal: noop,
setShowAnnotationFullModal: noop,
setShowModelModal: noop,
setShowExternalKnowledgeAPIModal: noop,
setShowModelLoadBalancingModal: noop,
setShowOpeningModal: noop,
setShowUpdatePluginModal: noop,
setShowEducationExpireNoticeModal: noop,
setShowTriggerEventsLimitModal: noop,
})
export const useModalContext = () => useContext(ModalContext)
// Adding a dangling comma to avoid the generic parsing issue in tsx, see:
// https://github.com/microsoft/TypeScript/issues/15713
export const useModalContextSelector = <T>(selector: (state: ModalContextState) => T): T =>
useContextSelector(ModalContext, selector)
export default ModalContext

View File

@ -1,14 +1,10 @@
'use client'
import type { Plan, UsagePlanInfo, UsageResetInfo } from '@/app/components/billing/type'
import type { Model, ModelProvider } from '@/app/components/header/account-setting/model-provider-page/declarations'
import type { RETRIEVE_METHOD } from '@/types/app'
import type { ReactNode } from 'react'
import { useQueryClient } from '@tanstack/react-query'
import dayjs from 'dayjs'
import { noop } from 'es-toolkit/function'
import { useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { createContext, useContext, useContextSelector } from 'use-context-selector'
import Toast from '@/app/components/base/toast'
import { setZendeskConversationFields } from '@/app/components/base/zendesk/utils'
import { defaultPlan } from '@/app/components/billing/config'
@ -25,93 +21,13 @@ import {
useModelProviders,
useSupportRetrievalMethods,
} from '@/service/use-common'
import {
useEducationStatus,
} from '@/service/use-education'
export type ProviderContextState = {
modelProviders: ModelProvider[]
refreshModelProviders: () => void
textGenerationModelList: Model[]
supportRetrievalMethods: RETRIEVE_METHOD[]
isAPIKeySet: boolean
plan: {
type: Plan
usage: UsagePlanInfo
total: UsagePlanInfo
reset: UsageResetInfo
}
isFetchedPlan: boolean
enableBilling: boolean
onPlanInfoChanged: () => void
enableReplaceWebAppLogo: boolean
modelLoadBalancingEnabled: boolean
datasetOperatorEnabled: boolean
enableEducationPlan: boolean
isEducationWorkspace: boolean
isEducationAccount: boolean
allowRefreshEducationVerify: boolean
educationAccountExpireAt: number | null
isLoadingEducationAccountInfo: boolean
isFetchingEducationAccountInfo: boolean
webappCopyrightEnabled: boolean
licenseLimit: {
workspace_members: {
size: number
limit: number
}
}
refreshLicenseLimit: () => void
isAllowTransferWorkspace: boolean
isAllowPublishAsCustomKnowledgePipelineTemplate: boolean
humanInputEmailDeliveryEnabled: boolean
}
export const baseProviderContextValue: ProviderContextState = {
modelProviders: [],
refreshModelProviders: noop,
textGenerationModelList: [],
supportRetrievalMethods: [],
isAPIKeySet: true,
plan: defaultPlan,
isFetchedPlan: false,
enableBilling: false,
onPlanInfoChanged: noop,
enableReplaceWebAppLogo: false,
modelLoadBalancingEnabled: false,
datasetOperatorEnabled: false,
enableEducationPlan: false,
isEducationWorkspace: false,
isEducationAccount: false,
allowRefreshEducationVerify: false,
educationAccountExpireAt: null,
isLoadingEducationAccountInfo: false,
isFetchingEducationAccountInfo: false,
webappCopyrightEnabled: false,
licenseLimit: {
workspace_members: {
size: 0,
limit: 0,
},
},
refreshLicenseLimit: noop,
isAllowTransferWorkspace: false,
isAllowPublishAsCustomKnowledgePipelineTemplate: false,
humanInputEmailDeliveryEnabled: false,
}
const ProviderContext = createContext<ProviderContextState>(baseProviderContextValue)
export const useProviderContext = () => useContext(ProviderContext)
// Adding a dangling comma to avoid the generic parsing issue in tsx, see:
// https://github.com/microsoft/TypeScript/issues/15713
export const useProviderContextSelector = <T,>(selector: (state: ProviderContextState) => T): T =>
useContextSelector(ProviderContext, selector)
import { useEducationStatus } from '@/service/use-education'
import { ProviderContext } from './provider-context'
type ProviderContextProviderProps = {
children: React.ReactNode
children: ReactNode
}
export const ProviderContextProvider = ({
children,
}: ProviderContextProviderProps) => {
@ -262,5 +178,3 @@ export const ProviderContextProvider = ({
</ProviderContext.Provider>
)
}
export default ProviderContext

View File

@ -0,0 +1,90 @@
'use client'
import type { Plan, UsagePlanInfo, UsageResetInfo } from '@/app/components/billing/type'
import type { Model, ModelProvider } from '@/app/components/header/account-setting/model-provider-page/declarations'
import type { RETRIEVE_METHOD } from '@/types/app'
import { noop } from 'es-toolkit/function'
import { createContext, useContext, useContextSelector } from 'use-context-selector'
import { defaultPlan } from '@/app/components/billing/config'
export type ProviderContextState = {
modelProviders: ModelProvider[]
refreshModelProviders: () => void
textGenerationModelList: Model[]
supportRetrievalMethods: RETRIEVE_METHOD[]
isAPIKeySet: boolean
plan: {
type: Plan
usage: UsagePlanInfo
total: UsagePlanInfo
reset: UsageResetInfo
}
isFetchedPlan: boolean
enableBilling: boolean
onPlanInfoChanged: () => void
enableReplaceWebAppLogo: boolean
modelLoadBalancingEnabled: boolean
datasetOperatorEnabled: boolean
enableEducationPlan: boolean
isEducationWorkspace: boolean
isEducationAccount: boolean
allowRefreshEducationVerify: boolean
educationAccountExpireAt: number | null
isLoadingEducationAccountInfo: boolean
isFetchingEducationAccountInfo: boolean
webappCopyrightEnabled: boolean
licenseLimit: {
workspace_members: {
size: number
limit: number
}
}
refreshLicenseLimit: () => void
isAllowTransferWorkspace: boolean
isAllowPublishAsCustomKnowledgePipelineTemplate: boolean
humanInputEmailDeliveryEnabled: boolean
}
export const baseProviderContextValue: ProviderContextState = {
modelProviders: [],
refreshModelProviders: noop,
textGenerationModelList: [],
supportRetrievalMethods: [],
isAPIKeySet: true,
plan: defaultPlan,
isFetchedPlan: false,
enableBilling: false,
onPlanInfoChanged: noop,
enableReplaceWebAppLogo: false,
modelLoadBalancingEnabled: false,
datasetOperatorEnabled: false,
enableEducationPlan: false,
isEducationWorkspace: false,
isEducationAccount: false,
allowRefreshEducationVerify: false,
educationAccountExpireAt: null,
isLoadingEducationAccountInfo: false,
isFetchingEducationAccountInfo: false,
webappCopyrightEnabled: false,
licenseLimit: {
workspace_members: {
size: 0,
limit: 0,
},
},
refreshLicenseLimit: noop,
isAllowTransferWorkspace: false,
isAllowPublishAsCustomKnowledgePipelineTemplate: false,
humanInputEmailDeliveryEnabled: false,
}
export const ProviderContext = createContext<ProviderContextState>(baseProviderContextValue)
export const useProviderContext = () => useContext(ProviderContext)
// Adding a dangling comma to avoid the generic parsing issue in tsx, see:
// https://github.com/microsoft/TypeScript/issues/15713
export const useProviderContextSelector = <T>(selector: (state: ProviderContextState) => T): T =>
useContextSelector(ProviderContext, selector)
export default ProviderContext

View File

@ -0,0 +1,24 @@
'use client'
import type { ReactNode } from 'react'
import { useWorkspaces } from '@/service/use-common'
import { WorkspacesContext } from './workspace-context'
type WorkspaceProviderProps = {
children: ReactNode
}
export const WorkspaceProvider = ({
children,
}: WorkspaceProviderProps) => {
const { data } = useWorkspaces()
return (
<WorkspacesContext.Provider value={{
workspaces: data?.workspaces || [],
}}
>
{children}
</WorkspacesContext.Provider>
)
}

View File

@ -0,0 +1,16 @@
'use client'
import type { IWorkspace } from '@/models/common'
import { createContext, useContext } from 'use-context-selector'
export type WorkspacesContextValue = {
workspaces: IWorkspace[]
}
export const WorkspacesContext = createContext<WorkspacesContextValue>({
workspaces: [],
})
export const useWorkspacesContext = () => useContext(WorkspacesContext)
export default WorkspacesContext

View File

@ -1,36 +0,0 @@
'use client'
import type { IWorkspace } from '@/models/common'
import { createContext, useContext } from 'use-context-selector'
import { useWorkspaces } from '@/service/use-common'
export type WorkspacesContextValue = {
workspaces: IWorkspace[]
}
const WorkspacesContext = createContext<WorkspacesContextValue>({
workspaces: [],
})
type IWorkspaceProviderProps = {
children: React.ReactNode
}
export const WorkspaceProvider = ({
children,
}: IWorkspaceProviderProps) => {
const { data } = useWorkspaces()
return (
<WorkspacesContext.Provider value={{
workspaces: data?.workspaces || [],
}}
>
{children}
</WorkspacesContext.Provider>
)
}
export const useWorkspacesContext = () => useContext(WorkspacesContext)
export default WorkspacesContext