@@ -3,21 +3,21 @@ import { customElement, state } from 'lit/decorators.js'
33import { base } from '../styles'
44
55import { Recipe , emptyGraph , todoAppMockup , RecipeNode } from '../data'
6- import { doLLM , grabJson } from '../llm'
6+ import { doLLM , grabJson , processUserInput } from '../llm'
77import { collectSymbols } from '../graph'
88import { listKeys } from '../state'
99
1010const codePrompt = `
1111 Your task is to take a user description or request and produce a series of nodes for a computation graph. Nodes can be code blocks or UI components and they communicate with named ports.
12- you will provide the required edges to connect data from the environment to the inputs of the node. The keys of \`in\` are the names of local inputs and the values are NodePaths (of the form [context, nodeId], where context is typically '.' meaning local namespace).
12+
13+ You will construct the graph using the available tools to add, remove, replace and list nodes.
14+ You will provide the required edges to connect data from the environment to the inputs of the node. The keys of \`in\` are the names of local inputs and the values are NodePaths (of the form [context, nodeId], where context is typically '.' meaning local namespace).
1315
1416 "Imagine some todos" ->
1517
16- \`\`\`json
17- [
18- {
19- "id": "todos",
20- "contentType": "text/javascript",
18+ addCodeNode({
19+ "id": "todos",
20+ "node": {
2121 "in": {},
2222 "outputType": {
2323 "type": "array",
@@ -29,10 +29,11 @@ const codePrompt = `
2929 }
3030 }
3131 },
32- "body": "return [{ label: 'Water my plants', checked: false }, { label: 'Buy milk', checked: true }];"
33- }
34- ]
35- \`\`\`
32+ },
33+ "code": \`
34+ return [{ label: 'Water my plants', checked: false }, { label: 'Buy milk', checked: true }];
35+ \`
36+ })
3637
3738 Tasks that take no inputs require no edges.
3839 All function bodies must take zero parameters. Inputs can be accessed via 'read' and 'deref'.
@@ -41,28 +42,40 @@ const codePrompt = `
4142
4243 "Remind me to water the plants" ->
4344
44- \`\`\`json
45- [
46- {
47- "id": "addReminder",
48- "contentType": "text/javascript",
45+ addCodeNode({
46+ "id": "addReminder",
47+ "node": {
4948 "in": {},
50- "outputType": {},
51- "body": "const todos = input('todos'); const newTodo = { label: 'water the plants', checked: false }; cost newTodos = [...todos, newTodo]; return newTodos;"
49+ "outputType": {
50+ "type": "array",
51+ "items": {
52+ "type": "object",
53+ "properties": {
54+ "label": { "type": "string" },
55+ "checked": { "type": "boolean" }
56+ }
57+ }
58+ },
59+ },
60+ "code": \`
61+ const todos = input('todos');
62+ const newTodo = { label: 'water the plants', checked: false };
63+ const newTodos = [...todos, newTodo];
64+ return newTodos;
65+ \`
5266 }
53- ]
54- \`\`\`
67+ )
5568
5669 Tasks that take no inputs require no edges.
5770
5871 ---
5972
73+
6074 "Take the existing todos and filter to unchecked" ->
61- \`\`\`json
62- [
63- {
64- "id": "filteredTodos",
65- "contentType": "text/javascript",
75+
76+ addCodeNode({
77+ "id": "filteredTodos",
78+ "node": {
6679 "in": {
6780 "todos": [".", "todos"]
6881 },
@@ -76,113 +89,108 @@ const codePrompt = `
7689 }
7790 }
7891 },
79- "body": "const todos = input('todos'); return todos.filter(todo => todo.checked);"
92+ },
93+ "code": \`
94+ const todos = input('todos');
95+ return todos.filter(todo => todo.checked);
96+ \`
8097 }
81- ]
82- \`\`\`
98+ )
8399
84100 Tasks that filter other data must pipe the data through the edges.
85101 All function bodies must take zero parameters. Inputs can be accessed via 'input()', values may be null.
86-
87- ContentType should be "text/javascript" for code.
88102 Always respond with code, even for static data. Wrap your response in a json block. Respond with nothing else.
89103
90104 ---
91105
92106 "render each image by url" ->
93107 images is an array of strings (URLs)
94108
95- \`\`\`json
96- [
97- {
98- "id": "imageUi",
99- "contentType": "application/json+vnd.common.ui",
109+ addUiNode({
110+ "id": "imageUi",
111+ "node": {
100112 "in": {
101113 "images": [".", "images"]
102114 },
103115 "outputType": {
104116 "$id": "https://common.tools/ui.schema.json"
105117 },
106- "body": {
107- "tag ": "ul",
108- "props ": {
109- "className ": "image"
110- },
111- "children": [
112- "type ": "repeat",
113- "binding ": "images ",
114- "template ": {
115- "tag ": "li",
116- "props ": {} ,
117- "children ": [
118- {
119- "tag": "img",
120- "props ": {
121- "src ": { type: 'string', binding: null },
122- }
118+ },
119+ "body ": {
120+ "tag ": "ul",
121+ "props ": {
122+ "className": "image"
123+ },
124+ "children ": [
125+ "type ": "repeat ",
126+ "binding ": "images",
127+ "template ": {
128+ "tag ": "li" ,
129+ "props ": {},
130+ "children": [
131+ {
132+ "tag ": "img",
133+ "props ": {
134+ "src": { type: 'string', binding: null },
123135 }
124- ],
125- }
126- ]
127- }
136+ }
137+ ],
138+ }
139+ ]
128140 }
129- ]
141+ })
130142
131143 Raw values can be passed through by setting binding to null.
132144
133145 ---
134146
135147 "render my todos" ->
136148
137- \`\`\`json
138- [
139- {
140- "id": "todoUi",
141- "contentType": "application/json+vnd.common.ui",
149+ addUiNode({
150+ "id": "todoUi",
151+ "node": {
142152 "in": {
143153 "todos": [".", "todos"]
144154 },
145155 "outputType": {
146156 "$id": "https://common.tools/ui.schema.json"
147157 },
148- "body": {
149- "tag": "ul",
150- "props": {
151- "className": "todo"
152- },
153- "children": {
154- "type": "repeat",
155- "binding": "todos",
156- "template": {
157- "tag": "li",
158- "props": {},
159- "children": [
160- {
161- "tag": "input",
162- "props": {
163- "type": "checkbox",
164- "checked": { type: 'boolean', binding: 'checked' }
165- }
166- },
167- {
168- "tag": "span",
169- "props": {
170- "className": "todo-label"
171- },
172- "children": [
173- { type: 'string', binding: 'label' }
174- ]
158+ },
159+ "body": {
160+ "tag": "ul",
161+ "props": {
162+ "className": "todo"
163+ },
164+ "children": {
165+ "type": "repeat",
166+ "binding": "todos",
167+ "template": {
168+ "tag": "li",
169+ "props": {},
170+ "children": [
171+ {
172+ "tag": "input",
173+ "props": {
174+ "type": "checkbox",
175+ "checked": { type: 'boolean', binding: 'checked' }
175176 }
176- ]
177- }
177+ },
178+ {
179+ "tag": "span",
180+ "props": {
181+ "className": "todo-label"
182+ },
183+ "children": [
184+ { type: 'string', binding: 'label' }
185+ ]
186+ }
187+ ]
178188 }
179189 }
180190 }
181- ]
182- \`\`\`
183-
184- ContentType should be "application/json+vnd.common.ui" for UI. UI trees cannot use any javascript methods, code blocks must prepare the data for the UI to consume.
191+ })
185192
193+ UI trees cannot use any javascript methods, code blocks must prepare the data for the UI to consume.
186194 notalk;justgo
187195`
188196
@@ -220,35 +228,46 @@ export class ComApp extends LitElement {
220228 this . graph = newGraph ;
221229 this . userInput = '' ;
222230
223- const localContext = `
224- The following nodes are available in the current context, these can be referenced by name when wiring the graph.
225-
226- \`\`\`json
227- ${ JSON . stringify ( symbols , null , 2 ) }
228- \`\`\`
231+ const systemContext = `
232+ Ensure you list the current state of the graph and make a detailed step-by-step plan for updating the graph to match the user's request.'
229233
230- The keys at the top of the list are the most recently created by the user, prefer these when making connections.
231- Ensure the you use the name you assign to the input in the body of the code for any read calls.
232- `
233-
234- const systemContext = ``
235-
236- const result = await doLLM ( input + localContext , codePrompt + systemContext , null )
237- const message = result ?. choices [ 0 ] ?. message
238- if ( message ) {
239- const data = grabJson ( message ?. content )
240- for ( const node of data ) {
241- node . messages = [ ]
242- newGraph . push ( node )
243- }
234+ Prefer to send tool calls in serial rather than in one large block, this way we can show the user the nodes as they are created.
235+ `
244236
245- // newNode.id = data.id || id
246- // newNode.contentType = data.contentType || 'text/javascript'
247- // newNode.outputType = data.outputType || {}
248- // newNode.body = data.body || '...'
249- // newNode.in = data.in || {}
250- // newNode.messages = data.messages || newNode.messages
251- }
237+ const availableFunctions = {
238+ listNodes : ( ) => {
239+ console . log ( 'listNodes' , this . graph )
240+ return JSON . stringify ( this . graph )
241+ } ,
242+ addCodeNode : ( { id, node, code } ) => {
243+ console . log ( 'addCodeNode' , id , node , code )
244+ newGraph . push ( { id, contentType : 'text/javascript' , ...node , body : code } )
245+ this . graph = JSON . parse ( JSON . stringify ( newGraph ) ) ;
246+ this . requestUpdate ( ) ;
247+ return `Added node: ${ id } `
248+ } ,
249+ addUiNode : ( { id, node, body } ) => {
250+ console . log ( 'addUiNode' , id , node , body )
251+ newGraph . push ( { id, contentType : 'application/json+vnd.common.ui' , ...node , body } )
252+ this . graph = JSON . parse ( JSON . stringify ( newGraph ) ) ;
253+ this . requestUpdate ( ) ;
254+ return `Added node: ${ id } `
255+ } ,
256+ replaceNode : ( ) => 'hello replace' ,
257+ deleteNode : ( ) => 'hello delete'
258+ } ;
259+ const result = await processUserInput ( input , codePrompt + systemContext , availableFunctions ) ;
260+ console . log ( 'result' , result ) ;
261+
262+ // const result = await doLLM(input + localContext, codePrompt + systemContext, null)
263+ // const message = result?.choices[0]?.message
264+ // if (message) {
265+ // const data = grabJson(message?.content)
266+ // for (const node of data) {
267+ // node.messages = []
268+ // newGraph.push(node)
269+ // }
270+ // }
252271
253272 this . graph = JSON . parse ( JSON . stringify ( newGraph ) ) ;
254273 console . log ( 'graph updated' , this . graph ) ;
0 commit comments