fix(web): align data contracts with backend schema

This commit is contained in:
yyh
2026-03-26 14:32:47 +08:00
parent f8e5421a01
commit 90225f07d9
26 changed files with 364 additions and 317 deletions

View File

@ -40,7 +40,8 @@ import { useAsyncWindowOpen } from '@/hooks/use-async-window-open'
import { useFormatTimeFromNow } from '@/hooks/use-format-time-from-now'
import { AccessMode } from '@/models/access-control'
import { useAppWhiteListSubjects, useGetUserCanAccessApp } from '@/service/access-control'
import { fetchAppDetailDirect, publishToCreatorsPlatform } from '@/service/apps'
import { fetchAppDetailDirect } from '@/service/apps'
import { consoleClient } from '@/service/client'
import { fetchInstalledAppList } from '@/service/explore'
import { useInvalidateAppWorkflow } from '@/service/use-workflow'
import { fetchPublishedWorkflow } from '@/service/workflow'
@ -286,7 +287,9 @@ const AppPublisher = ({
return
setPublishingToMarketplace(true)
try {
const result = await publishToCreatorsPlatform({ appID: appDetail.id })
const result = await consoleClient.apps.publishToCreatorsPlatform({
params: { appId: appDetail.id },
})
window.open(result.redirect_url, '_blank')
}
catch (error: any) {

View File

@ -1,6 +1,7 @@
'use client'
import type { MarketplaceTemplate } from '@/service/marketplace-templates'
import { skipToken, useQuery } from '@tanstack/react-query'
import { useCallback, useState } from 'react'
import { useTranslation } from 'react-i18next'
import AppIcon from '@/app/components/base/app-icon'
@ -8,10 +9,8 @@ import Button from '@/app/components/base/button'
import { Dialog, DialogCloseButton, DialogContent, DialogTitle } from '@/app/components/base/ui/dialog'
import { toast } from '@/app/components/base/ui/toast'
import { MARKETPLACE_API_PREFIX, MARKETPLACE_URL_PREFIX } from '@/config'
import {
fetchMarketplaceTemplateDSL,
useMarketplaceTemplateDetail,
} from '@/service/marketplace-templates'
import { marketplaceQuery } from '@/service/client'
import { fetchMarketplaceTemplateDSL } from '@/service/marketplace-templates'
type ImportFromMarketplaceTemplateModalProps = {
templateId: string
@ -26,7 +25,11 @@ const ImportFromMarketplaceTemplateModal = ({
}: ImportFromMarketplaceTemplateModalProps) => {
const { t } = useTranslation()
const { data, isLoading, isError } = useMarketplaceTemplateDetail(templateId)
const { data, isLoading, isError } = useQuery(marketplaceQuery.templateDetail.queryOptions({
input: templateId
? { params: { templateId } }
: skipToken,
}))
const template = data?.data ?? null
const [isImporting, setIsImporting] = useState(false)

View File

@ -1,7 +1,8 @@
'use client'
import type { FC } from 'react'
import { useQuery } from '@tanstack/react-query'
import type { WorkflowOnlineUsersResponse } from '@/models/app'
import { skipToken, useQuery } from '@tanstack/react-query'
import { useDebounceFn } from 'ahooks'
import { parseAsStringLiteral, useQueryState } from 'nuqs'
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
@ -18,7 +19,7 @@ import { useAppContext } from '@/context/app-context'
import { useGlobalPublicStore } from '@/context/global-public-context'
import { CheckModal } from '@/hooks/use-pay'
import dynamic from '@/next/dynamic'
import { fetchWorkflowOnlineUsers } from '@/service/apps'
import { consoleQuery } from '@/service/client'
import { useInfiniteAppList } from '@/service/use-apps'
import { AppModeEnum, AppModes } from '@/types/app'
import { cn } from '@/utils/classnames'
@ -131,11 +132,18 @@ const List: FC<Props> = ({
return Array.from(ids)
}, [apps])
const { data: onlineUsersByWorkflow = {}, refetch: refreshOnlineUsers } = useQuery({
queryKey: ['apps', 'workflow-online-users', workflowIds],
queryFn: () => fetchWorkflowOnlineUsers({ workflowIds }),
enabled: workflowIds.length > 0,
})
const { data: onlineUsersByWorkflow = {}, refetch: refreshOnlineUsers } = useQuery(
consoleQuery.apps.workflowOnlineUsers.queryOptions({
input: workflowIds.length
? { query: { workflow_ids: workflowIds.join(',') } }
: skipToken,
select: ({ data }) => data.reduce<Record<string, WorkflowOnlineUsersResponse['data'][number]['users']>>((acc, item) => {
if (item.workflow_id)
acc[item.workflow_id] = item.users
return acc
}, {}),
}),
)
useEffect(() => {
const timer = window.setInterval(() => {

View File

@ -136,10 +136,7 @@ export const useWorkflowComment = () => {
mentioned_user_ids: mentionedUserIds,
})
const createdAt = Number(newComment.created_at)
const createdAtSeconds = Number.isNaN(createdAt)
? Math.floor(Date.parse(newComment.created_at) / 1000)
: createdAt
const createdAtSeconds = newComment.created_at
const createdByAccount = {
id: userProfile?.id ?? '',
name: userProfile?.name ?? '',

View File

@ -1,5 +1,5 @@
import type { ContextGenerateModalHandle } from '../index'
import type { ContextGenerateResponse } from '@/service/debug'
import type { ContextGenerateResponse } from '@/contract/console/generator'
import { act, fireEvent, render, screen } from '@testing-library/react'
import * as React from 'react'
import { CodeLanguage } from '@/app/components/workflow/nodes/code/types'

View File

@ -1,4 +1,4 @@
import type { ContextGenerateResponse } from '@/service/debug'
import type { ContextGenerateResponse } from '@/contract/console/generator'
import { fireEvent, render, screen } from '@testing-library/react'
import * as React from 'react'
import { CodeLanguage } from '@/app/components/workflow/nodes/code/types'

View File

@ -1,6 +1,6 @@
import type { PointerEvent, RefObject } from 'react'
import type { VersionOption } from '../types'
import type { ContextGenerateResponse } from '@/service/debug'
import type { ContextGenerateResponse } from '@/contract/console/generator'
import { RiArrowDownSLine, RiCheckLine, RiCloseLine, RiPlayLargeLine } from '@remixicon/react'
import { useCallback, useMemo, useState } from 'react'
import { useTranslation } from 'react-i18next'

View File

@ -1,4 +1,4 @@
import type { ContextGenerateResponse } from '@/service/debug'
import type { ContextGenerateResponse } from '@/contract/console/generator'
import { act, renderHook, waitFor } from '@testing-library/react'
import useContextGenData from '../use-context-gen-data'

View File

@ -1,4 +1,4 @@
import type { ContextGenerateResponse } from '@/service/debug'
import type { ContextGenerateResponse } from '@/contract/console/generator'
import { useSessionStorageState } from 'ahooks'
import { useCallback } from 'react'
import { CONTEXT_GEN_STORAGE_SUFFIX, getContextGenStorageKey } from '../utils/storage'

View File

@ -10,7 +10,7 @@ import type {
ContextGenerateMessage,
ContextGenerateParameterInfo,
ContextGenerateResponse,
} from '@/service/debug'
} from '@/contract/console/generator'
import type { CompletionParams, Model, ModelModeType } from '@/types/app'
import { useBoolean, useSessionStorageState } from 'ahooks'
import { useCallback, useMemo, useRef, useState } from 'react'
@ -24,7 +24,8 @@ import { useStore } from '@/app/components/workflow/store'
import { STORAGE_KEYS } from '@/config/storage-keys'
import { useGetLanguage } from '@/context/i18n'
import { languages } from '@/i18n-config/language'
import { fetchContextGenerateSuggestedQuestions, generateContext } from '@/service/debug'
import { consoleClient } from '@/service/client'
import { fetchContextGenerateSuggestedQuestions } from '@/service/debug'
import { AppModeEnum } from '@/types/app'
import { storage } from '@/utils/storage'
import { CONTEXT_GEN_STORAGE_SUFFIX, getContextGenStorageKey } from '../utils/storage'
@ -35,7 +36,7 @@ export type ContextGenerateChatMessage = ContextGenerateMessage & {
durationMs?: number
}
export const normalizeCodeLanguage = (value?: string) => {
export const normalizeCodeLanguage = (value?: string): CodeLanguage => {
if (value === CodeLanguage.javascript)
return CodeLanguage.javascript
if (value === CodeLanguage.python3)
@ -43,6 +44,12 @@ export const normalizeCodeLanguage = (value?: string) => {
return CodeLanguage.python3
}
export const normalizeContextGenerateLanguage = (value?: string): 'python3' | 'javascript' => {
if (value === CodeLanguage.javascript)
return CodeLanguage.javascript
return CodeLanguage.python3
}
const createChatMessageId = () => {
return `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`
}
@ -454,19 +461,21 @@ const useContextGenerate = ({
setGeneratingTrue()
generateStartRef.current = Date.now()
try {
const response = await generateContext({
language: normalizeCodeLanguage(current?.code_language || codeNodeData?.code_language) as 'python3' | 'javascript',
prompt_messages: nextMessages.map(({ role, content, tool_call_id }) => ({
role,
content,
tool_call_id,
})),
model_config: {
...modelConfig,
const response = await consoleClient.generator.contextGenerate({
body: {
language: normalizeContextGenerateLanguage(current?.code_language || codeNodeData?.code_language),
prompt_messages: nextMessages.map(({ role, content, tool_call_id }) => ({
role,
content,
tool_call_id,
})),
model_config: {
...modelConfig,
},
available_vars: availableVarsPayload,
parameter_info: parameterInfo,
code_context: codeContext,
},
available_vars: availableVarsPayload,
parameter_info: parameterInfo,
code_context: codeContext,
})
if (response.error) {

View File

@ -1,7 +1,7 @@
'use client'
import type { CodeNodeType, OutputVar } from '@/app/components/workflow/nodes/code/types'
import type { Node, NodeOutPutVar } from '@/app/components/workflow/types'
import type { ContextGenerateResponse } from '@/service/debug'
import type { ContextGenerateResponse } from '@/contract/console/generator'
import * as React from 'react'
import { forwardRef, useCallback, useImperativeHandle, useMemo } from 'react'
import { useTranslation } from 'react-i18next'

View File

@ -15,7 +15,7 @@ import { NULL_STRATEGY } from '@/app/components/workflow/nodes/_base/constants'
import { Type } from '@/app/components/workflow/nodes/llm/types'
import { BlockEnum, EditionType, isPromptMessageContext, PromptRole, VarType } from '@/app/components/workflow/types'
import { generateNewNode, getNodeCustomTypeByNodeDataType, mergeNodeDefaultData } from '@/app/components/workflow/utils'
import { fetchNestedNodeGraph } from '@/service/workflow'
import { consoleClient } from '@/service/client'
import { FlowType } from '@/types/common'
// Constants
@ -427,11 +427,14 @@ export function useMixedVariableExtractor({
return
const parameterSchema = resolveNestedNodeParameterSchema(paramKey)
try {
const response = await fetchNestedNodeGraph(configsMap.flowType, configsMap.flowId, {
parent_node_id: toolNodeId,
parameter_key: paramKey,
context_source: [payload.agentId, 'context'],
parameter_schema: parameterSchema,
const response = await consoleClient.workflowDraft.nestedNodeGraph({
params: { appId: configsMap.flowId },
body: {
parent_node_id: toolNodeId,
parameter_key: paramKey,
context_source: [payload.agentId, 'context'],
parameter_schema: parameterSchema,
},
})
const nestedNode = response?.graph?.nodes?.find(node => node.id === payload.extractorNodeId)
const nestedNodeData = nestedNode?.data as Partial<LLMNodeType> | undefined

View File

@ -2,7 +2,6 @@ import type {
AppAssetDeleteResponse,
AppAssetFileDownloadUrlResponse,
AppAssetNode,
AppAssetPublishResponse,
AppAssetTreeResponse,
BatchUploadPayload,
BatchUploadResponse,
@ -111,16 +110,6 @@ export const reorderNodeContract = base
}>())
.output(type<AppAssetNode>())
export const publishContract = base
.route({
path: '/apps/{appId}/assets/publish',
method: 'POST',
})
.input(type<{
params: { appId: string }
}>())
.output(type<AppAssetPublishResponse>())
export const getFileUploadUrlContract = base
.route({
path: '/apps/{appId}/assets/files/upload',

View File

@ -1,7 +1,28 @@
import type { WorkflowOnlineUsersResponse } from '@/models/app'
import type { DSLImportResponse, WorkflowOnlineUsersResponse } from '@/models/app'
import { type } from '@orpc/contract'
import { base } from '../base'
export type AppExportBundleResponse = {
download_url: string
filename: string
}
export type AppRuntimeUpgradeResponse = {
result: 'success' | 'no_draft' | 'already_sandboxed'
new_app_id?: string
converted_agents?: number
skipped_agents?: number
}
export type PublishToCreatorsPlatformResponse = {
redirect_url: string
}
export type ImportBundlePrepareResponse = {
import_id: string
upload_url: string
}
export const workflowOnlineUsersContract = base
.route({
path: '/apps/workflows/online-users',
@ -25,3 +46,69 @@ export const appDeleteContract = base
}
}>())
.output(type<unknown>())
export const appExportBundleContract = base
.route({
path: '/apps/{appId}/export-bundle',
method: 'GET',
})
.input(type<{
params: {
appId: string
}
query?: {
include_secret?: boolean
workflow_id?: string
}
}>())
.output(type<AppExportBundleResponse>())
export const publishToCreatorsPlatformContract = base
.route({
path: '/apps/{appId}/publish-to-creators-platform',
method: 'POST',
})
.input(type<{
params: {
appId: string
}
}>())
.output(type<PublishToCreatorsPlatformResponse>())
export const upgradeAppRuntimeContract = base
.route({
path: '/apps/{appId}/upgrade-runtime',
method: 'POST',
})
.input(type<{
params: {
appId: string
}
}>())
.output(type<AppRuntimeUpgradeResponse>())
export const prepareImportBundleContract = base
.route({
path: '/apps/imports-bundle/prepare',
method: 'POST',
})
.output(type<ImportBundlePrepareResponse>())
export const confirmImportBundleContract = base
.route({
path: '/apps/imports-bundle/{importId}/confirm',
method: 'POST',
})
.input(type<{
params: {
importId: string
}
body?: {
name?: string
description?: string
icon_type?: string
icon?: string
icon_background?: string
}
}>())
.output(type<DSLImportResponse>())

View File

@ -0,0 +1,103 @@
import type { StructuredOutput } from '@/app/components/workflow/nodes/llm/types'
import type { BlockEnum, ValueSelector, VarType } from '@/app/components/workflow/types'
import type { CompletionParams } from '@/types/app'
import { type } from '@orpc/contract'
import { base } from '../base'
export type ContextGenerateMessage = {
role: 'user' | 'assistant' | 'system' | 'tool'
content: string
tool_call_id?: string
}
export type ContextGenerateAvailableVar = {
value_selector: ValueSelector
type: VarType
description?: string
node_id?: string
node_title?: string
node_type?: BlockEnum
schema?: StructuredOutput['schema'] | Record<string, unknown> | null
}
export type ContextGenerateParameterInfo = {
name: string
type?: string
description?: string
required?: boolean
options?: string[]
min?: number
max?: number
default?: string | number | boolean | null
multiple?: boolean
label?: string
}
export type ContextGenerateVariable = {
variable: string
value_selector: string[]
}
export type ContextGenerateCodeContext = {
code: string
outputs?: Record<string, { type: string }>
variables?: ContextGenerateVariable[]
}
export type ContextGenerateRequest = {
language?: 'python3' | 'javascript'
prompt_messages: ContextGenerateMessage[]
model_config: {
provider: string
name: string
completion_params?: CompletionParams
}
available_vars: ContextGenerateAvailableVar[]
parameter_info: ContextGenerateParameterInfo
code_context?: ContextGenerateCodeContext | null
}
export type ContextGenerateResponse = {
variables: ContextGenerateVariable[]
code_language: string
code: string
outputs: Record<string, { type: string }>
message: string
error: string
}
export type ContextGenerateSuggestedQuestionsRequest = {
language: string
model_config?: {
provider: string
name: string
completion_params?: CompletionParams
}
available_vars: ContextGenerateAvailableVar[]
parameter_info: ContextGenerateParameterInfo
}
export type ContextGenerateSuggestedQuestionsResponse = {
questions: string[]
error: string
}
export const contextGenerateContract = base
.route({
path: '/context-generate',
method: 'POST',
})
.input(type<{
body: ContextGenerateRequest
}>())
.output(type<ContextGenerateResponse>())
export const contextGenerateSuggestedQuestionsContract = base
.route({
path: '/context-generate/suggested-questions',
method: 'POST',
})
.input(type<{
body: ContextGenerateSuggestedQuestionsRequest
}>())
.output(type<ContextGenerateSuggestedQuestionsResponse>())

View File

@ -1,4 +1,3 @@
import type { CommonResponse } from '@/models/common'
import { type } from '@orpc/contract'
import { base } from '../base'
@ -6,7 +5,13 @@ export type UserProfile = {
id: string
name: string
email: string
avatar?: string | null
avatar_url?: string
last_login_at?: number | null
last_active_at?: number | null
created_at?: number | null
role?: string
status?: string
}
export type WorkflowCommentList = {
@ -60,12 +65,12 @@ export type WorkflowCommentDetail = {
export type WorkflowCommentCreateRes = {
id: string
created_at: string
created_at: number
}
export type WorkflowCommentUpdateRes = {
id: string
updated_at: string
updated_at: number
}
export type WorkflowCommentResolveRes = {
@ -75,20 +80,14 @@ export type WorkflowCommentResolveRes = {
resolved_at: number
}
export type WorkflowCommentReply = {
export type WorkflowCommentReplyCreateRes = {
id: string
comment_id: string
content: string
created_by: string
created_at: string
updated_at: string
mentioned_user_ids: string[]
author: {
id: string
name: string
email: string
avatar?: string
}
created_at: number
}
export type WorkflowCommentReplyUpdateRes = {
id: string
updated_at: number
}
export type CreateCommentParams = {
@ -173,7 +172,7 @@ export const workflowCommentDeleteContract = base
commentId: string
}
}>())
.output(type<CommonResponse>())
.output(type<unknown>())
export const workflowCommentResolveContract = base
.route({
@ -200,7 +199,7 @@ export const workflowCommentReplyCreateContract = base
}
body: CreateReplyParams
}>())
.output(type<WorkflowCommentReply>())
.output(type<WorkflowCommentReplyCreateRes>())
export const workflowCommentReplyUpdateContract = base
.route({
@ -215,7 +214,7 @@ export const workflowCommentReplyUpdateContract = base
}
body: CreateReplyParams
}>())
.output(type<WorkflowCommentReply>())
.output(type<WorkflowCommentReplyUpdateRes>())
export const workflowCommentReplyDeleteContract = base
.route({
@ -229,7 +228,7 @@ export const workflowCommentReplyDeleteContract = base
replyId: string
}
}>())
.output(type<CommonResponse>())
.output(type<unknown>())
export const workflowCommentMentionUsersContract = base
.route({

View File

@ -9,6 +9,7 @@ import type {
} from '@/app/components/base/features/types'
import type { ConversationVariable, EnvironmentVariable } from '@/app/components/workflow/types'
import type { CommonResponse } from '@/models/common'
import type { NestedNodeGraphPayload, NestedNodeGraphResponse } from '@/types/workflow'
import { type } from '@orpc/contract'
import { base } from '../base'
@ -97,5 +98,19 @@ export const workflowDraftNodeSkillsContract = base
type: string
provider: string
tool_name: string
enabled: boolean
}[]
}>())
export const workflowDraftNestedNodeGraphContract = base
.route({
path: '/apps/{appId}/workflows/draft/nested-node-graph',
method: 'POST',
})
.input(type<{
params: {
appId: string
}
body: NestedNodeGraphPayload
}>())
.output(type<NestedNodeGraphResponse>())

View File

@ -8,13 +8,20 @@ import {
getFileDownloadUrlContract,
getFileUploadUrlContract,
moveNodeContract,
publishContract,
renameNodeContract,
reorderNodeContract,
treeContract,
updateFileContentContract,
} from './console/app-asset'
import { appDeleteContract, workflowOnlineUsersContract } from './console/apps'
import {
appDeleteContract,
appExportBundleContract,
confirmImportBundleContract,
prepareImportBundleContract,
publishToCreatorsPlatformContract,
upgradeAppRuntimeContract,
workflowOnlineUsersContract,
} from './console/apps'
import { bindPartnerStackContract, invoicesContract } from './console/billing'
import {
exploreAppDetailContract,
@ -27,6 +34,10 @@ import {
exploreInstalledAppsContract,
exploreInstalledAppUninstallContract,
} from './console/explore'
import {
contextGenerateContract,
contextGenerateSuggestedQuestionsContract,
} from './console/generator'
import { changePreferredProviderTypeContract, modelProvidersModelsContract } from './console/model-providers'
import { notificationContract, notificationDismissContract } from './console/notification'
import { pluginCheckInstalledContract, pluginLatestVersionsContract } from './console/plugins'
@ -61,6 +72,7 @@ import {
import { trialAppDatasetsContract, trialAppInfoContract, trialAppParametersContract, trialAppWorkflowsContract } from './console/try-app'
import {
workflowDraftEnvironmentVariablesContract,
workflowDraftNestedNodeGraphContract,
workflowDraftNodeSkillsContract,
workflowDraftUpdateConversationVariablesContract,
workflowDraftUpdateEnvironmentVariablesContract,
@ -83,9 +95,20 @@ export const consoleRouterContract = {
avatar: accountAvatarContract,
},
systemFeatures: systemFeaturesContract,
generator: {
contextGenerate: contextGenerateContract,
contextGenerateSuggestedQuestions: contextGenerateSuggestedQuestionsContract,
},
apps: {
deleteApp: appDeleteContract,
workflowOnlineUsers: workflowOnlineUsersContract,
exportBundle: appExportBundleContract,
publishToCreatorsPlatform: publishToCreatorsPlatformContract,
upgradeRuntime: upgradeAppRuntimeContract,
importsBundle: {
prepare: prepareImportBundleContract,
confirm: confirmImportBundleContract,
},
},
explore: {
apps: exploreAppsContract,
@ -138,13 +161,13 @@ export const consoleRouterContract = {
renameNode: renameNodeContract,
moveNode: moveNodeContract,
reorderNode: reorderNodeContract,
publish: publishContract,
getFileUploadUrl: getFileUploadUrlContract,
batchUpload: batchUploadContract,
},
workflowDraft: {
environmentVariables: workflowDraftEnvironmentVariablesContract,
nodeSkills: workflowDraftNodeSkillsContract,
nestedNodeGraph: workflowDraftNestedNodeGraphContract,
updateEnvironmentVariables: workflowDraftUpdateEnvironmentVariablesContract,
updateConversationVariables: workflowDraftUpdateConversationVariablesContract,
updateFeatures: workflowDraftUpdateFeaturesContract,

View File

@ -44,7 +44,7 @@ export type DSLImportResponse = {
current_dsl_version?: string
imported_dsl_version?: string
error: string
leaked_dependencies: Dependency[]
leaked_dependencies?: Dependency[]
}
export type AppTemplatesResponse = {
@ -122,7 +122,7 @@ export type WorkflowOnlineUser = {
}
export type WorkflowOnlineUsersResponse = {
data: Record<string, WorkflowOnlineUser[]> | Array<{
data: Array<{
workflow_id: string
users: WorkflowOnlineUser[]
}>

View File

@ -1,5 +1,6 @@
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, WorkflowOnlineUser } from '@/models/app'
import type { AppExportBundleResponse, AppRuntimeUpgradeResponse } from '@/contract/console/apps'
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 { del, get, patch, post, put } from './base'
@ -9,33 +10,6 @@ export const fetchAppList = ({ url, params }: { url: string, params?: Record<str
return get<AppListResponse>(url, { params })
}
export const fetchWorkflowOnlineUsers = async ({ workflowIds }: { workflowIds: string[] }): Promise<Record<string, WorkflowOnlineUser[]>> => {
if (!workflowIds.length)
return {}
const params = { workflow_ids: workflowIds.join(',') }
const response = await consoleClient.apps.workflowOnlineUsers({
query: params,
})
if (!response || !response.data)
return {}
if (Array.isArray(response.data)) {
return response.data.reduce<Record<string, WorkflowOnlineUser[]>>((acc, item) => {
if (item?.workflow_id)
acc[item.workflow_id] = item.users || []
return acc
}, {})
}
return Object.entries(response.data).reduce<Record<string, WorkflowOnlineUser[]>>((acc, [workflowId, users]) => {
if (workflowId)
acc[workflowId] = users || []
return acc
}, {})
}
export const fetchAppDetail = ({ url, id }: { url: string, id: string }): Promise<AppDetailResponse> => {
return get<AppDetailResponse>(`${url}/${id}`)
}
@ -111,8 +85,10 @@ export const copyApp = ({
return post<AppDetailResponse>(`apps/${appID}/copy`, { body: { name, icon_type, icon, icon_background, mode, description } })
}
export const upgradeAppRuntime = (appID: string): Promise<{ result: string, new_app_id?: string, converted_agents?: number, skipped_agents?: number }> => {
return post(`apps/${appID}/upgrade-runtime`)
export const upgradeAppRuntime = (appID: string): Promise<AppRuntimeUpgradeResponse> => {
return consoleClient.apps.upgradeRuntime({
params: { appId: appID },
})
}
export const exportAppConfig = ({ appID, include = false, workflowID }: { appID: string, include?: boolean, workflowID?: string }): Promise<{ data: string }> => {
@ -125,28 +101,14 @@ export const exportAppConfig = ({ appID, include = false, workflowID }: { appID:
}
export const exportAppBundle = async ({ appID, include = false, workflowID }: { appID: string, include?: boolean, workflowID?: string }): Promise<void> => {
const { API_PREFIX, CSRF_COOKIE_NAME, CSRF_HEADER_NAME } = await import('@/config')
const Cookies = (await import('js-cookie')).default
const params = new URLSearchParams({
include_secret: include.toString(),
})
if (workflowID)
params.append('workflow_id', workflowID)
const url = `${API_PREFIX}/apps/${appID}/export-bundle?${params.toString()}`
const response = await fetch(url, {
method: 'GET',
credentials: 'include',
headers: {
[CSRF_HEADER_NAME]: Cookies.get(CSRF_COOKIE_NAME()) || '',
const result: AppExportBundleResponse = await consoleClient.apps.exportBundle({
params: { appId: appID },
query: {
include_secret: include,
...(workflowID ? { workflow_id: workflowID } : {}),
},
})
if (!response.ok)
throw new Error('Export bundle failed')
const result: { download_url: string, filename: string } = await response.json()
const a = document.createElement('a')
a.href = result.download_url
a.download = result.filename
@ -161,49 +123,6 @@ export const importDSLConfirm = ({ import_id }: { import_id: string }): Promise<
return post<DSLImportResponse>(`apps/imports/${import_id}/confirm`, { body: {} })
}
export type PublishToCreatorsPlatformResponse = {
redirect_url: string
}
export const publishToCreatorsPlatform = ({ appID }: { appID: string }): Promise<PublishToCreatorsPlatformResponse> => {
return post<PublishToCreatorsPlatformResponse>(`apps/${appID}/publish-to-creators-platform`, { body: {} })
}
export type ImportBundlePrepareResponse = {
import_id: string
upload_url: string
}
export const prepareImportBundle = (): Promise<ImportBundlePrepareResponse> => {
return post<ImportBundlePrepareResponse>('apps/imports-bundle/prepare', { body: {} })
}
export const confirmImportBundle = ({
import_id,
name,
description,
icon_type,
icon,
icon_background,
}: {
import_id: string
name?: string
description?: string
icon_type?: string
icon?: string
icon_background?: string
}): Promise<DSLImportResponse> => {
return post<DSLImportResponse>(`apps/imports-bundle/${import_id}/confirm`, {
body: {
name,
description,
icon_type,
icon,
icon_background,
},
})
}
export const importAppBundle = async ({
file,
name,
@ -219,10 +138,8 @@ export const importAppBundle = async ({
icon?: string
icon_background?: string
}): Promise<DSLImportResponse> => {
// Step 1: Prepare import and get upload URL
const { import_id, upload_url } = await prepareImportBundle()
const { import_id, upload_url } = await consoleClient.apps.importsBundle.prepare()
// Step 2: Upload file to presigned URL
const uploadResponse = await fetch(upload_url, {
method: 'PUT',
body: file,
@ -231,14 +148,15 @@ export const importAppBundle = async ({
if (!uploadResponse.ok)
throw new Error('Failed to upload bundle file')
// Step 3: Confirm import
return confirmImportBundle({
import_id,
name,
description,
icon_type,
icon,
icon_background,
return consoleClient.apps.importsBundle.confirm({
params: { importId: import_id },
body: {
name,
description,
icon_type,
icon,
icon_background,
},
})
}

View File

@ -1,10 +1,9 @@
import type { IOnCompleted, IOnData, IOnError, IOnFile, IOnMessageEnd, IOnMessageReplace, IOnThought } from './base'
import type { FileEntity } from '@/app/components/base/file-uploader/types'
import type { ModelParameterRule } from '@/app/components/header/account-setting/model-provider-page/declarations'
import type { StructuredOutput } from '@/app/components/workflow/nodes/llm/types'
import type { BlockEnum, ValueSelector, VarType } from '@/app/components/workflow/types'
import type { ContextGenerateSuggestedQuestionsRequest, ContextGenerateSuggestedQuestionsResponse } from '@/contract/console/generator'
import type { ChatPromptConfig, CompletionPromptConfig } from '@/models/debug'
import type { AppModeEnum, CompletionParams, ModelModeType } from '@/types/app'
import type { AppModeEnum, ModelModeType } from '@/types/app'
import { get, post, ssePost } from './base'
export type BasicAppFirstRes = {
@ -28,85 +27,6 @@ export type CodeGenRes = {
error?: string
}
export type ContextGenerateMessage = {
role: 'user' | 'assistant' | 'system' | 'tool'
content: string
tool_call_id?: string
}
// FIXME
export type ContextGenerateAvailableVar = {
value_selector: ValueSelector
type: VarType
description?: string
node_id?: string
node_title?: string
node_type?: BlockEnum
schema?: StructuredOutput['schema'] | Record<string, unknown> | null
}
export type ContextGenerateParameterInfo = {
name: string
type?: string
description?: string
required?: boolean
options?: string[]
min?: number
max?: number
default?: string | number | boolean | null
multiple?: boolean
label?: string
}
export type ContextGenerateCodeContext = {
code: string
outputs?: Record<string, { type: string }>
variables?: ContextGenerateVariable[]
}
export type ContextGenerateRequest = {
language?: 'python3' | 'javascript'
prompt_messages: ContextGenerateMessage[]
model_config: {
provider: string
name: string
completion_params?: CompletionParams
}
available_vars: ContextGenerateAvailableVar[]
parameter_info: ContextGenerateParameterInfo
code_context?: ContextGenerateCodeContext | null
}
export type ContextGenerateVariable = {
variable: string
value_selector: string[]
}
export type ContextGenerateResponse = {
variables: ContextGenerateVariable[]
code_language: string
code: string
outputs: Record<string, { type: string }>
message: string
error: string
}
export type ContextGenerateSuggestedQuestionsRequest = {
language: string
model_config?: {
provider: string
name: string
completion_params?: CompletionParams
}
available_vars: ContextGenerateAvailableVar[]
parameter_info: ContextGenerateParameterInfo
}
export type ContextGenerateSuggestedQuestionsResponse = {
questions: string[]
error: string
}
export type TextGenerationMessageFile = FileEntity & {
belongs_to?: 'assistant' | 'user' | string
}
@ -192,12 +112,6 @@ export const generateRule = (body: Record<string, unknown>) => {
})
}
export const generateContext = (body: ContextGenerateRequest) => {
return post<ContextGenerateResponse>('/context-generate', {
body,
})
}
export const fetchContextGenerateSuggestedQuestions = (
body: ContextGenerateSuggestedQuestionsRequest,
getAbortController?: (abortController: AbortController) => void,

View File

@ -1,6 +1,4 @@
import { useQuery } from '@tanstack/react-query'
import { MARKETPLACE_API_PREFIX } from '@/config'
import { marketplaceClient, marketplaceQuery } from '@/service/client'
export type MarketplaceTemplate = {
id: string
@ -24,16 +22,6 @@ export type MarketplaceTemplate = {
updated_at: string
}
export const useMarketplaceTemplateDetail = (templateId: string) => {
return useQuery({
queryKey: marketplaceQuery.templateDetail.queryKey({
input: { params: { templateId } },
}),
queryFn: () => marketplaceClient.templateDetail({ params: { templateId } }),
enabled: !!templateId,
})
}
export const fetchMarketplaceTemplateDSL = async (
templateId: string,
): Promise<string> => {

View File

@ -221,23 +221,6 @@ export const useReorderAppAssetNode = () => {
})
}
export const usePublishAppAssets = () => {
const queryClient = useQueryClient()
return useMutation({
mutationKey: consoleQuery.appAsset.publish.mutationKey(),
mutationFn: (appId: string) => {
return consoleClient.appAsset.publish({
params: { appId },
})
},
onSuccess: (_, appId) => {
queryClient.invalidateQueries({
queryKey: consoleQuery.appAsset.tree.key({ type: 'query', input: { params: { appId } } }),
})
},
})
}
export const useUploadFileWithPresignedUrl = () => {
const queryClient = useQueryClient()
return useMutation({
@ -299,9 +282,20 @@ export const useBatchUpload = () => {
}): Promise<BatchUploadNodeOutput[]> => {
const response = await consoleClient.appAsset.batchUpload({
params: { appId },
body: { children: tree, parent_id: parentId },
body: { children: tree },
})
if (parentId) {
await Promise.all(
response.children.map(node =>
consoleClient.appAsset.moveNode({
params: { appId, nodeId: node.id },
body: { parent_id: parentId },
}),
),
)
}
const uploadTasks: Array<{ path: string, file: File, url: string }> = []
const extractUploads = (nodes: BatchUploadNodeOutput[], pathPrefix: string = '') => {

View File

@ -5,11 +5,11 @@ import type {
WorkflowCommentCreateRes,
WorkflowCommentDetail,
WorkflowCommentList,
WorkflowCommentReply,
WorkflowCommentReplyCreateRes,
WorkflowCommentReplyUpdateRes,
WorkflowCommentResolveRes,
WorkflowCommentUpdateRes,
} from '@/contract/console/workflow-comment'
import type { CommonResponse } from '@/models/common'
import { consoleClient } from './client'
export type {
@ -22,7 +22,8 @@ export type {
WorkflowCommentDetailMention,
WorkflowCommentDetailReply,
WorkflowCommentList,
WorkflowCommentReply,
WorkflowCommentReplyCreateRes,
WorkflowCommentReplyUpdateRes,
WorkflowCommentResolveRes,
WorkflowCommentUpdateRes,
} from '@/contract/console/workflow-comment'
@ -54,7 +55,7 @@ export const updateWorkflowComment = async (appId: string, commentId: string, pa
})
}
export const deleteWorkflowComment = async (appId: string, commentId: string): Promise<CommonResponse> => {
export const deleteWorkflowComment = async (appId: string, commentId: string): Promise<unknown> => {
return consoleClient.workflowComments.delete({
params: { appId, commentId },
})
@ -66,14 +67,14 @@ export const resolveWorkflowComment = async (appId: string, commentId: string):
})
}
export const createWorkflowCommentReply = async (appId: string, commentId: string, params: CreateReplyParams): Promise<WorkflowCommentReply> => {
export const createWorkflowCommentReply = async (appId: string, commentId: string, params: CreateReplyParams): Promise<WorkflowCommentReplyCreateRes> => {
return consoleClient.workflowComments.replies.create({
params: { appId, commentId },
body: params,
})
}
export const updateWorkflowCommentReply = async (appId: string, commentId: string, replyId: string, params: CreateReplyParams): Promise<WorkflowCommentReply> => {
export const updateWorkflowCommentReply = async (appId: string, commentId: string, replyId: string, params: CreateReplyParams): Promise<WorkflowCommentReplyUpdateRes> => {
return consoleClient.workflowComments.replies.update({
params: {
appId,
@ -84,7 +85,7 @@ export const updateWorkflowCommentReply = async (appId: string, commentId: strin
})
}
export const deleteWorkflowCommentReply = async (appId: string, commentId: string, replyId: string): Promise<CommonResponse> => {
export const deleteWorkflowCommentReply = async (appId: string, commentId: string, replyId: string): Promise<unknown> => {
return consoleClient.workflowComments.replies.delete({
params: {
appId,

View File

@ -6,8 +6,6 @@ import type {
ConversationVariableResponse,
FetchWorkflowDraftResponse,
HumanInputFormData,
NestedNodeGraphPayload,
NestedNodeGraphResponse,
NodesDefaultConfigsResponse,
VarInInspect,
} from '@/types/workflow'
@ -37,10 +35,6 @@ export const fetchNodesDefaultConfigs = (url: string) => {
return get<NodesDefaultConfigsResponse>(url)
}
export const fetchNestedNodeGraph = (flowType: FlowType, flowId: string, payload: NestedNodeGraphPayload) => {
return post<NestedNodeGraphResponse>(`${getFlowPrefix(flowType)}/${flowId}/workflows/draft/nested-node-graph`, { body: payload }, { silent: true })
}
export const singleNodeRun = (flowType: FlowType, flowId: string, nodeId: string, params: object) => {
return post(`${getFlowPrefix(flowType)}/${flowId}/workflows/draft/nodes/${nodeId}/run`, { body: params })
}

View File

@ -178,7 +178,6 @@ export type BatchUploadNodeOutput = {
* Request payload for batch upload
*/
export type BatchUploadPayload = {
parent_id?: string | null
children: BatchUploadNodeInput[]
}