fix(web): ai prompt editor update

This commit is contained in:
zhaoying
2026-01-07 17:34:45 +08:00
parent 7a1131d8af
commit 72c27273e4
6 changed files with 98 additions and 27 deletions

View File

@@ -1144,6 +1144,8 @@ export const en = {
merge: 'Complete Aggregation',
vote: 'Key Information Extraction',
priority: 'Structured Integration',
addTool: 'Add Tool',
tool: 'Tool',
},
userMemory: {
userMemory: 'User Memory',
@@ -1201,6 +1203,7 @@ export const en = {
IMPLICIT_MEMORY: 'Implicit Memory',
EMOTIONAL_MEMORY: 'Emotional Memory',
EPISODIC_MEMORY: 'Episodic Memory',
FORGETTING_MANAGEMENT: 'Forgetting Management',
endUserProfile: 'Core Profile',
editEndUserProfile: 'Edit',
@@ -1833,6 +1836,7 @@ Memory Bear: After the rebellion, regional warlordism intensified for several re
status_code: 'Status Code',
max_attempts: 'Max Retry Attempts',
retry_interval: 'Retry Interval',
errorBranch: 'Error Branch',
},
'jinja-render': {
template: 'Code',

View File

@@ -633,6 +633,8 @@ export const zh = {
merge: '完整汇总',
vote: '关键信息提取',
priority: '结构化整合',
addTool: '添加工具',
tool: '工具',
},
// 角色管理相关翻译
role: {
@@ -1280,6 +1282,7 @@ export const zh = {
IMPLICIT_MEMORY: '隐性记忆',
EMOTIONAL_MEMORY: '情绪记忆',
EPISODIC_MEMORY: '情景记忆',
FORGETTING_MANAGEMENT: '遗忘',
endUserProfile: '核心档案',
editEndUserProfile: '编辑',
@@ -1933,6 +1936,7 @@ export const zh = {
status_code: '状态码',
max_attempts: '最大重试次数',
retry_interval: '重试间隔',
errorBranch: '异常分支',
},
'jinja-render': {
template: '代码',
@@ -2246,5 +2250,12 @@ export const zh = {
orderPayInfo: '支付信息',
create_time: '创建时间',
},
forgetDetail: {
title: '遗忘管理系统帮助AI智能管理记忆生命周期通过自动识别低价值记忆、设置遗忘策略和执行定期清理优化记忆库存储空间提升检索效率。',
overviewTitle: '核心指标概览',
totalMemory: '记忆总量',
MemoryHealth: '记忆健康度',
riskOfForgetting: '遗忘风险',
}
},
}

View File

@@ -19,7 +19,6 @@ import type {
MemoryConfig,
AiPromptModalRef,
Source,
ToolModalRef,
ToolOption
} from './types'
import type { Model } from '@/views/ModelManagement/types'
@@ -33,7 +32,6 @@ import { memoryConfigListUrl } from '@/api/memory'
import CustomSelect from '@/components/CustomSelect'
import aiPrompt from '@/assets/images/application/aiPrompt.png'
import AiPromptModal from './components/AiPromptModal'
import ToolModal from './components/ToolModal'
import ToolList from './components/ToolList'
const DescWrapper: FC<{desc: string, className?: string}> = ({desc, className}) => {
@@ -115,6 +113,7 @@ const Agent = forwardRef<AgentRef>((_props, ref) => {
const [variableList, setVariableList] = useState<Variable[]>([])
const [isSave, setIsSave] = useState(false)
const initialized = useRef(false)
const [toolList, setToolList] = useState<ToolOption[]>([])
// 初始化完成标记
useEffect(() => {
@@ -143,6 +142,11 @@ const Agent = forwardRef<AgentRef>((_props, ref) => {
if (isSave) return
setIsSave(true)
}, [values])
useEffect(() => {
if (!initialized.current) return
if (isSave) return
setIsSave(true)
}, [toolList])
useEffect(() => {
getModels()
@@ -294,7 +298,11 @@ const Agent = forwardRef<AgentRef>((_props, ref) => {
...(item.config || {})
}))
} as KnowledgeConfig : null,
tools: toolList
tools: toolList.map(vo => ({
tool_id: vo.tool_id,
operation: vo.operation,
enabled: vo.enabled
}))
}
console.log('params', rest, params)
@@ -347,18 +355,6 @@ const Agent = forwardRef<AgentRef>((_props, ref) => {
form.setFieldValue('system_prompt', value)
}
const toolModalRef = useRef<ToolModalRef>(null)
const [toolList, setToolList] = useState<ToolOption[]>([])
const handleAddTool = () => {
toolModalRef.current?.handleOpen()
}
const updateTools = (tool: ToolOption) => {
const tools = [...toolList, tool]
setToolList(tools)
form.setFieldValue('tools', tools)
}
console.log('toolList', toolList)
return (
<>
{loading && <Spin fullscreen></Spin>}
@@ -469,10 +465,6 @@ const Agent = forwardRef<AgentRef>((_props, ref) => {
defaultModel={defaultModel}
refresh={updatePrompt}
/>
<ToolModal
ref={toolModalRef}
refresh={updateTools}
/>
</>
);
});

View File

@@ -90,11 +90,19 @@ const AiPromptModal = forwardRef<AiPromptModalRef, AiPromptModalProps>(({
switch (item.event) {
case 'start':
currentPromptValueRef.current = ''
if (editorRef.current?.clear) {
editorRef.current.clear();
}
break;
case 'message':
if (content) {
currentPromptValueRef.current += content;
form.setFieldsValue({ current_prompt: currentPromptValueRef.current })
if (editorRef.current?.appendText) {
editorRef.current.appendText(content);
editorRef.current.scrollToBottom();
} else {
form.setFieldsValue({ current_prompt: currentPromptValueRef.current })
}
}
if (desc) {
setChatList(prev => {
@@ -107,6 +115,8 @@ const AiPromptModal = forwardRef<AiPromptModalRef, AiPromptModalProps>(({
break;
case 'end':
setLoading(false)
// 流结束时同步表单值
form.setFieldsValue({ current_prompt: currentPromptValueRef.current })
break
}
})

View File

@@ -4,7 +4,7 @@ import { LexicalComposer } from '@lexical/react/LexicalComposer';
import { RichTextPlugin } from '@lexical/react/LexicalRichTextPlugin';
import { ContentEditable } from '@lexical/react/LexicalContentEditable';
import { LexicalErrorBoundary } from '@lexical/react/LexicalErrorBoundary';
import { $getSelection } from 'lexical';
import { $getSelection, $getRoot, $createParagraphNode, $createTextNode, $isParagraphNode, $isTextNode } from 'lexical';
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext';
import InitialValuePlugin from './plugin/InitialValuePlugin'
import LineBreakPlugin from './plugin/LineBreakPlugin';
@@ -12,6 +12,9 @@ import InsertTextPlugin from './plugin/InsertTextPlugin';
export interface EditorRef {
insertText: (text: string) => void;
appendText: (text: string) => void;
clear: () => void;
scrollToBottom: () => void;
}
interface LexicalEditorProps {
@@ -46,6 +49,41 @@ const EditorContent = forwardRef<EditorRef, LexicalEditorProps>(({
selection.insertText(text);
}
});
},
appendText: (text: string) => {
editor.update(() => {
const root = $getRoot();
const lastChild = root.getLastChild();
if (lastChild && $isParagraphNode(lastChild)) {
const lastTextNode = lastChild.getLastChild();
if (lastTextNode && $isTextNode(lastTextNode)) {
const currentText = lastTextNode.getTextContent();
lastTextNode.setTextContent(currentText + text);
} else {
const textNode = $createTextNode(text);
lastChild.append(textNode);
}
} else {
const paragraph = $createParagraphNode();
const textNode = $createTextNode(text);
paragraph.append(textNode);
root.append(paragraph);
}
});
},
clear: () => {
editor.update(() => {
const root = $getRoot();
root.clear();
const paragraph = $createParagraphNode();
root.append(paragraph);
});
},
scrollToBottom: () => {
const editorElement = editor.getRootElement();
if (editorElement) {
editorElement.scrollTop = editorElement.scrollHeight;
}
}
}), [editor]);

View File

@@ -1,21 +1,37 @@
import { type FC, useEffect } from 'react';
import { type FC, useEffect, useRef } from 'react';
import { $getRoot, $createParagraphNode, $createTextNode } from 'lexical';
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext';
// 设置初始值的插件
const InitialValuePlugin: FC<{ value?: string }> = ({ value }) => {
const [editor] = useLexicalComposerContext();
const lastValueRef = useRef<string | undefined>(undefined);
useEffect(() => {
if (value) {
// 只有当value真正发生变化时才更新
if (lastValueRef.current !== value) {
editor.update(() => {
const root = $getRoot();
const currentText = root.getTextContent();
// 如果当前内容和新值相同,则不更新
if (currentText === (value || '')) {
return;
}
root.clear();
const paragraph = $createParagraphNode();
const textNode = $createTextNode(value);
paragraph.append(textNode);
root.append(paragraph);
if (value) {
const paragraph = $createParagraphNode();
const textNode = $createTextNode(value);
paragraph.append(textNode);
root.append(paragraph);
} else {
// 当value为undefined或空时创建一个空段落
const paragraph = $createParagraphNode();
root.append(paragraph);
}
});
lastValueRef.current = value;
}
}, [editor, value]);