feat: 单节点运行改造

This commit is contained in:
dolphin
2024-12-25 21:52:55 +08:00
parent 6d5be82921
commit cebc23ec0a
29 changed files with 172 additions and 74 deletions
+1
View File
@@ -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",
+1
View File
@@ -426,6 +426,7 @@
"createDescription": "通过描述角色和任务来创建你的助手",
"nextDescription": "助手可以调用多个技能和工具",
"searchAssistant": "搜索您需要的助手",
"searchApp": "搜索您需要的应用",
"manageAssistant": "在此页面管理您的助手,对助手上下线、编辑等等",
"establishAssistant": "创建助手",
"assistantName": "助手名称",
+1 -1
View File
@@ -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`)
+2 -1
View File
@@ -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);
}
}
+16 -10
View File
@@ -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
},
{
+1 -1
View File
@@ -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 } })
}