This commit is contained in:
Joel
2026-01-08 18:27:47 +08:00
6071 changed files with 508042 additions and 236877 deletions

View File

@ -1,16 +1,16 @@
import { buildProviderQuery } from './_tools_util'
describe('makeProviderQuery', () => {
test('collectionName without special chars', () => {
it('collectionName without special chars', () => {
expect(buildProviderQuery('ABC')).toBe('provider=ABC')
})
test('should escape &', () => {
it('should escape &', () => {
expect(buildProviderQuery('ABC&DEF')).toBe('provider=ABC%26DEF')
})
test('should escape /', () => {
it('should escape /', () => {
expect(buildProviderQuery('ABC/DEF')).toBe('provider=ABC%2FDEF')
})
test('should escape ?', () => {
it('should escape ?', () => {
expect(buildProviderQuery('ABC?DEF')).toBe('provider=ABC%3FDEF')
})
})

View File

@ -1,16 +1,16 @@
import { useInfiniteQuery, useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
import { get, post } from './base'
import { getUserCanAccess } from './share'
import type { AccessControlAccount, AccessControlGroup, AccessMode, Subject } from '@/models/access-control'
import type { App } from '@/types/app'
import { useInfiniteQuery, useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
import { useGlobalPublicStore } from '@/context/global-public-context'
import { get, post } from './base'
import { getUserCanAccess } from './share'
const NAME_SPACE = 'access-control'
export const useAppWhiteListSubjects = (appId: string | undefined, enabled: boolean) => {
return useQuery({
queryKey: [NAME_SPACE, 'app-whitelist-subjects', appId],
queryFn: () => get<{ groups: AccessControlGroup[]; members: AccessControlAccount[] }>(`/enterprise/webapp/app/subjects?appId=${appId}`),
queryFn: () => get<{ groups: AccessControlGroup[], members: AccessControlAccount[] }>(`/enterprise/webapp/app/subjects?appId=${appId}`),
enabled: !!appId && enabled,
staleTime: 0,
gcTime: 0,
@ -24,7 +24,7 @@ type SearchResults = {
hasMore: boolean
}
export const useSearchForWhiteListCandidates = (query: { keyword?: string; groupId?: AccessControlGroup['id']; resultsPerPage?: number }, enabled: boolean) => {
export const useSearchForWhiteListCandidates = (query: { keyword?: string, groupId?: AccessControlGroup['id'], resultsPerPage?: number }, enabled: boolean) => {
return useInfiniteQuery({
queryKey: [NAME_SPACE, 'app-whitelist-candidates', query],
queryFn: ({ pageParam }) => {
@ -70,10 +70,10 @@ export const useUpdateAccessMode = () => {
})
}
export const useGetUserCanAccessApp = ({ appId, isInstalledApp = true, enabled }: { appId?: string; isInstalledApp?: boolean; enabled?: boolean }) => {
export const useGetUserCanAccessApp = ({ appId, isInstalledApp = true, enabled }: { appId?: string, isInstalledApp?: boolean, enabled?: boolean }) => {
const systemFeatures = useGlobalPublicStore(s => s.systemFeatures)
return useQuery({
queryKey: [NAME_SPACE, 'user-can-access-app', appId],
queryKey: [NAME_SPACE, 'user-can-access-app', appId, systemFeatures.webapp_auth.enabled, isInstalledApp],
queryFn: () => {
if (systemFeatures.webapp_auth.enabled)
return getUserCanAccess(appId!, isInstalledApp)

View File

@ -1,7 +1,6 @@
import type { Fetcher } from 'swr'
import { del, get, post } from './base'
import type { AnnotationEnableStatus, AnnotationItemBasic, EmbeddingModelConfig } from '@/app/components/app/annotation/type'
import type { AnnotationCreateResponse, AnnotationEnableStatus, AnnotationItemBasic, EmbeddingModelConfig } from '@/app/components/app/annotation/type'
import { ANNOTATION_DEFAULT } from '@/config'
import { del, get, post } from './base'
export const fetchAnnotationConfig = (appId: string) => {
return get(`apps/${appId}/annotation-setting`)
@ -41,15 +40,15 @@ export const fetchExportAnnotationList = (appId: string) => {
}
export const addAnnotation = (appId: string, body: AnnotationItemBasic) => {
return post(`apps/${appId}/annotations`, { body })
return post<AnnotationCreateResponse>(`apps/${appId}/annotations`, { body })
}
export const annotationBatchImport: Fetcher<{ job_id: string; job_status: string }, { url: string; body: FormData }> = ({ url, body }) => {
return post<{ job_id: string; job_status: string }>(url, { body }, { bodyStringify: false, deleteContentType: true })
export const annotationBatchImport = ({ url, body }: { url: string, body: FormData }): Promise<{ job_id: string, job_status: string }> => {
return post<{ job_id: string, job_status: string }>(url, { body }, { bodyStringify: false, deleteContentType: true })
}
export const checkAnnotationBatchImportProgress: Fetcher<{ job_id: string; job_status: string }, { jobID: string; appId: string }> = ({ jobID, appId }) => {
return get<{ job_id: string; job_status: string }>(`/apps/${appId}/annotations/batch-import-status/${jobID}`)
export const checkAnnotationBatchImportProgress = ({ jobID, appId }: { jobID: string, appId: string }): Promise<{ job_id: string, job_status: string }> => {
return get<{ job_id: string, job_status: string }>(`/apps/${appId}/annotations/batch-import-status/${jobID}`)
}
export const editAnnotation = (appId: string, annotationId: string, body: AnnotationItemBasic) => {

View File

@ -1,41 +1,89 @@
import type { Fetcher } from 'swr'
import { del, get, patch, post, put } from './base'
import type { TracingProvider } from '@/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/type'
import type { ApiKeysListResponse, AppDailyConversationsResponse, AppDailyEndUsersResponse, AppDailyMessagesResponse, AppDetailResponse, AppListResponse, AppStatisticsResponse, AppTemplatesResponse, AppTokenCostsResponse, AppVoicesListResponse, CreateApiKeyResponse, DSLImportMode, DSLImportResponse, GenerationIntroductionResponse, TracingConfig, TracingStatus, UpdateAppModelConfigResponse, UpdateAppSiteCodeResponse, UpdateOpenAIKeyResponse, ValidateOpenAIKeyResponse, WebhookTriggerResponse, WorkflowDailyConversationsResponse } from '@/models/app'
import type { CommonResponse } from '@/models/common'
import type { AppIconType, AppModeEnum, ModelConfig } from '@/types/app'
import type { TracingProvider } from '@/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/type'
import { del, get, patch, post, put } from './base'
export const fetchAppList: Fetcher<AppListResponse, { url: string; params?: Record<string, any> }> = ({ url, params }) => {
export const fetchAppList = ({ url, params }: { url: string, params?: Record<string, any> }): Promise<AppListResponse> => {
return get<AppListResponse>(url, { params })
}
export const fetchAppDetail: Fetcher<AppDetailResponse, { url: string; id: string }> = ({ url, id }) => {
export const fetchAppDetail = ({ url, id }: { url: string, id: string }): Promise<AppDetailResponse> => {
return get<AppDetailResponse>(`${url}/${id}`)
}
// Direct API call function for non-SWR usage
export const fetchAppDetailDirect = async ({ url, id }: { url: string; id: string }): Promise<AppDetailResponse> => {
export const fetchAppDetailDirect = async ({ url, id }: { url: string, id: string }): Promise<AppDetailResponse> => {
return get<AppDetailResponse>(`${url}/${id}`)
}
export const fetchAppTemplates: Fetcher<AppTemplatesResponse, { url: string }> = ({ url }) => {
export const fetchAppTemplates = ({ url }: { url: string }): Promise<AppTemplatesResponse> => {
return get<AppTemplatesResponse>(url)
}
export const createApp: Fetcher<AppDetailResponse, { name: string; icon_type?: AppIconType; icon?: string; icon_background?: string; mode: AppModeEnum; description?: string; config?: ModelConfig }> = ({ name, icon_type, icon, icon_background, mode, description, config }) => {
export const createApp = ({
name,
icon_type,
icon,
icon_background,
mode,
description,
config,
}: {
name: string
icon_type?: AppIconType
icon?: string
icon_background?: string
mode: AppModeEnum
description?: string
config?: ModelConfig
}): Promise<AppDetailResponse> => {
return post<AppDetailResponse>('apps', { body: { name, icon_type, icon, icon_background, mode, description, model_config: config } })
}
export const updateAppInfo: Fetcher<AppDetailResponse, { appID: string; name: string; icon_type: AppIconType; icon: string; icon_background?: string; description: string; use_icon_as_answer_icon?: boolean; max_active_requests?: number | null }> = ({ appID, name, icon_type, icon, icon_background, description, use_icon_as_answer_icon, max_active_requests }) => {
export const updateAppInfo = ({
appID,
name,
icon_type,
icon,
icon_background,
description,
use_icon_as_answer_icon,
max_active_requests,
}: {
appID: string
name: string
icon_type: AppIconType
icon: string
icon_background?: string
description: string
use_icon_as_answer_icon?: boolean
max_active_requests?: number | null
}): Promise<AppDetailResponse> => {
const body = { name, icon_type, icon, icon_background, description, use_icon_as_answer_icon, max_active_requests }
return put<AppDetailResponse>(`apps/${appID}`, { body })
}
export const copyApp: Fetcher<AppDetailResponse, { appID: string; name: string; icon_type: AppIconType; icon: string; icon_background?: string | null; mode: AppModeEnum; description?: string }> = ({ appID, name, icon_type, icon, icon_background, mode, description }) => {
export const copyApp = ({
appID,
name,
icon_type,
icon,
icon_background,
mode,
description,
}: {
appID: string
name: string
icon_type: AppIconType
icon: string
icon_background?: string | null
mode: AppModeEnum
description?: string
}): Promise<AppDetailResponse> => {
return post<AppDetailResponse>(`apps/${appID}/copy`, { body: { name, icon_type, icon, icon_background, mode, description } })
}
export const exportAppConfig: Fetcher<{ data: string }, { appID: string; include?: boolean; workflowID?: string }> = ({ appID, include = false, workflowID }) => {
export const exportAppConfig = ({ appID, include = false, workflowID }: { appID: string, include?: boolean, workflowID?: string }): Promise<{ data: string }> => {
const params = new URLSearchParams({
include_secret: include.toString(),
})
@ -44,145 +92,139 @@ export const exportAppConfig: Fetcher<{ data: string }, { appID: string; include
return get<{ data: string }>(`apps/${appID}/export?${params.toString()}`)
}
// TODO: delete
export const importApp: Fetcher<AppDetailResponse, { data: string; name?: string; description?: string; icon_type?: AppIconType; icon?: string; icon_background?: string }> = ({ data, name, description, icon_type, icon, icon_background }) => {
return post<AppDetailResponse>('apps/import', { body: { data, name, description, icon_type, icon, icon_background } })
}
// TODO: delete
export const importAppFromUrl: Fetcher<AppDetailResponse, { url: string; name?: string; description?: string; icon?: string; icon_background?: string }> = ({ url, name, description, icon, icon_background }) => {
return post<AppDetailResponse>('apps/import/url', { body: { url, name, description, icon, icon_background } })
}
export const importDSL: Fetcher<DSLImportResponse, { mode: DSLImportMode; yaml_content?: string; yaml_url?: string; app_id?: string; name?: string; description?: string; icon_type?: AppIconType; icon?: string; icon_background?: string }> = ({ mode, yaml_content, yaml_url, app_id, name, description, icon_type, icon, icon_background }) => {
export const importDSL = ({ mode, yaml_content, yaml_url, app_id, name, description, icon_type, icon, icon_background }: { mode: DSLImportMode, yaml_content?: string, yaml_url?: string, app_id?: string, name?: string, description?: string, icon_type?: AppIconType, icon?: string, icon_background?: string }): Promise<DSLImportResponse> => {
return post<DSLImportResponse>('apps/imports', { body: { mode, yaml_content, yaml_url, app_id, name, description, icon, icon_type, icon_background } })
}
export const importDSLConfirm: Fetcher<DSLImportResponse, { import_id: string }> = ({ import_id }) => {
export const importDSLConfirm = ({ import_id }: { import_id: string }): Promise<DSLImportResponse> => {
return post<DSLImportResponse>(`apps/imports/${import_id}/confirm`, { body: {} })
}
export const switchApp: Fetcher<{ new_app_id: string }, { appID: string; name: string; icon_type: AppIconType; icon: string; icon_background?: string | null }> = ({ appID, name, icon_type, icon, icon_background }) => {
export const switchApp = ({ appID, name, icon_type, icon, icon_background }: { appID: string, name: string, icon_type: AppIconType, icon: string, icon_background?: string | null }): Promise<{ new_app_id: string }> => {
return post<{ new_app_id: string }>(`apps/${appID}/convert-to-workflow`, { body: { name, icon_type, icon, icon_background } })
}
export const deleteApp: Fetcher<CommonResponse, string> = (appID) => {
export const deleteApp = (appID: string): Promise<CommonResponse> => {
return del<CommonResponse>(`apps/${appID}`)
}
export const updateAppSiteStatus: Fetcher<AppDetailResponse, { url: string; body: Record<string, any> }> = ({ url, body }) => {
export const updateAppSiteStatus = ({ url, body }: { url: string, body: Record<string, any> }): Promise<AppDetailResponse> => {
return post<AppDetailResponse>(url, { body })
}
export const updateAppApiStatus: Fetcher<AppDetailResponse, { url: string; body: Record<string, any> }> = ({ url, body }) => {
export const updateAppApiStatus = ({ url, body }: { url: string, body: Record<string, any> }): Promise<AppDetailResponse> => {
return post<AppDetailResponse>(url, { body })
}
// path: /apps/{appId}/rate-limit
export const updateAppRateLimit: Fetcher<AppDetailResponse, { url: string; body: Record<string, any> }> = ({ url, body }) => {
export const updateAppRateLimit = ({ url, body }: { url: string, body: Record<string, any> }): Promise<AppDetailResponse> => {
return post<AppDetailResponse>(url, { body })
}
export const updateAppSiteAccessToken: Fetcher<UpdateAppSiteCodeResponse, { url: string }> = ({ url }) => {
export const updateAppSiteAccessToken = ({ url }: { url: string }): Promise<UpdateAppSiteCodeResponse> => {
return post<UpdateAppSiteCodeResponse>(url)
}
export const updateAppSiteConfig = ({ url, body }: { url: string; body: Record<string, any> }) => {
export const updateAppSiteConfig = ({ url, body }: { url: string, body: Record<string, any> }): Promise<AppDetailResponse> => {
return post<AppDetailResponse>(url, { body })
}
export const getAppDailyMessages: Fetcher<AppDailyMessagesResponse, { url: string; params: Record<string, any> }> = ({ url, params }) => {
export const getAppDailyMessages = ({ url, params }: { url: string, params: Record<string, any> }): Promise<AppDailyMessagesResponse> => {
return get<AppDailyMessagesResponse>(url, { params })
}
export const getAppDailyConversations: Fetcher<AppDailyConversationsResponse, { url: string; params: Record<string, any> }> = ({ url, params }) => {
export const getAppDailyConversations = ({ url, params }: { url: string, params: Record<string, any> }): Promise<AppDailyConversationsResponse> => {
return get<AppDailyConversationsResponse>(url, { params })
}
export const getWorkflowDailyConversations: Fetcher<WorkflowDailyConversationsResponse, { url: string; params: Record<string, any> }> = ({ url, params }) => {
export const getWorkflowDailyConversations = ({ url, params }: { url: string, params: Record<string, any> }): Promise<WorkflowDailyConversationsResponse> => {
return get<WorkflowDailyConversationsResponse>(url, { params })
}
export const getAppStatistics: Fetcher<AppStatisticsResponse, { url: string; params: Record<string, any> }> = ({ url, params }) => {
export const getAppStatistics = ({ url, params }: { url: string, params: Record<string, any> }): Promise<AppStatisticsResponse> => {
return get<AppStatisticsResponse>(url, { params })
}
export const getAppDailyEndUsers: Fetcher<AppDailyEndUsersResponse, { url: string; params: Record<string, any> }> = ({ url, params }) => {
export const getAppDailyEndUsers = ({ url, params }: { url: string, params: Record<string, any> }): Promise<AppDailyEndUsersResponse> => {
return get<AppDailyEndUsersResponse>(url, { params })
}
export const getAppTokenCosts: Fetcher<AppTokenCostsResponse, { url: string; params: Record<string, any> }> = ({ url, params }) => {
export const getAppTokenCosts = ({ url, params }: { url: string, params: Record<string, any> }): Promise<AppTokenCostsResponse> => {
return get<AppTokenCostsResponse>(url, { params })
}
export const updateAppModelConfig: Fetcher<UpdateAppModelConfigResponse, { url: string; body: Record<string, any> }> = ({ url, body }) => {
export const updateAppModelConfig = ({ url, body }: { url: string, body: Record<string, any> }): Promise<UpdateAppModelConfigResponse> => {
return post<UpdateAppModelConfigResponse>(url, { body })
}
// For temp testing
export const fetchAppListNoMock: Fetcher<AppListResponse, { url: string; params: Record<string, any> }> = ({ url, params }) => {
export const fetchAppListNoMock = ({ url, params }: { url: string, params: Record<string, any> }): Promise<AppListResponse> => {
return get<AppListResponse>(url, params)
}
export const fetchApiKeysList: Fetcher<ApiKeysListResponse, { url: string; params: Record<string, any> }> = ({ url, params }) => {
export const fetchApiKeysList = ({ url, params }: { url: string, params: Record<string, any> }): Promise<ApiKeysListResponse> => {
return get<ApiKeysListResponse>(url, params)
}
export const delApikey: Fetcher<CommonResponse, { url: string; params: Record<string, any> }> = ({ url, params }) => {
export const delApikey = ({ url, params }: { url: string, params: Record<string, any> }): Promise<CommonResponse> => {
return del<CommonResponse>(url, params)
}
export const createApikey: Fetcher<CreateApiKeyResponse, { url: string; body: Record<string, any> }> = ({ url, body }) => {
export const createApikey = ({ url, body }: { url: string, body: Record<string, any> }): Promise<CreateApiKeyResponse> => {
return post<CreateApiKeyResponse>(url, body)
}
export const validateOpenAIKey: Fetcher<ValidateOpenAIKeyResponse, { url: string; body: { token: string } }> = ({ url, body }) => {
export const validateOpenAIKey = ({ url, body }: { url: string, body: { token: string } }): Promise<ValidateOpenAIKeyResponse> => {
return post<ValidateOpenAIKeyResponse>(url, { body })
}
export const updateOpenAIKey: Fetcher<UpdateOpenAIKeyResponse, { url: string; body: { token: string } }> = ({ url, body }) => {
export const updateOpenAIKey = ({ url, body }: { url: string, body: { token: string } }): Promise<UpdateOpenAIKeyResponse> => {
return post<UpdateOpenAIKeyResponse>(url, { body })
}
export const generationIntroduction: Fetcher<GenerationIntroductionResponse, { url: string; body: { prompt_template: string } }> = ({ url, body }) => {
export const generationIntroduction = ({ url, body }: { url: string, body: { prompt_template: string } }): Promise<GenerationIntroductionResponse> => {
return post<GenerationIntroductionResponse>(url, { body })
}
export const fetchAppVoices: Fetcher<AppVoicesListResponse, { appId: string; language?: string }> = ({ appId, language }) => {
export const fetchAppVoices = ({ appId, language }: { appId: string, language?: string }): Promise<AppVoicesListResponse> => {
language = language || 'en-US'
return get<AppVoicesListResponse>(`apps/${appId}/text-to-audio/voices?language=${language}`)
}
// Tracing
export const fetchTracingStatus: Fetcher<TracingStatus, { appId: string }> = ({ appId }) => {
return get(`/apps/${appId}/trace`)
export const fetchTracingStatus = ({ appId }: { appId: string }): Promise<TracingStatus> => {
return get<TracingStatus>(`/apps/${appId}/trace`)
}
export const updateTracingStatus: Fetcher<CommonResponse, { appId: string; body: Record<string, any> }> = ({ appId, body }) => {
return post(`/apps/${appId}/trace`, { body })
export const updateTracingStatus = ({ appId, body }: { appId: string, body: Record<string, any> }): Promise<CommonResponse> => {
return post<CommonResponse>(`/apps/${appId}/trace`, { body })
}
// Webhook Trigger
export const fetchWebhookUrl: Fetcher<WebhookTriggerResponse, { appId: string; nodeId: string }> = ({ appId, nodeId }) => {
return get<WebhookTriggerResponse>(`apps/${appId}/workflows/triggers/webhook`, { params: { node_id: nodeId } })
export const fetchWebhookUrl = ({ appId, nodeId }: { appId: string, nodeId: string }): Promise<WebhookTriggerResponse> => {
return get<WebhookTriggerResponse>(
`apps/${appId}/workflows/triggers/webhook`,
{ params: { node_id: nodeId } },
{ silent: true },
)
}
export const fetchTracingConfig: Fetcher<TracingConfig & { has_not_configured: true }, { appId: string; provider: TracingProvider }> = ({ appId, provider }) => {
return get(`/apps/${appId}/trace-config`, {
export const fetchTracingConfig = ({ appId, provider }: { appId: string, provider: TracingProvider }): Promise<TracingConfig & { has_not_configured: true }> => {
return get<TracingConfig & { has_not_configured: true }>(`/apps/${appId}/trace-config`, {
params: {
tracing_provider: provider,
},
})
}
export const addTracingConfig: Fetcher<CommonResponse, { appId: string; body: TracingConfig }> = ({ appId, body }) => {
return post(`/apps/${appId}/trace-config`, { body })
export const addTracingConfig = ({ appId, body }: { appId: string, body: TracingConfig }): Promise<CommonResponse> => {
return post<CommonResponse>(`/apps/${appId}/trace-config`, { body })
}
export const updateTracingConfig: Fetcher<CommonResponse, { appId: string; body: TracingConfig }> = ({ appId, body }) => {
return patch(`/apps/${appId}/trace-config`, { body })
export const updateTracingConfig = ({ appId, body }: { appId: string, body: TracingConfig }): Promise<CommonResponse> => {
return patch<CommonResponse>(`/apps/${appId}/trace-config`, { body })
}
export const removeTracingConfig: Fetcher<CommonResponse, { appId: string; provider: TracingProvider }> = ({ appId, provider }) => {
return del(`/apps/${appId}/trace-config?tracing_provider=${provider}`)
export const removeTracingConfig = ({ appId, provider }: { appId: string, provider: TracingProvider }): Promise<CommonResponse> => {
return del<CommonResponse>(`/apps/${appId}/trace-config?tracing_provider=${provider}`)
}

View File

@ -1,9 +1,11 @@
import { API_PREFIX, CSRF_COOKIE_NAME, CSRF_HEADER_NAME, IS_CE_EDITION, PASSPORT_HEADER_NAME, PUBLIC_API_PREFIX, WEB_APP_SHARE_CODE_HEADER_NAME } from '@/config'
import { refreshAccessTokenOrRelogin } from './refresh-token'
import Toast from '@/app/components/base/toast'
import { basePath } from '@/utils/var'
import type { FetchOptionType, ResponseError } from './fetch'
import type { AnnotationReply, MessageEnd, MessageReplace, ThoughtItem } from '@/app/components/base/chat/chat/type'
import type { VisionFile } from '@/types/app'
import type {
DataSourceNodeCompletedResponse,
DataSourceNodeErrorResponse,
DataSourceNodeProcessingResponse,
} from '@/types/pipeline'
import type {
AgentLogResponse,
IterationFinishedResponse,
@ -21,16 +23,15 @@ import type {
WorkflowFinishedResponse,
WorkflowStartedResponse,
} from '@/types/workflow'
import type { FetchOptionType, ResponseError } from './fetch'
import { ContentType, base, getBaseOptions } from './fetch'
import { asyncRunSafe } from '@/utils'
import type {
DataSourceNodeCompletedResponse,
DataSourceNodeErrorResponse,
DataSourceNodeProcessingResponse,
} from '@/types/pipeline'
import Cookies from 'js-cookie'
import Toast from '@/app/components/base/toast'
import { API_PREFIX, CSRF_COOKIE_NAME, CSRF_HEADER_NAME, IS_CE_EDITION, PASSPORT_HEADER_NAME, PUBLIC_API_PREFIX, WEB_APP_SHARE_CODE_HEADER_NAME } from '@/config'
import { asyncRunSafe } from '@/utils'
import { basePath } from '@/utils/var'
import { base, ContentType, getBaseOptions } from './fetch'
import { refreshAccessTokenOrRelogin } from './refresh-token'
import { getWebAppPassport } from './webapp-auth'
const TIME_OUT = 100000
export type IOnDataMoreInfo = {
@ -144,7 +145,7 @@ function requiredWebSSOLogin(message?: string, code?: number) {
params.append('message', message)
if (code)
params.append('code', String(code))
globalThis.location.href = `${globalThis.location.origin}${basePath}/${WBB_APP_LOGIN_PATH}?${params.toString()}`
globalThis.location.href = `${globalThis.location.origin}${basePath}${WBB_APP_LOGIN_PATH}?${params.toString()}`
}
export function format(text: string) {
@ -464,7 +465,7 @@ export const ssePost = async (
if (!/^[23]\d{2}$/.test(String(res.status))) {
if (res.status === 401) {
if (isPublicAPI) {
res.json().then((data: { code?: string; message?: string }) => {
res.json().then((data: { code?: string, message?: string }) => {
if (isPublicAPI) {
if (data.code === 'web_app_access_denied')
requiredWebSSOLogin(data.message, 403)
@ -532,7 +533,8 @@ export const ssePost = async (
onDataSourceNodeCompleted,
onDataSourceNodeError,
)
}).catch((e) => {
})
.catch((e) => {
if (e.toString() !== 'AbortError: The user aborted a request.' && !e.toString().includes('TypeError: Cannot assign to read only property'))
Toast.notify({ type: 'error', message: e })
onError?.(e)

View File

@ -1,5 +1,5 @@
import { get } from './base'
import type { CurrentPlanInfoBackend, SubscriptionUrlsBackend } from '@/app/components/billing/type'
import { get, put } from './base'
export const fetchCurrentPlanInfo = () => {
return get<CurrentPlanInfoBackend>('/features')
@ -12,3 +12,13 @@ export const fetchSubscriptionUrls = (plan: string, interval: string) => {
export const fetchBillingUrl = () => {
return get<{ url: string }>('/billing/invoices')
}
export const bindPartnerStackInfo = (partnerKey: string, clickId: string) => {
return put(`/billing/partners/${partnerKey}/tenants`, {
body: {
click_id: clickId,
},
}, {
silent: true,
})
}

View File

@ -1,5 +1,16 @@
import type { Fetcher } from 'swr'
import { del, get, patch, post, put } from './base'
import type {
DefaultModelResponse,
Model,
ModelItem,
ModelLoadBalancingConfig,
ModelParameterRule,
ModelProvider,
ModelTypeEnum,
} from '@/app/components/header/account-setting/model-provider-page/declarations'
import type {
UpdateOpenAIKeyResponse,
ValidateOpenAIKeyResponse,
} from '@/models/app'
import type {
AccountIntegrate,
ApiBasedExtension,
@ -8,9 +19,9 @@ import type {
DataSourceNotion,
FileUploadConfigResponse,
ICurrentWorkspace,
IWorkspace,
InitValidateStatusResponse,
InvitationResponse,
IWorkspace,
LangGeniusVersionResponse,
Member,
ModerateResponse,
@ -22,21 +33,9 @@ import type {
SetupStatusResponse,
UserProfileOriginResponse,
} from '@/models/common'
import type {
UpdateOpenAIKeyResponse,
ValidateOpenAIKeyResponse,
} from '@/models/app'
import type {
DefaultModelResponse,
Model,
ModelItem,
ModelLoadBalancingConfig,
ModelParameterRule,
ModelProvider,
ModelTypeEnum,
} from '@/app/components/header/account-setting/model-provider-page/declarations'
import type { RETRIEVE_METHOD } from '@/types/app'
import type { SystemFeatures } from '@/types/feature'
import { del, get, patch, post, put } from './base'
type LoginSuccess = {
result: 'success'
@ -49,145 +48,145 @@ type LoginFail = {
message: string
}
type LoginResponse = LoginSuccess | LoginFail
export const login: Fetcher<LoginResponse, { url: string; body: Record<string, any> }> = ({ url, body }) => {
return post(url, { body }) as Promise<LoginResponse>
export const login = ({ url, body }: { url: string, body: Record<string, any> }): Promise<LoginResponse> => {
return post<LoginResponse>(url, { body })
}
export const webAppLogin: Fetcher<LoginResponse, { url: string; body: Record<string, any> }> = ({ url, body }) => {
return post(url, { body }, { isPublicAPI: true }) as Promise<LoginResponse>
export const webAppLogin = ({ url, body }: { url: string, body: Record<string, any> }): Promise<LoginResponse> => {
return post<LoginResponse>(url, { body }, { isPublicAPI: true })
}
export const setup: Fetcher<CommonResponse, { body: Record<string, any> }> = ({ body }) => {
export const setup = ({ body }: { body: Record<string, any> }): Promise<CommonResponse> => {
return post<CommonResponse>('/setup', { body })
}
export const initValidate: Fetcher<CommonResponse, { body: Record<string, any> }> = ({ body }) => {
export const initValidate = ({ body }: { body: Record<string, any> }): Promise<CommonResponse> => {
return post<CommonResponse>('/init', { body })
}
export const fetchInitValidateStatus = () => {
export const fetchInitValidateStatus = (): Promise<InitValidateStatusResponse> => {
return get<InitValidateStatusResponse>('/init')
}
export const fetchSetupStatus = () => {
export const fetchSetupStatus = (): Promise<SetupStatusResponse> => {
return get<SetupStatusResponse>('/setup')
}
export const fetchUserProfile: Fetcher<UserProfileOriginResponse, { url: string; params: Record<string, any> }> = ({ url, params }) => {
export const fetchUserProfile = ({ url, params }: { url: string, params: Record<string, any> }): Promise<UserProfileOriginResponse> => {
return get<UserProfileOriginResponse>(url, params, { needAllResponseContent: true })
}
export const updateUserProfile: Fetcher<CommonResponse, { url: string; body: Record<string, any> }> = ({ url, body }) => {
export const updateUserProfile = ({ url, body }: { url: string, body: Record<string, any> }): Promise<CommonResponse> => {
return post<CommonResponse>(url, { body })
}
export const fetchLangGeniusVersion: Fetcher<LangGeniusVersionResponse, { url: string; params: Record<string, any> }> = ({ url, params }) => {
export const fetchLangGeniusVersion = ({ url, params }: { url: string, params: Record<string, any> }): Promise<LangGeniusVersionResponse> => {
return get<LangGeniusVersionResponse>(url, { params })
}
export const oauth: Fetcher<OauthResponse, { url: string; params: Record<string, any> }> = ({ url, params }) => {
export const oauth = ({ url, params }: { url: string, params: Record<string, any> }): Promise<OauthResponse> => {
return get<OauthResponse>(url, { params })
}
export const oneMoreStep: Fetcher<CommonResponse, { url: string; body: Record<string, any> }> = ({ url, body }) => {
export const oneMoreStep = ({ url, body }: { url: string, body: Record<string, any> }): Promise<CommonResponse> => {
return post<CommonResponse>(url, { body })
}
export const fetchMembers: Fetcher<{ accounts: Member[] | null }, { url: string; params: Record<string, any> }> = ({ url, params }) => {
export const fetchMembers = ({ url, params }: { url: string, params: Record<string, any> }): Promise<{ accounts: Member[] | null }> => {
return get<{ accounts: Member[] | null }>(url, { params })
}
export const fetchProviders: Fetcher<Provider[] | null, { url: string; params: Record<string, any> }> = ({ url, params }) => {
export const fetchProviders = ({ url, params }: { url: string, params: Record<string, any> }): Promise<Provider[] | null> => {
return get<Provider[] | null>(url, { params })
}
export const validateProviderKey: Fetcher<ValidateOpenAIKeyResponse, { url: string; body: { token: string } }> = ({ url, body }) => {
export const validateProviderKey = ({ url, body }: { url: string, body: { token: string } }): Promise<ValidateOpenAIKeyResponse> => {
return post<ValidateOpenAIKeyResponse>(url, { body })
}
export const updateProviderAIKey: Fetcher<UpdateOpenAIKeyResponse, { url: string; body: { token: string | ProviderAzureToken | ProviderAnthropicToken } }> = ({ url, body }) => {
export const updateProviderAIKey = ({ url, body }: { url: string, body: { token: string | ProviderAzureToken | ProviderAnthropicToken } }): Promise<UpdateOpenAIKeyResponse> => {
return post<UpdateOpenAIKeyResponse>(url, { body })
}
export const fetchAccountIntegrates: Fetcher<{ data: AccountIntegrate[] | null }, { url: string; params: Record<string, any> }> = ({ url, params }) => {
export const fetchAccountIntegrates = ({ url, params }: { url: string, params: Record<string, any> }): Promise<{ data: AccountIntegrate[] | null }> => {
return get<{ data: AccountIntegrate[] | null }>(url, { params })
}
export const inviteMember: Fetcher<InvitationResponse, { url: string; body: Record<string, any> }> = ({ url, body }) => {
export const inviteMember = ({ url, body }: { url: string, body: Record<string, any> }): Promise<InvitationResponse> => {
return post<InvitationResponse>(url, { body })
}
export const updateMemberRole: Fetcher<CommonResponse, { url: string; body: Record<string, any> }> = ({ url, body }) => {
export const updateMemberRole = ({ url, body }: { url: string, body: Record<string, any> }): Promise<CommonResponse> => {
return put<CommonResponse>(url, { body })
}
export const deleteMemberOrCancelInvitation: Fetcher<CommonResponse, { url: string }> = ({ url }) => {
export const deleteMemberOrCancelInvitation = ({ url }: { url: string }): Promise<CommonResponse> => {
return del<CommonResponse>(url)
}
export const sendOwnerEmail = (body: { language?: string }) =>
export const sendOwnerEmail = (body: { language?: string }): Promise<CommonResponse & { data: string }> =>
post<CommonResponse & { data: string }>('/workspaces/current/members/send-owner-transfer-confirm-email', { body })
export const verifyOwnerEmail = (body: { code: string; token: string }) =>
post<CommonResponse & { is_valid: boolean; email: string; token: string }>('/workspaces/current/members/owner-transfer-check', { body })
export const verifyOwnerEmail = (body: { code: string, token: string }): Promise<CommonResponse & { is_valid: boolean, email: string, token: string }> =>
post<CommonResponse & { is_valid: boolean, email: string, token: string }>('/workspaces/current/members/owner-transfer-check', { body })
export const ownershipTransfer = (memberID: string, body: { token: string }) =>
post<CommonResponse & { is_valid: boolean; email: string; token: string }>(`/workspaces/current/members/${memberID}/owner-transfer`, { body })
export const ownershipTransfer = (memberID: string, body: { token: string }): Promise<CommonResponse & { is_valid: boolean, email: string, token: string }> =>
post<CommonResponse & { is_valid: boolean, email: string, token: string }>(`/workspaces/current/members/${memberID}/owner-transfer`, { body })
export const fetchFilePreview: Fetcher<{ content: string }, { fileID: string }> = ({ fileID }) => {
export const fetchFilePreview = ({ fileID }: { fileID: string }): Promise<{ content: string }> => {
return get<{ content: string }>(`/files/${fileID}/preview`)
}
export const fetchCurrentWorkspace: Fetcher<ICurrentWorkspace, { url: string; params: Record<string, any> }> = ({ url, params }) => {
return get<ICurrentWorkspace>(url, { params })
export const fetchCurrentWorkspace = ({ url, params }: { url: string, params: Record<string, any> }): Promise<ICurrentWorkspace> => {
return post<ICurrentWorkspace>(url, { body: params })
}
export const updateCurrentWorkspace: Fetcher<ICurrentWorkspace, { url: string; body: Record<string, any> }> = ({ url, body }) => {
export const updateCurrentWorkspace = ({ url, body }: { url: string, body: Record<string, any> }): Promise<ICurrentWorkspace> => {
return post<ICurrentWorkspace>(url, { body })
}
export const fetchWorkspaces: Fetcher<{ workspaces: IWorkspace[] }, { url: string; params: Record<string, any> }> = ({ url, params }) => {
export const fetchWorkspaces = ({ url, params }: { url: string, params: Record<string, any> }): Promise<{ workspaces: IWorkspace[] }> => {
return get<{ workspaces: IWorkspace[] }>(url, { params })
}
export const switchWorkspace: Fetcher<CommonResponse & { new_tenant: IWorkspace }, { url: string; body: Record<string, any> }> = ({ url, body }) => {
export const switchWorkspace = ({ url, body }: { url: string, body: Record<string, any> }): Promise<CommonResponse & { new_tenant: IWorkspace }> => {
return post<CommonResponse & { new_tenant: IWorkspace }>(url, { body })
}
export const updateWorkspaceInfo: Fetcher<ICurrentWorkspace, { url: string; body: Record<string, any> }> = ({ url, body }) => {
export const updateWorkspaceInfo = ({ url, body }: { url: string, body: Record<string, any> }): Promise<ICurrentWorkspace> => {
return post<ICurrentWorkspace>(url, { body })
}
export const fetchDataSource: Fetcher<{ data: DataSourceNotion[] }, { url: string }> = ({ url }) => {
export const fetchDataSource = ({ url }: { url: string }): Promise<{ data: DataSourceNotion[] }> => {
return get<{ data: DataSourceNotion[] }>(url)
}
export const syncDataSourceNotion: Fetcher<CommonResponse, { url: string }> = ({ url }) => {
export const syncDataSourceNotion = ({ url }: { url: string }): Promise<CommonResponse> => {
return get<CommonResponse>(url)
}
export const updateDataSourceNotionAction: Fetcher<CommonResponse, { url: string }> = ({ url }) => {
export const updateDataSourceNotionAction = ({ url }: { url: string }): Promise<CommonResponse> => {
return patch<CommonResponse>(url)
}
export const fetchPluginProviders: Fetcher<PluginProvider[] | null, string> = (url) => {
export const fetchPluginProviders = (url: string): Promise<PluginProvider[] | null> => {
return get<PluginProvider[] | null>(url)
}
export const validatePluginProviderKey: Fetcher<ValidateOpenAIKeyResponse, { url: string; body: { credentials: any } }> = ({ url, body }) => {
export const validatePluginProviderKey = ({ url, body }: { url: string, body: { credentials: any } }): Promise<ValidateOpenAIKeyResponse> => {
return post<ValidateOpenAIKeyResponse>(url, { body })
}
export const updatePluginProviderAIKey: Fetcher<UpdateOpenAIKeyResponse, { url: string; body: { credentials: any } }> = ({ url, body }) => {
export const updatePluginProviderAIKey = ({ url, body }: { url: string, body: { credentials: any } }): Promise<UpdateOpenAIKeyResponse> => {
return post<UpdateOpenAIKeyResponse>(url, { body })
}
export const invitationCheck: Fetcher<CommonResponse & { is_valid: boolean; data: { workspace_name: string; email: string; workspace_id: string } }, { url: string; params: { workspace_id?: string; email?: string; token: string } }> = ({ url, params }) => {
return get<CommonResponse & { is_valid: boolean; data: { workspace_name: string; email: string; workspace_id: string } }>(url, { params })
export const invitationCheck = ({ url, params }: { url: string, params: { workspace_id?: string, email?: string, token: string } }): Promise<CommonResponse & { is_valid: boolean, data: { workspace_name: string, email: string, workspace_id: string } }> => {
return get<CommonResponse & { is_valid: boolean, data: { workspace_name: string, email: string, workspace_id: string } }>(url, { params })
}
export const activateMember: Fetcher<LoginResponse, { url: string; body: any }> = ({ url, body }) => {
export const activateMember = ({ url, body }: { url: string, body: any }): Promise<LoginResponse> => {
return post<LoginResponse>(url, { body })
}
export const fetchModelProviders: Fetcher<{ data: ModelProvider[] }, string> = (url) => {
export const fetchModelProviders = (url: string): Promise<{ data: ModelProvider[] }> => {
return get<{ data: ModelProvider[] }>(url)
}
@ -195,197 +194,197 @@ export type ModelProviderCredentials = {
credentials?: Record<string, string | undefined | boolean>
load_balancing: ModelLoadBalancingConfig
}
export const fetchModelProviderCredentials: Fetcher<ModelProviderCredentials, string> = (url) => {
export const fetchModelProviderCredentials = (url: string): Promise<ModelProviderCredentials> => {
return get<ModelProviderCredentials>(url)
}
export const fetchModelLoadBalancingConfig: Fetcher<{
export const fetchModelLoadBalancingConfig = (url: string): Promise<{
credentials?: Record<string, string | undefined | boolean>
load_balancing: ModelLoadBalancingConfig
}, string> = (url) => {
}> => {
return get<{
credentials?: Record<string, string | undefined | boolean>
load_balancing: ModelLoadBalancingConfig
}>(url)
}
export const fetchModelProviderModelList: Fetcher<{ data: ModelItem[] }, string> = (url) => {
export const fetchModelProviderModelList = (url: string): Promise<{ data: ModelItem[] }> => {
return get<{ data: ModelItem[] }>(url)
}
export const fetchModelList: Fetcher<{ data: Model[] }, string> = (url) => {
export const fetchModelList = (url: string): Promise<{ data: Model[] }> => {
return get<{ data: Model[] }>(url)
}
export const validateModelProvider: Fetcher<ValidateOpenAIKeyResponse, { url: string; body: any }> = ({ url, body }) => {
export const validateModelProvider = ({ url, body }: { url: string, body: any }): Promise<ValidateOpenAIKeyResponse> => {
return post<ValidateOpenAIKeyResponse>(url, { body })
}
export const validateModelLoadBalancingCredentials: Fetcher<ValidateOpenAIKeyResponse, { url: string; body: any }> = ({ url, body }) => {
export const validateModelLoadBalancingCredentials = ({ url, body }: { url: string, body: any }): Promise<ValidateOpenAIKeyResponse> => {
return post<ValidateOpenAIKeyResponse>(url, { body })
}
export const setModelProvider: Fetcher<CommonResponse, { url: string; body: any }> = ({ url, body }) => {
export const setModelProvider = ({ url, body }: { url: string, body: any }): Promise<CommonResponse> => {
return post<CommonResponse>(url, { body })
}
export const deleteModelProvider: Fetcher<CommonResponse, { url: string; body?: any }> = ({ url, body }) => {
export const deleteModelProvider = ({ url, body }: { url: string, body?: any }): Promise<CommonResponse> => {
return del<CommonResponse>(url, { body })
}
export const changeModelProviderPriority: Fetcher<CommonResponse, { url: string; body: any }> = ({ url, body }) => {
export const changeModelProviderPriority = ({ url, body }: { url: string, body: any }): Promise<CommonResponse> => {
return post<CommonResponse>(url, { body })
}
export const setModelProviderModel: Fetcher<CommonResponse, { url: string; body: any }> = ({ url, body }) => {
export const setModelProviderModel = ({ url, body }: { url: string, body: any }): Promise<CommonResponse> => {
return post<CommonResponse>(url, { body })
}
export const deleteModelProviderModel: Fetcher<CommonResponse, { url: string }> = ({ url }) => {
export const deleteModelProviderModel = ({ url }: { url: string }): Promise<CommonResponse> => {
return del<CommonResponse>(url)
}
export const getPayUrl: Fetcher<{ url: string }, string> = (url) => {
export const getPayUrl = (url: string): Promise<{ url: string }> => {
return get<{ url: string }>(url)
}
export const fetchDefaultModal: Fetcher<{ data: DefaultModelResponse }, string> = (url) => {
export const fetchDefaultModal = (url: string): Promise<{ data: DefaultModelResponse }> => {
return get<{ data: DefaultModelResponse }>(url)
}
export const updateDefaultModel: Fetcher<CommonResponse, { url: string; body: any }> = ({ url, body }) => {
export const updateDefaultModel = ({ url, body }: { url: string, body: any }): Promise<CommonResponse> => {
return post<CommonResponse>(url, { body })
}
export const fetchModelParameterRules: Fetcher<{ data: ModelParameterRule[] }, string> = (url) => {
export const fetchModelParameterRules = (url: string): Promise<{ data: ModelParameterRule[] }> => {
return get<{ data: ModelParameterRule[] }>(url)
}
export const fetchFileUploadConfig: Fetcher<FileUploadConfigResponse, { url: string }> = ({ url }) => {
export const fetchFileUploadConfig = ({ url }: { url: string }): Promise<FileUploadConfigResponse> => {
return get<FileUploadConfigResponse>(url)
}
export const fetchNotionConnection: Fetcher<{ data: string }, string> = (url) => {
return get(url) as Promise<{ data: string }>
export const fetchNotionConnection = (url: string): Promise<{ data: string }> => {
return get<{ data: string }>(url)
}
export const fetchDataSourceNotionBinding: Fetcher<{ result: string }, string> = (url) => {
return get(url) as Promise<{ result: string }>
export const fetchDataSourceNotionBinding = (url: string): Promise<{ result: string }> => {
return get<{ result: string }>(url)
}
export const fetchApiBasedExtensionList: Fetcher<ApiBasedExtension[], string> = (url) => {
return get(url) as Promise<ApiBasedExtension[]>
export const fetchApiBasedExtensionList = (url: string): Promise<ApiBasedExtension[]> => {
return get<ApiBasedExtension[]>(url)
}
export const fetchApiBasedExtensionDetail: Fetcher<ApiBasedExtension, string> = (url) => {
return get(url) as Promise<ApiBasedExtension>
export const fetchApiBasedExtensionDetail = (url: string): Promise<ApiBasedExtension> => {
return get<ApiBasedExtension>(url)
}
export const addApiBasedExtension: Fetcher<ApiBasedExtension, { url: string; body: ApiBasedExtension }> = ({ url, body }) => {
return post(url, { body }) as Promise<ApiBasedExtension>
export const addApiBasedExtension = ({ url, body }: { url: string, body: ApiBasedExtension }): Promise<ApiBasedExtension> => {
return post<ApiBasedExtension>(url, { body })
}
export const updateApiBasedExtension: Fetcher<ApiBasedExtension, { url: string; body: ApiBasedExtension }> = ({ url, body }) => {
return post(url, { body }) as Promise<ApiBasedExtension>
export const updateApiBasedExtension = ({ url, body }: { url: string, body: ApiBasedExtension }): Promise<ApiBasedExtension> => {
return post<ApiBasedExtension>(url, { body })
}
export const deleteApiBasedExtension: Fetcher<{ result: string }, string> = (url) => {
return del(url) as Promise<{ result: string }>
export const deleteApiBasedExtension = (url: string): Promise<{ result: string }> => {
return del<{ result: string }>(url)
}
export const fetchCodeBasedExtensionList: Fetcher<CodeBasedExtension, string> = (url) => {
return get(url) as Promise<CodeBasedExtension>
export const fetchCodeBasedExtensionList = (url: string): Promise<CodeBasedExtension> => {
return get<CodeBasedExtension>(url)
}
export const moderate = (url: string, body: { app_id: string; text: string }) => {
return post(url, { body }) as Promise<ModerateResponse>
export const moderate = (url: string, body: { app_id: string, text: string }): Promise<ModerateResponse> => {
return post<ModerateResponse>(url, { body })
}
type RetrievalMethodsRes = {
retrieval_method: RETRIEVE_METHOD[]
}
export const fetchSupportRetrievalMethods: Fetcher<RetrievalMethodsRes, string> = (url) => {
export const fetchSupportRetrievalMethods = (url: string): Promise<RetrievalMethodsRes> => {
return get<RetrievalMethodsRes>(url)
}
export const getSystemFeatures = () => {
export const getSystemFeatures = (): Promise<SystemFeatures> => {
return get<SystemFeatures>('/system-features')
}
export const enableModel = (url: string, body: { model: string; model_type: ModelTypeEnum }) =>
export const enableModel = (url: string, body: { model: string, model_type: ModelTypeEnum }): Promise<CommonResponse> =>
patch<CommonResponse>(url, { body })
export const disableModel = (url: string, body: { model: string; model_type: ModelTypeEnum }) =>
export const disableModel = (url: string, body: { model: string, model_type: ModelTypeEnum }): Promise<CommonResponse> =>
patch<CommonResponse>(url, { body })
export const sendForgotPasswordEmail: Fetcher<CommonResponse & { data: string }, { url: string; body: { email: string } }> = ({ url, body }) =>
export const sendForgotPasswordEmail = ({ url, body }: { url: string, body: { email: string } }): Promise<CommonResponse & { data: string }> =>
post<CommonResponse & { data: string }>(url, { body })
export const verifyForgotPasswordToken: Fetcher<CommonResponse & { is_valid: boolean; email: string }, { url: string; body: { token: string } }> = ({ url, body }) => {
return post(url, { body }) as Promise<CommonResponse & { is_valid: boolean; email: string }>
export const verifyForgotPasswordToken = ({ url, body }: { url: string, body: { token: string } }): Promise<CommonResponse & { is_valid: boolean, email: string }> => {
return post<CommonResponse & { is_valid: boolean, email: string }>(url, { body })
}
export const changePasswordWithToken: Fetcher<CommonResponse, { url: string; body: { token: string; new_password: string; password_confirm: string } }> = ({ url, body }) =>
export const changePasswordWithToken = ({ url, body }: { url: string, body: { token: string, new_password: string, password_confirm: string } }): Promise<CommonResponse> =>
post<CommonResponse>(url, { body })
export const sendWebAppForgotPasswordEmail: Fetcher<CommonResponse & { data: string }, { url: string; body: { email: string } }> = ({ url, body }) =>
export const sendWebAppForgotPasswordEmail = ({ url, body }: { url: string, body: { email: string } }): Promise<CommonResponse & { data: string }> =>
post<CommonResponse & { data: string }>(url, { body }, { isPublicAPI: true })
export const verifyWebAppForgotPasswordToken: Fetcher<CommonResponse & { is_valid: boolean; email: string }, { url: string; body: { token: string } }> = ({ url, body }) => {
return post(url, { body }, { isPublicAPI: true }) as Promise<CommonResponse & { is_valid: boolean; email: string }>
export const verifyWebAppForgotPasswordToken = ({ url, body }: { url: string, body: { token: string } }): Promise<CommonResponse & { is_valid: boolean, email: string }> => {
return post<CommonResponse & { is_valid: boolean, email: string }>(url, { body }, { isPublicAPI: true })
}
export const changeWebAppPasswordWithToken: Fetcher<CommonResponse, { url: string; body: { token: string; new_password: string; password_confirm: string } }> = ({ url, body }) =>
export const changeWebAppPasswordWithToken = ({ url, body }: { url: string, body: { token: string, new_password: string, password_confirm: string } }): Promise<CommonResponse> =>
post<CommonResponse>(url, { body }, { isPublicAPI: true })
export const uploadRemoteFileInfo = (url: string, isPublic?: boolean, silent?: boolean) => {
return post<{ id: string; name: string; size: number; mime_type: string; url: string }>('/remote-files/upload', { body: { url } }, { isPublicAPI: isPublic, silent })
export const uploadRemoteFileInfo = (url: string, isPublic?: boolean, silent?: boolean): Promise<{ id: string, name: string, size: number, mime_type: string, url: string }> => {
return post<{ id: string, name: string, size: number, mime_type: string, url: string }>('/remote-files/upload', { body: { url } }, { isPublicAPI: isPublic, silent })
}
export const sendEMailLoginCode = (email: string, language = 'en-US') =>
export const sendEMailLoginCode = (email: string, language = 'en-US'): Promise<CommonResponse & { data: string }> =>
post<CommonResponse & { data: string }>('/email-code-login', { body: { email, language } })
export const emailLoginWithCode = (data: { email: string; code: string; token: string; language: string }) =>
export const emailLoginWithCode = (data: { email: string, code: string, token: string, language: string }): Promise<LoginResponse> =>
post<LoginResponse>('/email-code-login/validity', { body: data })
export const sendResetPasswordCode = (email: string, language = 'en-US') =>
post<CommonResponse & { data: string; message?: string; code?: string }>('/forgot-password', { body: { email, language } })
export const sendResetPasswordCode = (email: string, language = 'en-US'): Promise<CommonResponse & { data: string, message?: string, code?: string }> =>
post<CommonResponse & { data: string, message?: string, code?: string }>('/forgot-password', { body: { email, language } })
export const verifyResetPasswordCode = (body: { email: string; code: string; token: string }) =>
post<CommonResponse & { is_valid: boolean; token: string }>('/forgot-password/validity', { body })
export const verifyResetPasswordCode = (body: { email: string, code: string, token: string }): Promise<CommonResponse & { is_valid: boolean, token: string }> =>
post<CommonResponse & { is_valid: boolean, token: string }>('/forgot-password/validity', { body })
export const sendWebAppEMailLoginCode = (email: string, language = 'en-US') =>
export const sendWebAppEMailLoginCode = (email: string, language = 'en-US'): Promise<CommonResponse & { data: string }> =>
post<CommonResponse & { data: string }>('/email-code-login', { body: { email, language } }, { isPublicAPI: true })
export const webAppEmailLoginWithCode = (data: { email: string; code: string; token: string }) =>
export const webAppEmailLoginWithCode = (data: { email: string, code: string, token: string }): Promise<LoginResponse> =>
post<LoginResponse>('/email-code-login/validity', { body: data }, { isPublicAPI: true })
export const sendWebAppResetPasswordCode = (email: string, language = 'en-US') =>
post<CommonResponse & { data: string; message?: string; code?: string }>('/forgot-password', { body: { email, language } }, { isPublicAPI: true })
export const sendWebAppResetPasswordCode = (email: string, language = 'en-US'): Promise<CommonResponse & { data: string, message?: string, code?: string }> =>
post<CommonResponse & { data: string, message?: string, code?: string }>('/forgot-password', { body: { email, language } }, { isPublicAPI: true })
export const verifyWebAppResetPasswordCode = (body: { email: string; code: string; token: string }) =>
post<CommonResponse & { is_valid: boolean; token: string }>('/forgot-password/validity', { body }, { isPublicAPI: true })
export const verifyWebAppResetPasswordCode = (body: { email: string, code: string, token: string }): Promise<CommonResponse & { is_valid: boolean, token: string }> =>
post<CommonResponse & { is_valid: boolean, token: string }>('/forgot-password/validity', { body }, { isPublicAPI: true })
export const sendDeleteAccountCode = () =>
export const sendDeleteAccountCode = (): Promise<CommonResponse & { data: string }> =>
get<CommonResponse & { data: string }>('/account/delete/verify')
export const verifyDeleteAccountCode = (body: { code: string; token: string }) =>
export const verifyDeleteAccountCode = (body: { code: string, token: string }): Promise<CommonResponse & { is_valid: boolean }> =>
post<CommonResponse & { is_valid: boolean }>('/account/delete', { body })
export const submitDeleteAccountFeedback = (body: { feedback: string; email: string }) =>
export const submitDeleteAccountFeedback = (body: { feedback: string, email: string }): Promise<CommonResponse> =>
post<CommonResponse>('/account/delete/feedback', { body })
export const getDocDownloadUrl = (doc_name: string) =>
export const getDocDownloadUrl = (doc_name: string): Promise<{ url: string }> =>
get<{ url: string }>('/compliance/download', { params: { doc_name } }, { silent: true })
export const sendVerifyCode = (body: { email: string; phase: string; token?: string }) =>
export const sendVerifyCode = (body: { email: string, phase: string, token?: string }): Promise<CommonResponse & { data: string }> =>
post<CommonResponse & { data: string }>('/account/change-email', { body })
export const verifyEmail = (body: { email: string; code: string; token: string }) =>
post<CommonResponse & { is_valid: boolean; email: string; token: string }>('/account/change-email/validity', { body })
export const verifyEmail = (body: { email: string, code: string, token: string }): Promise<CommonResponse & { is_valid: boolean, email: string, token: string }> =>
post<CommonResponse & { is_valid: boolean, email: string, token: string }>('/account/change-email/validity', { body })
export const resetEmail = (body: { new_email: string; token: string }) =>
export const resetEmail = (body: { new_email: string, token: string }): Promise<CommonResponse> =>
post<CommonResponse>('/account/change-email/reset', { body })
export const checkEmailExisted = (body: { email: string }) =>
export const checkEmailExisted = (body: { email: string }): Promise<CommonResponse> =>
post<CommonResponse>('/account/change-email/check-email-unique', { body }, { silent: true })

View File

@ -1,8 +1,13 @@
import type { Fetcher } from 'swr'
import qs from 'qs'
import { del, get, patch, post, put } from './base'
import type { CreateExternalAPIReq } from '@/app/components/datasets/external-api/declarations'
import type { CreateKnowledgeBaseReq } from '@/app/components/datasets/external-knowledge-base/create/declarations'
import type {
ApiKeysListResponse,
CreateApiKeyResponse,
} from '@/models/app'
import type { CommonResponse, DataSourceNotionWorkspace } from '@/models/common'
import type {
CreateDocumentReq,
createDocumentResponse,
DataSet,
DataSetListResponse,
ErrorDocsResponse,
@ -22,17 +27,11 @@ import type {
IndexingStatusResponse,
ProcessRuleResponse,
RelatedAppResponse,
createDocumentResponse,
} from '@/models/datasets'
import type { CreateKnowledgeBaseReq } from '@/app/components/datasets/external-knowledge-base/create/declarations'
import type { CreateExternalAPIReq } from '@/app/components/datasets/external-api/declarations'
import type { CommonResponse, DataSourceNotionWorkspace } from '@/models/common'
import { DataSourceProvider } from '@/models/common'
import type {
ApiKeysListResponse,
CreateApiKeyResponse,
} from '@/models/app'
import type { RetrievalConfig } from '@/types/app'
import qs from 'qs'
import { DataSourceProvider } from '@/models/common'
import { del, get, patch, post, put } from './base'
// apis for documents in a dataset
@ -50,172 +49,173 @@ export type SortType = 'created_at' | 'hit_count' | '-created_at' | '-hit_count'
export type MetadataType = 'all' | 'only' | 'without'
export const fetchDatasetDetail: Fetcher<DataSet, string> = (datasetId: string) => {
export const fetchDatasetDetail = (datasetId: string): Promise<DataSet> => {
return get<DataSet>(`/datasets/${datasetId}`)
}
export const updateDatasetSetting: Fetcher<DataSet, {
export const updateDatasetSetting = ({
datasetId,
body,
}: {
datasetId: string
body: Partial<Pick<DataSet,
'name' | 'description' | 'permission' | 'partial_member_list' | 'indexing_technique' | 'retrieval_model' | 'embedding_model' | 'embedding_model_provider' | 'icon_info' | 'doc_form'
>>
}> = ({ datasetId, body }) => {
body: Partial<Pick<DataSet, 'name' | 'description' | 'permission' | 'partial_member_list' | 'indexing_technique' | 'retrieval_model' | 'embedding_model' | 'embedding_model_provider' | 'icon_info' | 'doc_form'>>
}): Promise<DataSet> => {
return patch<DataSet>(`/datasets/${datasetId}`, { body })
}
export const fetchDatasetRelatedApps: Fetcher<RelatedAppResponse, string> = (datasetId: string) => {
export const fetchDatasetRelatedApps = (datasetId: string): Promise<RelatedAppResponse> => {
return get<RelatedAppResponse>(`/datasets/${datasetId}/related-apps`)
}
export const fetchDatasets: Fetcher<DataSetListResponse, FetchDatasetsParams> = ({ url, params }) => {
export const fetchDatasets = ({ url, params }: FetchDatasetsParams): Promise<DataSetListResponse> => {
const urlParams = qs.stringify(params, { indices: false })
return get<DataSetListResponse>(`${url}?${urlParams}`)
}
export const createEmptyDataset: Fetcher<DataSet, { name: string }> = ({ name }) => {
export const createEmptyDataset = ({ name }: { name: string }): Promise<DataSet> => {
return post<DataSet>('/datasets', { body: { name } })
}
export const checkIsUsedInApp: Fetcher<{ is_using: boolean }, string> = (id) => {
export const checkIsUsedInApp = (id: string): Promise<{ is_using: boolean }> => {
return get<{ is_using: boolean }>(`/datasets/${id}/use-check`, {}, {
silent: true,
})
}
export const deleteDataset: Fetcher<DataSet, string> = (datasetID) => {
export const deleteDataset = (datasetID: string): Promise<DataSet> => {
return del<DataSet>(`/datasets/${datasetID}`)
}
export const fetchExternalAPIList: Fetcher<ExternalAPIListResponse, { url: string }> = ({ url }) => {
export const fetchExternalAPIList = ({ url }: { url: string }): Promise<ExternalAPIListResponse> => {
return get<ExternalAPIListResponse>(url)
}
export const fetchExternalAPI: Fetcher<ExternalAPIItem, { apiTemplateId: string }> = ({ apiTemplateId }) => {
export const fetchExternalAPI = ({ apiTemplateId }: { apiTemplateId: string }): Promise<ExternalAPIItem> => {
return get<ExternalAPIItem>(`/datasets/external-knowledge-api/${apiTemplateId}`)
}
export const updateExternalAPI: Fetcher<ExternalAPIItem, { apiTemplateId: string; body: ExternalAPIItem }> = ({ apiTemplateId, body }) => {
export const updateExternalAPI = ({ apiTemplateId, body }: { apiTemplateId: string, body: ExternalAPIItem }): Promise<ExternalAPIItem> => {
return patch<ExternalAPIItem>(`/datasets/external-knowledge-api/${apiTemplateId}`, { body })
}
export const deleteExternalAPI: Fetcher<ExternalAPIDeleteResponse, { apiTemplateId: string }> = ({ apiTemplateId }) => {
export const deleteExternalAPI = ({ apiTemplateId }: { apiTemplateId: string }): Promise<ExternalAPIDeleteResponse> => {
return del<ExternalAPIDeleteResponse>(`/datasets/external-knowledge-api/${apiTemplateId}`)
}
export const checkUsageExternalAPI: Fetcher<ExternalAPIUsage, { apiTemplateId: string }> = ({ apiTemplateId }) => {
export const checkUsageExternalAPI = ({ apiTemplateId }: { apiTemplateId: string }): Promise<ExternalAPIUsage> => {
return get<ExternalAPIUsage>(`/datasets/external-knowledge-api/${apiTemplateId}/use-check`)
}
export const createExternalAPI: Fetcher<ExternalAPIItem, { body: CreateExternalAPIReq }> = ({ body }) => {
export const createExternalAPI = ({ body }: { body: CreateExternalAPIReq }): Promise<ExternalAPIItem> => {
return post<ExternalAPIItem>('/datasets/external-knowledge-api', { body })
}
export const createExternalKnowledgeBase: Fetcher<ExternalKnowledgeItem, { body: CreateKnowledgeBaseReq }> = ({ body }) => {
export const createExternalKnowledgeBase = ({ body }: { body: CreateKnowledgeBaseReq }): Promise<ExternalKnowledgeItem> => {
return post<ExternalKnowledgeItem>('/datasets/external', { body })
}
export const fetchDefaultProcessRule: Fetcher<ProcessRuleResponse, { url: string }> = ({ url }) => {
export const fetchDefaultProcessRule = ({ url }: { url: string }): Promise<ProcessRuleResponse> => {
return get<ProcessRuleResponse>(url)
}
export const fetchProcessRule: Fetcher<ProcessRuleResponse, { params: { documentId: string } }> = ({ params: { documentId } }) => {
export const fetchProcessRule = ({ params: { documentId } }: { params: { documentId: string } }): Promise<ProcessRuleResponse> => {
return get<ProcessRuleResponse>('/datasets/process-rule', { params: { document_id: documentId } })
}
export const createFirstDocument: Fetcher<createDocumentResponse, { body: CreateDocumentReq }> = ({ body }) => {
export const createFirstDocument = ({ body }: { body: CreateDocumentReq }): Promise<createDocumentResponse> => {
return post<createDocumentResponse>('/datasets/init', { body })
}
export const createDocument: Fetcher<createDocumentResponse, { datasetId: string; body: CreateDocumentReq }> = ({ datasetId, body }) => {
export const createDocument = ({ datasetId, body }: { datasetId: string, body: CreateDocumentReq }): Promise<createDocumentResponse> => {
return post<createDocumentResponse>(`/datasets/${datasetId}/documents`, { body })
}
export const fetchIndexingEstimate: Fetcher<IndexingEstimateResponse, CommonDocReq> = ({ datasetId, documentId }) => {
export const fetchIndexingEstimate = ({ datasetId, documentId }: CommonDocReq): Promise<IndexingEstimateResponse> => {
return get<IndexingEstimateResponse>(`/datasets/${datasetId}/documents/${documentId}/indexing-estimate`, {})
}
export const fetchIndexingEstimateBatch: Fetcher<IndexingEstimateResponse, BatchReq> = ({ datasetId, batchId }) => {
export const fetchIndexingEstimateBatch = ({ datasetId, batchId }: BatchReq): Promise<IndexingEstimateResponse> => {
return get<IndexingEstimateResponse>(`/datasets/${datasetId}/batch/${batchId}/indexing-estimate`, {})
}
export const fetchIndexingStatus: Fetcher<IndexingStatusResponse, CommonDocReq> = ({ datasetId, documentId }) => {
export const fetchIndexingStatus = ({ datasetId, documentId }: CommonDocReq): Promise<IndexingStatusResponse> => {
return get<IndexingStatusResponse>(`/datasets/${datasetId}/documents/${documentId}/indexing-status`, {})
}
export const fetchIndexingStatusBatch: Fetcher<IndexingStatusBatchResponse, BatchReq> = ({ datasetId, batchId }) => {
export const fetchIndexingStatusBatch = ({ datasetId, batchId }: BatchReq): Promise<IndexingStatusBatchResponse> => {
return get<IndexingStatusBatchResponse>(`/datasets/${datasetId}/batch/${batchId}/indexing-status`, {})
}
export const renameDocumentName: Fetcher<CommonResponse, CommonDocReq & { name: string }> = ({ datasetId, documentId, name }) => {
export const renameDocumentName = ({ datasetId, documentId, name }: CommonDocReq & { name: string }): Promise<CommonResponse> => {
return post<CommonResponse>(`/datasets/${datasetId}/documents/${documentId}/rename`, {
body: { name },
})
}
export const pauseDocIndexing: Fetcher<CommonResponse, CommonDocReq> = ({ datasetId, documentId }) => {
export const pauseDocIndexing = ({ datasetId, documentId }: CommonDocReq): Promise<CommonResponse> => {
return patch<CommonResponse>(`/datasets/${datasetId}/documents/${documentId}/processing/pause`)
}
export const resumeDocIndexing: Fetcher<CommonResponse, CommonDocReq> = ({ datasetId, documentId }) => {
export const resumeDocIndexing = ({ datasetId, documentId }: CommonDocReq): Promise<CommonResponse> => {
return patch<CommonResponse>(`/datasets/${datasetId}/documents/${documentId}/processing/resume`)
}
export const preImportNotionPages: Fetcher<{ notion_info: DataSourceNotionWorkspace[] }, { url: string; datasetId?: string }> = ({ url, datasetId }) => {
export const preImportNotionPages = ({ url, datasetId }: { url: string, datasetId?: string }): Promise<{ notion_info: DataSourceNotionWorkspace[] }> => {
return get<{ notion_info: DataSourceNotionWorkspace[] }>(url, { params: { dataset_id: datasetId } })
}
export const modifyDocMetadata: Fetcher<CommonResponse, CommonDocReq & { body: { doc_type: string; doc_metadata: Record<string, any> } }> = ({ datasetId, documentId, body }) => {
export const modifyDocMetadata = ({ datasetId, documentId, body }: CommonDocReq & { body: { doc_type: string, doc_metadata: Record<string, any> } }): Promise<CommonResponse> => {
return put<CommonResponse>(`/datasets/${datasetId}/documents/${documentId}/metadata`, { body })
}
// hit testing
export const hitTesting: Fetcher<HitTestingResponse, { datasetId: string; queryText: string; retrieval_model: RetrievalConfig }> = ({ datasetId, queryText, retrieval_model }) => {
export const hitTesting = ({ datasetId, queryText, retrieval_model }: { datasetId: string, queryText: string, retrieval_model: RetrievalConfig }): Promise<HitTestingResponse> => {
return post<HitTestingResponse>(`/datasets/${datasetId}/hit-testing`, { body: { query: queryText, retrieval_model } })
}
export const externalKnowledgeBaseHitTesting: Fetcher<ExternalKnowledgeBaseHitTestingResponse, { datasetId: string; query: string; external_retrieval_model: { top_k: number; score_threshold: number; score_threshold_enabled: boolean } }> = ({ datasetId, query, external_retrieval_model }) => {
export const externalKnowledgeBaseHitTesting = ({ datasetId, query, external_retrieval_model }: { datasetId: string, query: string, external_retrieval_model: { top_k: number, score_threshold: number, score_threshold_enabled: boolean } }): Promise<ExternalKnowledgeBaseHitTestingResponse> => {
return post<ExternalKnowledgeBaseHitTestingResponse>(`/datasets/${datasetId}/external-hit-testing`, { body: { query, external_retrieval_model } })
}
export const fetchTestingRecords: Fetcher<HitTestingRecordsResponse, { datasetId: string; params: { page: number; limit: number } }> = ({ datasetId, params }) => {
export const fetchTestingRecords = ({ datasetId, params }: { datasetId: string, params: { page: number, limit: number } }): Promise<HitTestingRecordsResponse> => {
return get<HitTestingRecordsResponse>(`/datasets/${datasetId}/queries`, { params })
}
export const fetchFileIndexingEstimate: Fetcher<FileIndexingEstimateResponse, IndexingEstimateParams> = (body: IndexingEstimateParams) => {
export const fetchFileIndexingEstimate = (body: IndexingEstimateParams): Promise<FileIndexingEstimateResponse> => {
return post<FileIndexingEstimateResponse>('/datasets/indexing-estimate', { body })
}
export const fetchNotionPagePreview: Fetcher<{ content: string }, { workspaceID: string; pageID: string; pageType: string; credentialID: string; }> = ({ workspaceID, pageID, pageType, credentialID }) => {
return get<{ content: string }>(`notion/workspaces/${workspaceID}/pages/${pageID}/${pageType}/preview`, {
export const fetchNotionPagePreview = ({ pageID, pageType, credentialID }: { pageID: string, pageType: string, credentialID: string }): Promise<{ content: string }> => {
return get<{ content: string }>(`notion/pages/${pageID}/${pageType}/preview`, {
params: {
credential_id: credentialID,
},
})
}
export const fetchApiKeysList: Fetcher<ApiKeysListResponse, { url: string; params: Record<string, any> }> = ({ url, params }) => {
export const fetchApiKeysList = ({ url, params }: { url: string, params: Record<string, any> }): Promise<ApiKeysListResponse> => {
return get<ApiKeysListResponse>(url, params)
}
export const delApikey: Fetcher<CommonResponse, { url: string; params: Record<string, any> }> = ({ url, params }) => {
export const delApikey = ({ url, params }: { url: string, params: Record<string, any> }): Promise<CommonResponse> => {
return del<CommonResponse>(url, params)
}
export const createApikey: Fetcher<CreateApiKeyResponse, { url: string; body: Record<string, any> }> = ({ url, body }) => {
export const createApikey = ({ url, body }: { url: string, body: Record<string, any> }): Promise<CreateApiKeyResponse> => {
return post<CreateApiKeyResponse>(url, body)
}
export const fetchDataSources = () => {
export const fetchDataSources = (): Promise<CommonResponse> => {
return get<CommonResponse>('api-key-auth/data-source')
}
export const createDataSourceApiKeyBinding: Fetcher<CommonResponse, Record<string, any>> = (body) => {
export const createDataSourceApiKeyBinding = (body: Record<string, any>): Promise<CommonResponse> => {
return post<CommonResponse>('api-key-auth/data-source/binding', { body })
}
export const removeDataSourceApiKeyBinding: Fetcher<CommonResponse, string> = (id: string) => {
export const removeDataSourceApiKeyBinding = (id: string): Promise<CommonResponse> => {
return del<CommonResponse>(`api-key-auth/data-source/${id}`)
}
export const createFirecrawlTask: Fetcher<CommonResponse, Record<string, any>> = (body) => {
export const createFirecrawlTask = (body: Record<string, any>): Promise<CommonResponse> => {
return post<CommonResponse>('website/crawl', {
body: {
...body,
@ -224,7 +224,7 @@ export const createFirecrawlTask: Fetcher<CommonResponse, Record<string, any>> =
})
}
export const checkFirecrawlTaskStatus: Fetcher<CommonResponse, string> = (jobId: string) => {
export const checkFirecrawlTaskStatus = (jobId: string): Promise<CommonResponse> => {
return get<CommonResponse>(`website/crawl/status/${jobId}`, {
params: {
provider: DataSourceProvider.fireCrawl,
@ -234,7 +234,7 @@ export const checkFirecrawlTaskStatus: Fetcher<CommonResponse, string> = (jobId:
})
}
export const createJinaReaderTask: Fetcher<CommonResponse, Record<string, any>> = (body) => {
export const createJinaReaderTask = (body: Record<string, any>): Promise<CommonResponse> => {
return post<CommonResponse>('website/crawl', {
body: {
...body,
@ -243,7 +243,7 @@ export const createJinaReaderTask: Fetcher<CommonResponse, Record<string, any>>
})
}
export const checkJinaReaderTaskStatus: Fetcher<CommonResponse, string> = (jobId: string) => {
export const checkJinaReaderTaskStatus = (jobId: string): Promise<CommonResponse> => {
return get<CommonResponse>(`website/crawl/status/${jobId}`, {
params: {
provider: 'jinareader',
@ -253,7 +253,7 @@ export const checkJinaReaderTaskStatus: Fetcher<CommonResponse, string> = (jobId
})
}
export const createWatercrawlTask: Fetcher<CommonResponse, Record<string, any>> = (body) => {
export const createWatercrawlTask = (body: Record<string, any>): Promise<CommonResponse> => {
return post<CommonResponse>('website/crawl', {
body: {
...body,
@ -262,7 +262,7 @@ export const createWatercrawlTask: Fetcher<CommonResponse, Record<string, any>>
})
}
export const checkWatercrawlTaskStatus: Fetcher<CommonResponse, string> = (jobId: string) => {
export const checkWatercrawlTaskStatus = (jobId: string): Promise<CommonResponse> => {
return get<CommonResponse>(`website/crawl/status/${jobId}`, {
params: {
provider: DataSourceProvider.waterCrawl,
@ -276,14 +276,14 @@ export type FileTypesRes = {
allowed_extensions: string[]
}
export const fetchSupportFileTypes: Fetcher<FileTypesRes, { url: string }> = ({ url }) => {
export const fetchSupportFileTypes = ({ url }: { url: string }): Promise<FileTypesRes> => {
return get<FileTypesRes>(url)
}
export const getErrorDocs: Fetcher<ErrorDocsResponse, { datasetId: string }> = ({ datasetId }) => {
export const getErrorDocs = ({ datasetId }: { datasetId: string }): Promise<ErrorDocsResponse> => {
return get<ErrorDocsResponse>(`/datasets/${datasetId}/error-docs`)
}
export const retryErrorDocs: Fetcher<CommonResponse, { datasetId: string; document_ids: string[] }> = ({ datasetId, document_ids }) => {
export const retryErrorDocs = ({ datasetId, document_ids }: { datasetId: string, document_ids: string[] }): Promise<CommonResponse> => {
return post<CommonResponse>(`/datasets/${datasetId}/retry`, { body: { document_ids } })
}

View File

@ -1,8 +1,8 @@
import { get, post, ssePost } from './base'
import type { IOnCompleted, IOnData, IOnError, IOnMessageReplace } from './base'
import type { ModelParameterRule } from '@/app/components/header/account-setting/model-provider-page/declarations'
import type { ChatPromptConfig, CompletionPromptConfig } from '@/models/debug'
import type { AppModeEnum, ModelModeType } from '@/types/app'
import type { ModelParameterRule } from '@/app/components/header/account-setting/model-provider-page/declarations'
import { get, post, ssePost } from './base'
export type BasicAppFirstRes = {
prompt: string
@ -88,8 +88,8 @@ export const fetchPromptTemplate = ({
mode,
modelName,
hasSetDataSet,
}: { appMode: AppModeEnum; mode: ModelModeType; modelName: string; hasSetDataSet: boolean }) => {
return get<Promise<{ chat_prompt_config: ChatPromptConfig; completion_prompt_config: CompletionPromptConfig; stop: [] }>>('/app/prompt-templates', {
}: { appMode: AppModeEnum, mode: ModelModeType, modelName: string, hasSetDataSet: boolean }) => {
return get<Promise<{ chat_prompt_config: ChatPromptConfig, completion_prompt_config: CompletionPromptConfig, stop: [] }>>('/app/prompt-templates', {
params: {
app_mode: appMode,
model_mode: mode,
@ -102,6 +102,6 @@ export const fetchPromptTemplate = ({
export const fetchTextGenerationMessage = ({
appId,
messageId,
}: { appId: string; messageId: string }) => {
}: { appId: string, messageId: string }) => {
return get<Promise<any>>(`/apps/${appId}/messages/${messageId}`)
}

View File

@ -1,48 +1,99 @@
'use client'
import type { FC } from 'react'
import React from 'react'
import useSWR, { useSWRConfig } from 'swr'
import { createApp, fetchAppDetail, fetchAppList, getAppDailyConversations, getAppDailyEndUsers, updateAppApiStatus, updateAppModelConfig, updateAppRateLimit, updateAppSiteAccessToken, updateAppSiteConfig, updateAppSiteStatus } from '../apps'
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
import * as React from 'react'
import Loading from '@/app/components/base/loading'
import { AppModeEnum } from '@/types/app'
import { createApp, updateAppApiStatus, updateAppModelConfig, updateAppRateLimit, updateAppSiteAccessToken, updateAppSiteConfig, updateAppSiteStatus } from '../apps'
import {
useAppDailyConversations,
useAppDailyEndUsers,
useAppDetail,
useAppList,
} from '../use-apps'
const Service: FC = () => {
const { data: appList, error: appListError } = useSWR({ url: '/apps', params: { page: 1 } }, fetchAppList)
const { data: firstApp, error: appDetailError } = useSWR({ url: '/apps', id: '1' }, fetchAppDetail)
const { data: updateAppSiteStatusRes, error: err1 } = useSWR({ url: '/apps', id: '1', body: { enable_site: false } }, updateAppSiteStatus)
const { data: updateAppApiStatusRes, error: err2 } = useSWR({ url: '/apps', id: '1', body: { enable_api: true } }, updateAppApiStatus)
const { data: updateAppRateLimitRes, error: err3 } = useSWR({ url: '/apps', id: '1', body: { api_rpm: 10, api_rph: 20 } }, updateAppRateLimit)
const { data: updateAppSiteCodeRes, error: err4 } = useSWR({ url: '/apps', id: '1', body: {} }, updateAppSiteAccessToken)
const { data: updateAppSiteConfigRes, error: err5 } = useSWR({ url: '/apps', id: '1', body: { title: 'title test', author: 'author test' } }, updateAppSiteConfig)
const { data: getAppDailyConversationsRes, error: err6 } = useSWR({ url: '/apps', id: '1', body: { start: '1', end: '2' } }, getAppDailyConversations)
const { data: getAppDailyEndUsersRes, error: err7 } = useSWR({ url: '/apps', id: '1', body: { start: '1', end: '2' } }, getAppDailyEndUsers)
const { data: updateAppModelConfigRes, error: err8 } = useSWR({ url: '/apps', id: '1', body: { model_id: 'gpt-100' } }, updateAppModelConfig)
const appId = '1'
const queryClient = useQueryClient()
const { mutate } = useSWRConfig()
const { data: appList, error: appListError, isLoading: isAppListLoading } = useAppList({ page: 1, limit: 30, name: '' })
const { data: firstApp, error: appDetailError, isLoading: isAppDetailLoading } = useAppDetail(appId)
const handleCreateApp = async () => {
await createApp({
const { data: updateAppSiteStatusRes, error: err1, isLoading: isUpdatingSiteStatus } = useQuery({
queryKey: ['demo', 'updateAppSiteStatus', appId],
queryFn: () => updateAppSiteStatus({ url: '/apps', body: { enable_site: false } }),
})
const { data: updateAppApiStatusRes, error: err2, isLoading: isUpdatingApiStatus } = useQuery({
queryKey: ['demo', 'updateAppApiStatus', appId],
queryFn: () => updateAppApiStatus({ url: '/apps', body: { enable_api: true } }),
})
const { data: updateAppRateLimitRes, error: err3, isLoading: isUpdatingRateLimit } = useQuery({
queryKey: ['demo', 'updateAppRateLimit', appId],
queryFn: () => updateAppRateLimit({ url: '/apps', body: { api_rpm: 10, api_rph: 20 } }),
})
const { data: updateAppSiteCodeRes, error: err4, isLoading: isUpdatingSiteCode } = useQuery({
queryKey: ['demo', 'updateAppSiteAccessToken', appId],
queryFn: () => updateAppSiteAccessToken({ url: '/apps' }),
})
const { data: updateAppSiteConfigRes, error: err5, isLoading: isUpdatingSiteConfig } = useQuery({
queryKey: ['demo', 'updateAppSiteConfig', appId],
queryFn: () => updateAppSiteConfig({ url: '/apps', body: { title: 'title test', author: 'author test' } }),
})
const { data: getAppDailyConversationsRes, error: err6, isLoading: isConversationsLoading } = useAppDailyConversations(appId, { start: '1', end: '2' })
const { data: getAppDailyEndUsersRes, error: err7, isLoading: isEndUsersLoading } = useAppDailyEndUsers(appId, { start: '1', end: '2' })
const { data: updateAppModelConfigRes, error: err8, isLoading: isUpdatingModelConfig } = useQuery({
queryKey: ['demo', 'updateAppModelConfig', appId],
queryFn: () => updateAppModelConfig({ url: '/apps', body: { model_id: 'gpt-100' } }),
})
const { mutateAsync: mutateCreateApp } = useMutation({
mutationKey: ['demo', 'createApp'],
mutationFn: () => createApp({
name: `new app${Math.round(Math.random() * 100)}`,
mode: AppModeEnum.CHAT,
})
// reload app list
mutate({ url: '/apps', params: { page: 1 } })
}),
onSuccess: () => {
queryClient.invalidateQueries({
queryKey: ['apps', 'list'],
})
},
})
const handleCreateApp = async () => {
await mutateCreateApp()
}
if (appListError || appDetailError || err1 || err2 || err3 || err4 || err5 || err6 || err7 || err8)
return <div>{JSON.stringify(appListError)}</div>
return <div>{JSON.stringify(appListError ?? appDetailError ?? err1 ?? err2 ?? err3 ?? err4 ?? err5 ?? err6 ?? err7 ?? err8)}</div>
if (!appList || !firstApp || !updateAppSiteStatusRes || !updateAppApiStatusRes || !updateAppRateLimitRes || !updateAppSiteCodeRes || !updateAppSiteConfigRes || !getAppDailyConversationsRes || !getAppDailyEndUsersRes || !updateAppModelConfigRes)
const isLoading = isAppListLoading
|| isAppDetailLoading
|| isUpdatingSiteStatus
|| isUpdatingApiStatus
|| isUpdatingRateLimit
|| isUpdatingSiteCode
|| isUpdatingSiteConfig
|| isConversationsLoading
|| isEndUsersLoading
|| isUpdatingModelConfig
if (isLoading || !appList || !firstApp || !updateAppSiteStatusRes || !updateAppApiStatusRes || !updateAppRateLimitRes || !updateAppSiteCodeRes || !updateAppSiteConfigRes || !getAppDailyConversationsRes || !getAppDailyEndUsersRes || !updateAppModelConfigRes)
return <Loading />
return (
<div>
<div className='flex flex-col gap-3'>
<div className="flex flex-col gap-3">
<div>
<div>1.App list</div>
<div>
{appList.data.map(item => (
<div key={item.id}>{item.id} {item.name}</div>
<div key={item.id}>
{item.id}
{' '}
{item.name}
</div>
))}
</div>
</div>

View File

@ -1,6 +1,6 @@
import { del, get, patch, post } from './base'
import type { App, AppCategory } from '@/models/explore'
import type { AccessMode } from '@/models/access-control'
import type { App, AppCategory } from '@/models/explore'
import { del, get, patch } from './base'
export const fetchAppList = () => {
return get<{
@ -17,14 +17,6 @@ export const fetchInstalledAppList = (app_id?: string | null) => {
return get(`/installed-apps${app_id ? `?app_id=${app_id}` : ''}`)
}
export const installApp = (id: string) => {
return post('/installed-apps', {
body: {
app_id: id,
},
})
}
export const uninstallApp = (id: string) => {
return del(`/installed-apps/${id}`)
}
@ -37,10 +29,6 @@ export const updatePinStatus = (id: string, isPinned: boolean) => {
})
}
export const getToolProviders = () => {
return get('/workspaces/current/tool-providers')
}
export const getAppAccessModeByAppId = (appId: string) => {
return get<{ accessMode: AccessMode }>(`/enterprise/webapp/app/access-mode?appId=${appId}`)
}

View File

@ -1,9 +1,9 @@
import type { AfterResponseHook, BeforeErrorHook, BeforeRequestHook, Hooks } from 'ky'
import ky from 'ky'
import type { IOtherOptions } from './base'
import Cookies from 'js-cookie'
import ky from 'ky'
import Toast from '@/app/components/base/toast'
import { API_PREFIX, APP_VERSION, CSRF_COOKIE_NAME, CSRF_HEADER_NAME, IS_MARKETPLACE, MARKETPLACE_API_PREFIX, PASSPORT_HEADER_NAME, PUBLIC_API_PREFIX, WEB_APP_SHARE_CODE_HEADER_NAME } from '@/config'
import Cookies from 'js-cookie'
import { getWebAppAccessToken, getWebAppPassport } from './webapp-auth'
const TIME_OUT = 100000
@ -24,7 +24,8 @@ export type FetchOptionType = Omit<RequestInit, 'body'> & {
}
const afterResponse204: AfterResponseHook = async (_request, _options, response) => {
if (response.status === 204) return Response.json({ result: 'success' })
if (response.status === 204)
return Response.json({ result: 'success' })
}
export type ResponseError = {
@ -209,8 +210,9 @@ async function base<T>(url: string, options: FetchOptionType = {}, otherOptions:
if (
contentType
&& [ContentType.download, ContentType.audio, ContentType.downloadZip].includes(contentType)
)
) {
return await res.blob() as T
}
return await res.json() as T
}

View File

@ -1,8 +1,6 @@
import groupBy from 'lodash-es/groupBy'
import type { MutationOptions } from '@tanstack/react-query'
import { useMutation } from '@tanstack/react-query'
import { createDocument, createFirstDocument, fetchDefaultProcessRule, fetchFileIndexingEstimate } from '../datasets'
import type { IndexingType } from '@/app/components/datasets/create/step-two'
import type { DataSourceProvider, NotionPage } from '@/models/common'
import type {
ChunkingMode,
CrawlOptions,
@ -10,6 +8,7 @@ import type {
CreateDatasetReq,
CreateDatasetResponse,
CreateDocumentReq,
createDocumentResponse,
CustomFile,
DataSourceType,
FileIndexingEstimateResponse,
@ -17,10 +16,11 @@ import type {
NotionInfo,
ProcessRule,
ProcessRuleResponse,
createDocumentResponse,
} from '@/models/datasets'
import type { DataSourceProvider, NotionPage } from '@/models/common'
import { useMutation } from '@tanstack/react-query'
import { groupBy } from 'es-toolkit/compat'
import { post } from '../base'
import { createDocument, createFirstDocument, fetchDefaultProcessRule, fetchFileIndexingEstimate } from '../datasets'
const NAME_SPACE = 'knowledge/create-dataset'

View File

@ -1,23 +1,86 @@
import type { MutationOptions } from '@tanstack/react-query'
import { useInfiniteQuery, useMutation, useQuery } from '@tanstack/react-query'
import type { ApiKeysListResponse } from '@/models/app'
import type { CommonResponse } from '@/models/common'
import type {
DataSet,
DataSetListResponse,
DatasetListRequest,
DataSetListResponse,
ErrorDocsResponse,
ExternalAPIListResponse,
FetchDatasetsParams,
HitTestingRecordsResponse,
IndexingStatusBatchRequest,
IndexingStatusBatchResponse,
ProcessRuleResponse,
RelatedAppResponse,
} from '@/models/datasets'
import {
keepPreviousData,
useInfiniteQuery,
useMutation,
useQuery,
useQueryClient,
} from '@tanstack/react-query'
import qs from 'qs'
import { get, post } from '../base'
import { useInvalid } from '../use-base'
import qs from 'qs'
import type { CommonResponse } from '@/models/common'
const NAME_SPACE = 'dataset'
const DatasetListKey = [NAME_SPACE, 'list']
const normalizeDatasetsParams = (params: Partial<FetchDatasetsParams['params']> = {}) => {
const {
page = 1,
limit,
ids,
tag_ids,
include_all,
keyword,
} = params
return {
page,
...(limit ? { limit } : {}),
...(ids?.length ? { ids } : {}),
...(tag_ids?.length ? { tag_ids } : {}),
...(include_all !== undefined ? { include_all } : {}),
...(keyword ? { keyword } : {}),
}
}
type UseInfiniteDatasetsOptions = {
enabled?: boolean
refetchOnMount?: boolean | 'always'
staleTime?: number
refetchOnReconnect?: boolean
refetchOnWindowFocus?: boolean
}
export const useInfiniteDatasets = (
params: Partial<FetchDatasetsParams['params']>,
options?: UseInfiniteDatasetsOptions,
) => {
const normalizedParams = normalizeDatasetsParams(params)
const buildUrl = (pageParam: number | undefined) => {
const queryString = qs.stringify({
...normalizedParams,
page: pageParam ?? normalizedParams.page,
}, { indices: false })
return `/datasets?${queryString}`
}
return useInfiniteQuery<DataSetListResponse>({
queryKey: [...DatasetListKey, 'infinite', normalizedParams],
queryFn: ({ pageParam = normalizedParams.page }) => get<DataSetListResponse>(buildUrl(pageParam as number | undefined)),
getNextPageParam: lastPage => lastPage.has_more ? lastPage.page + 1 : undefined,
initialPageParam: normalizedParams.page,
staleTime: 0,
refetchOnMount: 'always',
...options,
})
}
export const useDatasetList = (params: DatasetListRequest) => {
const { initialPage, tag_ids, limit, include_all, keyword } = params
return useInfiniteQuery({
@ -70,10 +133,12 @@ export const useIndexingStatusBatch = (
})
}
export const useProcessRule = (documentId: string) => {
export const useProcessRule = (documentId?: string) => {
return useQuery<ProcessRuleResponse>({
queryKey: [NAME_SPACE, 'process-rule', documentId],
queryFn: () => get<ProcessRuleResponse>('/datasets/process-rule', { params: { document_id: documentId } }),
enabled: !!documentId,
refetchOnWindowFocus: false,
})
}
@ -97,3 +162,57 @@ export const useDisableDatasetServiceApi = () => {
mutationFn: (datasetId: string) => post<CommonResponse>(`/datasets/${datasetId}/api-keys/disable`),
})
}
export const useDatasetApiKeys = (options?: { enabled?: boolean }) => {
return useQuery<ApiKeysListResponse>({
queryKey: [NAME_SPACE, 'api-keys'],
queryFn: () => get<ApiKeysListResponse>('/datasets/api-keys'),
enabled: options?.enabled ?? true,
})
}
export const useInvalidateDatasetApiKeys = () => {
const queryClient = useQueryClient()
return () => {
queryClient.invalidateQueries({
queryKey: [NAME_SPACE, 'api-keys'],
})
}
}
export const useExternalKnowledgeApiList = (options?: { enabled?: boolean }) => {
return useQuery<ExternalAPIListResponse>({
queryKey: [NAME_SPACE, 'external-knowledge-api'],
queryFn: () => get<ExternalAPIListResponse>('/datasets/external-knowledge-api'),
enabled: options?.enabled ?? true,
})
}
export const useInvalidateExternalKnowledgeApiList = () => {
const queryClient = useQueryClient()
return () => {
queryClient.invalidateQueries({
queryKey: [NAME_SPACE, 'external-knowledge-api'],
})
}
}
export const useDatasetTestingRecords = (
datasetId?: string,
params?: { page: number, limit: number },
) => {
return useQuery<HitTestingRecordsResponse>({
queryKey: [NAME_SPACE, 'testing-records', datasetId, params],
queryFn: () => get<HitTestingRecordsResponse>(`/datasets/${datasetId}/queries`, { params }),
enabled: !!datasetId && !!params,
placeholderData: keepPreviousData,
})
}
export const useDatasetErrorDocs = (datasetId?: string) => {
return useQuery<ErrorDocsResponse>({
queryKey: [NAME_SPACE, 'error-docs', datasetId],
queryFn: () => get<ErrorDocsResponse>(`/datasets/${datasetId}/error-docs`),
enabled: !!datasetId,
})
}

View File

@ -1,14 +1,15 @@
import type { MetadataType, SortType } from '../datasets'
import type { CommonResponse } from '@/models/common'
import type { DocumentDetailResponse, DocumentListResponse, UpdateDocumentBatchParams } from '@/models/datasets'
import {
useMutation,
useQuery,
} from '@tanstack/react-query'
import { del, get, patch } from '../base'
import { useInvalid } from '../use-base'
import type { MetadataType, SortType } from '../datasets'
import { pauseDocIndexing, resumeDocIndexing } from '../datasets'
import type { DocumentDetailResponse, DocumentListResponse, UpdateDocumentBatchParams } from '@/models/datasets'
import { normalizeStatusForQuery } from '@/app/components/datasets/documents/status-filter'
import { DocumentActionType } from '@/models/datasets'
import type { CommonResponse } from '@/models/common'
import { del, get, patch, post } from '../base'
import { pauseDocIndexing, resumeDocIndexing } from '../datasets'
import { useInvalid } from '../use-base'
const NAME_SPACE = 'knowledge/document'
@ -20,15 +21,26 @@ export const useDocumentList = (payload: {
page: number
limit: number
sort?: SortType
},
status?: string
}
refetchInterval?: number | false
}) => {
const { query, datasetId, refetchInterval } = payload
const { keyword, page, limit, sort } = query
const { keyword, page, limit, sort, status } = query
const normalizedStatus = normalizeStatusForQuery(status)
const params: Record<string, number | string> = {
keyword,
page,
limit,
}
if (sort)
params.sort = sort
if (normalizedStatus && normalizedStatus !== 'all')
params.status = normalizedStatus
return useQuery<DocumentListResponse>({
queryKey: [...useDocumentListKey, datasetId, keyword, page, limit, sort],
queryKey: [...useDocumentListKey, datasetId, params],
queryFn: () => get<DocumentListResponse>(`/datasets/${datasetId}/documents`, {
params: query,
params,
}),
refetchInterval,
})
@ -111,7 +123,7 @@ export const useDocumentDetail = (payload: {
}) => {
const { datasetId, documentId, params } = payload
return useQuery<DocumentDetailResponse>({
queryKey: [...useDocumentDetailKey, 'withoutMetaData', datasetId, documentId],
queryKey: [...useDocumentDetailKey, 'withoutMetaData', datasetId, documentId, params],
queryFn: () => get<DocumentDetailResponse>(`/datasets/${datasetId}/documents/${documentId}`, { params }),
})
}
@ -123,7 +135,7 @@ export const useDocumentMetadata = (payload: {
}) => {
const { datasetId, documentId, params } = payload
return useQuery<DocumentDetailResponse>({
queryKey: [...useDocumentDetailKey, 'onlyMetaData', datasetId, documentId],
queryKey: [...useDocumentDetailKey, 'onlyMetaData', datasetId, documentId, params],
queryFn: () => get<DocumentDetailResponse>(`/datasets/${datasetId}/documents/${documentId}`, { params }),
})
}
@ -151,3 +163,15 @@ export const useDocumentResume = () => {
},
})
}
export const useDocumentBatchRetryIndex = () => {
return useMutation({
mutationFn: ({ datasetId, documentIds }: { datasetId: string, documentIds: string[] }) => {
return post<CommonResponse>(`/datasets/${datasetId}/retry`, {
body: {
document_ids: documentIds,
},
})
},
})
}

View File

@ -1 +1,45 @@
export {}
import type {
ExternalKnowledgeBaseHitTestingRequest,
ExternalKnowledgeBaseHitTestingResponse,
HitTestingRecordsRequest,
HitTestingRecordsResponse,
HitTestingRequest,
HitTestingResponse,
} from '@/models/datasets'
import { useMutation, useQuery } from '@tanstack/react-query'
import { get, post } from '../base'
import { useInvalid } from '../use-base'
const NAME_SPACE = 'hit-testing'
const HitTestingRecordsKey = [NAME_SPACE, 'records']
export const useHitTestingRecords = (params: HitTestingRecordsRequest) => {
const { datasetId, page, limit } = params
return useQuery({
queryKey: [...HitTestingRecordsKey, datasetId, page, limit],
queryFn: () => get<HitTestingRecordsResponse>(`/datasets/${datasetId}/queries`, { params: { page, limit } }),
})
}
export const useInvalidateHitTestingRecords = (datasetId: string) => {
return useInvalid([...HitTestingRecordsKey, datasetId])
}
export const useHitTesting = (datasetId: string) => {
return useMutation({
mutationKey: [NAME_SPACE, 'hit-testing', datasetId],
mutationFn: (params: HitTestingRequest) => post<HitTestingResponse>(`/datasets/${datasetId}/hit-testing`, {
body: params,
}),
})
}
export const useExternalKnowledgeBaseHitTesting = (datasetId: string) => {
return useMutation({
mutationKey: [NAME_SPACE, 'external-knowledge-base-hit-testing', datasetId],
mutationFn: (params: ExternalKnowledgeBaseHitTestingRequest) => post<ExternalKnowledgeBaseHitTestingResponse>(`/datasets/${datasetId}/external-hit-testing`, {
body: params,
}),
})
}

View File

@ -1,6 +1,6 @@
import type { DataSourceNotionWorkspace } from '@/models/common'
import { useQuery, useQueryClient } from '@tanstack/react-query'
import { get } from '../base'
import type { DataSourceNotionWorkspace } from '@/models/common'
type PreImportNotionPagesParams = {
datasetId: string

View File

@ -1,12 +1,12 @@
import { DataType } from '@/app/components/datasets/metadata/types'
import { act, renderHook } from '@testing-library/react'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { act, renderHook } from '@testing-library/react'
import { DataType } from '@/app/components/datasets/metadata/types'
import { useBatchUpdateDocMetadata } from '@/service/knowledge/use-metadata'
import { useDocumentListKey } from './use-document'
// Mock the post function to avoid real network requests
jest.mock('@/service/base', () => ({
post: jest.fn().mockResolvedValue({ success: true }),
vi.mock('@/service/base', () => ({
post: vi.fn().mockResolvedValue({ success: true }),
}))
const NAME_SPACE = 'dataset-metadata'
@ -28,7 +28,7 @@ describe('useBatchUpdateDocMetadata', () => {
const { result } = renderHook(() => useBatchUpdateDocMetadata(), { wrapper })
// Spy on queryClient.invalidateQueries
const invalidateSpy = jest.spyOn(queryClient, 'invalidateQueries')
const invalidateSpy = vi.spyOn(queryClient, 'invalidateQueries')
// Correct payload type: each document has its own metadata_list array

View File

@ -1,9 +1,9 @@
import type { BuiltInMetadataItem, MetadataBatchEditToServer, MetadataItemWithValueLength } from '@/app/components/datasets/metadata/types'
import { del, get, patch, post } from '../base'
import { useDocumentListKey, useInvalidDocumentList } from './use-document'
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
import { useInvalid } from '../use-base'
import type { DocumentDetailResponse } from '@/models/datasets'
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
import { del, get, patch, post } from '../base'
import { useInvalid } from '../use-base'
import { useDocumentListKey, useInvalidDocumentList } from './use-document'
const NAME_SPACE = 'dataset-metadata'

View File

@ -1,5 +1,3 @@
import { useMutation, useQuery } from '@tanstack/react-query'
import { del, get, patch, post } from '../base'
import type { CommonResponse } from '@/models/common'
import type {
BatchImportResponse,
@ -7,9 +5,11 @@ import type {
ChildSegmentsResponse,
ChunkingMode,
SegmentDetailModel,
SegmentUpdater,
SegmentsResponse,
SegmentUpdater,
} from '@/models/datasets'
import { useMutation, useQuery } from '@tanstack/react-query'
import { del, get, patch, post } from '../base'
const NAME_SPACE = 'segment'
@ -32,9 +32,9 @@ export const useSegmentList = (
disable?: boolean,
) => {
const { datasetId, documentId, params } = payload
const { page, limit, keyword, enabled } = params
return useQuery<SegmentsResponse>({
queryKey: [...useSegmentListKey, { datasetId, documentId, page, limit, keyword, enabled }],
queryKey: [...useSegmentListKey, datasetId, documentId, params],
queryFn: () => {
return get<SegmentsResponse>(`/datasets/${datasetId}/documents/${documentId}/segments`, { params })
},
@ -45,9 +45,9 @@ export const useSegmentList = (
export const useUpdateSegment = () => {
return useMutation({
mutationKey: [NAME_SPACE, 'update'],
mutationFn: (payload: { datasetId: string; documentId: string; segmentId: string; body: SegmentUpdater }) => {
mutationFn: (payload: { datasetId: string, documentId: string, segmentId: string, body: SegmentUpdater }) => {
const { datasetId, documentId, segmentId, body } = payload
return patch<{ data: SegmentDetailModel; doc_form: ChunkingMode }>(`/datasets/${datasetId}/documents/${documentId}/segments/${segmentId}`, { body })
return patch<{ data: SegmentDetailModel, doc_form: ChunkingMode }>(`/datasets/${datasetId}/documents/${documentId}/segments/${segmentId}`, { body })
},
})
}
@ -55,9 +55,9 @@ export const useUpdateSegment = () => {
export const useAddSegment = () => {
return useMutation({
mutationKey: [NAME_SPACE, 'add'],
mutationFn: (payload: { datasetId: string; documentId: string; body: SegmentUpdater }) => {
mutationFn: (payload: { datasetId: string, documentId: string, body: SegmentUpdater }) => {
const { datasetId, documentId, body } = payload
return post<{ data: SegmentDetailModel; doc_form: ChunkingMode }>(`/datasets/${datasetId}/documents/${documentId}/segment`, { body })
return post<{ data: SegmentDetailModel, doc_form: ChunkingMode }>(`/datasets/${datasetId}/documents/${documentId}/segment`, { body })
},
})
}
@ -65,7 +65,7 @@ export const useAddSegment = () => {
export const useEnableSegment = () => {
return useMutation({
mutationKey: [NAME_SPACE, 'enable'],
mutationFn: (payload: { datasetId: string; documentId: string; segmentIds: string[] }) => {
mutationFn: (payload: { datasetId: string, documentId: string, segmentIds: string[] }) => {
const { datasetId, documentId, segmentIds } = payload
const query = segmentIds.map(id => `segment_id=${id}`).join('&')
return patch<CommonResponse>(`/datasets/${datasetId}/documents/${documentId}/segment/enable?${query}`)
@ -76,7 +76,7 @@ export const useEnableSegment = () => {
export const useDisableSegment = () => {
return useMutation({
mutationKey: [NAME_SPACE, 'disable'],
mutationFn: (payload: { datasetId: string; documentId: string; segmentIds: string[] }) => {
mutationFn: (payload: { datasetId: string, documentId: string, segmentIds: string[] }) => {
const { datasetId, documentId, segmentIds } = payload
const query = segmentIds.map(id => `segment_id=${id}`).join('&')
return patch<CommonResponse>(`/datasets/${datasetId}/documents/${documentId}/segment/disable?${query}`)
@ -87,7 +87,7 @@ export const useDisableSegment = () => {
export const useDeleteSegment = () => {
return useMutation({
mutationKey: [NAME_SPACE, 'delete'],
mutationFn: (payload: { datasetId: string; documentId: string; segmentIds: string[] }) => {
mutationFn: (payload: { datasetId: string, documentId: string, segmentIds: string[] }) => {
const { datasetId, documentId, segmentIds } = payload
const query = segmentIds.map(id => `segment_id=${id}`).join('&')
return del<CommonResponse>(`/datasets/${datasetId}/documents/${documentId}/segments?${query}`)
@ -111,9 +111,9 @@ export const useChildSegmentList = (
disable?: boolean,
) => {
const { datasetId, documentId, segmentId, params } = payload
const { page, limit, keyword } = params
return useQuery({
queryKey: [...useChildSegmentListKey, { datasetId, documentId, segmentId, page, limit, keyword }],
queryKey: [...useChildSegmentListKey, datasetId, documentId, segmentId, params],
queryFn: () => {
return get<ChildSegmentsResponse>(`/datasets/${datasetId}/documents/${documentId}/segments/${segmentId}/child_chunks`, { params })
},
@ -124,7 +124,7 @@ export const useChildSegmentList = (
export const useDeleteChildSegment = () => {
return useMutation({
mutationKey: [NAME_SPACE, 'childChunk', 'delete'],
mutationFn: (payload: { datasetId: string; documentId: string; segmentId: string; childChunkId: string }) => {
mutationFn: (payload: { datasetId: string, documentId: string, segmentId: string, childChunkId: string }) => {
const { datasetId, documentId, segmentId, childChunkId } = payload
return del<CommonResponse>(`/datasets/${datasetId}/documents/${documentId}/segments/${segmentId}/child_chunks/${childChunkId}`)
},
@ -134,7 +134,7 @@ export const useDeleteChildSegment = () => {
export const useAddChildSegment = () => {
return useMutation({
mutationKey: [NAME_SPACE, 'childChunk', 'add'],
mutationFn: (payload: { datasetId: string; documentId: string; segmentId: string; body: { content: string } }) => {
mutationFn: (payload: { datasetId: string, documentId: string, segmentId: string, body: { content: string } }) => {
const { datasetId, documentId, segmentId, body } = payload
return post<{ data: ChildChunkDetail }>(`/datasets/${datasetId}/documents/${documentId}/segments/${segmentId}/child_chunks`, { body })
},
@ -144,7 +144,7 @@ export const useAddChildSegment = () => {
export const useUpdateChildSegment = () => {
return useMutation({
mutationKey: [NAME_SPACE, 'childChunk', 'update'],
mutationFn: (payload: { datasetId: string; documentId: string; segmentId: string; childChunkId: string; body: { content: string } }) => {
mutationFn: (payload: { datasetId: string, documentId: string, segmentId: string, childChunkId: string, body: { content: string } }) => {
const { datasetId, documentId, segmentId, childChunkId, body } = payload
return patch<{ data: ChildChunkDetail }>(`/datasets/${datasetId}/documents/${documentId}/segments/${segmentId}/child_chunks/${childChunkId}`, { body })
},
@ -154,7 +154,7 @@ export const useUpdateChildSegment = () => {
export const useSegmentBatchImport = () => {
return useMutation({
mutationKey: [NAME_SPACE, 'batchImport'],
mutationFn: (payload: { url: string; body: { upload_file_id: string } }) => {
mutationFn: (payload: { url: string, body: { upload_file_id: string } }) => {
const { url, body } = payload
return post<BatchImportResponse>(url, { body })
},

View File

@ -1,80 +1,38 @@
import type { Fetcher } from 'swr'
import { get, post } from './base'
import type {
AgentLogDetailRequest,
AgentLogDetailResponse,
AnnotationsCountResponse,
ChatConversationFullDetailResponse,
ChatConversationsRequest,
ChatConversationsResponse,
ChatMessagesRequest,
ChatMessagesResponse,
CompletionConversationFullDetailResponse,
CompletionConversationsRequest,
CompletionConversationsResponse,
ConversationListResponse,
LogMessageAnnotationsRequest,
LogMessageAnnotationsResponse,
LogMessageFeedbacksRequest,
LogMessageFeedbacksResponse,
WorkflowLogsResponse,
WorkflowRunDetailResponse,
} from '@/models/log'
import type { NodeTracingListResponse } from '@/types/workflow'
export const fetchConversationList: Fetcher<ConversationListResponse, { name: string; appId: string; params?: Record<string, any> }> = ({ appId, params }) => {
return get<ConversationListResponse>(`/console/api/apps/${appId}/messages`, params)
}
// (Text Generation Application) Session List
export const fetchCompletionConversations: Fetcher<CompletionConversationsResponse, { url: string; params?: CompletionConversationsRequest }> = ({ url, params }) => {
return get<CompletionConversationsResponse>(url, { params })
}
// (Text Generation Application) Session Detail
export const fetchCompletionConversationDetail: Fetcher<CompletionConversationFullDetailResponse, { url: string }> = ({ url }) => {
return get<CompletionConversationFullDetailResponse>(url, {})
}
// (Chat Application) Session List
export const fetchChatConversations: Fetcher<ChatConversationsResponse, { url: string; params?: ChatConversationsRequest }> = ({ url, params }) => {
return get<ChatConversationsResponse>(url, { params })
}
// (Chat Application) Session Detail
export const fetchChatConversationDetail: Fetcher<ChatConversationFullDetailResponse, { url: string }> = ({ url }) => {
return get<ChatConversationFullDetailResponse>(url, {})
}
import { get, post } from './base'
// (Chat Application) Message list in one session
export const fetchChatMessages: Fetcher<ChatMessagesResponse, { url: string; params: ChatMessagesRequest }> = ({ url, params }) => {
export const fetchChatMessages = ({ url, params }: { url: string, params: ChatMessagesRequest }): Promise<ChatMessagesResponse> => {
return get<ChatMessagesResponse>(url, { params })
}
export const updateLogMessageFeedbacks: Fetcher<LogMessageFeedbacksResponse, { url: string; body: LogMessageFeedbacksRequest }> = ({ url, body }) => {
export const updateLogMessageFeedbacks = ({ url, body }: { url: string, body: LogMessageFeedbacksRequest }): Promise<LogMessageFeedbacksResponse> => {
return post<LogMessageFeedbacksResponse>(url, { body })
}
export const updateLogMessageAnnotations: Fetcher<LogMessageAnnotationsResponse, { url: string; body: LogMessageAnnotationsRequest }> = ({ url, body }) => {
export const updateLogMessageAnnotations = ({ url, body }: { url: string, body: LogMessageAnnotationsRequest }): Promise<LogMessageAnnotationsResponse> => {
return post<LogMessageAnnotationsResponse>(url, { body })
}
export const fetchAnnotationsCount: Fetcher<AnnotationsCountResponse, { url: string }> = ({ url }) => {
return get<AnnotationsCountResponse>(url)
}
export const fetchWorkflowLogs: Fetcher<WorkflowLogsResponse, { url: string; params: Record<string, any> }> = ({ url, params }) => {
return get<WorkflowLogsResponse>(url, { params })
}
export const fetchRunDetail = (url: string) => {
export const fetchRunDetail = (url: string): Promise<WorkflowRunDetailResponse> => {
return get<WorkflowRunDetailResponse>(url)
}
export const fetchTracingList: Fetcher<NodeTracingListResponse, { url: string }> = ({ url }) => {
export const fetchTracingList = ({ url }: { url: string }): Promise<NodeTracingListResponse> => {
return get<NodeTracingListResponse>(url)
}
export const fetchAgentLogDetail = ({ appID, params }: { appID: string; params: AgentLogDetailRequest }) => {
export const fetchAgentLogDetail = ({ appID, params }: { appID: string, params: AgentLogDetailRequest }): Promise<AgentLogDetailResponse> => {
return get<AgentLogDetailResponse>(`/apps/${appID}/agent/logs`, { params })
}

View File

@ -1,5 +1,7 @@
import type { Fetcher } from 'swr'
import { get, getMarketplace, post, upload } from './base'
import type {
MarketplaceCollectionPluginsResponse,
MarketplaceCollectionsResponse,
} from '@/app/components/plugins/marketplace/types'
import type {
Dependency,
InstallPackageResponse,
@ -13,10 +15,7 @@ import type {
updatePackageResponse,
uploadGitHubResponse,
} from '@/app/components/plugins/types'
import type {
MarketplaceCollectionPluginsResponse,
MarketplaceCollectionsResponse,
} from '@/app/components/plugins/marketplace/types'
import { get, getMarketplace, post, upload } from './base'
export const uploadFile = async (file: File, isBundle: boolean) => {
const formData = new FormData()
@ -33,8 +32,7 @@ export const updateFromMarketPlace = async (body: Record<string, string>) => {
})
}
export const updateFromGitHub = async (repoUrl: string, selectedVersion: string, selectedPackage: string,
originalPlugin: string, newPlugin: string) => {
export const updateFromGitHub = async (repoUrl: string, selectedVersion: string, selectedPackage: string, originalPlugin: string, newPlugin: string) => {
return post<updatePackageResponse>('/workspaces/current/plugin/upgrade/github', {
body: {
repo: repoUrl,
@ -83,11 +81,11 @@ export const fetchPluginInfoFromMarketPlace = async ({
return getMarketplace<{ data: { plugin: PluginInfoFromMarketPlace, version: { version: string } } }>(`/plugins/${org}/${name}`)
}
export const fetchMarketplaceCollections: Fetcher<MarketplaceCollectionsResponse, { url: string; }> = ({ url }) => {
export const fetchMarketplaceCollections = ({ url }: { url: string }): Promise<MarketplaceCollectionsResponse> => {
return get<MarketplaceCollectionsResponse>(url)
}
export const fetchMarketplaceCollectionPlugins: Fetcher<MarketplaceCollectionPluginsResponse, { url: string }> = ({ url }) => {
export const fetchMarketplaceCollectionPlugins = ({ url }: { url: string }): Promise<MarketplaceCollectionPluginsResponse> => {
return get<MarketplaceCollectionPluginsResponse>(url)
}

View File

@ -16,20 +16,27 @@ import type {
IOnWorkflowFinished,
IOnWorkflowStarted,
} from './base'
import {
del as consoleDel, get as consoleGet, patch as consolePatch, post as consolePost,
delPublic as del, getPublic as get, patchPublic as patch, postPublic as post, ssePost,
} from './base'
import type { FeedbackType } from '@/app/components/base/chat/chat/type'
import type { ChatConfig } from '@/app/components/base/chat/types'
import type { AccessMode } from '@/models/access-control'
import type {
AppConversationData,
AppData,
AppMeta,
ConversationItem,
} from '@/models/share'
import type { ChatConfig } from '@/app/components/base/chat/types'
import type { AccessMode } from '@/models/access-control'
import { WEB_APP_SHARE_CODE_HEADER_NAME } from '@/config'
import {
del as consoleDel,
get as consoleGet,
patch as consolePatch,
post as consolePost,
delPublic as del,
getPublic as get,
patchPublic as patch,
postPublic as post,
ssePost,
} from './base'
import { getWebAppAccessToken } from './webapp-auth'
export enum AppSourceType {
@ -71,18 +78,19 @@ export const stopChatMessageResponding = async (appId: string, taskId: string, a
return getAction('post', appSourceType)(getUrl(`chat-messages/${taskId}/stop`, appSourceType, installedAppId))
}
export const sendCompletionMessage = async (body: Record<string, any>, { onData, onCompleted, onError, onMessageReplace }: {
export const sendCompletionMessage = async (body: Record<string, any>, { onData, onCompleted, onError, onMessageReplace, getAbortController }: {
onData: IOnData
onCompleted: IOnCompleted
onError: IOnError
onMessageReplace: IOnMessageReplace
getAbortController?: (abortController: AbortController) => void
}, appSourceType: AppSourceType, installedAppId = '') => {
return ssePost(getUrl('completion-messages', appSourceType, installedAppId), {
body: {
...body,
response_mode: 'streaming',
},
}, { onData, onCompleted, isPublicAPI: getIsPublicAPI(appSourceType), onError, onMessageReplace })
}, { onData, onCompleted, isPublicAPI: getIsPublicAPI(appSourceType), onError, onMessageReplace, getAbortController })
}
export const sendWorkflowMessage = async (
@ -139,6 +147,12 @@ export const sendWorkflowMessage = async (
})
}
export const stopWorkflowMessage = async (_appId: string, taskId: string, appSourceType: AppSourceType, installedAppId = '') => {
if (!taskId)
return
return getAction('post', appSourceType)(getUrl(`workflows/tasks/${taskId}/stop`, appSourceType, installedAppId))
}
export const fetchAppInfo = async () => {
return get('/site') as Promise<AppData>
}
@ -241,7 +255,7 @@ export const fetchAppMeta = async (appSourceType: AppSourceType, installedAppId
return (getAction('get', appSourceType))(getUrl('meta', appSourceType, installedAppId)) as Promise<AppMeta>
}
export const updateFeedback = async ({ url, body }: { url: string; body: FeedbackType }, appSourceType: AppSourceType, installedAppId = '') => {
export const updateFeedback = async ({ url, body }: { url: string, body: FeedbackType }, appSourceType: AppSourceType, installedAppId = '') => {
return (getAction('post', appSourceType))(getUrl(url, appSourceType, installedAppId), { body })
}
@ -275,10 +289,14 @@ export const audioToText = (url: string, appSourceType: AppSourceType, body: For
return (getAction('post', appSourceType))(url, { body }, { bodyStringify: false, deleteContentType: true }) as Promise<{ text: string }>
}
export const textToAudioStream = (url: string, appSourceType: AppSourceType, header: { content_type: string }, body: { streaming: boolean; voice?: string; message_id?: string; text?: string | null | undefined }) => {
export const textToAudioStream = (url: string, appSourceType: AppSourceType, header: { content_type: string }, body: { streaming: boolean, voice?: string, message_id?: string, text?: string | null | undefined }) => {
return (getAction('post', appSourceType))(url, { body, header }, { needAllResponseContent: true })
}
export const textToAudio = (url: string, appSourceType: AppSourceType, body: FormData) => {
return (getAction('post', appSourceType))(url, { body }, { bodyStringify: false, deleteContentType: true }) as Promise<{ data: string }>
}
export const fetchAccessToken = async ({ userId, appCode }: { userId?: string, appCode: string }) => {
const headers = new Headers()
headers.append(WEB_APP_SHARE_CODE_HEADER_NAME, appCode)

View File

@ -7,10 +7,10 @@ export const getUserSAMLSSOUrl = (invite_token?: string) => {
export const getUserOIDCSSOUrl = (invite_token?: string) => {
const url = invite_token ? `/enterprise/sso/oidc/login?invite_token=${invite_token}` : '/enterprise/sso/oidc/login'
return get<{ url: string; state: string }>(url)
return get<{ url: string, state: string }>(url)
}
export const getUserOAuth2SSOUrl = (invite_token?: string) => {
const url = invite_token ? `/enterprise/sso/oauth2/login?invite_token=${invite_token}` : '/enterprise/sso/oauth2/login'
return get<{ url: string; state: string }>(url)
return get<{ url: string, state: string }>(url)
}

View File

@ -1,5 +1,5 @@
import { del, get, patch, post } from './base'
import type { Tag } from '@/app/components/base/tag-management/constant'
import { del, get, patch, post } from './base'
export const fetchTagList = (type: string) => {
return get<Tag[]>('/tags', { params: { type } })

View File

@ -1,4 +1,3 @@
import { get, post } from './base'
import type {
Collection,
CustomCollectionBackend,
@ -9,6 +8,7 @@ import type {
WorkflowToolProviderResponse,
} from '@/app/components/tools/types'
import { buildProviderQuery } from './_tools_util'
import { get, post } from './base'
export const fetchCollectionList = () => {
return get<Collection[]>('/workspaces/current/tool-providers')
@ -58,7 +58,7 @@ export const removeBuiltInToolCredential = (collectionName: string) => {
}
export const parseParamsSchema = (schema: string) => {
return post<{ parameters_schema: CustomParamSchema[]; schema_type: string }>('/workspaces/current/tool-provider/api/schema', {
return post<{ parameters_schema: CustomParamSchema[], schema_type: string }>('/workspaces/current/tool-provider/api/schema', {
body: {
schema,
},

View File

@ -1,14 +1,14 @@
import {
get,
} from './base'
import type { Viewport } from 'reactflow'
import type { Edge, Node } from '@/app/components/workflow/types'
import type { DataSetListResponse } from '@/models/datasets'
import type {
SiteInfo,
} from '@/models/share'
import type { AppModeEnum, ModelConfig } from '@/types/app'
import qs from 'qs'
import type { DataSetListResponse } from '@/models/datasets'
import type { Edge, Node } from '@/app/components/workflow/types'
import type { Viewport } from 'reactflow'
import {
get,
} from './base'
export type TryAppInfo = {
name: string

View File

@ -1,31 +1,64 @@
import { get, post } from './base'
import type { App } from '@/types/app'
import type { AppListResponse } from '@/models/app'
import { useInvalid } from './use-base'
import { useQuery } from '@tanstack/react-query'
import type { GeneratorType } from '@/app/components/app/configuration/config/automatic/types'
import type {
ApiKeysListResponse,
AppDailyConversationsResponse,
AppDailyEndUsersResponse,
AppDailyMessagesResponse,
AppListResponse,
AppStatisticsResponse,
AppTokenCostsResponse,
AppVoicesListResponse,
WorkflowDailyConversationsResponse,
} from '@/models/app'
import type { App, AppModeEnum } from '@/types/app'
import {
keepPreviousData,
useInfiniteQuery,
useQuery,
useQueryClient,
} from '@tanstack/react-query'
import { get, post } from './base'
import { useInvalid } from './use-base'
const NAME_SPACE = 'apps'
// TODO paging for list
type AppListParams = {
page?: number
limit?: number
name?: string
mode?: AppModeEnum | 'all'
tag_ids?: string[]
is_created_by_me?: boolean
}
type DateRangeParams = {
start?: string
end?: string
}
const normalizeAppListParams = (params: AppListParams) => {
const {
page = 1,
limit = 30,
name = '',
mode,
tag_ids,
is_created_by_me,
} = params
return {
page,
limit,
name,
...(mode && mode !== 'all' ? { mode } : {}),
...(tag_ids?.length ? { tag_ids } : {}),
...(is_created_by_me ? { is_created_by_me } : {}),
}
}
const appListKey = (params: AppListParams) => [NAME_SPACE, 'list', params]
const useAppFullListKey = [NAME_SPACE, 'full-list']
export const useAppFullList = () => {
return useQuery<AppListResponse>({
queryKey: useAppFullListKey,
queryFn: () => get<AppListResponse>('/apps', { params: { page: 1, limit: 100 } }),
})
}
export const useInvalidateAppFullList = () => {
return useInvalid(useAppFullListKey)
}
export const useAppDetail = (appID: string) => {
return useQuery<App>({
queryKey: [NAME_SPACE, 'detail', appID],
queryFn: () => get<App>(`/apps/${appID}`),
})
}
export const useGenerateRuleTemplate = (type: GeneratorType, disabled?: boolean) => {
return useQuery({
@ -39,3 +72,143 @@ export const useGenerateRuleTemplate = (type: GeneratorType, disabled?: boolean)
retry: 0,
})
}
export const useAppDetail = (appID: string) => {
return useQuery<App>({
queryKey: [NAME_SPACE, 'detail', appID],
queryFn: () => get<App>(`/apps/${appID}`),
enabled: !!appID,
})
}
export const useAppList = (params: AppListParams, options?: { enabled?: boolean }) => {
const normalizedParams = normalizeAppListParams(params)
return useQuery<AppListResponse>({
queryKey: appListKey(normalizedParams),
queryFn: () => get<AppListResponse>('/apps', { params: normalizedParams }),
...options,
})
}
export const useAppFullList = () => {
return useQuery<AppListResponse>({
queryKey: useAppFullListKey,
queryFn: () => get<AppListResponse>('/apps', { params: { page: 1, limit: 100, name: '' } }),
})
}
export const useInvalidateAppFullList = () => {
return useInvalid(useAppFullListKey)
}
export const useInfiniteAppList = (params: AppListParams, options?: { enabled?: boolean }) => {
const normalizedParams = normalizeAppListParams(params)
return useInfiniteQuery<AppListResponse>({
queryKey: appListKey(normalizedParams),
queryFn: ({ pageParam = normalizedParams.page }) => get<AppListResponse>('/apps', { params: { ...normalizedParams, page: pageParam } }),
getNextPageParam: lastPage => lastPage.has_more ? lastPage.page + 1 : undefined,
initialPageParam: normalizedParams.page,
placeholderData: keepPreviousData,
...options,
})
}
export const useInvalidateAppList = () => {
const queryClient = useQueryClient()
return () => {
queryClient.invalidateQueries({
queryKey: [NAME_SPACE, 'list'],
})
}
}
const useAppStatisticsQuery = <T>(metric: string, appId: string, params?: DateRangeParams) => {
return useQuery<T>({
queryKey: [NAME_SPACE, 'statistics', metric, appId, params],
queryFn: () => get<T>(`/apps/${appId}/statistics/${metric}`, { params }),
enabled: !!appId,
})
}
const useWorkflowStatisticsQuery = <T>(metric: string, appId: string, params?: DateRangeParams) => {
return useQuery<T>({
queryKey: [NAME_SPACE, 'workflow-statistics', metric, appId, params],
queryFn: () => get<T>(`/apps/${appId}/workflow/statistics/${metric}`, { params }),
enabled: !!appId,
})
}
export const useAppDailyMessages = (appId: string, params?: DateRangeParams) => {
return useAppStatisticsQuery<AppDailyMessagesResponse>('daily-messages', appId, params)
}
export const useAppDailyConversations = (appId: string, params?: DateRangeParams) => {
return useAppStatisticsQuery<AppDailyConversationsResponse>('daily-conversations', appId, params)
}
export const useAppDailyEndUsers = (appId: string, params?: DateRangeParams) => {
return useAppStatisticsQuery<AppDailyEndUsersResponse>('daily-end-users', appId, params)
}
export const useAppAverageSessionInteractions = (appId: string, params?: DateRangeParams) => {
return useAppStatisticsQuery<AppStatisticsResponse>('average-session-interactions', appId, params)
}
export const useAppAverageResponseTime = (appId: string, params?: DateRangeParams) => {
return useAppStatisticsQuery<AppStatisticsResponse>('average-response-time', appId, params)
}
export const useAppTokensPerSecond = (appId: string, params?: DateRangeParams) => {
return useAppStatisticsQuery<AppStatisticsResponse>('tokens-per-second', appId, params)
}
export const useAppSatisfactionRate = (appId: string, params?: DateRangeParams) => {
return useAppStatisticsQuery<AppStatisticsResponse>('user-satisfaction-rate', appId, params)
}
export const useAppTokenCosts = (appId: string, params?: DateRangeParams) => {
return useAppStatisticsQuery<AppTokenCostsResponse>('token-costs', appId, params)
}
export const useWorkflowDailyConversations = (appId: string, params?: DateRangeParams) => {
return useWorkflowStatisticsQuery<WorkflowDailyConversationsResponse>('daily-conversations', appId, params)
}
export const useWorkflowDailyTerminals = (appId: string, params?: DateRangeParams) => {
return useWorkflowStatisticsQuery<AppDailyEndUsersResponse>('daily-terminals', appId, params)
}
export const useWorkflowTokenCosts = (appId: string, params?: DateRangeParams) => {
return useWorkflowStatisticsQuery<AppTokenCostsResponse>('token-costs', appId, params)
}
export const useWorkflowAverageInteractions = (appId: string, params?: DateRangeParams) => {
return useWorkflowStatisticsQuery<AppStatisticsResponse>('average-app-interactions', appId, params)
}
export const useAppVoices = (appId?: string, language?: string) => {
return useQuery<AppVoicesListResponse>({
queryKey: [NAME_SPACE, 'voices', appId, language || 'en-US'],
queryFn: () => get<AppVoicesListResponse>(`/apps/${appId}/text-to-audio/voices`, { params: { language: language || 'en-US' } }),
enabled: !!appId,
})
}
export const useAppApiKeys = (appId?: string, options?: { enabled?: boolean }) => {
return useQuery<ApiKeysListResponse>({
queryKey: [NAME_SPACE, 'api-keys', appId],
queryFn: () => get<ApiKeysListResponse>(`/apps/${appId}/api-keys`),
enabled: !!appId && (options?.enabled ?? true),
})
}
export const useInvalidateAppApiKeys = () => {
const queryClient = useQueryClient()
return (appId?: string) => {
if (!appId)
return
queryClient.invalidateQueries({
queryKey: [NAME_SPACE, 'api-keys', appId],
})
}
}

View File

@ -1,5 +1,6 @@
import type { QueryKey } from '@tanstack/react-query'
import {
type QueryKey,
useQueryClient,
} from '@tanstack/react-query'

View File

@ -0,0 +1,22 @@
import { useMutation, useQuery } from '@tanstack/react-query'
import { bindPartnerStackInfo, fetchBillingUrl } from '@/service/billing'
const NAME_SPACE = 'billing'
export const useBindPartnerStackInfo = () => {
return useMutation({
mutationKey: [NAME_SPACE, 'bind-partner-stack'],
mutationFn: (data: { partnerKey: string, clickId: string }) => bindPartnerStackInfo(data.partnerKey, data.clickId),
})
}
export const useBillingUrl = (enabled: boolean) => {
return useQuery({
queryKey: [NAME_SPACE, 'url'],
enabled,
queryFn: async () => {
const res = await fetchBillingUrl()
return res.url
},
})
}

View File

@ -1,22 +1,124 @@
import { get, post } from './base'
import type { FileTypesRes } from './datasets'
import type {
Model,
ModelParameterRule,
ModelProvider,
ModelTypeEnum,
} from '@/app/components/header/account-setting/model-provider-page/declarations'
import type {
AccountIntegrate,
ApiBasedExtension,
CodeBasedExtension,
CommonResponse,
DataSourceNotion,
FileUploadConfigResponse,
ICurrentWorkspace,
IWorkspace,
LangGeniusVersionResponse,
Member,
PluginProvider,
StructuredOutputRulesRequestBody,
StructuredOutputRulesResponse,
UserProfileResponse,
} from '@/models/common'
import type { RETRIEVE_METHOD } from '@/types/app'
import { useMutation, useQuery } from '@tanstack/react-query'
import type { FileTypesRes } from './datasets'
import { IS_DEV } from '@/config'
import { get, post } from './base'
import { useInvalid } from './use-base'
const NAME_SPACE = 'common'
export const commonQueryKeys = {
fileUploadConfig: [NAME_SPACE, 'file-upload-config'] as const,
userProfile: [NAME_SPACE, 'user-profile'] as const,
currentWorkspace: [NAME_SPACE, 'current-workspace'] as const,
workspaces: [NAME_SPACE, 'workspaces'] as const,
members: [NAME_SPACE, 'members'] as const,
filePreview: (fileID: string) => [NAME_SPACE, 'file-preview', fileID] as const,
schemaDefinitions: [NAME_SPACE, 'schema-type-definitions'] as const,
isLogin: [NAME_SPACE, 'is-login'] as const,
modelProviders: [NAME_SPACE, 'model-providers'] as const,
modelList: (type: ModelTypeEnum) => [NAME_SPACE, 'model-list', type] as const,
defaultModel: (type: ModelTypeEnum) => [NAME_SPACE, 'default-model', type] as const,
retrievalMethods: [NAME_SPACE, 'support-retrieval-methods'] as const,
accountIntegrates: [NAME_SPACE, 'account-integrates'] as const,
pluginProviders: [NAME_SPACE, 'plugin-providers'] as const,
notionConnection: [NAME_SPACE, 'notion-connection'] as const,
apiBasedExtensions: [NAME_SPACE, 'api-based-extensions'] as const,
codeBasedExtensions: (module?: string) => [NAME_SPACE, 'code-based-extensions', module] as const,
invitationCheck: (params?: { workspace_id?: string, email?: string, token?: string }) => [
NAME_SPACE,
'invitation-check',
params?.workspace_id ?? '',
params?.email ?? '',
params?.token ?? '',
] as const,
notionBinding: (code?: string | null) => [NAME_SPACE, 'notion-binding', code] as const,
modelParameterRules: (provider?: string, model?: string) => [NAME_SPACE, 'model-parameter-rules', provider, model] as const,
langGeniusVersion: (currentVersion?: string | null) => [NAME_SPACE, 'langgenius-version', currentVersion] as const,
forgotPasswordValidity: (token?: string | null) => [NAME_SPACE, 'forgot-password-validity', token] as const,
dataSourceIntegrates: [NAME_SPACE, 'data-source-integrates'] as const,
}
export const useFileUploadConfig = () => {
return useQuery<FileUploadConfigResponse>({
queryKey: [NAME_SPACE, 'file-upload-config'],
queryKey: commonQueryKeys.fileUploadConfig,
queryFn: () => get<FileUploadConfigResponse>('/files/upload'),
})
}
type UserProfileWithMeta = {
profile: UserProfileResponse
meta: {
currentVersion: string | null
currentEnv: string | null
}
}
export const useUserProfile = () => {
return useQuery<UserProfileWithMeta>({
queryKey: commonQueryKeys.userProfile,
queryFn: async () => {
const response = await get<Response>('/account/profile', {}, { needAllResponseContent: true }) as Response
const profile = await response.clone().json() as UserProfileResponse
return {
profile,
meta: {
currentVersion: response.headers.get('x-version'),
currentEnv: IS_DEV
? 'DEVELOPMENT'
: response.headers.get('x-env'),
},
}
},
staleTime: 0,
gcTime: 0,
})
}
export const useLangGeniusVersion = (currentVersion?: string | null, enabled?: boolean) => {
return useQuery<LangGeniusVersionResponse>({
queryKey: commonQueryKeys.langGeniusVersion(currentVersion || undefined),
queryFn: () => get<LangGeniusVersionResponse>('/version', { params: { current_version: currentVersion } }),
enabled: !!currentVersion && (enabled ?? true),
})
}
export const useCurrentWorkspace = () => {
return useQuery<ICurrentWorkspace>({
queryKey: commonQueryKeys.currentWorkspace,
queryFn: () => post<ICurrentWorkspace>('/workspaces/current', { body: {} }),
})
}
export const useWorkspaces = () => {
return useQuery<{ workspaces: IWorkspace[] }>({
queryKey: commonQueryKeys.workspaces,
queryFn: () => get<{ workspaces: IWorkspace[] }>('/workspaces'),
})
}
export const useGenerateStructuredOutputRules = () => {
return useMutation({
mutationKey: [NAME_SPACE, 'generate-structured-output-rules'],
@ -74,10 +176,8 @@ type MemberResponse = {
export const useMembers = () => {
return useQuery<MemberResponse>({
queryKey: [NAME_SPACE, 'members'],
queryFn: (params: Record<string, any>) => get<MemberResponse>('/workspaces/current/members', {
params,
}),
queryKey: commonQueryKeys.members,
queryFn: () => get<MemberResponse>('/workspaces/current/members', { params: {} }),
})
}
@ -87,7 +187,7 @@ type FilePreviewResponse = {
export const useFilePreview = (fileID: string) => {
return useQuery<FilePreviewResponse>({
queryKey: [NAME_SPACE, 'file-preview', fileID],
queryKey: commonQueryKeys.filePreview(fileID),
queryFn: () => get<FilePreviewResponse>(`/files/${fileID}/preview`),
enabled: !!fileID,
})
@ -102,7 +202,7 @@ export type SchemaTypeDefinition = {
export const useSchemaTypeDefinitions = () => {
return useQuery<SchemaTypeDefinition[]>({
queryKey: [NAME_SPACE, 'schema-type-definitions'],
queryKey: commonQueryKeys.schemaDefinitions,
queryFn: () => get<SchemaTypeDefinition[]>('/spec/schema-definitions'),
})
}
@ -113,7 +213,7 @@ type isLogin = {
export const useIsLogin = () => {
return useQuery<isLogin>({
queryKey: [NAME_SPACE, 'is-login'],
queryKey: commonQueryKeys.isLogin,
staleTime: 0,
gcTime: 0,
queryFn: async (): Promise<isLogin> => {
@ -121,13 +221,12 @@ export const useIsLogin = () => {
await get('/account/profile', {}, {
silent: true,
})
}
catch (e: any) {
if(e.status === 401)
return { logged_in: false }
return { logged_in: true }
}
return { logged_in: true }
catch {
// Any error (401, 500, network error, etc.) means not logged in
return { logged_in: false }
}
},
})
}
@ -138,3 +237,141 @@ export const useLogout = () => {
mutationFn: () => post('/logout'),
})
}
type ForgotPasswordValidity = CommonResponse & { is_valid: boolean, email: string }
export const useVerifyForgotPasswordToken = (token?: string | null) => {
return useQuery<ForgotPasswordValidity>({
queryKey: commonQueryKeys.forgotPasswordValidity(token),
queryFn: () => post<ForgotPasswordValidity>('/forgot-password/validity', { body: { token } }),
enabled: !!token,
staleTime: 0,
gcTime: 0,
retry: false,
})
}
type OneMoreStepPayload = {
invitation_code: string
interface_language: string
timezone: string
}
export const useOneMoreStep = () => {
return useMutation({
mutationKey: [NAME_SPACE, 'one-more-step'],
mutationFn: (body: OneMoreStepPayload) => post<CommonResponse>('/account/init', { body }),
})
}
export const useModelProviders = () => {
return useQuery<{ data: ModelProvider[] }>({
queryKey: commonQueryKeys.modelProviders,
queryFn: () => get<{ data: ModelProvider[] }>('/workspaces/current/model-providers'),
})
}
export const useModelListByType = (type: ModelTypeEnum, enabled = true) => {
return useQuery<{ data: Model[] }>({
queryKey: commonQueryKeys.modelList(type),
queryFn: () => get<{ data: Model[] }>(`/workspaces/current/models/model-types/${type}`),
enabled,
})
}
export const useDefaultModelByType = (type: ModelTypeEnum, enabled = true) => {
return useQuery({
queryKey: commonQueryKeys.defaultModel(type),
queryFn: () => get(`/workspaces/current/default-model?model_type=${type}`),
enabled,
})
}
export const useSupportRetrievalMethods = () => {
return useQuery<{ retrieval_method: RETRIEVE_METHOD[] }>({
queryKey: commonQueryKeys.retrievalMethods,
queryFn: () => get<{ retrieval_method: RETRIEVE_METHOD[] }>('/datasets/retrieval-setting'),
})
}
export const useAccountIntegrates = () => {
return useQuery<{ data: AccountIntegrate[] | null }>({
queryKey: commonQueryKeys.accountIntegrates,
queryFn: () => get<{ data: AccountIntegrate[] | null }>('/account/integrates'),
})
}
type DataSourceIntegratesOptions = {
enabled?: boolean
initialData?: { data: DataSourceNotion[] }
}
export const useDataSourceIntegrates = (options: DataSourceIntegratesOptions = {}) => {
const { enabled = true, initialData } = options
return useQuery<{ data: DataSourceNotion[] }>({
queryKey: commonQueryKeys.dataSourceIntegrates,
queryFn: () => get<{ data: DataSourceNotion[] }>('/data-source/integrates'),
enabled,
initialData,
})
}
export const useInvalidDataSourceIntegrates = () => {
return useInvalid(commonQueryKeys.dataSourceIntegrates)
}
export const usePluginProviders = () => {
return useQuery<PluginProvider[] | null>({
queryKey: commonQueryKeys.pluginProviders,
queryFn: () => get<PluginProvider[] | null>('/workspaces/current/tool-providers'),
})
}
export const useCodeBasedExtensions = (module: string) => {
return useQuery<CodeBasedExtension>({
queryKey: commonQueryKeys.codeBasedExtensions(module),
queryFn: () => get<CodeBasedExtension>(`/code-based-extension?module=${module}`),
})
}
export const useNotionConnection = (enabled: boolean) => {
return useQuery<{ data: string }>({
queryKey: commonQueryKeys.notionConnection,
queryFn: () => get<{ data: string }>('/oauth/data-source/notion'),
enabled,
})
}
export const useApiBasedExtensions = () => {
return useQuery<ApiBasedExtension[]>({
queryKey: commonQueryKeys.apiBasedExtensions,
queryFn: () => get<ApiBasedExtension[]>('/api-based-extension'),
})
}
export const useInvitationCheck = (params?: { workspace_id?: string, email?: string, token?: string }, enabled?: boolean) => {
return useQuery({
queryKey: commonQueryKeys.invitationCheck(params),
queryFn: () => get<{
is_valid: boolean
data: { workspace_name: string, email: string, workspace_id: string }
result: string
}>('/activate/check', { params }),
enabled: enabled ?? !!params?.token,
retry: false,
})
}
export const useNotionBinding = (code?: string | null, enabled?: boolean) => {
return useQuery({
queryKey: commonQueryKeys.notionBinding(code),
queryFn: () => get<{ result: string }>('/oauth/data-source/binding/notion', { params: { code } }),
enabled: !!code && (enabled ?? true),
})
}
export const useModelParameterRules = (provider?: string, model?: string, enabled?: boolean) => {
return useQuery<{ data: ModelParameterRule[] }>({
queryKey: commonQueryKeys.modelParameterRules(provider, model),
queryFn: () => get<{ data: ModelParameterRule[] }>(`/workspaces/current/model-providers/${provider}/models/parameter-rules`, { params: { model } }),
enabled: !!provider && !!model && (enabled ?? true),
})
}

View File

@ -0,0 +1,18 @@
import { useMutation } from '@tanstack/react-query'
import { checkIsUsedInApp, deleteDataset } from './datasets'
const NAME_SPACE = 'dataset-card'
export const useCheckDatasetUsage = () => {
return useMutation({
mutationKey: [NAME_SPACE, 'check-usage'],
mutationFn: (datasetId: string) => checkIsUsedInApp(datasetId),
})
}
export const useDeleteDataset = () => {
return useMutation({
mutationKey: [NAME_SPACE, 'delete'],
mutationFn: (datasetId: string) => deleteDataset(datasetId),
})
}

View File

@ -1,13 +1,13 @@
import type {
DataSourceAuth,
DataSourceCredential,
} from '@/app/components/header/account-setting/data-source-page-new/types'
import {
useMutation,
useQuery,
} from '@tanstack/react-query'
import { get } from './base'
import { useInvalid } from './use-base'
import type {
DataSourceAuth,
DataSourceCredential,
} from '@/app/components/header/account-setting/data-source-page-new/types'
const NAME_SPACE = 'data-source-auth'
@ -49,7 +49,8 @@ export const useGetDataSourceOAuthUrl = (
authorization_url: string
state: string
context_id: string
}>(`/oauth/plugin/${provider}/datasource/get-authorization-url?credential_id=${credentialId}`)
}
>(`/oauth/plugin/${provider}/datasource/get-authorization-url?credential_id=${credentialId}`)
},
})
}

View File

@ -1,10 +1,10 @@
import { get, post } from './base'
import type { EducationAddParams } from '@/app/education-apply/types'
import {
useMutation,
useQuery,
} from '@tanstack/react-query'
import { get, post } from './base'
import { useInvalid } from './use-base'
import type { EducationAddParams } from '@/app/education-apply/types'
const NAME_SPACE = 'education'
@ -46,7 +46,7 @@ export const useEducationAutocomplete = () => {
page = 0,
limit = 40,
} = searchParams
return get<{ data: string[]; has_next: boolean; curr_page: number }>(`/account/education/autocomplete?keywords=${keywords}&page=${page}&limit=${limit}`)
return get<{ data: string[], has_next: boolean, curr_page: number }>(`/account/education/autocomplete?keywords=${keywords}&page=${page}&limit=${limit}`)
},
})
}
@ -59,7 +59,7 @@ export const useEducationStatus = (disable?: boolean) => {
return get<{ is_student: boolean, allow_refresh: boolean, expire_at: number | null }>('/account/education')
},
retry: false,
gcTime: 0, // No cache. Prevent switch account caused stale data
staleTime: 0, // Data expires immediately, ensuring fresh data on refetch
})
}

View File

@ -1,4 +1,3 @@
import { get, post } from './base'
import type {
EndpointsResponse,
} from '@/app/components/plugins/types'
@ -7,6 +6,7 @@ import {
useQuery,
useQueryClient,
} from '@tanstack/react-query'
import { get, post } from './base'
const NAME_SPACE = 'endpoints'
@ -29,7 +29,8 @@ export const useInvalidateEndpointList = () => {
queryClient.invalidateQueries(
{
queryKey: [NAME_SPACE, 'list', pluginID],
})
},
)
}
}

View File

@ -1,11 +1,30 @@
import type { App, AppCategory } from '@/models/explore'
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
import { useGlobalPublicStore } from '@/context/global-public-context'
import { AccessMode } from '@/models/access-control'
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
import { fetchBanners, fetchInstalledAppList, getAppAccessModeByAppId, uninstallApp, updatePinStatus } from './explore'
import { fetchAppList, fetchBanners, fetchInstalledAppList, getAppAccessModeByAppId, uninstallApp, updatePinStatus } from './explore'
import { AppSourceType, fetchAppMeta, fetchAppParams } from './share'
const NAME_SPACE = 'explore'
type ExploreAppListData = {
categories: AppCategory[]
allList: App[]
}
export const useExploreAppList = () => {
return useQuery<ExploreAppListData>({
queryKey: [NAME_SPACE, 'appList'],
queryFn: async () => {
const { categories, recommended_apps } = await fetchAppList()
return {
categories,
allList: [...recommended_apps].sort((a, b) => a.position - b.position),
}
},
})
}
export const useGetInstalledApps = () => {
return useQuery({
queryKey: [NAME_SPACE, 'installedApps'],
@ -30,7 +49,7 @@ export const useUpdateAppPinStatus = () => {
const client = useQueryClient()
return useMutation({
mutationKey: [NAME_SPACE, 'updateAppPinStatus'],
mutationFn: ({ appId, isPinned }: { appId: string; isPinned: boolean }) => updatePinStatus(appId, isPinned),
mutationFn: ({ appId, isPinned }: { appId: string, isPinned: boolean }) => updatePinStatus(appId, isPinned),
onSuccess: () => {
client.invalidateQueries({ queryKey: [NAME_SPACE, 'installedApps'] })
},
@ -40,7 +59,7 @@ export const useUpdateAppPinStatus = () => {
export const useGetInstalledAppAccessModeByAppId = (appId: string | null) => {
const systemFeatures = useGlobalPublicStore(s => s.systemFeatures)
return useQuery({
queryKey: [NAME_SPACE, 'appAccessMode', appId],
queryKey: [NAME_SPACE, 'appAccessMode', appId, systemFeatures.webapp_auth.enabled],
queryFn: () => {
if (systemFeatures.webapp_auth.enabled === false) {
return {

View File

@ -1,4 +1,5 @@
import type { FlowType } from '@/types/common'
import { curry } from 'es-toolkit/compat'
import {
useDeleteAllInspectorVars as useDeleteAllInspectorVarsInner,
useDeleteInspectVar as useDeleteInspectVarInner,
@ -9,7 +10,6 @@ import {
useResetConversationVar as useResetConversationVarInner,
useResetToLastRunValue as useResetToLastRunValueInner,
} from './use-workflow'
import { curry } from 'lodash-es'
type Params = {
flowType: FlowType

89
web/service/use-log.ts Normal file
View File

@ -0,0 +1,89 @@
import type {
AnnotationsCountResponse,
ChatConversationFullDetailResponse,
ChatConversationsRequest,
ChatConversationsResponse,
CompletionConversationFullDetailResponse,
CompletionConversationsRequest,
CompletionConversationsResponse,
WorkflowLogsResponse,
} from '@/models/log'
import { useQuery } from '@tanstack/react-query'
import { get } from './base'
const NAME_SPACE = 'log'
// ============ Annotations Count ============
export const useAnnotationsCount = (appId: string) => {
return useQuery<AnnotationsCountResponse>({
queryKey: [NAME_SPACE, 'annotations-count', appId],
queryFn: () => get<AnnotationsCountResponse>(`/apps/${appId}/annotations/count`),
enabled: !!appId,
})
}
// ============ Chat Conversations ============
type ChatConversationsParams = {
appId: string
params?: Partial<ChatConversationsRequest>
}
export const useChatConversations = ({ appId, params }: ChatConversationsParams) => {
return useQuery<ChatConversationsResponse>({
queryKey: [NAME_SPACE, 'chat-conversations', appId, params],
queryFn: () => get<ChatConversationsResponse>(`/apps/${appId}/chat-conversations`, { params }),
enabled: !!appId,
})
}
// ============ Completion Conversations ============
type CompletionConversationsParams = {
appId: string
params?: Partial<CompletionConversationsRequest>
}
export const useCompletionConversations = ({ appId, params }: CompletionConversationsParams) => {
return useQuery<CompletionConversationsResponse>({
queryKey: [NAME_SPACE, 'completion-conversations', appId, params],
queryFn: () => get<CompletionConversationsResponse>(`/apps/${appId}/completion-conversations`, { params }),
enabled: !!appId,
})
}
// ============ Chat Conversation Detail ============
export const useChatConversationDetail = (appId?: string, conversationId?: string) => {
return useQuery<ChatConversationFullDetailResponse>({
queryKey: [NAME_SPACE, 'chat-conversation-detail', appId, conversationId],
queryFn: () => get<ChatConversationFullDetailResponse>(`/apps/${appId}/chat-conversations/${conversationId}`),
enabled: !!appId && !!conversationId,
})
}
// ============ Completion Conversation Detail ============
export const useCompletionConversationDetail = (appId?: string, conversationId?: string) => {
return useQuery<CompletionConversationFullDetailResponse>({
queryKey: [NAME_SPACE, 'completion-conversation-detail', appId, conversationId],
queryFn: () => get<CompletionConversationFullDetailResponse>(`/apps/${appId}/completion-conversations/${conversationId}`),
enabled: !!appId && !!conversationId,
})
}
// ============ Workflow Logs ============
type WorkflowLogsParams = {
appId: string
params?: Record<string, string | number | boolean | undefined>
}
export const useWorkflowLogs = ({ appId, params }: WorkflowLogsParams) => {
return useQuery<WorkflowLogsResponse>({
queryKey: [NAME_SPACE, 'workflow-logs', appId, params],
queryFn: () => get<WorkflowLogsResponse>(`/apps/${appId}/workflow-app-logs`, { params }),
enabled: !!appId,
})
}

View File

@ -1,9 +1,3 @@
import {
del,
get,
post,
put,
} from './base'
import type {
ModelCredential,
ModelItem,
@ -16,6 +10,12 @@ import {
useQuery,
// useQueryClient,
} from '@tanstack/react-query'
import {
del,
get,
post,
put,
} from './base'
const NAME_SPACE = 'models'
@ -82,7 +82,7 @@ export const useGetModelCredential = (
) => {
return useQuery({
enabled,
queryKey: [NAME_SPACE, 'model-list', provider, model, modelType, credentialId],
queryKey: [NAME_SPACE, 'model-list', provider, model, modelType, credentialId, configFrom],
queryFn: () => get<ModelCredential>(`/workspaces/current/model-providers/${provider}/models/credentials?model=${model}&model_type=${modelType}&config_from=${configFrom}${credentialId ? `&credential_id=${credentialId}` : ''}`),
staleTime: 0,
gcTime: 0,

View File

@ -1,5 +1,5 @@
import { post } from './base'
import { useMutation, useQuery } from '@tanstack/react-query'
import { post } from './base'
const NAME_SPACE = 'oauth-provider'

View File

@ -1,7 +1,7 @@
import type { MutationOptions } from '@tanstack/react-query'
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
import { del, get, patch, post } from './base'
import { DatasourceType } from '@/models/pipeline'
import type { ToolCredential } from '@/app/components/tools/types'
import type { DataSourceItem } from '@/app/components/workflow/block-selector/types'
import type { IconInfo } from '@/models/datasets'
import type {
ConversionResponse,
DatasourceNodeSingleRunRequest,
@ -31,18 +31,17 @@ import type {
UpdateTemplateInfoRequest,
UpdateTemplateInfoResponse,
} from '@/models/pipeline'
import type { DataSourceItem } from '@/app/components/workflow/block-selector/types'
import type { ToolCredential } from '@/app/components/tools/types'
import type { IconInfo } from '@/models/datasets'
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
import { DatasourceType } from '@/models/pipeline'
import { del, get, patch, post } from './base'
import { useInvalid } from './use-base'
const NAME_SPACE = 'pipeline'
export const PipelineTemplateListQueryKeyPrefix = [NAME_SPACE, 'template-list']
export const usePipelineTemplateList = (params: PipelineTemplateListParams, enabled = true) => {
const { type, language } = params
return useQuery<PipelineTemplateListResponse>({
queryKey: [...PipelineTemplateListQueryKeyPrefix, type, language],
queryKey: [...PipelineTemplateListQueryKeyPrefix, params],
queryFn: () => {
return get<PipelineTemplateListResponse>('/rag/pipeline/templates', { params })
},
@ -248,7 +247,7 @@ export const useUpdateDataSourceCredentials = (
pluginId,
credentials,
name,
}: { provider: string; pluginId: string; credentials: Record<string, any>; name: string; }) => {
}: { provider: string, pluginId: string, credentials: Record<string, any>, name: string }) => {
return post('/auth/plugin/datasource', {
body: {
provider,
@ -303,7 +302,7 @@ export const useExportPipelineDSL = () => {
mutationFn: ({
pipelineId,
include = false,
}: { pipelineId: string; include?: boolean }) => {
}: { pipelineId: string, include?: boolean }) => {
return get<ExportTemplateDSLResponse>(`/rag/pipelines/${pipelineId}/exports?include_secret=${include}`)
},
})
@ -318,10 +317,10 @@ export const usePublishAsCustomizedPipeline = () => {
icon_info,
description,
}: {
pipelineId: string,
name: string,
icon_info: IconInfo,
description?: string,
pipelineId: string
name: string
icon_info: IconInfo
description?: string
}) => {
return post(`/rag/pipelines/${pipelineId}/customized/publish`, {
body: {

View File

@ -1,14 +1,14 @@
import type { FormSchema } from '@/app/components/base/form/types'
import type {
Credential,
CredentialTypeEnum,
} from '@/app/components/plugins/plugin-auth/types'
import {
useMutation,
useQuery,
} from '@tanstack/react-query'
import { del, get, post } from './base'
import { useInvalid } from './use-base'
import type {
Credential,
CredentialTypeEnum,
} from '@/app/components/plugins/plugin-auth/types'
import type { FormSchema } from '@/app/components/base/form/types'
const NAME_SPACE = 'plugins-auth'
@ -112,7 +112,8 @@ export const useGetPluginOAuthUrl = (
authorization_url: string
state: string
context_id: string
}>(url)
}
>(url)
},
})
}

View File

@ -1,50 +1,48 @@
import { useCallback, useEffect, useState } from 'react'
import type { MutateOptions, QueryOptions } from '@tanstack/react-query'
import type {
FormOption,
ModelProvider,
} from '@/app/components/header/account-setting/model-provider-page/declarations'
import { fetchModelProviderModelList } from '@/service/common'
import { fetchPluginInfoFromMarketPlace } from '@/service/plugins'
import type {
PluginsSearchParams,
} from '@/app/components/plugins/marketplace/types'
import type {
DebugInfo as DebugInfoTypes,
Dependency,
GitHubItemAndMarketPlaceDependency,
InstallPackageResponse,
InstallStatusResponse,
InstalledLatestVersionResponse,
InstalledPluginListWithTotalResponse,
InstallPackageResponse,
InstallStatusResponse,
PackageDependency,
Plugin,
PluginDeclaration,
PluginDetail,
PluginInfoFromMarketPlace,
PluginTask,
PluginsFromMarketplaceByInfoResponse,
PluginsFromMarketplaceResponse,
PluginTask,
ReferenceSetting,
uploadGitHubResponse,
VersionInfo,
VersionListResponse,
uploadGitHubResponse,
} from '@/app/components/plugins/types'
import { TaskStatus } from '@/app/components/plugins/types'
import { PluginCategoryEnum } from '@/app/components/plugins/types'
import type {
PluginsSearchParams,
} from '@/app/components/plugins/marketplace/types'
import { get, getMarketplace, post, postMarketplace } from './base'
import type { MutateOptions, QueryOptions } from '@tanstack/react-query'
import {
useInfiniteQuery,
useMutation,
useQuery,
useQueryClient,
} from '@tanstack/react-query'
import { useInvalidateAllBuiltInTools } from './use-tools'
import useReferenceSetting from '@/app/components/plugins/plugin-page/use-reference-setting'
import { uninstallPlugin } from '@/service/plugins'
import { cloneDeep } from 'es-toolkit/object'
import { useCallback, useEffect, useState } from 'react'
import useRefreshPluginList from '@/app/components/plugins/install-plugin/hooks/use-refresh-plugin-list'
import { cloneDeep } from 'lodash-es'
import { getFormattedPlugin } from '@/app/components/plugins/marketplace/utils'
import useReferenceSetting from '@/app/components/plugins/plugin-page/use-reference-setting'
import { PluginCategoryEnum, TaskStatus } from '@/app/components/plugins/types'
import { fetchModelProviderModelList } from '@/service/common'
import { fetchPluginInfoFromMarketPlace, uninstallPlugin } from '@/service/plugins'
import { get, getMarketplace, post, postMarketplace } from './base'
import { useInvalidateAllBuiltInTools } from './use-tools'
const NAME_SPACE = 'plugins'
@ -53,7 +51,7 @@ export const useCheckInstalled = ({
pluginIds,
enabled,
}: {
pluginIds: string[],
pluginIds: string[]
enabled: boolean
}) => {
return useQuery<{ plugins: PluginDetail[] }>({
@ -165,10 +163,12 @@ export const useInstalledPluginList = (disable?: boolean, pageSize = 100) => {
const total = data?.pages[0].total ?? 0
return {
data: disable ? undefined : {
plugins,
total,
},
data: disable
? undefined
: {
plugins,
total,
},
isLastPage: !hasNextPage,
loadNextPage: () => {
fetchNextPage()
@ -200,7 +200,8 @@ export const useInvalidateInstalledPluginList = () => {
queryClient.invalidateQueries(
{
queryKey: useInstalledPluginListKey,
})
},
)
invalidateAllBuiltInTools()
}
}
@ -300,8 +301,8 @@ export const useInstallOrUpdate = ({
return useMutation({
mutationFn: (data: {
payload: Dependency[],
plugin: Plugin[],
payload: Dependency[]
plugin: Plugin[]
installedInfo: Record<string, VersionInfo>
}) => {
const { payload, plugin, installedInfo } = data
@ -456,7 +457,8 @@ export const useInvalidateReferenceSettings = () => {
queryClient.invalidateQueries(
{
queryKey: useReferenceSettingKey,
})
},
)
}
}
@ -612,12 +614,11 @@ export const usePluginTaskList = (category?: PluginCategoryEnum | string) => {
const taskAllFailed = lastData?.tasks.every(task => task.status === TaskStatus.failed)
if (taskDone && lastData?.tasks.length && !taskAllFailed)
refreshPluginList(category ? { category } as any : undefined, !category)
}, [initialized, isRefetching, data, category, refreshPluginList])
}, [isRefetching])
useEffect(() => {
if (isFetched && !initialized)
setInitialized(true)
}, [isFetched, initialized])
setInitialized(true)
}, [])
const handleRefetch = useCallback(() => {
refetch()
@ -634,8 +635,9 @@ export const usePluginTaskList = (category?: PluginCategoryEnum | string) => {
export const useMutationClearTaskPlugin = () => {
return useMutation({
mutationFn: ({ taskId, pluginId }: { taskId: string; pluginId: string }) => {
return post<{ success: boolean }>(`/workspaces/current/plugin/tasks/${taskId}/delete/${pluginId}`)
mutationFn: ({ taskId, pluginId }: { taskId: string, pluginId: string }) => {
const encodedPluginId = encodeURIComponent(pluginId)
return post<{ success: boolean }>(`/workspaces/current/plugin/tasks/${taskId}/delete/${encodedPluginId}`)
},
})
}
@ -657,7 +659,7 @@ export const usePluginManifestInfo = (pluginUID: string) => {
})
}
export const useDownloadPlugin = (info: { organization: string; pluginName: string; version: string }, needDownload: boolean) => {
export const useDownloadPlugin = (info: { organization: string, pluginName: string, version: string }, needDownload: boolean) => {
return useQuery({
queryKey: [NAME_SPACE, 'downloadPlugin', info],
queryFn: () => getMarketplace<Blob>(`/plugins/${info.organization}/${info.pluginName}/${info.version}/download`),
@ -675,19 +677,21 @@ export const useMutationCheckDependencies = () => {
}
export const useModelInList = (currentProvider?: ModelProvider, modelId?: string) => {
const provider = currentProvider?.provider
return useQuery({
queryKey: ['modelInList', currentProvider?.provider, modelId],
queryKey: ['modelInList', provider, modelId],
queryFn: async () => {
if (!modelId || !currentProvider) return false
if (!modelId || !provider)
return false
try {
const modelsData = await fetchModelProviderModelList(`/workspaces/current/model-providers/${currentProvider?.provider}/models`)
const modelsData = await fetchModelProviderModelList(`/workspaces/current/model-providers/${provider}/models`)
return !!modelId && !!modelsData.data.find(item => item.model === modelId)
}
catch {
return false
}
},
enabled: !!modelId && !!currentProvider,
enabled: !!modelId && !!provider,
})
}
@ -695,7 +699,8 @@ export const usePluginInfo = (providerName?: string) => {
return useQuery({
queryKey: ['pluginInfo', providerName],
queryFn: async () => {
if (!providerName) return null
if (!providerName)
return null
const parts = providerName.split('/')
const org = parts[0]
const name = parts[1]
@ -738,7 +743,7 @@ export const usePluginReadme = ({ plugin_unique_identifier, language }: { plugin
export const usePluginReadmeAsset = ({ file_name, plugin_unique_identifier }: { file_name?: string, plugin_unique_identifier?: string }) => {
const normalizedFileName = file_name?.replace(/(^\.\/_assets\/|^_assets\/)/, '')
return useQuery({
queryKey: ['pluginReadmeAsset', plugin_unique_identifier, file_name],
queryKey: ['pluginReadmeAsset', plugin_unique_identifier, normalizedFileName],
queryFn: () => get<Blob>('/workspaces/current/plugin/asset', { params: { plugin_unique_identifier, file_name: normalizedFileName } }, { silent: true }),
enabled: !!plugin_unique_identifier && !!file_name && /(^\.\/_assets|^_assets)/.test(file_name),
})

View File

@ -0,0 +1,284 @@
import type { ReactNode } from 'react'
import type { AppConversationData, ConversationItem } from '@/models/share'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { act, renderHook, waitFor } from '@testing-library/react'
import {
AppSourceType,
fetchChatList,
fetchConversations,
generationConversationName,
} from './share'
import {
shareQueryKeys,
useInvalidateShareConversations,
useShareChatList,
useShareConversationName,
useShareConversations,
} from './use-share'
vi.mock('./share', () => ({
fetchChatList: vi.fn(),
fetchConversations: vi.fn(),
generationConversationName: vi.fn(),
fetchAppInfo: vi.fn(),
fetchAppMeta: vi.fn(),
fetchAppParams: vi.fn(),
getAppAccessModeByAppCode: vi.fn(),
}))
const mockFetchConversations = vi.mocked(fetchConversations)
const mockFetchChatList = vi.mocked(fetchChatList)
const mockGenerationConversationName = vi.mocked(generationConversationName)
const createQueryClient = () => new QueryClient({
defaultOptions: {
queries: {
retry: false,
},
},
})
const createWrapper = (queryClient: QueryClient) => {
return ({ children }: { children: ReactNode }) => (
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
)
}
const renderShareHook = <T,>(hook: () => T) => {
const queryClient = createQueryClient()
const wrapper = createWrapper(queryClient)
return {
queryClient,
...renderHook(hook, { wrapper }),
}
}
const createConversationItem = (overrides: Partial<ConversationItem> = {}): ConversationItem => ({
id: 'conversation-1',
name: 'Conversation 1',
inputs: null,
introduction: 'Intro',
...overrides,
})
const createConversationData = (overrides: Partial<AppConversationData> = {}): AppConversationData => ({
data: [createConversationItem()],
has_more: false,
limit: 20,
...overrides,
})
// Scenario: share conversation list queries behave consistently with params and enablement.
describe('useShareConversations', () => {
beforeEach(() => {
vi.clearAllMocks()
})
it('should fetch conversations when enabled for non-installed apps', async () => {
// Arrange
const params = {
isInstalledApp: false,
appId: undefined,
pinned: true,
limit: 50,
appSourceType: AppSourceType.webApp,
}
const response = createConversationData()
mockFetchConversations.mockResolvedValueOnce(response)
// Act
const { result, queryClient } = renderShareHook(() => useShareConversations(params))
// Assert
await waitFor(() => {
expect(mockFetchConversations).toHaveBeenCalledWith(false, undefined, undefined, true, 50)
})
await waitFor(() => {
expect(result.current.data).toEqual(response)
})
expect(queryClient.getQueryCache().find({ queryKey: shareQueryKeys.conversationList(params) })).toBeDefined()
})
it('should not fetch conversations when installed app lacks appId', async () => {
// Arrange
const params = {
isInstalledApp: true,
appId: undefined,
appSourceType: AppSourceType.installedApp,
}
// Act
const { result } = renderShareHook(() => useShareConversations(params))
// Assert
await waitFor(() => {
expect(result.current.fetchStatus).toBe('idle')
})
expect(mockFetchConversations).not.toHaveBeenCalled()
})
})
// Scenario: chat list queries respect conversation ID and app installation rules.
describe('useShareChatList', () => {
beforeEach(() => {
vi.clearAllMocks()
})
it('should fetch chat list when conversationId is provided', async () => {
// Arrange
const params = {
conversationId: 'conversation-1',
isInstalledApp: true,
appId: 'app-1',
appSourceType: AppSourceType.installedApp,
}
const response = { data: [] }
mockFetchChatList.mockResolvedValueOnce(response)
// Act
const { result } = renderShareHook(() => useShareChatList(params))
// Assert
await waitFor(() => {
expect(mockFetchChatList).toHaveBeenCalledWith('conversation-1', true, 'app-1')
})
await waitFor(() => {
expect(result.current.data).toEqual(response)
})
})
it('should not fetch chat list when conversationId is empty', async () => {
// Arrange
const params = {
conversationId: '',
isInstalledApp: false,
appId: undefined,
appSourceType: AppSourceType.webApp,
}
// Act
const { result } = renderShareHook(() => useShareChatList(params))
// Assert
await waitFor(() => {
expect(result.current.fetchStatus).toBe('idle')
})
expect(mockFetchChatList).not.toHaveBeenCalled()
})
it('should always consider data stale to ensure fresh data on conversation switch (GitHub #30378)', async () => {
// This test verifies that chat list data is always considered stale (staleTime: 0)
// which ensures fresh data is fetched when switching back to a conversation.
// Without this, users would see outdated messages until double-switching.
const queryClient = createQueryClient()
const wrapper = createWrapper(queryClient)
const params = {
conversationId: 'conversation-1',
isInstalledApp: false,
appId: undefined,
appSourceType: AppSourceType.webApp,
}
const initialResponse = { data: [{ id: '1', content: 'initial' }] }
const updatedResponse = { data: [{ id: '1', content: 'initial' }, { id: '2', content: 'new message' }] }
// First fetch
mockFetchChatList.mockResolvedValueOnce(initialResponse)
const { result, unmount } = renderHook(() => useShareChatList(params), { wrapper })
await waitFor(() => {
expect(result.current.data).toEqual(initialResponse)
})
expect(mockFetchChatList).toHaveBeenCalledTimes(1)
// Unmount (simulates switching away from conversation)
unmount()
// Remount with same params (simulates switching back)
// With staleTime: 0, this should trigger a background refetch
mockFetchChatList.mockResolvedValueOnce(updatedResponse)
const { result: result2 } = renderHook(() => useShareChatList(params), { wrapper })
// Should immediately return cached data
expect(result2.current.data).toEqual(initialResponse)
// Should trigger background refetch due to staleTime: 0
await waitFor(() => {
expect(mockFetchChatList).toHaveBeenCalledTimes(2)
})
// Should update with fresh data
await waitFor(() => {
expect(result2.current.data).toEqual(updatedResponse)
})
})
})
// Scenario: conversation name queries follow enabled flags and installation constraints.
describe('useShareConversationName', () => {
beforeEach(() => {
vi.clearAllMocks()
})
it('should fetch conversation name when enabled and conversationId exists', async () => {
// Arrange
const params = {
conversationId: 'conversation-2',
isInstalledApp: false,
appId: undefined,
appSourceType: AppSourceType.webApp,
}
const response = createConversationItem({ id: 'conversation-2', name: 'Generated' })
mockGenerationConversationName.mockResolvedValueOnce(response)
// Act
const { result } = renderShareHook(() => useShareConversationName(params))
// Assert
await waitFor(() => {
expect(mockGenerationConversationName).toHaveBeenCalledWith(false, undefined, 'conversation-2')
})
await waitFor(() => {
expect(result.current.data).toEqual(response)
})
})
it('should not fetch conversation name when disabled via options', async () => {
// Arrange
const params = {
conversationId: 'conversation-3',
isInstalledApp: false,
appId: undefined,
appSourceType: AppSourceType.webApp,
}
// Act
const { result } = renderShareHook(() => useShareConversationName(params, { enabled: false }))
// Assert
await waitFor(() => {
expect(result.current.fetchStatus).toBe('idle')
})
expect(mockGenerationConversationName).not.toHaveBeenCalled()
})
})
// Scenario: invalidation helper clears share conversation caches.
describe('useInvalidateShareConversations', () => {
beforeEach(() => {
vi.clearAllMocks()
})
it('should invalidate share conversations query key when invoked', () => {
// Arrange
const { result, queryClient } = renderShareHook(() => useInvalidateShareConversations())
const invalidateSpy = vi.spyOn(queryClient, 'invalidateQueries')
// Act
act(() => {
result.current()
})
// Assert
expect(invalidateSpy).toHaveBeenCalledWith({ queryKey: shareQueryKeys.conversations })
})
})

View File

@ -1,11 +1,59 @@
import type { AppConversationData, ConversationItem } from '@/models/share'
import { useQuery } from '@tanstack/react-query'
import { AppSourceType, fetchAppInfo, fetchAppMeta, fetchAppParams, getAppAccessModeByAppCode } from './share'
import {
AppSourceType,
fetchAppInfo,
fetchAppMeta,
fetchAppParams,
fetchChatList,
fetchConversations,
generationConversationName,
getAppAccessModeByAppCode,
} from './share'
import { useInvalid } from './use-base'
const NAME_SPACE = 'webapp'
type ShareConversationsParams = {
appSourceType: AppSourceType
appId?: string
lastId?: string
pinned?: boolean
limit?: number
}
type ShareChatListParams = {
conversationId: string
appSourceType: AppSourceType
appId?: string
}
type ShareConversationNameParams = {
conversationId: string
appSourceType: AppSourceType
appId?: string
}
type ShareQueryOptions = {
enabled?: boolean
refetchOnWindowFocus?: boolean
refetchOnReconnect?: boolean
}
export const shareQueryKeys = {
appAccessMode: (code: string | null) => [NAME_SPACE, 'appAccessMode', code] as const,
appInfo: [NAME_SPACE, 'appInfo'] as const,
appParams: [NAME_SPACE, 'appParams'] as const,
appMeta: [NAME_SPACE, 'appMeta'] as const,
conversations: [NAME_SPACE, 'conversations'] as const,
conversationList: (params: ShareConversationsParams) => [NAME_SPACE, 'conversations', params] as const,
chatList: (params: ShareChatListParams) => [NAME_SPACE, 'chatList', params] as const,
conversationName: (params: ShareConversationNameParams) => [NAME_SPACE, 'conversationName', params] as const,
}
export const useGetWebAppAccessModeByCode = (code: string | null) => {
return useQuery({
queryKey: [NAME_SPACE, 'appAccessMode', code],
queryKey: shareQueryKeys.appAccessMode(code),
queryFn: () => getAppAccessModeByAppCode(code!),
enabled: !!code,
staleTime: 0, // backend change the access mode may cause the logic error. Because /permission API is no cached.
@ -15,7 +63,7 @@ export const useGetWebAppAccessModeByCode = (code: string | null) => {
export const useGetWebAppInfo = () => {
return useQuery({
queryKey: [NAME_SPACE, 'appInfo'],
queryKey: shareQueryKeys.appInfo,
queryFn: () => {
return fetchAppInfo()
},
@ -24,7 +72,7 @@ export const useGetWebAppInfo = () => {
export const useGetWebAppParams = () => {
return useQuery({
queryKey: [NAME_SPACE, 'appParams'],
queryKey: shareQueryKeys.appParams,
queryFn: () => {
return fetchAppParams(AppSourceType.webApp)
},
@ -33,9 +81,71 @@ export const useGetWebAppParams = () => {
export const useGetWebAppMeta = () => {
return useQuery({
queryKey: [NAME_SPACE, 'appMeta'],
queryKey: shareQueryKeys.appMeta,
queryFn: () => {
return fetchAppMeta(AppSourceType.webApp)
},
})
}
export const useShareConversations = (params: ShareConversationsParams, options: ShareQueryOptions = {}) => {
const {
enabled = true,
refetchOnReconnect,
refetchOnWindowFocus,
} = options
const isEnabled = enabled && (params.appSourceType !== AppSourceType.installedApp || !!params.appId)
return useQuery<AppConversationData>({
queryKey: shareQueryKeys.conversationList(params),
queryFn: () => fetchConversations(
params.appSourceType,
params.appId,
params.lastId,
params.pinned,
params.limit,
),
enabled: isEnabled,
refetchOnReconnect,
refetchOnWindowFocus,
})
}
export const useShareChatList = (params: ShareChatListParams, options: ShareQueryOptions = {}) => {
const {
enabled = true,
refetchOnReconnect,
refetchOnWindowFocus,
} = options
const isEnabled = enabled && (params.appSourceType !== AppSourceType.installedApp || !!params.appId) && !!params.conversationId
return useQuery({
queryKey: shareQueryKeys.chatList(params),
queryFn: () => fetchChatList(params.conversationId, params.appSourceType, params.appId),
enabled: isEnabled,
refetchOnReconnect,
refetchOnWindowFocus,
// Always consider chat list data stale to ensure fresh data when switching
// back to a conversation. This fixes issue where recent messages don't appear
// until switching away and back again (GitHub issue #30378).
staleTime: 0,
})
}
export const useShareConversationName = (params: ShareConversationNameParams, options: ShareQueryOptions = {}) => {
const {
enabled = true,
refetchOnReconnect,
refetchOnWindowFocus,
} = options
const isEnabled = enabled && (params.appSourceType !== AppSourceType.installedApp || !!params.appId) && !!params.conversationId
return useQuery<ConversationItem>({
queryKey: shareQueryKeys.conversationName(params),
queryFn: () => generationConversationName(params.appSourceType, params.appId, params.conversationId),
enabled: isEnabled,
refetchOnReconnect,
refetchOnWindowFocus,
})
}
export const useInvalidateShareConversations = () => {
return useInvalid(shareQueryKeys.conversations)
}

View File

@ -1,12 +1,12 @@
import type { QueryOptions } from '@tanstack/react-query'
import type {
StrategyPluginDetail,
} from '@/app/components/plugins/types'
import { useInvalid } from './use-base'
import type { QueryOptions } from '@tanstack/react-query'
import {
useQuery,
} from '@tanstack/react-query'
import { fetchStrategyDetail, fetchStrategyList } from './strategy'
import { useInvalid } from './use-base'
const NAME_SPACE = 'agent_strategy'

View File

@ -1,19 +1,19 @@
import { del, get, post, put } from './base'
import type { QueryKey } from '@tanstack/react-query'
import type {
Collection,
MCPServerDetail,
Tool,
} from '@/app/components/tools/types'
import { CollectionType } from '@/app/components/tools/types'
import type { RAGRecommendedPlugins, ToolWithProvider } from '@/app/components/workflow/types'
import type { AppIconType } from '@/types/app'
import { useInvalid } from './use-base'
import type { QueryKey } from '@tanstack/react-query'
import {
useMutation,
useQuery,
useQueryClient,
} from '@tanstack/react-query'
import { CollectionType } from '@/app/components/tools/types'
import { del, get, post, put } from './base'
import { useInvalid } from './use-base'
const NAME_SPACE = 'tools'
@ -160,8 +160,8 @@ export const useDeleteMCP = ({
export const useAuthorizeMCP = () => {
return useMutation({
mutationKey: [NAME_SPACE, 'authorize-mcp'],
mutationFn: (payload: { provider_id: string; }) => {
return post<{ result?: string; authorization_url?: string }>('/workspaces/current/tool-provider/mcp/auth', {
mutationFn: (payload: { provider_id: string }) => {
return post<{ result?: string, authorization_url?: string }>('/workspaces/current/tool-provider/mcp/auth', {
body: payload,
})
},
@ -171,7 +171,7 @@ export const useAuthorizeMCP = () => {
export const useUpdateMCPAuthorizationToken = () => {
return useMutation({
mutationKey: [NAME_SPACE, 'refresh-mcp-server-code'],
mutationFn: (payload: { provider_id: string; authorization_code: string }) => {
mutationFn: (payload: { provider_id: string, authorization_code: string }) => {
return get<MCPServerDetail>('/workspaces/current/tool-provider/mcp/token', {
params: {
...payload,
@ -194,7 +194,8 @@ export const useInvalidateMCPTools = () => {
queryClient.invalidateQueries(
{
queryKey: [NAME_SPACE, 'get-MCP-provider-tool', providerID],
})
},
)
}
}
@ -217,7 +218,8 @@ export const useInvalidateMCPServerDetail = () => {
queryClient.invalidateQueries(
{
queryKey: [NAME_SPACE, 'MCPServerDetail', appID],
})
},
)
}
}
@ -281,7 +283,8 @@ export const useInvalidateBuiltinProviderInfo = () => {
queryClient.invalidateQueries(
{
queryKey: [NAME_SPACE, 'builtin-provider-info', providerName],
})
},
)
}
}
@ -330,15 +333,24 @@ export const useRemoveProviderCredentials = ({
const useRAGRecommendedPluginListKey = [NAME_SPACE, 'rag-recommended-plugins']
export const useRAGRecommendedPlugins = () => {
export const useRAGRecommendedPlugins = (type: 'tool' | 'datasource' | 'all' = 'all') => {
return useQuery<RAGRecommendedPlugins>({
queryKey: useRAGRecommendedPluginListKey,
queryFn: () => get<RAGRecommendedPlugins>('/rag/pipelines/recommended-plugins'),
queryKey: [...useRAGRecommendedPluginListKey, type],
queryFn: () => get<RAGRecommendedPlugins>('/rag/pipelines/recommended-plugins', {
params: {
type,
},
}),
})
}
export const useInvalidateRAGRecommendedPlugins = () => {
return useInvalid(useRAGRecommendedPluginListKey)
const queryClient = useQueryClient()
return (type: 'tool' | 'datasource' | 'all' = 'all') => {
queryClient.invalidateQueries({
queryKey: [...useRAGRecommendedPluginListKey, type],
})
}
}
// App Triggers API hooks

View File

@ -1,5 +1,4 @@
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
import { del, get, post } from './base'
import type { FormOption } from '@/app/components/base/form/types'
import type {
TriggerLogEntity,
TriggerOAuthClientParams,
@ -9,7 +8,9 @@ import type {
TriggerSubscriptionBuilder,
TriggerWithProvider,
} from '@/app/components/workflow/block-selector/types'
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
import { CollectionType } from '@/app/components/tools/types'
import { del, get, post } from './base'
import { useInvalid } from './use-base'
const NAME_SPACE = 'triggers'
@ -25,6 +26,7 @@ const convertToTriggerWithProvider = (provider: TriggerProviderApiEntity): Trigg
author: provider.author,
description: provider.description,
icon: provider.icon || '',
icon_dark: provider.icon_dark || '',
label: provider.label,
type: CollectionType.trigger,
team_credentials: {},
@ -148,9 +150,9 @@ export const useUpdateTriggerSubscriptionBuilder = () => {
provider: string
subscriptionBuilderId: string
name?: string
properties?: Record<string, any>
parameters?: Record<string, any>
credentials?: Record<string, any>
properties?: Record<string, unknown>
parameters?: Record<string, unknown>
credentials?: Record<string, unknown>
}) => {
const { provider, subscriptionBuilderId, ...body } = payload
return post<TriggerSubscriptionBuilder>(
@ -161,17 +163,35 @@ export const useUpdateTriggerSubscriptionBuilder = () => {
})
}
export const useVerifyTriggerSubscriptionBuilder = () => {
export const useVerifyAndUpdateTriggerSubscriptionBuilder = () => {
return useMutation({
mutationKey: [NAME_SPACE, 'verify-subscription-builder'],
mutationKey: [NAME_SPACE, 'verify-and-update-subscription-builder'],
mutationFn: (payload: {
provider: string
subscriptionBuilderId: string
credentials?: Record<string, any>
credentials?: Record<string, unknown>
}) => {
const { provider, subscriptionBuilderId, ...body } = payload
return post<{ verified: boolean }>(
`/workspaces/current/trigger-provider/${provider}/subscriptions/builder/verify/${subscriptionBuilderId}`,
`/workspaces/current/trigger-provider/${provider}/subscriptions/builder/verify-and-update/${subscriptionBuilderId}`,
{ body },
{ silent: true },
)
},
})
}
export const useVerifyTriggerSubscription = () => {
return useMutation({
mutationKey: [NAME_SPACE, 'verify-subscription'],
mutationFn: (payload: {
provider: string
subscriptionId: string
credentials?: Record<string, unknown>
}) => {
const { provider, subscriptionId, ...body } = payload
return post<{ verified: boolean }>(
`/workspaces/current/trigger-provider/${provider}/subscriptions/verify/${subscriptionId}`,
{ body },
{ silent: true },
)
@ -183,7 +203,7 @@ export type BuildTriggerSubscriptionPayload = {
provider: string
subscriptionBuilderId: string
name?: string
parameters?: Record<string, any>
parameters?: Record<string, unknown>
}
export const useBuildTriggerSubscription = () => {
@ -210,6 +230,27 @@ export const useDeleteTriggerSubscription = () => {
})
}
export type UpdateTriggerSubscriptionPayload = {
subscriptionId: string
name?: string
properties?: Record<string, unknown>
parameters?: Record<string, unknown>
credentials?: Record<string, unknown>
}
export const useUpdateTriggerSubscription = () => {
return useMutation({
mutationKey: [NAME_SPACE, 'update-subscription'],
mutationFn: (payload: UpdateTriggerSubscriptionPayload) => {
const { subscriptionId, ...body } = payload
return post<{ result: string, id: string }>(
`/workspaces/current/trigger-provider/${subscriptionId}/subscriptions/update`,
{ body },
)
},
})
}
export const useTriggerSubscriptionBuilderLogs = (
provider: string,
subscriptionBuilderId: string,
@ -273,7 +314,7 @@ export const useInitiateTriggerOAuth = () => {
return useMutation({
mutationKey: [NAME_SPACE, 'initiate-oauth'],
mutationFn: (provider: string) => {
return get<{ authorization_url: string; subscription_builder: TriggerSubscriptionBuilder }>(
return get<{ authorization_url: string, subscription_builder: TriggerSubscriptionBuilder }>(
`/workspaces/current/trigger-provider/${provider}/subscriptions/oauth/authorize`,
{},
{ silent: true },
@ -289,22 +330,49 @@ export const useTriggerPluginDynamicOptions = (payload: {
action: string
parameter: string
credential_id: string
extra?: Record<string, any>
credentials?: Record<string, unknown>
extra?: Record<string, unknown>
}, enabled = true) => {
return useQuery<{ options: Array<{ value: string; label: any }> }>({
queryKey: [NAME_SPACE, 'dynamic-options', payload.plugin_id, payload.provider, payload.action, payload.parameter, payload.credential_id, payload.extra],
queryFn: () => get<{ options: Array<{ value: string; label: any }> }>(
'/workspaces/current/plugin/parameters/dynamic-options',
{
params: {
...payload,
provider_type: 'trigger', // Add required provider_type parameter
return useQuery<{ options: FormOption[] }>({
queryKey: [NAME_SPACE, 'dynamic-options', payload.plugin_id, payload.provider, payload.action, payload.parameter, payload.credential_id, payload.credentials, payload.extra],
queryFn: () => {
// Use new endpoint with POST when credentials provided (for edit mode)
if (payload.credentials) {
return post<{ options: FormOption[] }>(
'/workspaces/current/plugin/parameters/dynamic-options-with-credentials',
{
body: {
plugin_id: payload.plugin_id,
provider: payload.provider,
action: payload.action,
parameter: payload.parameter,
credential_id: payload.credential_id,
credentials: payload.credentials,
},
},
{ silent: true },
)
}
// Use original GET endpoint for normal cases
return get<{ options: FormOption[] }>(
'/workspaces/current/plugin/parameters/dynamic-options',
{
params: {
plugin_id: payload.plugin_id,
provider: payload.provider,
action: payload.action,
parameter: payload.parameter,
credential_id: payload.credential_id,
provider_type: 'trigger',
},
},
},
{ silent: true },
),
{ silent: true },
)
},
enabled: enabled && !!payload.plugin_id && !!payload.provider && !!payload.action && !!payload.parameter && !!payload.credential_id,
retry: 0,
staleTime: 0,
gcTime: 0,
})
}

View File

@ -1,7 +1,7 @@
import { useQuery } from '@tanstack/react-query'
import { fetchTryAppDatasets, fetchTryAppFlowPreview, fetchTryAppInfo } from './try-app'
import { AppSourceType, fetchAppParams } from './share'
import type { DataSetListResponse } from '@/models/datasets'
import { useQuery } from '@tanstack/react-query'
import { AppSourceType, fetchAppParams } from './share'
import { fetchTryAppDatasets, fetchTryAppFlowPreview, fetchTryAppInfo } from './try-app'
const NAME_SPACE = 'try-app'

View File

@ -1,5 +1,5 @@
import { del, get, patch, post, put } from './base'
import { useInfiniteQuery, useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
import type { CommonResponse } from '@/models/common'
import type { FlowType } from '@/types/common'
import type {
FetchWorkflowDraftPageParams,
FetchWorkflowDraftPageResponse,
@ -9,10 +9,11 @@ import type {
UpdateWorkflowParams,
VarInInspect,
WorkflowConfigResponse,
WorkflowRunHistoryResponse,
} from '@/types/workflow'
import type { CommonResponse } from '@/models/common'
import { useInfiniteQuery, useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
import { del, get, patch, post, put } from './base'
import { useInvalid, useReset } from './use-base'
import type { FlowType } from '@/types/common'
import { getFlowPrefix } from './utils'
const NAME_SPACE = 'workflow'
@ -25,13 +26,22 @@ export const useAppWorkflow = (appID: string) => {
})
}
export const useWorkflowRunHistory = (url?: string, enabled = true) => {
return useQuery<WorkflowRunHistoryResponse>({
queryKey: [NAME_SPACE, 'runHistory', url],
queryFn: () => get<WorkflowRunHistoryResponse>(url as string),
enabled: !!url && enabled,
})
}
export const useInvalidateAppWorkflow = () => {
const queryClient = useQueryClient()
return (appID: string) => {
queryClient.invalidateQueries(
{
queryKey: [NAME_SPACE, 'publish', appID],
})
},
)
}
}
@ -123,7 +133,7 @@ export const useInvalidLastRun = (flowType: FlowType, flowId: string, nodeId: st
// Rerun workflow or change the version of workflow
export const useInvalidAllLastRun = (flowType?: FlowType, flowId?: string) => {
return useInvalid([NAME_SPACE, flowType, 'last-run', flowId])
return useInvalid([...useLastRunKey, flowType, flowId])
}
export const useConversationVarValues = (flowType?: FlowType, flowId?: string) => {

View File

@ -1,3 +1,4 @@
import { FlowType } from '@/types/common'
/**
* Test suite for service utility functions
*
@ -12,7 +13,6 @@
* with a fallback to 'apps' for undefined or unknown flow types.
*/
import { flowPrefixMap, getFlowPrefix } from './utils'
import { FlowType } from '@/types/common'
describe('Service Utils', () => {
describe('flowPrefixMap', () => {

View File

@ -30,10 +30,13 @@ type isWebAppLogin = {
app_logged_in: boolean
}
export async function webAppLoginStatus(shareCode: string) {
export async function webAppLoginStatus(shareCode: string, userId?: string) {
// always need to check login to prevent passport from being outdated
// check remotely, the access token could be in cookie (enterprise SSO redirected with https)
const { logged_in, app_logged_in } = await getPublic<isWebAppLogin>(`/login/status?app_code=${shareCode}`)
const params = new URLSearchParams({ app_code: shareCode })
if (userId)
params.append('user_id', userId)
const { logged_in, app_logged_in } = await getPublic<isWebAppLogin>(`/login/status?${params.toString()}`)
return {
userLoggedIn: logged_in,
appLoggedIn: app_logged_in,

View File

@ -1,8 +1,8 @@
import { produce } from 'immer'
import type { Edge, Node } from '@/app/components/workflow/types'
import { BlockEnum } from '@/app/components/workflow/types'
import type { PluginTriggerNodeType } from '@/app/components/workflow/nodes/trigger-plugin/types'
import type { Edge, Node } from '@/app/components/workflow/types'
import type { FetchWorkflowDraftResponse } from '@/types/workflow'
import { produce } from 'immer'
import { BlockEnum } from '@/app/components/workflow/types'
export type TriggerPluginNodePayload = {
title: string

View File

@ -1,16 +1,13 @@
import type { Fetcher } from 'swr'
import { get, post } from './base'
import type { BlockEnum } from '@/app/components/workflow/types'
import type { CommonResponse } from '@/models/common'
import type { FlowType } from '@/types/common'
import type {
ChatRunHistoryResponse,
ConversationVariableResponse,
FetchWorkflowDraftResponse,
NodesDefaultConfigsResponse,
WorkflowRunHistoryResponse,
VarInInspect,
} from '@/types/workflow'
import type { BlockEnum } from '@/app/components/workflow/types'
import type { VarInInspect } from '@/types/workflow'
import type { FlowType } from '@/types/common'
import { get, post } from './base'
import { getFlowPrefix } from './utils'
export const fetchWorkflowDraft = (url: string) => {
@ -21,21 +18,13 @@ export const syncWorkflowDraft = ({ url, params }: {
url: string
params: Pick<FetchWorkflowDraftResponse, 'graph' | 'features' | 'environment_variables' | 'conversation_variables'>
}) => {
return post<CommonResponse & { updated_at: number; hash: string }>(url, { body: params }, { silent: true })
return post<CommonResponse & { updated_at: number, hash: string }>(url, { body: params }, { silent: true })
}
export const fetchNodesDefaultConfigs: Fetcher<NodesDefaultConfigsResponse, string> = (url) => {
export const fetchNodesDefaultConfigs = (url: string) => {
return get<NodesDefaultConfigsResponse>(url)
}
export const fetchWorkflowRunHistory: Fetcher<WorkflowRunHistoryResponse, string> = (url) => {
return get<WorkflowRunHistoryResponse>(url)
}
export const fetchChatRunHistory: Fetcher<ChatRunHistoryResponse, string> = (url) => {
return get<ChatRunHistoryResponse>(url)
}
export const singleNodeRun = (flowType: FlowType, flowId: string, nodeId: string, params: object) => {
return post(`${getFlowPrefix(flowType)}/${flowId}/workflows/draft/nodes/${nodeId}/run`, { body: params })
}
@ -48,7 +37,7 @@ export const getLoopSingleNodeRunUrl = (flowType: FlowType, isChatFlow: boolean,
return `${getFlowPrefix(flowType)}/${flowId}/${isChatFlow ? 'advanced-chat/' : ''}workflows/draft/loop/nodes/${nodeId}/run`
}
export const fetchPublishedWorkflow: Fetcher<FetchWorkflowDraftResponse, string> = (url) => {
export const fetchPublishedWorkflow = (url: string) => {
return get<FetchWorkflowDraftResponse>(url)
}
@ -68,15 +57,13 @@ export const fetchPipelineNodeDefault = (pipelineId: string, blockType: BlockEnu
})
}
// TODO: archived
export const updateWorkflowDraftFromDSL = (appId: string, data: string) => {
return post<FetchWorkflowDraftResponse>(`apps/${appId}/workflows/draft/import`, { body: { data } })
}
export const fetchCurrentValueOfConversationVariable: Fetcher<ConversationVariableResponse, {
export const fetchCurrentValueOfConversationVariable = ({
url,
params,
}: {
url: string
params: { conversation_id: string }
}> = ({ url, params }) => {
}) => {
return get<ConversationVariableResponse>(url, { params })
}