@@ -4,10 +4,11 @@ import {
44 UI ,
55 NAME ,
66 lift ,
7- generateData ,
7+ llm ,
88 handler ,
99 str ,
1010 createJsonSchema ,
11+ cell ,
1112} from "@commontools/common-builder" ;
1213
1314
@@ -25,12 +26,37 @@ const updateTitle = handler<{ detail: { value: string } }, { title: string }>(
2526 ( { detail } , state ) => detail ?. value && ( state . title = detail . value ) ,
2627) ;
2728
28- const systemPrompt = `generate/modify a document based on input, respond within a json block , e.g.
29- \`\`\`json
30- ...
31- \`\`\`
29+ const buildPrompt = lift ( ( { prompt, data } ) => {
30+ let fullPrompt = prompt ;
31+ if ( data ) {
32+ fullPrompt += `\n\nHere's the previous JSON for reference:\n\`\`\`json\n${ JSON . stringify ( data , null , 2 ) } \n\`\`\`` ;
33+ }
3234
33- No field can be set to null or undefined.` ;
35+ return {
36+ messages : [ fullPrompt , '```json\n' ] ,
37+ system : `generate/modify a document based on input, respond within a json block , e.g.
38+ \`\`\`json
39+ ...
40+ \`\`\`
41+
42+ No field can be set to null or undefined.` ,
43+ stop : '```'
44+ }
45+ } ) ;
46+
47+ const grabJSON = lift < { result ?: string } , any > ( ( { result } ) => {
48+ if ( ! result ) {
49+ return { } ;
50+ }
51+ const jsonMatch = result . match ( / ` ` ` j s o n \n ( [ \s \S ] + ?) ` ` ` / ) ;
52+ if ( ! jsonMatch ) {
53+ console . error ( "No JSON found in text:" , result ) ;
54+ return { } ;
55+ }
56+ let d = JSON . parse ( jsonMatch [ 1 ] ) ;
57+ console . log ( "grabJSON" , d ) ;
58+ return d ;
59+ } ) ;
3460
3561const deriveJsonSchema = lift ( ( { data } ) => {
3662 const schema = createJsonSchema ( { } , data ) ?. [ "properties" ] ;
@@ -49,27 +75,25 @@ const addToPrompt = handler<
4975 { prompt : string } ,
5076 { prompt : string ; query : string }
5177> ( ( e , state ) => {
52- state . prompt += "\n" + e . prompt ;
78+ state . prompt = ( state . prompt ? state . prompt + "\n" : "" ) + e . prompt ;
5379 state . query = state . prompt ;
5480} ) ;
5581
56- const buildJSONGenMessages = lift ( ( { prompt, data } ) => {
57- console . log ( "prompt" , prompt , data ) ;
58- let fullPrompt = prompt ;
59- if ( data ) {
60- fullPrompt += `\n\nHere's the previous JSON for reference:\n\`\`\`json\n${ JSON . stringify ( data , null , 2 ) } \n\`\`\`` ;
61- }
62- return [ fullPrompt , '```json\n' ]
63- } ) ;
64-
65- const onAcceptData = handler < void , { data : any ; lastData : any ; result : any } > (
82+ const onAcceptData = handler < void , { data : any ; lastData : any ; generatedData : any } > (
6683 ( _ , state ) => {
67- console . log ( "accept data" , state . data , state . result ) ;
6884 state . lastData = JSON . parse ( JSON . stringify ( state . data ) ) ;
69- state . data = JSON . parse ( JSON . stringify ( state . result ) )
85+ state . data = JSON . parse ( JSON . stringify ( state . generatedData ) )
7086 }
7187) ;
7288
89+ const tail = lift < { pending : boolean , partial ?: string , lines : number } , string > ( ( { pending, partial, lines } ) => {
90+ if ( ! partial || ! pending ) {
91+ return "" ;
92+ }
93+ return partial . split ( '\n' ) . slice ( - lines ) . join ( '\n' ) ;
94+ } ) ;
95+
96+
7397export const dataDesigner = recipe < {
7498 title : string ;
7599 prompt : string ;
@@ -81,16 +105,12 @@ export const dataDesigner = recipe<{
81105
82106 const schema = deriveJsonSchema ( { data } ) ;
83107 const query = copy ( { value : prompt } ) ;
84- const lastData = copy ( { value : data } ) ;
85- lastData . setDefault ( { } ) ;
108+ // using copy for lastData was causing re-running llm whenever data changed in iframes
109+ const lastData = cell ( { key : 'value' } )
86110 tap ( { lastData } ) ;
87111
88- const { result } = generateData < any > ( {
89- messages : buildJSONGenMessages ( { prompt, data : lastData } ) ,
90- system : systemPrompt ,
91- mode : "json" ,
92- } ) ;
93- tap ( { result } )
112+ const { result, pending, partial} = llm ( buildPrompt ( { prompt, data : lastData } ) ) ;
113+ const generatedData = grabJSON ( { result } ) ;
94114
95115 return {
96116 [ NAME ] : str `${ title } ` ,
@@ -109,10 +129,10 @@ export const dataDesigner = recipe<{
109129 onkeyup =${ onInput ( { value : query } ) }
110130 style="width: 100%; min-height: 128px;"
111131 > </ textarea >
112-
113- < pre > ${ formatData ( { obj : result } ) } </ pre >
132+ < pre > ${ tail ( { partial , pending , lines : 5 } ) }
133+ < pre > ${ formatData ( { obj : generatedData } ) } </ pre >
114134 < common-button
115- onclick =${ onAcceptData ( { data, lastData, result } ) }
135+ onclick =${ onAcceptData ( { data, lastData, generatedData } ) }
116136 > Accept</ common-button >
117137
118138 </ div > ` ,
0 commit comments