feat: 单节点运行改造
This commit is contained in:
@@ -429,6 +429,7 @@
|
||||
"createDescription": "Create your assistant by describing roles and tasks",
|
||||
"nextDescription": "Assistant can call multiple skills and tools",
|
||||
"searchAssistant": "Search for the assistant you need",
|
||||
"searchApp": "Search for the app you need",
|
||||
"manageAssistant": "Manage your assistant on this page, including online and offline activities, editing, and more",
|
||||
"establishAssistant": "Create Assistant",
|
||||
"assistantName": "Assistant Name",
|
||||
|
||||
@@ -426,6 +426,7 @@
|
||||
"createDescription": "通过描述角色和任务来创建你的助手",
|
||||
"nextDescription": "助手可以调用多个技能和工具",
|
||||
"searchAssistant": "搜索您需要的助手",
|
||||
"searchApp": "搜索您需要的应用",
|
||||
"manageAssistant": "在此页面管理您的助手,对助手上下线、编辑等等",
|
||||
"establishAssistant": "创建助手",
|
||||
"assistantName": "助手名称",
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
"systemModelSettings": "系统模型设置",
|
||||
"knowledgeBaseModel": "知识库模型",
|
||||
"assistantModel": "助手模型",
|
||||
"large models and RAG nodes": "评测模型",
|
||||
"evaluationModel": "评测模型",
|
||||
"workflowModel": "工作流模型",
|
||||
"defaultEmbeddingModel": "知识库默认embedding模型",
|
||||
"sourceTracingModel": "知识库溯源模型",
|
||||
|
||||
@@ -180,6 +180,20 @@ export const updateEvaluationModelConfig = async (data: any): Promise<any> => {
|
||||
return await axios.post(`/api/v1/llm/evaluation`, data)
|
||||
}
|
||||
|
||||
/**
|
||||
* llm 助手节点默认模型
|
||||
*/
|
||||
export async function setLlmDefaultModel(data: { model_id: string }): Promise<any> {
|
||||
return await axios.post(`/api/v1/llm/workflow`, data)
|
||||
}
|
||||
|
||||
/**
|
||||
* llm 助手节点默认模型
|
||||
*/
|
||||
export async function getLlmDefaultModel(): Promise<any> {
|
||||
return await axios.get(`/api/v1/llm/workflow`)
|
||||
}
|
||||
|
||||
// 获取助手模型可选列表
|
||||
export const getAssistantModelList = async (): Promise<any> => {
|
||||
return await axios.get(`/api/v1/llm/assistant/llm_list`)
|
||||
|
||||
@@ -341,4 +341,5 @@ export async function changeCurrentVersion({ flow_id, version_id }: { flow_id: s
|
||||
*/
|
||||
export async function runTestCase(data: { question_list, version_list, node_id, inputs }): Promise<any[]> {
|
||||
return await axios.post(`/api/v1/flows/compare`, data);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -60,7 +60,7 @@ export const onlineWorkflow = async (flow, status = ''): Promise<any> => {
|
||||
/**
|
||||
* 上线指定版本工作流
|
||||
*/
|
||||
export const onlineWorkflowApi = async (data: {flow_id, version_id, status}) => {
|
||||
export const onlineWorkflowApi = async (data: { flow_id, version_id, status }) => {
|
||||
return await axios.patch(`/api/v1/workflow/status`, data);
|
||||
};
|
||||
|
||||
@@ -281,14 +281,14 @@ const workflowTemplate = [
|
||||
"key": "system_prompt",
|
||||
"label": "系统提示词",
|
||||
"type": "var_textarea",
|
||||
"test": "input",
|
||||
"test": "var",
|
||||
"value": ""
|
||||
},
|
||||
{
|
||||
"key": "user_prompt",
|
||||
"label": "用户提示词",
|
||||
"type": "var_textarea",
|
||||
"test": "input",
|
||||
"test": "var",
|
||||
"value": "",
|
||||
"required": true
|
||||
}
|
||||
@@ -381,7 +381,7 @@ const workflowTemplate = [
|
||||
"key": "system_prompt",
|
||||
"label": "系统提示词",
|
||||
"type": "var_textarea",
|
||||
"test": "input",
|
||||
"test": "var",
|
||||
"value": "",
|
||||
"placeholder": "助手画像",
|
||||
"required": true
|
||||
@@ -390,7 +390,7 @@ const workflowTemplate = [
|
||||
"key": "user_prompt",
|
||||
"label": "用户提示词",
|
||||
"type": "var_textarea",
|
||||
"test": "input",
|
||||
"test": "var",
|
||||
"value": "",
|
||||
"placeholder": "用户消息内容",
|
||||
"required": true
|
||||
@@ -488,7 +488,7 @@ const workflowTemplate = [
|
||||
"key": "user_question",
|
||||
"label": "输入变量",
|
||||
"type": "var_select",
|
||||
"test": "input",
|
||||
"test": "var",
|
||||
"value": "",
|
||||
"required": true,
|
||||
"placeholder": "请选择检索问题"
|
||||
@@ -541,9 +541,9 @@ const workflowTemplate = [
|
||||
{
|
||||
"key": "user_question",
|
||||
"label": "用户问题",
|
||||
"global": "self=system_prompt,user_prompt",
|
||||
"global": "self=user_prompt",
|
||||
"type": "user_question",
|
||||
"test": "input",
|
||||
"test": "var",
|
||||
"help": "当选择多个问题时,将会多次运行本节点,每次运行时从批量问题中取一项进行处理。",
|
||||
"linkage": "output_user_input",
|
||||
"value": [],
|
||||
@@ -551,9 +551,8 @@ const workflowTemplate = [
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"key": "retrieved_result",
|
||||
"key": "knowledge",
|
||||
"label": "检索范围",
|
||||
"global": "self=system_prompt,user_prompt",
|
||||
"type": "knowledge_select_multi",
|
||||
"placeholder": "请选择知识库",
|
||||
"value": {
|
||||
@@ -575,6 +574,12 @@ const workflowTemplate = [
|
||||
"type": "number",
|
||||
"value": 15000,
|
||||
"help": "通过此参数控制最终传给模型的知识库检索结果文本长度,超过模型支持的最大上下文长度可能会导致报错。"
|
||||
},
|
||||
{
|
||||
"key": "retrieved_result",
|
||||
"label": "检索结果",
|
||||
"type": "var",
|
||||
"global": "self=user_prompt"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -593,6 +598,7 @@ const workflowTemplate = [
|
||||
"label": "用户提示词",
|
||||
"type": "var_textarea",
|
||||
"value": "用户问题:{{#user_question#}}\n参考文本:{{#retrieved_result#}}\n你的回答:",
|
||||
"test": "var",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
|
||||
@@ -162,7 +162,7 @@ export default function apps() {
|
||||
return <div className="h-full relative">
|
||||
<div className="px-10 py-10 h-full overflow-y-scroll scrollbar-hide relative bg-background-main border-t">
|
||||
<div className="flex gap-4">
|
||||
<SearchInput className="w-64" placeholder="搜索您需要的应用" onChange={(e) => search(e.target.value)}></SearchInput>
|
||||
<SearchInput className="w-64" placeholder={t('build.searchApp')} onChange={(e) => search(e.target.value)}></SearchInput>
|
||||
<SelectType all onChange={(v) => {
|
||||
tempTypeRef.current = v
|
||||
filterData({ type: v })
|
||||
|
||||
@@ -18,7 +18,7 @@ export default function editAssistant() {
|
||||
const { id: assisId } = useParams()
|
||||
const navigate = useNavigate()
|
||||
// assistant data
|
||||
const { assistantState, changed, loadAssistantState, saveAfter, destroy } = useAssistantStore()
|
||||
const { assistantState, changed, loadAssistantState,changeStatus, saveAfter, destroy } = useAssistantStore()
|
||||
const { startNewRound, insetSystemMsg, insetBsMsg, destory, setShowGuideQuestion } = useMessageStore()
|
||||
|
||||
useEffect(() => {
|
||||
@@ -81,8 +81,10 @@ export default function editAssistant() {
|
||||
navigate('/build')
|
||||
}, 1200);
|
||||
} else {
|
||||
// TODO 下线接口
|
||||
// 更新 assistantState.status = 1
|
||||
captureAndAlertRequestErrorHoc(changeAssistantStatusApi(assistantState.id, 1)).then(res => {
|
||||
if (res === false) return
|
||||
changeStatus(1)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -313,7 +313,9 @@ export default function ChatInput({ autoRun, clear, form, wsUrl, onBeforSend, on
|
||||
const isError = !!data.message.reason
|
||||
const event = new CustomEvent('nodeLogEvent', {
|
||||
detail: {
|
||||
nodeId: node_id, action: isError ? '' : data.type === 'start' ? 'loading' : 'success', data: isError ? { 'error': data.message.reason } : data.message.log_data
|
||||
nodeId: node_id,
|
||||
action: isError ? '' : data.type === 'start' ? 'loading' : 'success',
|
||||
data: isError ? { 'error': data.message.reason } : data.message.log_data
|
||||
}
|
||||
})
|
||||
window.dispatchEvent(event)
|
||||
|
||||
@@ -42,7 +42,7 @@ export const ChatTest = forwardRef((props, ref) => {
|
||||
|
||||
const host = appConfig.websocketHost || ''
|
||||
return <div
|
||||
className={`${small ? 'bottom-2 right-4 w-52' : 'w-1/2 h-full right-0 bottom-0'} transition-all fixed rounded-2xl bg-[#fff] z-10 border shadow-sm overflow-hidden`}
|
||||
className={`${small ? 'bottom-2 right-4 w-52' : 'w-1/2 h-full right-0 bottom-0'} transition-all fixed rounded-2xl bg-[#fff] dark:bg-[#1B1B1B] z-10 border shadow-sm overflow-hidden`}
|
||||
>
|
||||
<div className="flex justify-between items-center bg-background-main px-4 py-1">
|
||||
<span className="text-sm font-bold">工作流预览</span>
|
||||
|
||||
@@ -165,7 +165,7 @@ export default function MessageBsChoose({ type = 'choose', logo, data }: { type?
|
||||
: <div>
|
||||
{data.message.options.map(opt => <div
|
||||
key={opt.id}
|
||||
className="min-w-56 bg-[#fff] rounded-xl p-4 mt-2 hover:bg-gray-200 cursor-pointer flex justify-between"
|
||||
className="min-w-56 bg-[#fff] dark:bg-background rounded-xl p-4 mt-2 hover:bg-gray-200 cursor-pointer flex justify-between"
|
||||
onClick={() => handleSelect(opt)}
|
||||
>
|
||||
{opt.label}
|
||||
|
||||
@@ -56,9 +56,9 @@ export default function MessageUser({ mark = false, useName = '', data, onMarkCl
|
||||
</div> : (!Array.isArray(data.message) && <div className="flex justify-between mt-2">
|
||||
<span></span>
|
||||
<div className="flex gap-0.5 text-gray-400 cursor-pointer self-end">
|
||||
{!running && <SquarePen className="size-6 p-1 hover:text-gray-500" onClick={() => handleResend(false)} />}
|
||||
{/* {!running && <SquarePen className="size-6 p-1 hover:text-gray-500" onClick={() => handleResend(false)} />}
|
||||
{!running && <RefreshCw className="size-6 p-1 hover:text-gray-500" onClick={() => handleResend(true)} />}
|
||||
{appConfig.dialogQuickSearch && <Search className="size-6 p-1 hover:text-gray-500" onClick={handleSearch} />}
|
||||
{appConfig.dialogQuickSearch && <Search className="size-6 p-1 hover:text-gray-500" onClick={handleSearch} />} */}
|
||||
</div>
|
||||
</div>)
|
||||
}
|
||||
|
||||
@@ -95,8 +95,12 @@ export const useMessageStore = create<State & Actions>((set, get) => ({
|
||||
set((state) => {
|
||||
let newChat = cloneDeep(state.messages);
|
||||
const { category, flow_id, chat_id, message_id, files, is_bot, extra, liked, message, receiver, type, source, user_id } = data
|
||||
// 删除与历史消息中message_id相同的消息,则删除
|
||||
newChat = newChat.filter((item => !(item.message_id === message_id && item.his)))
|
||||
newChat.push({
|
||||
category, flow_id, chat_id, message_id, files, is_bot,
|
||||
category, flow_id, chat_id,
|
||||
message_id: message_id,
|
||||
files, is_bot,
|
||||
message, receiver, source, user_id,
|
||||
liked: !!liked,
|
||||
end: type === 'over',
|
||||
@@ -172,7 +176,7 @@ export const useMessageStore = create<State & Actions>((set, get) => ({
|
||||
async loadHistoryMsg(flowid, chatId, { lastMsg }) {
|
||||
const res = await getChatHistory(flowid, chatId, 30, 0)
|
||||
const msgs = handleHistoryMsg(res)
|
||||
const hisMessages = msgs.reverse()
|
||||
const hisMessages = msgs.map(el => ({ ...el, his: true })).reverse()
|
||||
currentChatId = chatId
|
||||
if (msgs.length && lastMsg) {
|
||||
hisMessages.push({
|
||||
@@ -191,6 +195,8 @@ export const useMessageStore = create<State & Actions>((set, get) => ({
|
||||
return msgs
|
||||
},
|
||||
|
||||
|
||||
|
||||
// stream end old
|
||||
updateCurrentMessage(data) {
|
||||
const messages = get().messages
|
||||
|
||||
@@ -33,7 +33,7 @@ export default function NodeLogo({ type, className = '', colorStr = '' }) {
|
||||
if (type === 'tool') {
|
||||
const keys = Object.keys(Colors)
|
||||
const _colorKey = keys[parseInt(colorStr.charCodeAt(0) + '', 16) % keys.length]
|
||||
return <div className={cname(`${Colors[_colorKey]} p-[5px] rounded-md`, className)}><Hammer size={14} /></div>
|
||||
return <div className={cname(`${Colors[_colorKey]} p-[5px] rounded-md dark:text-gray-600`, className)}><Hammer size={14} /></div>
|
||||
}
|
||||
|
||||
const IconComp = Icons[type] || Hammer
|
||||
|
||||
@@ -44,7 +44,7 @@ const Log = ({ type, name, data }) => {
|
||||
</span>
|
||||
</div>
|
||||
{open && (
|
||||
<div className="absolute top-0 left-full w-96 rounded-lg shadow-lg p-2 bg-[#F7F8FB] z-10">
|
||||
<div className="absolute top-0 left-full w-96 rounded-lg shadow-lg p-2 bg-[#F7F8FB] dark:bg-[#303134] z-10">
|
||||
<div className="flex justify-between items-center mb-2">
|
||||
<div className="relative z-10 flex gap-2">
|
||||
<NodeLogo type={type} colorStr={name} />
|
||||
@@ -72,22 +72,40 @@ export default function RunLog({ node, children }) {
|
||||
const { nodeId, action, data } = e.detail
|
||||
if (nodeId !== node.id && nodeId !== '*') return
|
||||
|
||||
if (data && Object.keys(data).length) {
|
||||
if (data) {
|
||||
const newData = data.reduce((res, item) => {
|
||||
if (item.type === 'variable') {
|
||||
const key = item.key.split('.')
|
||||
res[key[key.length - 1]] = item.value
|
||||
} else {
|
||||
res[item.key] = item.value
|
||||
}
|
||||
return res
|
||||
}, {})
|
||||
let result = {};
|
||||
let hasKeys = []
|
||||
|
||||
node.group_params.forEach(group => {
|
||||
group.params.forEach(param => {
|
||||
if (data[param.key] !== undefined) {
|
||||
result[param.label] = data[param.key];
|
||||
if (newData[param.key] !== undefined) {
|
||||
result[param.label || param.key] = newData[param.key];
|
||||
hasKeys.push(param.key)
|
||||
} else if (param.key === 'tool_list') {
|
||||
// tool
|
||||
param.value.some(p => {
|
||||
if (newData[p.tool_key] !== undefined) {
|
||||
result[p.label] = newData[p.tool_key];
|
||||
hasKeys.push(p.tool_key)
|
||||
return true
|
||||
}
|
||||
})
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
for (let key in data) {
|
||||
for (let key in newData) {
|
||||
if (!hasKeys.includes(key)) {
|
||||
result[key] = data[key];
|
||||
result[key] = newData[key];
|
||||
}
|
||||
}
|
||||
setData(result)
|
||||
@@ -111,7 +129,7 @@ export default function RunLog({ node, children }) {
|
||||
<span className='text-sm text-primary'>运行中</span>
|
||||
</div>
|
||||
|
||||
if (state === Status.success) return < div className='bisheng-node-top flex justify-between bg-[#E6FBF1] [#FCEAEA]' >
|
||||
if (state === Status.success) return < div className='bisheng-node-top flex justify-between bg-[#E6FBF1] dark:bg-[#303134]' >
|
||||
<div className='flex items-center gap-2 text-sm'>
|
||||
<div className='rounded-full w-4 h-4 bg-[#00C78C] text-gray-50 flex items-center justify-center'><Check size={14} /></div>
|
||||
<span>运行成功</span>
|
||||
@@ -119,7 +137,7 @@ export default function RunLog({ node, children }) {
|
||||
{!noLog && <Log type={node.type} name={node.name} data={data} />}
|
||||
</div>
|
||||
|
||||
return <div className='bisheng-node-top flex justify-between bg-[#FCEAEA]'>
|
||||
return <div className='bisheng-node-top flex justify-between bg-[#FCEAEA] dark:bg-[#303134]'>
|
||||
<div className='flex items-center gap-2 text-sm'>
|
||||
<div className='rounded-full w-4 h-4 bg-[#F04438] text-gray-50 flex items-center justify-center'><X size={14} /></div>
|
||||
<span>运行失败</span>
|
||||
|
||||
@@ -45,7 +45,7 @@ export const ResultText = ({ title, value }: { title: string, value: any }) => {
|
||||
<p>{title}</p>
|
||||
{copyed ? <CopyCheck size={14} /> : <Copy size={14} className="cursor-pointer" onClick={handleCopy} />}
|
||||
</div>
|
||||
<textarea defaultValue={text} disabled className="w-full min-h-28 p-2 block text-muted-foreground " />
|
||||
<textarea defaultValue={text} disabled className="w-full min-h-28 p-2 block text-muted-foreground dark:bg-black " />
|
||||
</div>
|
||||
}
|
||||
|
||||
@@ -71,17 +71,40 @@ export const RunTest = forwardRef((props, ref) => {
|
||||
node.group_params.forEach((group) => {
|
||||
group.params.forEach((param) => {
|
||||
if (param.test === 'input') {
|
||||
if (param.type === 'code_input') {
|
||||
// code_input类型特殊处理
|
||||
return param.value.forEach(val => {
|
||||
setInputs((prev) => {
|
||||
return [...prev, { key: val.key, required: false, label: val.key, value: '' }]
|
||||
})
|
||||
if (node.type === "tool") {
|
||||
return setInputs((prev) => {
|
||||
return [...prev, { key: node.tool_key, required: false, label: param.label, value: '' }]
|
||||
})
|
||||
}
|
||||
setInputs((prev) => {
|
||||
return [...prev, { key: param.key, required: !!param.required, label: param.label || param.key, value: '' }]
|
||||
// if (param.type === 'code_input') {
|
||||
// code_input类型特殊处理
|
||||
return param.value.forEach(val => {
|
||||
setInputs((prev) => {
|
||||
return [...prev, { key: val.key, required: false, label: val.key, value: '' }]
|
||||
})
|
||||
})
|
||||
} else if (param.test === 'var') {
|
||||
let allVarInput = []
|
||||
if (param.type === 'var_textarea') {
|
||||
const regex = /{{#(.*?)#}}/g;
|
||||
const parts = param.value.split(regex);
|
||||
allVarInput = parts.reduce((res, part, index) => {
|
||||
if (index % 2 === 1) {
|
||||
res.push({ key: part, required: false, label: param.varZh?.[part] || part, value: '' })
|
||||
}
|
||||
return res
|
||||
}, [])
|
||||
} else if (param.type === 'var_select') {
|
||||
allVarInput = [{ key: param.value, required: false, label: param.varZh?.[param.value] || param.value, value: '' }]
|
||||
} else if (param.type === 'user_question') {
|
||||
allVarInput = param.value.map(part =>
|
||||
({ key: part, required: false, label: param.varZh?.[part] || part, value: '' })
|
||||
)
|
||||
}
|
||||
|
||||
setInputs(prev => [...prev,
|
||||
// 非本节点
|
||||
...allVarInput.filter(input => !input.key.startsWith(node.id))])
|
||||
}
|
||||
})
|
||||
})
|
||||
@@ -108,7 +131,7 @@ export const RunTest = forwardRef((props, ref) => {
|
||||
}, {}),
|
||||
node
|
||||
).then(res => {
|
||||
const result = Object.keys(res).map(key => ({ title: key, text: res[key] }))
|
||||
const result = res.map(item => ({ title: item.key, text: item.value }))
|
||||
setResults(result)
|
||||
setLoading(false)
|
||||
})
|
||||
@@ -130,7 +153,7 @@ export const RunTest = forwardRef((props, ref) => {
|
||||
单节点运行
|
||||
</SheetTitle>
|
||||
</SheetHeader>
|
||||
<div className="px-2 pt-2 pb-10 h-[calc(100vh-40px)] overflow-y-auto bg-[#fff]">
|
||||
<div className="px-2 pt-2 pb-10 h-[calc(100vh-40px)] overflow-y-auto bg-[#fff] dark:bg-[#303134]">
|
||||
{
|
||||
inputs.map((input) => <div className="mb-2" key={input.key}>
|
||||
<Label className="flex items-center bisheng-label mb-2">
|
||||
|
||||
@@ -22,7 +22,7 @@ const Item = ({ nodeId, validate, sameKey, item, index, onUpdateItem, onDeleteIt
|
||||
|
||||
useEffect(() => {
|
||||
if (!validate) return setError(false);
|
||||
if (item.key === '' || !/^[a-zA-Z0-9_]{1,50}$/.test(item.key)) {
|
||||
if (item.key === '' || !/^[a-zA-Z_][a-zA-Z0-9_]{1,50}$/.test(item.key)) {
|
||||
setError(true);
|
||||
} else {
|
||||
setError(false);
|
||||
@@ -100,8 +100,8 @@ export default function CodeInputItem({ nodeId, data, onValidate, onChange }) {
|
||||
if (item.key === '') {
|
||||
msg = '变量名称不能为空'
|
||||
return true
|
||||
} else if (!/^[a-zA-Z0-9_]*$/.test(item.key)) {
|
||||
msg = '变量名称只能包含英文字符、数字和下划线'
|
||||
} else if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(item.key)) {
|
||||
msg = '变量名称只能包含英文字符、数字和下划线,且不能以数字开头'
|
||||
return true
|
||||
} else if (item.key.length > 50) {
|
||||
msg = '变量名称不能超过 50 个字符'
|
||||
|
||||
@@ -34,14 +34,14 @@ const Item = ({ item, index, validate, onUpdateItem, onDeleteItem }) => {
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectGroup>
|
||||
<SelectItem value="int">int</SelectItem>
|
||||
{/* <SelectItem value="int">int</SelectItem>
|
||||
<SelectItem value="float">float</SelectItem>
|
||||
<SelectItem value="complex">complex</SelectItem>
|
||||
<SelectItem value="bool">bool</SelectItem>
|
||||
<SelectItem value="NoneType">NoneType</SelectItem>
|
||||
<SelectItem value="NoneType">NoneType</SelectItem> */}
|
||||
<SelectItem value="str">str</SelectItem>
|
||||
<SelectItem value="list">list</SelectItem>
|
||||
<SelectItem value="tuple">tuple</SelectItem>
|
||||
{/* <SelectItem value="tuple">tuple</SelectItem>
|
||||
<SelectItem value="dict">dict</SelectItem>
|
||||
<SelectItem value="set">set</SelectItem>
|
||||
<SelectItem value="frozenset">frozenset</SelectItem>
|
||||
@@ -49,7 +49,7 @@ const Item = ({ item, index, validate, onUpdateItem, onDeleteItem }) => {
|
||||
<SelectItem value="bytes">bytes</SelectItem>
|
||||
<SelectItem value="bytearray">bytearray</SelectItem>
|
||||
<SelectItem value="memoryview">memoryview</SelectItem>
|
||||
<SelectItem value="function">function</SelectItem>
|
||||
<SelectItem value="function">function</SelectItem> */}
|
||||
</SelectGroup>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
@@ -89,22 +89,27 @@ export default function CodeOutputItem({ data, onChange, onValidate }) {
|
||||
}, 100);
|
||||
|
||||
let msg = ''
|
||||
const nameSet = new Set()
|
||||
items.some(item => {
|
||||
if (item.key === '') {
|
||||
msg = '变量名称不能为空'
|
||||
return true
|
||||
} else if (!/^[a-zA-Z0-9_]*$/.test(item.key)) {
|
||||
msg = '变量名称只能包含英文字符、数字和下划线'
|
||||
return true
|
||||
} else if (nameSet.has(item.key)) {
|
||||
msg = '变量名已存在';
|
||||
return true;
|
||||
} else if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(item.key)) {
|
||||
msg = '变量名称只能包含英文字符、数字和下划线,且不能以数字开头';
|
||||
return true;
|
||||
} else if (item.key.length > 50) {
|
||||
msg = '变量名称不能超过 50 个字符'
|
||||
return true
|
||||
}
|
||||
nameSet.add(item.key)
|
||||
})
|
||||
return msg || false
|
||||
})
|
||||
|
||||
return () => onValidate(() => {})
|
||||
return () => onValidate(() => { })
|
||||
}, [data.value])
|
||||
|
||||
return (
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Label } from "@/components/bs-ui/label";
|
||||
import Cascader from "@/components/bs-ui/select/cascader";
|
||||
import { getAssistantModelList, getModelListApi } from "@/controllers/API/finetune";
|
||||
import { getAssistantModelList, getLlmDefaultModel, getModelListApi } from "@/controllers/API/finetune";
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
|
||||
export default function ModelItem({ agent = false, data, onChange, onValidate }) {
|
||||
@@ -32,8 +32,10 @@ export default function ModelItem({ agent = false, data, onChange, onValidate })
|
||||
// return { llmOptions, embeddings }
|
||||
})
|
||||
|
||||
// TODO更新默认值
|
||||
// !data.value && onChange(60)
|
||||
// 更新默认值
|
||||
getLlmDefaultModel().then(res => {
|
||||
res && !data.value && onChange(res.model_id)
|
||||
})
|
||||
}, [])
|
||||
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ export default function ToolItem({ data, onChange }) {
|
||||
<ToolsSheet select={value} onSelect={(val) => {
|
||||
const newValue = [...value, val]
|
||||
setValue(newValue)
|
||||
onChange(newValue.map(el => ({ key: el.id, label: el.name })))
|
||||
onChange(newValue.map(el => ({ key: el.id, label: el.name, tool_key: el.tool_key })))
|
||||
}}>
|
||||
<Button onClick={() => { }} variant='outline' className="border-primary text-primary mt-2">
|
||||
{data.label}
|
||||
|
||||
@@ -92,7 +92,7 @@ export const useFileUpload = (_files, onFilesChange) => {
|
||||
const handleFileUpload = () => {
|
||||
const input = document.createElement("input");
|
||||
input.type = "file";
|
||||
input.accept = "image/*,application/pdf"; // Restrict to images
|
||||
input.accept = "image/*,application/pdf,text/plain,application/vnd.openxmlformats-officedocument.wordprocessingml.document,application/vnd.openxmlformats-officedocument.presentationml.presentation,text/markdown,text/html,application/vnd.ms-excel,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,application/msword,application/vnd.ms-powerpoint,.png,.jpg,.jpeg"; // Restrict to images
|
||||
input.style.display = "none";
|
||||
input.multiple = false;
|
||||
|
||||
|
||||
@@ -166,7 +166,7 @@ function CustomNode({ data: node, selected, isConnectable }: { data: WorkflowNod
|
||||
</NodeToolbar>
|
||||
|
||||
<div
|
||||
className={cname(`bisheng-node hover:border-primary/10 hover:border-primary/10 ${node.type === 'condition' ? 'w-auto min-w-80' : ''} ${selected ? 'border-primary/10' : ' border-transparent'}`, nodeError && 'border-red-500')}
|
||||
className={cname(`bisheng-node hover:border-primary/10 ${node.type === 'condition' ? 'w-auto min-w-80' : ''} ${selected ? 'border-primary/10' : ' border-transparent'}`, nodeError && 'border-red-500')}
|
||||
data-id={node.id}
|
||||
>
|
||||
{/* top */}
|
||||
|
||||
@@ -92,6 +92,9 @@ const Header = ({ flow, onTabChange, preFlow }) => {
|
||||
variant: 'success',
|
||||
description: `${version?.name} 已下线`
|
||||
})
|
||||
|
||||
flow.status = 1
|
||||
refrenshVersions()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -154,6 +157,7 @@ const Header = ({ flow, onTabChange, preFlow }) => {
|
||||
const [loading, setLoading] = useState(false)
|
||||
const { flow: f, setFlow, setFitView } = useFlowStore()
|
||||
const { versions, version, lastVersionIndexRef, isOnlineVersion, isOnlineVersionFun, changeName, deleteVersion, refrenshVersions, setCurrentVersion } = useVersion(flow)
|
||||
|
||||
// 切换版本
|
||||
const handleChangeVersion = async (versionId) => {
|
||||
setLoading(true)
|
||||
@@ -322,9 +326,9 @@ const Header = ({ flow, onTabChange, preFlow }) => {
|
||||
)}
|
||||
>{t('skills.saveVersion')}</ActionButton>
|
||||
}
|
||||
{isOnlineVersion ? <Button size="sm" className={`${!dark && 'bg-[#fff]'} h-8 px-6`} onClick={handleOfflineClick}>
|
||||
{isOnlineVersion ? <Button size="sm" className={`h-8 px-6`} onClick={handleOfflineClick}>
|
||||
下线
|
||||
</Button> : <Button size="sm" className={`${!dark && 'bg-[#fff]'} h-8 px-6`} onClick={handleOnlineClick}>
|
||||
</Button> : <Button size="sm" className={`h-8 px-6`} onClick={handleOnlineClick}>
|
||||
上线
|
||||
</Button>}
|
||||
<Popover open={open} onOpenChange={setOpen}>
|
||||
@@ -444,6 +448,7 @@ const useNodeEvent = (flow) => {
|
||||
*/
|
||||
const branchLines: { branch: string, nodeIds: { branch: string, nodeId: string }[], end: boolean }[] = []
|
||||
const nodeMap = {}
|
||||
const treeNodeIdSet = new Set()
|
||||
const startEdge = flow.edges.find(node => node.source.indexOf('start') === 0)
|
||||
if (!startEdge) return ['请先链接开始节点']
|
||||
const startNodeId = startEdge.source
|
||||
@@ -461,6 +466,7 @@ const useNodeEvent = (flow) => {
|
||||
const [source, target] = [edge.source.split('_')[0], edge.target.split('_')[0]]
|
||||
const _branchId = `${branchId}_${index}`
|
||||
const _nodeIds = [...nodeIds, { branch: _branchId, nodeId: edge.target, type: findOutType(edge.target) }]
|
||||
treeNodeIdSet.add(edge.target);
|
||||
|
||||
if (target === 'end') {
|
||||
// stop when loop or end
|
||||
@@ -480,6 +486,10 @@ const useNodeEvent = (flow) => {
|
||||
}
|
||||
|
||||
traverseTree(startNodeId, '0', [{ branch: '0', nodeId: startNodeId, type: '' }])
|
||||
|
||||
if (treeNodeIdSet.size !== flow.nodes.length - 1) {
|
||||
return ['存在未连接的节点,请检查连线是否完整。']
|
||||
}
|
||||
// console.log('flow :>> ', flow.edges, branchLines);
|
||||
|
||||
// 并行校验
|
||||
|
||||
@@ -56,7 +56,7 @@ export default function FlowPage() {
|
||||
|
||||
return (
|
||||
<div className="flow-page-positioning">
|
||||
{flow && <Panne flow={copyFlow} preFlow={preFlow} />}
|
||||
{copyFlow && <Panne flow={copyFlow} preFlow={preFlow} />}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ export default function CreateTemp({ flow, open, type, setOpen, onCreated }: { f
|
||||
useEffect(() => {
|
||||
open && setData({
|
||||
name: flow.name,
|
||||
description: flow.description
|
||||
description: flow.description || ''
|
||||
})
|
||||
}, [open])
|
||||
|
||||
@@ -37,7 +37,7 @@ export default function CreateTemp({ flow, open, type, setOpen, onCreated }: { f
|
||||
const { name, description } = data
|
||||
if (!name) errorlist.push(`请填写${labelName}名称`)
|
||||
if (name.length > 30) errorlist.push(`${labelName}名称过长,不要超过50字`)
|
||||
if (!description) errorlist.push(`加些描述能够快速让别人理解您创造的${labelName}`)
|
||||
if (!description && type === AppType.ASSISTANT) errorlist.push(`加些描述能够快速让别人理解您创造的${labelName}`)
|
||||
if (description.length > 200) errorlist.push(`${labelName}描述不可超过 200 字`)
|
||||
if (errorlist.length) message({
|
||||
variant: 'error',
|
||||
|
||||
@@ -85,9 +85,9 @@ export default function ChatPanne({ customWsHost = '', appendHistory = false, da
|
||||
|
||||
if (isV1) {
|
||||
const res = await loadFlowHistoryMsg(_flow.id, chatId, {
|
||||
lastMsg: '本轮会话已结束'
|
||||
lastMsg: ''
|
||||
});
|
||||
setAutoRun(!res.length);
|
||||
setAutoRun(true);
|
||||
} else {
|
||||
clearMsgs();
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ export function transformEvent(event: string): string {
|
||||
export function transformObjectType(object: string): string {
|
||||
switch(object) {
|
||||
case 'none': return '无'
|
||||
case 'workflow': return '工作流'
|
||||
case 'work_flow': return '工作流'
|
||||
case 'flow': return '技能'
|
||||
case 'assistant': return '助手'
|
||||
case 'knowledge': return '知识库'
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { LoadingIcon } from "@/components/bs-icons/loading";
|
||||
import { Button } from "@/components/bs-ui/button";
|
||||
import { Label } from "@/components/bs-ui/label";
|
||||
import { useToast } from "@/components/bs-ui/toast/use-toast";
|
||||
import { getEvaluationModelConfig, updateEvaluationModelConfig } from "@/controllers/API/finetune";
|
||||
import { getLlmDefaultModel, setLlmDefaultModel } from "@/controllers/API/finetune";
|
||||
import { captureAndAlertRequestErrorHoc } from "@/controllers/request";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { ModelSelect } from "./KnowledgeModel";
|
||||
import { LoadingIcon } from "@/components/bs-icons/loading";
|
||||
|
||||
export default function WorkflowModel({ llmOptions, onBack }) {
|
||||
const { t } = useTranslation('model')
|
||||
@@ -14,8 +14,7 @@ export default function WorkflowModel({ llmOptions, onBack }) {
|
||||
const [loading, setLoading] = useState(true)
|
||||
useEffect(() => {
|
||||
setLoading(true)
|
||||
// TODO 获取默认值
|
||||
getEvaluationModelConfig().then(res => {
|
||||
getLlmDefaultModel().then(res => {
|
||||
setSelectedModel(res.model_id)
|
||||
setLoading(false)
|
||||
})
|
||||
@@ -29,8 +28,8 @@ export default function WorkflowModel({ llmOptions, onBack }) {
|
||||
const data = {
|
||||
model_id: selectedModel
|
||||
};
|
||||
// TODO 更新
|
||||
captureAndAlertRequestErrorHoc(updateEvaluationModelConfig(data).then(res => {
|
||||
|
||||
captureAndAlertRequestErrorHoc(setLlmDefaultModel(data).then(res => {
|
||||
message({ variant: 'success', description: t('model.saveSuccess') })
|
||||
}));
|
||||
};
|
||||
|
||||
@@ -73,6 +73,14 @@ export const useAssistantStore = create<State & Actions>((set) => ({
|
||||
saveAfter() {
|
||||
set({ changed: false })
|
||||
},
|
||||
changeStatus(status) {
|
||||
set((state) => ({
|
||||
assistantState: {
|
||||
...state.assistantState,
|
||||
status: status
|
||||
}
|
||||
}));
|
||||
},
|
||||
destroy: () => {
|
||||
set({ assistantState: { ...assistantTemp } })
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user