mirror of
https://github.com/langgenius/dify.git
synced 2026-04-26 18:27:15 +08:00
add prompt_template of LLM node sync
This commit is contained in:
parent
753234fdfe
commit
5fb6b51998
@ -17,6 +17,12 @@ type WorkflowVariable = {
|
|||||||
variable: string
|
variable: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type PromptTemplateItem = {
|
||||||
|
id: string
|
||||||
|
role: string
|
||||||
|
text: string
|
||||||
|
}
|
||||||
|
|
||||||
const createVariable = (name: string, overrides: Partial<WorkflowVariable> = {}): WorkflowVariable => ({
|
const createVariable = (name: string, overrides: Partial<WorkflowVariable> = {}): WorkflowVariable => ({
|
||||||
default: '',
|
default: '',
|
||||||
hint: '',
|
hint: '',
|
||||||
@ -53,6 +59,43 @@ const createNodeSnapshot = (variableNames: string[]): Node<{ variables: Workflow
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const LLM_NODE_ID = 'llm-node'
|
||||||
|
|
||||||
|
const createLLMNodeSnapshot = (promptTemplates: PromptTemplateItem[]): Node<any> => ({
|
||||||
|
id: LLM_NODE_ID,
|
||||||
|
type: 'custom',
|
||||||
|
position: { x: 200, y: 120 },
|
||||||
|
positionAbsolute: { x: 200, y: 120 },
|
||||||
|
height: 320,
|
||||||
|
width: 460,
|
||||||
|
selected: false,
|
||||||
|
selectable: true,
|
||||||
|
draggable: true,
|
||||||
|
sourcePosition: 'right',
|
||||||
|
targetPosition: 'left',
|
||||||
|
data: {
|
||||||
|
type: 'llm',
|
||||||
|
title: 'LLM',
|
||||||
|
selected: false,
|
||||||
|
context: {
|
||||||
|
enabled: false,
|
||||||
|
variable_selector: [],
|
||||||
|
},
|
||||||
|
model: {
|
||||||
|
mode: 'chat',
|
||||||
|
name: 'gemini-2.5-pro',
|
||||||
|
provider: 'langgenius/gemini/google',
|
||||||
|
completion_params: {
|
||||||
|
temperature: 0.7,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
vision: {
|
||||||
|
enabled: false,
|
||||||
|
},
|
||||||
|
prompt_template: promptTemplates,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
const getVariables = (node: Node): string[] => {
|
const getVariables = (node: Node): string[] => {
|
||||||
const variables = (node.data as any)?.variables ?? []
|
const variables = (node.data as any)?.variables ?? []
|
||||||
return variables.map((item: WorkflowVariable) => item.variable)
|
return variables.map((item: WorkflowVariable) => item.variable)
|
||||||
@ -63,6 +106,10 @@ const getVariableObject = (node: Node, name: string): WorkflowVariable | undefin
|
|||||||
return variables.find((item: WorkflowVariable) => item.variable === name)
|
return variables.find((item: WorkflowVariable) => item.variable === name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getPromptTemplates = (node: Node): PromptTemplateItem[] => {
|
||||||
|
return ((node.data as any)?.prompt_template ?? []) as PromptTemplateItem[]
|
||||||
|
}
|
||||||
|
|
||||||
describe('CollaborationManager syncNodes', () => {
|
describe('CollaborationManager syncNodes', () => {
|
||||||
let manager: CollaborationManager
|
let manager: CollaborationManager
|
||||||
|
|
||||||
@ -177,4 +224,59 @@ describe('CollaborationManager syncNodes', () => {
|
|||||||
expect(finalVariables).toEqual(['a', 'b'])
|
expect(finalVariables).toEqual(['a', 'b'])
|
||||||
expect(getVariableObject(finalNode!, 'b')).toBeDefined()
|
expect(getVariableObject(finalNode!, 'b')).toBeDefined()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('synchronizes prompt_template list updates across collaborators', () => {
|
||||||
|
const promptManager = new CollaborationManager()
|
||||||
|
const doc = new LoroDoc()
|
||||||
|
;(promptManager as any).doc = doc
|
||||||
|
;(promptManager as any).nodesMap = doc.getMap('nodes')
|
||||||
|
;(promptManager as any).edgesMap = doc.getMap('edges')
|
||||||
|
|
||||||
|
const baseTemplate = [
|
||||||
|
{
|
||||||
|
id: 'abcfa5f9-3c44-4252-aeba-4b6eaf0acfc4',
|
||||||
|
role: 'system',
|
||||||
|
text: 'avc',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
const baseNode = createLLMNodeSnapshot(baseTemplate)
|
||||||
|
;(promptManager as any).syncNodes([], [deepClone(baseNode)])
|
||||||
|
|
||||||
|
const updatedTemplates = [
|
||||||
|
...baseTemplate,
|
||||||
|
{
|
||||||
|
id: 'user-1',
|
||||||
|
role: 'user',
|
||||||
|
text: 'hello world',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
const updatedNode = createLLMNodeSnapshot(updatedTemplates)
|
||||||
|
;(promptManager as any).syncNodes([deepClone(baseNode)], [deepClone(updatedNode)])
|
||||||
|
|
||||||
|
const stored = (promptManager.getNodes() as Node[]).find(node => node.id === LLM_NODE_ID)
|
||||||
|
expect(stored).toBeDefined()
|
||||||
|
|
||||||
|
const storedTemplates = getPromptTemplates(stored!)
|
||||||
|
expect(storedTemplates).toHaveLength(2)
|
||||||
|
expect(storedTemplates[0]).toEqual(baseTemplate[0])
|
||||||
|
expect(storedTemplates[1]).toEqual(updatedTemplates[1])
|
||||||
|
|
||||||
|
const editedTemplates = [
|
||||||
|
{
|
||||||
|
id: 'abcfa5f9-3c44-4252-aeba-4b6eaf0acfc4',
|
||||||
|
role: 'system',
|
||||||
|
text: 'updated system prompt',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
const editedNode = createLLMNodeSnapshot(editedTemplates)
|
||||||
|
|
||||||
|
;(promptManager as any).syncNodes([deepClone(updatedNode)], [deepClone(editedNode)])
|
||||||
|
|
||||||
|
const final = (promptManager.getNodes() as Node[]).find(node => node.id === LLM_NODE_ID)
|
||||||
|
const finalTemplates = getPromptTemplates(final!)
|
||||||
|
expect(finalTemplates).toHaveLength(1)
|
||||||
|
expect(finalTemplates[0].text).toBe('updated system prompt')
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@ -77,6 +77,16 @@ export class CollaborationManager {
|
|||||||
return typeof list.getAttached === 'function' ? list.getAttached() ?? list : list
|
return typeof list.getAttached === 'function' ? list.getAttached() ?? list : list
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private ensurePromptTemplateList(nodeContainer: LoroMap<any>): LoroList<any> {
|
||||||
|
const dataContainer = this.ensureDataContainer(nodeContainer)
|
||||||
|
let list = dataContainer.get('prompt_template') as any
|
||||||
|
|
||||||
|
if (!list || typeof list.kind !== 'function' || list.kind() !== 'List')
|
||||||
|
list = dataContainer.setContainer('prompt_template', new LoroList())
|
||||||
|
|
||||||
|
return typeof list.getAttached === 'function' ? list.getAttached() ?? list : list
|
||||||
|
}
|
||||||
|
|
||||||
private exportNode(nodeId: string): Node {
|
private exportNode(nodeId: string): Node {
|
||||||
const container = this.getNodeContainer(nodeId)
|
const container = this.getNodeContainer(nodeId)
|
||||||
const json = container.toJSON() as any
|
const json = container.toJSON() as any
|
||||||
@ -139,6 +149,8 @@ export class CollaborationManager {
|
|||||||
|
|
||||||
if (key === 'variables')
|
if (key === 'variables')
|
||||||
this.syncVariables(container, Array.isArray(value) ? value : [])
|
this.syncVariables(container, Array.isArray(value) ? value : [])
|
||||||
|
else if (key === 'prompt_template')
|
||||||
|
this.syncPromptTemplate(container, Array.isArray(value) ? value : [])
|
||||||
else
|
else
|
||||||
dataContainer.set(key, cloneDeep(value))
|
dataContainer.set(key, cloneDeep(value))
|
||||||
})
|
})
|
||||||
@ -150,6 +162,8 @@ export class CollaborationManager {
|
|||||||
|
|
||||||
if (key === 'variables')
|
if (key === 'variables')
|
||||||
dataContainer.delete('variables')
|
dataContainer.delete('variables')
|
||||||
|
else if (key === 'prompt_template')
|
||||||
|
dataContainer.delete('prompt_template')
|
||||||
|
|
||||||
else
|
else
|
||||||
dataContainer.delete(key)
|
dataContainer.delete(key)
|
||||||
@ -183,6 +197,28 @@ export class CollaborationManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private syncPromptTemplate(nodeContainer: LoroMap<any>, desired: any[]): void {
|
||||||
|
const list = this.ensurePromptTemplateList(nodeContainer)
|
||||||
|
const current = list.toJSON() as any[]
|
||||||
|
const target = Array.isArray(desired) ? desired : []
|
||||||
|
const minLength = Math.min(current.length, target.length)
|
||||||
|
|
||||||
|
for (let i = 0; i < minLength; i += 1) {
|
||||||
|
if (!isEqual(current[i], target[i])) {
|
||||||
|
list.delete(i, 1)
|
||||||
|
list.insert(i, cloneDeep(target[i]))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (current.length > target.length) {
|
||||||
|
list.delete(target.length, current.length - target.length)
|
||||||
|
}
|
||||||
|
else if (target.length > current.length) {
|
||||||
|
for (let i = current.length; i < target.length; i += 1)
|
||||||
|
list.insert(i, cloneDeep(target[i]))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private getNodePanelPresenceSnapshot(): NodePanelPresenceMap {
|
private getNodePanelPresenceSnapshot(): NodePanelPresenceMap {
|
||||||
const snapshot: NodePanelPresenceMap = {}
|
const snapshot: NodePanelPresenceMap = {}
|
||||||
Object.entries(this.nodePanelPresence).forEach(([nodeId, viewers]) => {
|
Object.entries(this.nodePanelPresence).forEach(([nodeId, viewers]) => {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user