@@ -20,6 +20,7 @@ type TestNode = {
2020 _connectedSourceHandleIds ?: string [ ]
2121 _connectedTargetHandleIds ?: string [ ]
2222 variables ?: { variable : string ; value_selector : string [ ] } [ ]
23+ prompt_template ?: { role : string ; text : string } [ ]
2324 items ?: {
2425 variable_selector : string [ ]
2526 input_type : 'variable' | 'constant'
@@ -141,6 +142,58 @@ describe('useInsertSnippet', () => {
141142 } )
142143
143144 describe ( 'Insert Flow' , ( ) => {
145+ it ( 'should keep prompt references parseable across insertions at the same timestamp' , async ( ) => {
146+ const timestamp = vi . spyOn ( Date , 'now' ) . mockReturnValue ( 1791619342702 )
147+ try {
148+ seedPublishedWorkflow ( queryClient , {
149+ graph : {
150+ nodes : [
151+ {
152+ id : 'extractor-with-hyphens' ,
153+ position : { x : 0 , y : 0 } ,
154+ data : { type : 'document-extractor' } ,
155+ } ,
156+ {
157+ id : 'llm-with-hyphens' ,
158+ position : { x : 300 , y : 0 } ,
159+ data : {
160+ type : 'llm' ,
161+ model : { mode : 'chat' } ,
162+ prompt_template : [ { role : 'system' , text : '{{#extractor-with-hyphens.text#}}' } ] ,
163+ } ,
164+ } ,
165+ ] ,
166+ edges : [ { id : 'edge' , source : 'extractor-with-hyphens' , target : 'llm-with-hyphens' } ] ,
167+ } ,
168+ } )
169+ const { result } = renderUseInsertSnippet ( )
170+
171+ for ( let insertion = 0 ; insertion < 2 ; insertion ++ ) {
172+ await act ( async ( ) => {
173+ await result . current . handleInsertSnippet ( 'snippet-1' )
174+ } )
175+ const nextNodes = mockSetNodes . mock . calls [ insertion ] ! [ 0 ] as TestNode [ ]
176+ const insertedNodes = nextNodes . slice ( - 2 )
177+ const extractor = insertedNodes [ 0 ] !
178+ const llm = insertedNodes [ 1 ] !
179+ // Runtime variable selectors accept at most 50 alphanumeric/underscore characters.
180+ for ( const node of insertedNodes ) expect ( node . id ) . toMatch ( / ^ \w { 1 , 50 } $ / )
181+ expect ( llm . data . prompt_template ) . toEqual ( [
182+ { role : 'system' , text : `{{#${ extractor . id } .text#}}` } ,
183+ ] )
184+ expect ( new Set ( nextNodes . map ( ( node ) => node . id ) ) . size ) . toBe ( nextNodes . length )
185+ const edges = mockSetEdges . mock . calls [ insertion ] ! [ 0 ] as TestEdge [ ]
186+ expect ( edges . at ( - 1 ) ) . toEqual (
187+ expect . objectContaining ( { source : extractor . id , target : llm . id } ) ,
188+ )
189+ mockGetNodes . mockReturnValue ( nextNodes )
190+ mockEdges = edges
191+ }
192+ } finally {
193+ timestamp . mockRestore ( )
194+ }
195+ } )
196+
144197 it ( 'should append remapped snippet graph into current workflow graph' , async ( ) => {
145198 seedPublishedWorkflow ( queryClient , {
146199 graph : {
@@ -269,8 +322,8 @@ describe('useInsertSnippet', () => {
269322 } )
270323
271324 const nextNodes = mockSetNodes . mock . calls [ 0 ] ! [ 0 ] as TestNode [ ]
272- const insertedLLMNode = nextNodes . find ( ( node ) => node . id . includes ( 'snippet- llm') ) !
273- const insertedCodeNode = nextNodes . find ( ( node ) => node . id . includes ( 'snippet- code') ) !
325+ const insertedLLMNode = nextNodes . find ( ( node ) => node . data . type === ' llm') !
326+ const insertedCodeNode = nextNodes . find ( ( node ) => node . data . type === ' code') !
274327
275328 expect ( insertedLLMNode . id ) . not . toBe ( 'snippet-llm' )
276329 expect ( insertedCodeNode . data . variables ) . toEqual ( [
@@ -461,22 +514,22 @@ describe('useInsertSnippet', () => {
461514 } )
462515
463516 const firstInsertion = mockSetNodes . mock . calls [ 0 ] ! [ 0 ] as TestNode [ ]
464- const firstSource = firstInsertion . find ( ( node ) => node . id . includes ( 'snippet-source' ) ) !
465- const firstAssigner = firstInsertion . find ( ( node ) => node . id . includes ( 'snippet- assigner') ) !
517+ const firstSource = firstInsertion . find ( ( node ) => node . data . type === 'code' ) !
518+ const firstAssigner = firstInsertion . find ( ( node ) => node . data . type === ' assigner') !
466519 mockGetNodes . mockReturnValue ( firstInsertion )
467520
468521 await act ( async ( ) => {
469522 await result . current . handleInsertSnippet ( 'snippet-1' )
470523 } )
471524
472525 const secondInsertion = mockSetNodes . mock . calls [ 1 ] ! [ 0 ] as TestNode [ ]
473- const insertedSources = secondInsertion . filter ( ( node ) => node . id . includes ( 'snippet-source' ) )
526+ const insertedSources = secondInsertion . filter ( ( node ) => node . data . type === 'code' )
474527 const secondSource = insertedSources . find ( ( node ) => node . id !== firstSource . id ) !
475528 const firstAssignerAfterSecondInsert = secondInsertion . find (
476529 ( node ) => node . id === firstAssigner . id ,
477530 ) !
478531 const secondAssigner = secondInsertion . find (
479- ( node ) => node . id . includes ( 'snippet- assigner') && node . id !== firstAssigner . id ,
532+ ( node ) => node . data . type === ' assigner' && node . id !== firstAssigner . id ,
480533 ) !
481534
482535 expect ( firstAssignerAfterSecondInsert . data . items ! [ 0 ] ) . toEqual (
@@ -727,12 +780,10 @@ describe('useInsertSnippet', () => {
727780
728781 const nextNodes = mockSetNodes . mock . calls [ 0 ] ! [ 0 ] as TestNode [ ]
729782 const insertedEntry = nextNodes . find (
730- ( node ) =>
731- node . id !== 'prev-node' && node . id !== 'next-node' && node . id . includes ( 'snippet-entry' ) ,
783+ ( node ) => node . id !== 'prev-node' && node . id !== 'next-node' && node . data . type === 'llm' ,
732784 ) !
733785 const insertedExit = nextNodes . find (
734- ( node ) =>
735- node . id !== 'prev-node' && node . id !== 'next-node' && node . id . includes ( 'snippet-exit' ) ,
786+ ( node ) => node . id !== 'prev-node' && node . id !== 'next-node' && node . data . type === 'code' ,
736787 ) !
737788 const shiftedNextNode = nextNodes . find ( ( node ) => node . id === 'next-node' ) !
738789 expect ( insertedEntry . position ) . toEqual ( { x : 300 , y : 0 } )
0 commit comments