dify/web/app/components/share/text-generation/result/result-request.ts
FFXN 0e320290e1
feat: evaluation (#35353)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: jyong <718720800@qq.com>
Co-authored-by: Yansong Zhang <916125788@qq.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: hj24 <mambahj24@gmail.com>
Co-authored-by: hj24 <huangjian@dify.ai>
Co-authored-by: Joel <iamjoel007@gmail.com>
Co-authored-by: Stephen Zhou <38493346+hyoban@users.noreply.github.com>
Co-authored-by: CodingOnStar <hanxujiang@dify.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: 非法操作 <hjlarry@163.com>
Co-authored-by: Ayush Baluni <73417844+aayushbaluni@users.noreply.github.com>
Co-authored-by: yyh <92089059+lyzno1@users.noreply.github.com>
Co-authored-by: jimcody1995 <jjimcody@gmail.com>
Co-authored-by: James <63717587+jamesrayammons@users.noreply.github.com>
Co-authored-by: Yunlu Wen <yunlu.wen@dify.ai>
Co-authored-by: Stephen Zhou <hi@hyoban.cc>
Co-authored-by: Coding On Star <447357187@qq.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: jerryzai <jerryzh8710@protonmail.com>
Co-authored-by: NVIDIAN <speedy.hpc@hotmail.com>
Co-authored-by: ai-hpc <ai-hpc@users.noreply.github.com>
Co-authored-by: Asuka Minato <i@asukaminato.eu.org>
Co-authored-by: Junghwan <70629228+shaun0927@users.noreply.github.com>
Co-authored-by: HeYinKazune <70251095+HeYin-OS@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: yyh <yuanyouhuilyz@gmail.com>
Co-authored-by: Jingyi <jingyi.qi@dify.ai>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: sxxtony <166789813+sxxtony@users.noreply.github.com>
2026-04-17 16:37:21 +08:00

157 lines
4.3 KiB
TypeScript

import type { FileEntity } from '@/app/components/base/file-uploader/types'
import type { PromptConfig } from '@/models/debug'
import type { VisionFile, VisionSettings } from '@/types/app'
import { getProcessedFiles } from '@/app/components/base/file-uploader/utils'
import { TransferMethod } from '@/types/app'
import { formatBooleanInputs } from '@/utils/model-config'
export type ResultInputValue
= | string
| boolean
| number
| string[]
| Record<string, unknown>
| FileEntity
| FileEntity[]
| undefined
type Translate = (key: string, options?: Record<string, unknown>) => string
type ValidationResult = {
canSend: boolean
notification?: {
type: 'error' | 'info'
message: string
}
}
type ValidateResultRequestParams = {
completionFiles: VisionFile[]
inputs: Record<string, ResultInputValue>
isCallBatchAPI: boolean
promptConfig: PromptConfig | null
t: Translate
}
type BuildResultRequestDataParams = {
completionFiles: VisionFile[]
inputs: Record<string, ResultInputValue>
promptConfig: PromptConfig | null
visionConfig: VisionSettings
}
const isMissingRequiredInput = (
variable: PromptConfig['prompt_variables'][number],
value: ResultInputValue,
) => {
if (value === undefined || value === null)
return true
if (variable.type === 'file-list')
return !Array.isArray(value) || value.length === 0
if (['string', 'paragraph', 'number', 'json_object', 'select'].includes(variable.type))
return typeof value !== 'string' ? false : value.trim() === ''
return false
}
const hasPendingLocalFiles = (completionFiles: VisionFile[]) => {
return completionFiles.some(item => item.transfer_method === TransferMethod.local_file && !item.upload_file_id)
}
export const validateResultRequest = ({
completionFiles,
inputs,
isCallBatchAPI,
promptConfig,
t,
}: ValidateResultRequestParams): ValidationResult => {
if (isCallBatchAPI)
return { canSend: true }
const promptVariables = promptConfig?.prompt_variables
if (!promptVariables?.length) {
if (hasPendingLocalFiles(completionFiles)) {
return {
canSend: false,
notification: {
type: 'info',
message: t('errorMessage.waitForFileUpload', { ns: 'appDebug' }),
},
}
}
return { canSend: true }
}
const requiredVariables = promptVariables.filter(({ key, name, required, type }) => {
if (type === 'boolean' || type === 'checkbox')
return false
return (!key || !key.trim()) || (!name || !name.trim()) || required === undefined || required === null || required
})
const missingRequiredVariable = requiredVariables.find(variable => isMissingRequiredInput(variable, inputs[variable.key]))?.name
if (missingRequiredVariable) {
return {
canSend: false,
notification: {
type: 'error',
message: t('errorMessage.valueOfVarRequired', {
ns: 'appDebug',
key: missingRequiredVariable,
}),
},
}
}
if (hasPendingLocalFiles(completionFiles)) {
return {
canSend: false,
notification: {
type: 'info',
message: t('errorMessage.waitForFileUpload', { ns: 'appDebug' }),
},
}
}
return { canSend: true }
}
export const buildResultRequestData = ({
completionFiles,
inputs,
promptConfig,
visionConfig,
}: BuildResultRequestDataParams) => {
const processedInputs = {
...formatBooleanInputs(promptConfig?.prompt_variables, inputs as Record<string, string | number | boolean | object>),
}
promptConfig?.prompt_variables.forEach((variable) => {
const value = processedInputs[variable.key]
if (variable.type === 'file' && value && typeof value === 'object' && !Array.isArray(value)) {
processedInputs[variable.key] = getProcessedFiles([value as FileEntity])[0]!
return
}
if (variable.type === 'file-list' && Array.isArray(value) && value.length > 0)
processedInputs[variable.key] = getProcessedFiles(value as FileEntity[])
})
return {
inputs: processedInputs,
...(visionConfig.enabled && completionFiles.length > 0
? {
files: completionFiles.map((item) => {
if (item.transfer_method === TransferMethod.local_file)
return { ...item, url: '' }
return item
}),
}
: {}),
}
}