pull/22121/head
liuchangsheng@wisdomidata.com 12 months ago
commit 6a0a86b959

@ -129,7 +129,9 @@ def reset_encrypt_key_pair():
click.echo(click.style("No workspaces found. Run /install first.", fg="red")) click.echo(click.style("No workspaces found. Run /install first.", fg="red"))
return return
tenant.encrypt_public_key = generate_key_pair(tenant.id) pem_public, pem_private = generate_key_pair(tenant.id)
tenant.encrypt_public_key = pem_public
tenant.encrypt_private_key = pem_private
db.session.query(Provider).filter(Provider.provider_type == "custom", Provider.tenant_id == tenant.id).delete() db.session.query(Provider).filter(Provider.provider_type == "custom", Provider.tenant_id == tenant.id).delete()
db.session.query(ProviderModel).filter(ProviderModel.tenant_id == tenant.id).delete() db.session.query(ProviderModel).filter(ProviderModel.tenant_id == tenant.id).delete()

@ -100,6 +100,11 @@ export const BLOCKS: Block[] = [
type: BlockEnum.Agent, type: BlockEnum.Agent,
title: 'Agent', title: 'Agent',
}, },
{
classification: BlockClassificationEnum.Default,
type: BlockEnum.Vanna,
title: 'Vanna',
},
] ]
export const BLOCK_CLASSIFICATIONS: string[] = [ export const BLOCK_CLASSIFICATIONS: string[] = [

@ -22,6 +22,7 @@ import IterationStartDefault from './nodes/iteration-start/default'
import AgentDefault from './nodes/agent/default' import AgentDefault from './nodes/agent/default'
import LoopStartDefault from './nodes/loop-start/default' import LoopStartDefault from './nodes/loop-start/default'
import LoopEndDefault from './nodes/loop-end/default' import LoopEndDefault from './nodes/loop-end/default'
import VannaDefault from './nodes/vanna/default'
type NodesExtraData = { type NodesExtraData = {
author: string author: string
@ -240,6 +241,16 @@ export const NODES_EXTRA_DATA: Record<BlockEnum, NodesExtraData> = {
getAvailableNextNodes: ListFilterDefault.getAvailableNextNodes, getAvailableNextNodes: ListFilterDefault.getAvailableNextNodes,
checkValid: AgentDefault.checkValid, checkValid: AgentDefault.checkValid,
}, },
[BlockEnum.Vanna]: {
author: '维斯德',
about: '',
availablePrevNodes: [],
availableNextNodes: [],
getAvailablePrevNodes: VannaDefault.getAvailablePrevNodes,
getAvailableNextNodes: VannaDefault.getAvailableNextNodes,
checkValid: VannaDefault.checkValid,
},
} }
export const NODES_INITIAL_DATA = { export const NODES_INITIAL_DATA = {
@ -471,7 +482,7 @@ export const SUPPORT_OUTPUT_VARS_NODE = [
BlockEnum.HttpRequest, BlockEnum.Tool, BlockEnum.VariableAssigner, BlockEnum.VariableAggregator, BlockEnum.QuestionClassifier, BlockEnum.HttpRequest, BlockEnum.Tool, BlockEnum.VariableAssigner, BlockEnum.VariableAggregator, BlockEnum.QuestionClassifier,
BlockEnum.ParameterExtractor, BlockEnum.Iteration, BlockEnum.Loop, BlockEnum.ParameterExtractor, BlockEnum.Iteration, BlockEnum.Loop,
BlockEnum.DocExtractor, BlockEnum.ListFilter, BlockEnum.DocExtractor, BlockEnum.ListFilter,
BlockEnum.Agent, BlockEnum.Agent,BlockEnum.Vanna
] ]
export const LLM_OUTPUT_STRUCT: Var[] = [ export const LLM_OUTPUT_STRUCT: Var[] = [

@ -477,6 +477,21 @@ const formatItem = (
}) as Var[] }) as Var[]
break break
} }
case BlockEnum.Vanna: {
res.vars = [
{
variable: 'output',
type: VarType.string,
},
{
variable: 'sql',
type: VarType.string,
},
]
break
}
} }
const { error_strategy } = data const { error_strategy } = data

@ -38,6 +38,8 @@ import ListFilterNode from './list-operator/node'
import ListFilterPanel from './list-operator/panel' import ListFilterPanel from './list-operator/panel'
import AgentNode from './agent/node' import AgentNode from './agent/node'
import AgentPanel from './agent/panel' import AgentPanel from './agent/panel'
import VannaNode from './vanna/node'
import VannaPanel from './vanna/panel'
import { TransferMethod } from '@/types/app' import { TransferMethod } from '@/types/app'
export const NodeComponentMap: Record<string, ComponentType<any>> = { export const NodeComponentMap: Record<string, ComponentType<any>> = {
@ -61,6 +63,7 @@ export const NodeComponentMap: Record<string, ComponentType<any>> = {
[BlockEnum.DocExtractor]: DocExtractorNode, [BlockEnum.DocExtractor]: DocExtractorNode,
[BlockEnum.ListFilter]: ListFilterNode, [BlockEnum.ListFilter]: ListFilterNode,
[BlockEnum.Agent]: AgentNode, [BlockEnum.Agent]: AgentNode,
[BlockEnum.Vanna]: VannaNode,
} }
export const PanelComponentMap: Record<string, ComponentType<any>> = { export const PanelComponentMap: Record<string, ComponentType<any>> = {
@ -84,6 +87,7 @@ export const PanelComponentMap: Record<string, ComponentType<any>> = {
[BlockEnum.DocExtractor]: DocExtractorPanel, [BlockEnum.DocExtractor]: DocExtractorPanel,
[BlockEnum.ListFilter]: ListFilterPanel, [BlockEnum.ListFilter]: ListFilterPanel,
[BlockEnum.Agent]: AgentPanel, [BlockEnum.Agent]: AgentPanel,
[BlockEnum.Vanna]: VannaPanel,
} }
export const CUSTOM_NODE_TYPE = 'custom' export const CUSTOM_NODE_TYPE = 'custom'

@ -32,6 +32,8 @@ export const getOperators = (type?: MetadataFilteringVariableType) => {
ComparisonOperator.endWith, ComparisonOperator.endWith,
ComparisonOperator.empty, ComparisonOperator.empty,
ComparisonOperator.notEmpty, ComparisonOperator.notEmpty,
ComparisonOperator.in,
ComparisonOperator.notIn,
] ]
case MetadataFilteringVariableType.number: case MetadataFilteringVariableType.number:
return [ return [
@ -43,6 +45,8 @@ export const getOperators = (type?: MetadataFilteringVariableType) => {
ComparisonOperator.lessThanOrEqual, ComparisonOperator.lessThanOrEqual,
ComparisonOperator.empty, ComparisonOperator.empty,
ComparisonOperator.notEmpty, ComparisonOperator.notEmpty,
ComparisonOperator.in,
ComparisonOperator.notIn,
] ]
default: default:
return [ return [

@ -0,0 +1,45 @@
import type {NodeDefault} from '../../types'
import {BlockEnum} from '../../types'
import type {VannaNodeType} from './types'
import {ALL_CHAT_AVAILABLE_BLOCKS, ALL_COMPLETION_AVAILABLE_BLOCKS} from '@/app/components/workflow/blocks'
import {ReasoningModeType} from "@/app/components/workflow/nodes/parameter-extractor/types";
const nodeDefault: NodeDefault<VannaNodeType> = {
defaultValue: {
query: [],
model: {
provider: '',
name: '',
mode: 'chat',
completion_params: {
temperature: 0.7,
},
},
reasoning_mode: ReasoningModeType.prompt,
vision: {
enabled: false,
},
},
getAvailablePrevNodes(isChatMode: boolean) {
return isChatMode
? ALL_CHAT_AVAILABLE_BLOCKS
: ALL_COMPLETION_AVAILABLE_BLOCKS.filter(type => type !== BlockEnum.End)
},
getAvailableNextNodes(isChatMode: boolean) {
return isChatMode ? ALL_CHAT_AVAILABLE_BLOCKS : ALL_COMPLETION_AVAILABLE_BLOCKS
},
checkValid(payload: VannaNodeType) {
let isValid = true
let errorMessages = ''
if (!payload.query || payload.query.length === 0){
errorMessages = '输入变量不能为空'
}
return {
isValid,
errorMessage: errorMessages,
}
},
}
export default nodeDefault

@ -0,0 +1,35 @@
import type { FC } from 'react'
import React from 'react'
import type { VannaNodeType } from './types'
import type { NodeProps } from '@/app/components/workflow/types'
import ModelSelector from "@/app/components/header/account-setting/model-provider-page/model-selector";
import {
useTextGenerationCurrentProviderAndModelAndModelList
} from "@/app/components/header/account-setting/model-provider-page/hooks";
const Node: FC<NodeProps<VannaNodeType>> = ({
data,
}) => {
const { provider, name: modelId } = data.model || {}
const {
textGenerationModelList,
} = useTextGenerationCurrentProviderAndModelAndModelList()
const hasSetModel = provider && modelId
return (
<div className='mb-1 space-y-0.5 px-3 py-1'>
{hasSetModel && (
<ModelSelector
defaultModel={{ provider, model: modelId }}
modelList={textGenerationModelList}
triggerClassName='!h-6 !rounded-md'
readonly
/>
)}
</div>
)
}
export default React.memo(Node)

@ -0,0 +1,155 @@
import React, {FC} from 'react'
import {useTranslation} from 'react-i18next'
import useConfig from './use-config'
import type {VannaNodeType} from './types'
import Field from '@/app/components/workflow/nodes/_base/components/field'
import {InputVarType, NodePanelProps} from '@/app/components/workflow/types'
import ModelParameterModal from "@/app/components/header/account-setting/model-provider-page/model-parameter-modal";
import VarReferencePicker from "@/app/components/workflow/nodes/_base/components/variable/var-reference-picker";
import BeforeRunForm from "@/app/components/workflow/nodes/_base/components/before-run-form";
import ResultPanel from "@/app/components/workflow/run/result-panel";
import type {Props as FormProps} from "@/app/components/workflow/nodes/_base/components/before-run-form/form";
import {findVariableWhenOnLLMVision} from "@/app/components/workflow/nodes/utils";
import Split from "@/app/components/workflow/nodes/_base/components/split";
import OutputVars, {VarItem} from "@/app/components/workflow/nodes/_base/components/output-vars";
const i18nPrefix = 'workflow.nodes.parameterExtractor'
const Panel: FC<NodePanelProps<VannaNodeType>> = (
{
id,
data,
}) => {
const {t} = useTranslation()
const {
readOnly,
inputs,
handleCompletionParamsChange,
handleInputVarChange,
handleModelChanged,
isShowSingleRun,
hideSingleRun,
handleRun,
handleStop,
runResult,
runningStatus,
filterVar,
varInputs,
inputVarValues,
setInputVarValues,
isVisionModel,
availableVisionVars,
visionFiles,
setVisionFiles,
} = useConfig(id, data)
const model = inputs.model
const singleRunForms = (() => {
const forms: FormProps[] = []
forms.push(
{
label: t('workflow.nodes.llm.singleRun.variable')!,
inputs: [{
label: t(`${i18nPrefix}.inputVar`)!,
variable: 'query',
type: InputVarType.paragraph,
required: true,
}, ...varInputs],
values: inputVarValues,
onChange: setInputVarValues,
},
)
if (isVisionModel && data.vision?.enabled && data.vision?.configs?.variable_selector) {
const currentVariable = findVariableWhenOnLLMVision(data.vision.configs.variable_selector, availableVisionVars)
forms.push(
{
label: t('workflow.nodes.llm.vision')!,
inputs: [{
label: currentVariable?.variable as any,
variable: '#files#',
type: currentVariable?.formType as any,
required: false,
}],
values: {'#files#': visionFiles},
onChange: keyValue => setVisionFiles((keyValue as any)['#files#']),
},
)
}
return forms
})()
return (
<div className='mt-2'>
<div className='space-y-4 px-4 pb-4'>
<Field
title={'模型'}
>
<ModelParameterModal
popupClassName='!w-[387px]'
isInWorkflow
isAdvancedMode={true}
mode={model?.mode}
provider={model?.provider}
completionParams={model?.completion_params}
modelId={model?.name}
setModel={handleModelChanged}
onCompletionParamsChange={handleCompletionParamsChange}
hideDebugWithMultipleModel
debugWithMultipleModel={false}
readonly={readOnly}
/>
</Field>
<Field
title={'输入变量'}
>
<VarReferencePicker
readonly={readOnly}
nodeId={id}
isShowNodeName
value={inputs.query || []}
onChange={handleInputVarChange}
filterVar={filterVar}
/>
</Field>
</div>
<Split />
<div>
<OutputVars>
<VarItem
name='output'
type='string'
description={'查询出结果的数据'}
/>
<VarItem
name='sql'
type='string'
description={'生成的sql语句'}
/>
</OutputVars>
</div>
{isShowSingleRun && (
<BeforeRunForm
nodeName={inputs.title}
onHide={hideSingleRun}
forms={singleRunForms}
runningStatus={runningStatus}
onRun={handleRun}
onStop={handleStop}
result={<ResultPanel {...runResult} showSteps={false}/>}
/>
)}
</div>
)
}
export default React.memo(Panel)

@ -0,0 +1,20 @@
import type {
CommonNodeType,
Memory,
ModelConfig, ValueSelector,
VisionSetting
} from '@/app/components/workflow/types'
import {Param, ReasoningModeType} from "@/app/components/workflow/nodes/parameter-extractor/types";
export type VannaNodeType = CommonNodeType & {
model: ModelConfig
query: ValueSelector
instruction: string
reasoning_mode: ReasoningModeType
parameters: Param[]
memory?: Memory
vision: {
enabled: boolean
configs?: VisionSetting
}
}

@ -0,0 +1,162 @@
import type {VannaNodeType} from './types'
import useNodeCrud from '@/app/components/workflow/nodes/_base/hooks/use-node-crud'
import {
useNodesReadOnly,
} from '@/app/components/workflow/hooks'
import {useCallback, useRef, useState} from "react";
import produce from "immer";
import {ValueSelector, Var, VarType} from "@/app/components/workflow/types";
import useOneStepRun from "@/app/components/workflow/nodes/_base/hooks/use-one-step-run";
import useConfigVision from "@/app/components/workflow/hooks/use-config-vision";
import useAvailableVarList from "@/app/components/workflow/nodes/_base/hooks/use-available-var-list";
const useConfig = (id: string, payload: VannaNodeType) => {
const {nodesReadOnly: readOnly} = useNodesReadOnly()
const {inputs, setInputs} = useNodeCrud<VannaNodeType>(id, payload)
const inputRef = useRef(inputs)
const [modelChanged, setModelChanged] = useState(false)
// model
const model = inputs.model || {
provider: '',
name: '',
mode: 'chat',
completion_params: {
temperature: 0.7,
},
}
const modelMode = inputs.model?.mode
// single run
const {
isShowSingleRun,
hideSingleRun,
getInputVars,
runningStatus,
handleRun,
handleStop,
runInputData,
runInputDataRef,
setRunInputData,
runResult,
} = useOneStepRun<VannaNodeType>({
id,
data: inputs,
defaultRunInputData: {
'query': '',
'#files#': [],
},
})
const {
isVisionModel,
handleVisionResolutionEnabledChange,
handleVisionResolutionChange,
handleModelChanged: handleVisionConfigAfterModelChanged,
} = useConfigVision(model, {
payload: inputs.vision,
onChange: (newPayload) => {
const newInputs = produce(inputs, (draft) => {
draft.vision = newPayload
})
setInputs(newInputs)
},
})
const filterVisionInputVar = useCallback((varPayload: Var) => {
return [VarType.file, VarType.arrayFile].includes(varPayload.type)
}, [])
const {
availableVars: availableVisionVars,
} = useAvailableVarList(id, {
onlyLeafNodeVar: false,
filterVar: filterVisionInputVar,
})
const handleModelChanged = useCallback((model: { provider: string; modelId: string; mode?: string }) => {
const newInputs = produce(inputRef.current, (draft) => {
draft.model.provider = model.provider
draft.model.name = model.modelId
draft.model.mode = model.mode!
})
setInputs(newInputs)
setModelChanged(true)
}, [setInputs])
const handleCompletionParamsChange = useCallback((newParams: Record<string, any>) => {
debugger
const newInputs = produce(inputs, (draft) => {
draft.model.completion_params = newParams
})
setInputs(newInputs)
}, [inputs, setInputs])
const handleInputVarChange = useCallback((newInputVar: ValueSelector | string) => {
const newInputs = produce(inputs, (draft) => {
draft.query = newInputVar as ValueSelector || []
})
setInputs(newInputs)
}, [inputs, setInputs])
const filterVar = useCallback((varPayload: Var) => {
return [VarType.string].includes(varPayload.type)
}, [])
const varInputs = getInputVars([inputs.instruction])
const inputVarValues = (() => {
const vars: Record<string, any> = {}
Object.keys(runInputData)
.forEach((key) => {
vars[key] = runInputData[key]
})
return vars
})()
const setInputVarValues = useCallback((newPayload: Record<string, any>) => {
setRunInputData(newPayload)
}, [setRunInputData])
const visionFiles = runInputData['#files#']
const setVisionFiles = useCallback((newFiles: any[]) => {
setRunInputData({
...runInputDataRef.current,
'#files#': newFiles,
})
}, [runInputDataRef, setRunInputData])
return {
readOnly,
inputs,
filterVar,
isShowSingleRun,
hideSingleRun,
getInputVars,
runningStatus,
handleRun,
handleStop,
runInputData,
runInputDataRef,
setRunInputData,
isVisionModel,
runResult,
varInputs,
inputVarValues,
setInputVarValues,
handleModelChanged,
handleInputVarChange,
handleCompletionParamsChange,
availableVisionVars,
visionFiles,
setVisionFiles
}
}
export default useConfig

@ -0,0 +1,5 @@
import type {VannaNodeType} from './types'
export const checkNodeValid = (payload: VannaNodeType) => {
return true
}

@ -41,6 +41,7 @@ export enum BlockEnum {
Loop = 'loop', Loop = 'loop',
LoopStart = 'loop-start', LoopStart = 'loop-start',
LoopEnd = 'loop-end', LoopEnd = 'loop-end',
Vanna = 'vanna',
} }
export enum ControlMode { export enum ControlMode {

@ -32,6 +32,7 @@ export const canRunBySingle = (nodeType: BlockEnum) => {
|| nodeType === BlockEnum.Agent || nodeType === BlockEnum.Agent
|| nodeType === BlockEnum.DocExtractor || nodeType === BlockEnum.DocExtractor
|| nodeType === BlockEnum.Loop || nodeType === BlockEnum.Loop
|| nodeType === BlockEnum.Vanna
} }
type ConnectedSourceOrTargetNodesChange = { type ConnectedSourceOrTargetNodesChange = {

@ -242,6 +242,7 @@ const translation = {
'code': '代码执行', 'code': '代码执行',
'template-transform': '模板转换', 'template-transform': '模板转换',
'http-request': 'HTTP 请求', 'http-request': 'HTTP 请求',
'vanna': 'Vanna',
'variable-assigner': '变量赋值器', 'variable-assigner': '变量赋值器',
'variable-aggregator': '变量聚合器', 'variable-aggregator': '变量聚合器',
'assigner': '变量赋值', 'assigner': '变量赋值',
@ -576,8 +577,8 @@ const translation = {
'not empty': '不为空', 'not empty': '不为空',
'null': '空', 'null': '空',
'not null': '不为空', 'not null': '不为空',
'in': '', 'in': '',
'not in': '不', 'not in': '不',
'all of': '全部是', 'all of': '全部是',
'exists': '存在', 'exists': '存在',
'not exists': '不存在', 'not exists': '不存在',

Loading…
Cancel
Save