darabos commited on
Commit
a4fa27a
·
2 Parent(s): 90b31da 23acd42

Merge remote-tracking branch 'origin/main' into darabos-folders

Browse files
examples/LynxScribe Data Cleaning.lynxkite.json ADDED
@@ -0,0 +1,536 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "edges": [
3
+ {
4
+ "id": "LynxScribe Message 3 LynxScribe Task Solver 1",
5
+ "source": "LynxScribe Message 3",
6
+ "sourceHandle": "output",
7
+ "target": "LynxScribe Task Solver 1",
8
+ "targetHandle": "system_prompt"
9
+ },
10
+ {
11
+ "id": "LynxScribe Message 1 LynxScribe Task Solver 1",
12
+ "source": "LynxScribe Message 1",
13
+ "sourceHandle": "output",
14
+ "target": "LynxScribe Task Solver 1",
15
+ "targetHandle": "instruction_prompt"
16
+ },
17
+ {
18
+ "id": "Read Excel 1 LynxScribe Task Solver 1",
19
+ "source": "Read Excel 1",
20
+ "sourceHandle": "output",
21
+ "target": "LynxScribe Task Solver 1",
22
+ "targetHandle": "dataframe"
23
+ },
24
+ {
25
+ "id": "LynxScribe Task Solver 1 View DataFrame 1",
26
+ "source": "LynxScribe Task Solver 1",
27
+ "sourceHandle": "output",
28
+ "target": "View DataFrame 1",
29
+ "targetHandle": "input"
30
+ },
31
+ {
32
+ "id": "Read Excel 1 View DataFrame 2",
33
+ "source": "Read Excel 1",
34
+ "sourceHandle": "output",
35
+ "target": "View DataFrame 2",
36
+ "targetHandle": "input"
37
+ }
38
+ ],
39
+ "env": "LynxScribe",
40
+ "nodes": [
41
+ {
42
+ "data": {
43
+ "__execution_delay": 0.0,
44
+ "collapsed": null,
45
+ "display": null,
46
+ "error": null,
47
+ "input_metadata": null,
48
+ "meta": {
49
+ "inputs": {},
50
+ "name": "LynxScribe Message",
51
+ "outputs": {
52
+ "output": {
53
+ "name": "output",
54
+ "position": "top",
55
+ "type": {
56
+ "type": "None"
57
+ }
58
+ }
59
+ },
60
+ "params": {
61
+ "prompt_content": {
62
+ "default": null,
63
+ "name": "prompt_content",
64
+ "type": {
65
+ "format": "textarea"
66
+ }
67
+ },
68
+ "prompt_role": {
69
+ "default": null,
70
+ "name": "prompt_role",
71
+ "type": {
72
+ "enum": [
73
+ "SYSTEM",
74
+ "USER"
75
+ ]
76
+ }
77
+ }
78
+ },
79
+ "position": {
80
+ "x": 653.0,
81
+ "y": 954.0
82
+ },
83
+ "type": "basic"
84
+ },
85
+ "params": {
86
+ "prompt_content": "You are an AI assistant designed to clean and extract structured address information from raw text.\nYour goal is to identify and extract relevant address components while ignoring any unrelated information.\nThe output must be formatted as a structured dictionary.\n\nYour task is to parse an address from raw text and return a dictionary with the following keys:\n - zip_code: The postal or ZIP code.\n - country: The country name.\n - state_or_county: The state, province, or county (if applicable).\n - city: The city or town name.\n - district: The district or borough name (if mentioned).\n - street_type: The type of public space (e.g., street, avenue, boulevard, square).\n - street_name: The name of the public space (e.g., Main, Baker, Champs-\u00c9lys\u00e9es).\n - house_number: The house or building number.\n - floor: The floor number (if mentioned).\n - flat_number: The apartment or unit number (if mentioned).\n - additional_info: Any other useful details, such as building names, or known landmarks.\n\nIf any information is missing from the input, leave the corresponding key as an empty string.\n\nYou must return only a python dictionary with the following keys:\n`zip_code`, `country`, `state_or_county`, `city`, `district`, `street_name`, \n`house_number`, `floor`, `flat_number`, `additional_info`.\n\nDo not include any extra text, comments, or explanations\u2014only return the dictionary.",
87
+ "prompt_role": "SYSTEM"
88
+ },
89
+ "status": "done",
90
+ "title": "LynxScribe Message"
91
+ },
92
+ "dragHandle": ".bg-primary",
93
+ "height": 354.0,
94
+ "id": "LynxScribe Message 3",
95
+ "position": {
96
+ "x": 36.0,
97
+ "y": 569.0
98
+ },
99
+ "type": "basic",
100
+ "width": 740.0
101
+ },
102
+ {
103
+ "data": {
104
+ "__execution_delay": 0.0,
105
+ "collapsed": null,
106
+ "display": null,
107
+ "error": null,
108
+ "input_metadata": null,
109
+ "meta": {
110
+ "inputs": {},
111
+ "name": "LynxScribe Message",
112
+ "outputs": {
113
+ "output": {
114
+ "name": "output",
115
+ "position": "top",
116
+ "type": {
117
+ "type": "None"
118
+ }
119
+ }
120
+ },
121
+ "params": {
122
+ "prompt_content": {
123
+ "default": null,
124
+ "name": "prompt_content",
125
+ "type": {
126
+ "format": "textarea"
127
+ }
128
+ },
129
+ "prompt_role": {
130
+ "default": null,
131
+ "name": "prompt_role",
132
+ "type": {
133
+ "enum": [
134
+ "SYSTEM",
135
+ "USER"
136
+ ]
137
+ }
138
+ }
139
+ },
140
+ "position": {
141
+ "x": 1498.0,
142
+ "y": 660.0
143
+ },
144
+ "type": "basic"
145
+ },
146
+ "params": {
147
+ "prompt_content": "Extract structured address information from the following text: {message_parts}",
148
+ "prompt_role": "USER"
149
+ },
150
+ "status": "done",
151
+ "title": "LynxScribe Message"
152
+ },
153
+ "dragHandle": ".bg-primary",
154
+ "height": 347.0,
155
+ "id": "LynxScribe Message 1",
156
+ "position": {
157
+ "x": 817.0,
158
+ "y": 566.0
159
+ },
160
+ "type": "basic",
161
+ "width": 498.0
162
+ },
163
+ {
164
+ "data": {
165
+ "__execution_delay": 0.0,
166
+ "collapsed": null,
167
+ "display": null,
168
+ "error": null,
169
+ "input_metadata": null,
170
+ "meta": {
171
+ "inputs": {},
172
+ "name": "Read Excel",
173
+ "outputs": {
174
+ "output": {
175
+ "name": "output",
176
+ "position": "right",
177
+ "type": {
178
+ "type": "None"
179
+ }
180
+ }
181
+ },
182
+ "params": {
183
+ "columns": {
184
+ "default": "",
185
+ "name": "columns",
186
+ "type": {
187
+ "type": "<class 'str'>"
188
+ }
189
+ },
190
+ "file_path": {
191
+ "default": null,
192
+ "name": "file_path",
193
+ "type": {
194
+ "type": "<class 'str'>"
195
+ }
196
+ },
197
+ "sheet_name": {
198
+ "default": "Sheet1",
199
+ "name": "sheet_name",
200
+ "type": {
201
+ "type": "<class 'str'>"
202
+ }
203
+ }
204
+ },
205
+ "position": {
206
+ "x": 236.0,
207
+ "y": 150.0
208
+ },
209
+ "type": "basic"
210
+ },
211
+ "params": {
212
+ "columns": "",
213
+ "file_path": "uploads/task_solver_examples.xlsx",
214
+ "sheet_name": "address_example"
215
+ },
216
+ "status": "done",
217
+ "title": "Read Excel"
218
+ },
219
+ "dragHandle": ".bg-primary",
220
+ "height": 284.0,
221
+ "id": "Read Excel 1",
222
+ "position": {
223
+ "x": 41.0,
224
+ "y": 168.0
225
+ },
226
+ "type": "basic",
227
+ "width": 332.0
228
+ },
229
+ {
230
+ "data": {
231
+ "__execution_delay": 0.0,
232
+ "collapsed": null,
233
+ "display": null,
234
+ "error": null,
235
+ "input_metadata": null,
236
+ "meta": {
237
+ "inputs": {
238
+ "dataframe": {
239
+ "name": "dataframe",
240
+ "position": "left",
241
+ "type": {
242
+ "type": "<class 'inspect._empty'>"
243
+ }
244
+ },
245
+ "instruction_prompt": {
246
+ "name": "instruction_prompt",
247
+ "position": "bottom",
248
+ "type": {
249
+ "type": "<class 'inspect._empty'>"
250
+ }
251
+ },
252
+ "system_prompt": {
253
+ "name": "system_prompt",
254
+ "position": "bottom",
255
+ "type": {
256
+ "type": "<class 'inspect._empty'>"
257
+ }
258
+ }
259
+ },
260
+ "name": "LynxScribe Task Solver",
261
+ "outputs": {
262
+ "output": {
263
+ "name": "output",
264
+ "position": "right",
265
+ "type": {
266
+ "type": "None"
267
+ }
268
+ }
269
+ },
270
+ "params": {
271
+ "llm_interface": {
272
+ "default": "openai",
273
+ "name": "llm_interface",
274
+ "type": {
275
+ "type": "<class 'str'>"
276
+ }
277
+ },
278
+ "llm_model_name": {
279
+ "default": "gpt-4o",
280
+ "name": "llm_model_name",
281
+ "type": {
282
+ "type": "<class 'str'>"
283
+ }
284
+ },
285
+ "new_column_names": {
286
+ "default": "processed_field",
287
+ "name": "new_column_names",
288
+ "type": {
289
+ "type": "<class 'str'>"
290
+ }
291
+ }
292
+ },
293
+ "position": {
294
+ "x": 1511.0,
295
+ "y": 220.0
296
+ },
297
+ "type": "basic"
298
+ },
299
+ "params": {
300
+ "llm_interface": "openai",
301
+ "llm_model_name": "gpt-4o-mini",
302
+ "new_column_names": "zip_code, country, state_or_county, city, district, street_name, house_number, floor, flat_number, additional_info"
303
+ },
304
+ "status": "done",
305
+ "title": "LynxScribe Task Solver"
306
+ },
307
+ "dragHandle": ".bg-primary",
308
+ "height": 282.0,
309
+ "id": "LynxScribe Task Solver 1",
310
+ "position": {
311
+ "x": 626.0,
312
+ "y": 170.0
313
+ },
314
+ "type": "basic",
315
+ "width": 272.0
316
+ },
317
+ {
318
+ "data": {
319
+ "display": {
320
+ "dataframes": {
321
+ "df": {
322
+ "columns": [
323
+ "message_parts",
324
+ "zip_code",
325
+ "country",
326
+ "state_or_county",
327
+ "city",
328
+ "district",
329
+ "street_name",
330
+ "house_number",
331
+ "floor",
332
+ "flat_number",
333
+ "additional_info"
334
+ ],
335
+ "data": [
336
+ [
337
+ "John's old apartment: 742 Evergreen Terrace, Springfield, IL 62704, USA. Call me at +1-555-1234 or email [email protected].",
338
+ "62704",
339
+ "USA",
340
+ "IL",
341
+ "Springfield",
342
+ "",
343
+ "Evergreen Terrace",
344
+ "742",
345
+ "",
346
+ "",
347
+ "John's old apartment"
348
+ ],
349
+ [
350
+ "Visit our office at 56B Baker Street, Marylebone, London W1U 8ED, UK. (Nearest Tube: Baker Street). Contact: [email protected].",
351
+ "W1U 8ED",
352
+ "UK",
353
+ "",
354
+ "London",
355
+ "Marylebone",
356
+ "Baker",
357
+ "56B",
358
+ "",
359
+ "",
360
+ ""
361
+ ],
362
+ [
363
+ "New residence: 300, 5th Avenue, New York, NY 10001, USA. Floor 12, Apt 1204. My new phone is (212) 555-6789.",
364
+ "10001",
365
+ "USA",
366
+ "NY",
367
+ "New York",
368
+ "",
369
+ "5th Avenue",
370
+ "300",
371
+ "12",
372
+ "1204",
373
+ ""
374
+ ],
375
+ [
376
+ "We just moved to 23 rue de la Paix, 75002 Paris, France. Floor 3, Flat 5. Send mail to my old address instead.",
377
+ "75002",
378
+ "France",
379
+ "",
380
+ "Paris",
381
+ "",
382
+ "rue de la Paix",
383
+ "23",
384
+ "3",
385
+ "5",
386
+ ""
387
+ ],
388
+ [
389
+ "Warehouse location: 1024 Industrial Blvd, Houston, TX 77002, USA. Not open on weekends. Customer support: [email protected].",
390
+ "77002",
391
+ "USA",
392
+ "TX",
393
+ "Houston",
394
+ "",
395
+ "Industrial Blvd",
396
+ "1024",
397
+ "",
398
+ "",
399
+ "Warehouse location"
400
+ ],
401
+ [
402
+ "My grandma lives at 2F, 15-3 Shinjuku, Tokyo 160-0022, Japan. Don't use my old phone number anymore!",
403
+ "160-0022",
404
+ "Japan",
405
+ "",
406
+ "Tokyo",
407
+ "Shinjuku",
408
+ "Shinjuku",
409
+ "15-3",
410
+ "2F",
411
+ "",
412
+ ""
413
+ ],
414
+ [
415
+ "Delivery address: Apt 9, 88 Queen's Road Central, Central, Hong Kong. Landmark: Opposite IFC Mall.",
416
+ "",
417
+ "Hong Kong",
418
+ "",
419
+ "Central",
420
+ "",
421
+ "Queen's Road Central",
422
+ "88",
423
+ "",
424
+ "9",
425
+ "Opposite IFC Mall"
426
+ ]
427
+ ]
428
+ }
429
+ }
430
+ },
431
+ "error": null,
432
+ "input_metadata": null,
433
+ "meta": {
434
+ "inputs": {
435
+ "input": {
436
+ "name": "input",
437
+ "position": "bottom",
438
+ "type": {
439
+ "type": "<class 'inspect._empty'>"
440
+ }
441
+ }
442
+ },
443
+ "name": "View DataFrame",
444
+ "outputs": {},
445
+ "params": {},
446
+ "position": {
447
+ "x": 1719.0,
448
+ "y": 332.0
449
+ },
450
+ "type": "table_view"
451
+ },
452
+ "params": {},
453
+ "status": "done",
454
+ "title": "View DataFrame"
455
+ },
456
+ "dragHandle": ".bg-primary",
457
+ "height": 316.0,
458
+ "id": "View DataFrame 1",
459
+ "position": {
460
+ "x": 1139.0,
461
+ "y": 91.0
462
+ },
463
+ "type": "table_view",
464
+ "width": 1118.0
465
+ },
466
+ {
467
+ "data": {
468
+ "display": {
469
+ "dataframes": {
470
+ "df": {
471
+ "columns": [
472
+ "message_parts"
473
+ ],
474
+ "data": [
475
+ [
476
+ "John's old apartment: 742 Evergreen Terrace, Springfield, IL 62704, USA. Call me at +1-555-1234 or email [email protected]."
477
+ ],
478
+ [
479
+ "Visit our office at 56B Baker Street, Marylebone, London W1U 8ED, UK. (Nearest Tube: Baker Street). Contact: [email protected]."
480
+ ],
481
+ [
482
+ "New residence: 300, 5th Avenue, New York, NY 10001, USA. Floor 12, Apt 1204. My new phone is (212) 555-6789."
483
+ ],
484
+ [
485
+ "We just moved to 23 rue de la Paix, 75002 Paris, France. Floor 3, Flat 5. Send mail to my old address instead."
486
+ ],
487
+ [
488
+ "Warehouse location: 1024 Industrial Blvd, Houston, TX 77002, USA. Not open on weekends. Customer support: [email protected]."
489
+ ],
490
+ [
491
+ "My grandma lives at 2F, 15-3 Shinjuku, Tokyo 160-0022, Japan. Don't use my old phone number anymore!"
492
+ ],
493
+ [
494
+ "Delivery address: Apt 9, 88 Queen's Road Central, Central, Hong Kong. Landmark: Opposite IFC Mall."
495
+ ]
496
+ ]
497
+ }
498
+ }
499
+ },
500
+ "error": null,
501
+ "input_metadata": null,
502
+ "meta": {
503
+ "inputs": {
504
+ "input": {
505
+ "name": "input",
506
+ "position": "bottom",
507
+ "type": {
508
+ "type": "<class 'inspect._empty'>"
509
+ }
510
+ }
511
+ },
512
+ "name": "View DataFrame",
513
+ "outputs": {},
514
+ "params": {},
515
+ "position": {
516
+ "x": 1083.0,
517
+ "y": 134.0
518
+ },
519
+ "type": "table_view"
520
+ },
521
+ "params": {},
522
+ "status": "done",
523
+ "title": "View DataFrame"
524
+ },
525
+ "dragHandle": ".bg-primary",
526
+ "height": 200.0,
527
+ "id": "View DataFrame 2",
528
+ "position": {
529
+ "x": 515.0,
530
+ "y": -135.125
531
+ },
532
+ "type": "table_view",
533
+ "width": 200.0
534
+ }
535
+ ]
536
+ }
examples/LynxScribe Image Search.lynxkite.json CHANGED
@@ -14,6 +14,13 @@
14
  "target": "LynxScribe Image RAG Builder 1",
15
  "targetHandle": "image_descriptions"
16
  },
 
 
 
 
 
 
 
17
  {
18
  "id": "Input chat 1 LynxScribe Image RAG Query 1",
19
  "source": "Input chat 1",
@@ -27,13 +34,6 @@
27
  "sourceHandle": "output",
28
  "target": "LynxScribe Image RAG Query 1",
29
  "targetHandle": "rag_graph"
30
- },
31
- {
32
- "id": "LynxScribe Image RAG Query 1 LynxScribe Image Result Viewer 1",
33
- "source": "LynxScribe Image RAG Query 1",
34
- "sourceHandle": "output",
35
- "target": "LynxScribe Image Result Viewer 1",
36
- "targetHandle": "embedding_similarities"
37
  }
38
  ],
39
  "env": "LynxScribe",
@@ -292,14 +292,10 @@
292
  }
293
  }
294
  },
295
- "position": {
296
- "x": 1260.0,
297
- "y": 166.0
298
- },
299
  "type": "basic"
300
  },
301
  "params": {
302
- "chat": "Show me a picture about doctors and patients!"
303
  },
304
  "status": "done",
305
  "title": "Input chat"
@@ -316,6 +312,42 @@
316
  },
317
  {
318
  "data": {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
319
  "display": null,
320
  "error": null,
321
  "input_metadata": null,
@@ -356,64 +388,26 @@
356
  }
357
  },
358
  "position": {
359
- "x": 1987.0,
360
- "y": 365.0
361
  },
362
  "type": "basic"
363
  },
364
  "params": {
365
- "top_k": 3.0
366
  },
367
  "status": "done",
368
  "title": "LynxScribe Image RAG Query"
369
  },
370
  "dragHandle": ".bg-primary",
371
- "height": 207.0,
372
  "id": "LynxScribe Image RAG Query 1",
373
  "position": {
374
- "x": 1160.0,
375
- "y": -40.0
376
  },
377
  "type": "basic",
378
- "width": 283.0
379
- },
380
- {
381
- "data": {
382
- "display": "https://storage.googleapis.com/lynxkite_public_data/lynxscribe-images/image-rag-test/bethesda-naval-medical-center-80380_1280.jpg",
383
- "error": null,
384
- "input_metadata": null,
385
- "meta": {
386
- "inputs": {
387
- "embedding_similarities": {
388
- "name": "embedding_similarities",
389
- "position": "left",
390
- "type": {
391
- "type": "<class 'inspect._empty'>"
392
- }
393
- }
394
- },
395
- "name": "LynxScribe Image Result Viewer",
396
- "outputs": {},
397
- "params": {},
398
- "position": {
399
- "x": 2326.0,
400
- "y": 319.0
401
- },
402
- "type": "image"
403
- },
404
- "params": {},
405
- "status": "done",
406
- "title": "LynxScribe Image Result Viewer"
407
- },
408
- "dragHandle": ".bg-primary",
409
- "height": 515.0,
410
- "id": "LynxScribe Image Result Viewer 1",
411
- "position": {
412
- "x": 1657.0,
413
- "y": -193.0
414
- },
415
- "type": "image",
416
- "width": 707.0
417
  }
418
  ]
419
  }
 
14
  "target": "LynxScribe Image RAG Builder 1",
15
  "targetHandle": "image_descriptions"
16
  },
17
+ {
18
+ "id": "LynxScribe Image RAG Query 1 LynxScribe Image Result Viewer 1",
19
+ "source": "LynxScribe Image RAG Query 1",
20
+ "sourceHandle": "output",
21
+ "target": "LynxScribe Image Result Viewer 1",
22
+ "targetHandle": "embedding_similarities"
23
+ },
24
  {
25
  "id": "Input chat 1 LynxScribe Image RAG Query 1",
26
  "source": "Input chat 1",
 
34
  "sourceHandle": "output",
35
  "target": "LynxScribe Image RAG Query 1",
36
  "targetHandle": "rag_graph"
 
 
 
 
 
 
 
37
  }
38
  ],
39
  "env": "LynxScribe",
 
292
  }
293
  }
294
  },
 
 
 
 
295
  "type": "basic"
296
  },
297
  "params": {
298
+ "chat": "Show me a picture about healthy lifestyle"
299
  },
300
  "status": "done",
301
  "title": "Input chat"
 
312
  },
313
  {
314
  "data": {
315
+ "display": "https://storage.googleapis.com/lynxkite_public_data/lynxscribe-images/image-rag-test/food-405521_1280.jpg",
316
+ "error": null,
317
+ "input_metadata": null,
318
+ "meta": {
319
+ "inputs": {
320
+ "embedding_similarities": {
321
+ "name": "embedding_similarities",
322
+ "position": "left",
323
+ "type": {
324
+ "type": "<class 'inspect._empty'>"
325
+ }
326
+ }
327
+ },
328
+ "name": "LynxScribe Image Result Viewer",
329
+ "outputs": {},
330
+ "params": {},
331
+ "type": "image"
332
+ },
333
+ "params": {},
334
+ "status": "done",
335
+ "title": "LynxScribe Image Result Viewer"
336
+ },
337
+ "dragHandle": ".bg-primary",
338
+ "height": 1008.0,
339
+ "id": "LynxScribe Image Result Viewer 1",
340
+ "position": {
341
+ "x": 1674.3708499095837,
342
+ "y": -254.88365280289335
343
+ },
344
+ "type": "image",
345
+ "width": 677.0
346
+ },
347
+ {
348
+ "data": {
349
+ "__execution_delay": 0.0,
350
+ "collapsed": null,
351
  "display": null,
352
  "error": null,
353
  "input_metadata": null,
 
388
  }
389
  },
390
  "position": {
391
+ "x": 1611.0,
392
+ "y": 353.0
393
  },
394
  "type": "basic"
395
  },
396
  "params": {
397
+ "top_k": "1"
398
  },
399
  "status": "done",
400
  "title": "LynxScribe Image RAG Query"
401
  },
402
  "dragHandle": ".bg-primary",
403
+ "height": 212.0,
404
  "id": "LynxScribe Image RAG Query 1",
405
  "position": {
406
+ "x": 1106.0332007233271,
407
+ "y": -44.51280289330922
408
  },
409
  "type": "basic",
410
+ "width": 281.0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
411
  }
412
  ]
413
  }
examples/uploads/task_solver_examples.xlsx ADDED
Binary file (11.3 kB). View file
 
lynxkite-app/src/lynxkite_app/crdt.py CHANGED
@@ -273,6 +273,7 @@ async def execute(name: str, ws_crdt: pycrdt.Map, ws_pyd: workspace.Workspace, d
273
  nc["data"]["status"] = "planned"
274
  # Nodes get a reference to their CRDT maps, so they can update them as the results come in.
275
  np._crdt = nc
 
276
  await workspace.execute(ws_pyd)
277
  workspace.save(ws_pyd, path)
278
  print(f"Finished running {name} in {ws_pyd.env}.")
 
273
  nc["data"]["status"] = "planned"
274
  # Nodes get a reference to their CRDT maps, so they can update them as the results come in.
275
  np._crdt = nc
276
+ ws_pyd = ws_pyd.normalize()
277
  await workspace.execute(ws_pyd)
278
  workspace.save(ws_pyd, path)
279
  print(f"Finished running {name} in {ws_pyd.env}.")
lynxkite-core/src/lynxkite/core/executors/simple.py ADDED
@@ -0,0 +1,64 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """A LynxKite executor that simply passes the output of one box to the other."""
2
+
3
+ import os
4
+ from .. import ops
5
+ from .. import workspace
6
+ import traceback
7
+ import inspect
8
+ import graphlib
9
+
10
+
11
+ def register(env: str):
12
+ """Registers the one-by-one executor."""
13
+ ops.EXECUTORS[env] = lambda ws: execute(ws, ops.CATALOGS[env])
14
+
15
+
16
+ async def await_if_needed(obj):
17
+ if inspect.isawaitable(obj):
18
+ return await obj
19
+ return obj
20
+
21
+
22
+ async def execute(ws: workspace.Workspace, catalog: ops.Catalog):
23
+ nodes = {n.id: n for n in ws.nodes}
24
+ dependencies = {n: [] for n in nodes}
25
+ in_edges = {n: {} for n in nodes}
26
+ for e in ws.edges:
27
+ dependencies[e.target].append(e.source)
28
+ assert e.targetHandle not in in_edges[e.target], f"Duplicate input for {e.target}"
29
+ in_edges[e.target][e.targetHandle] = e.source, e.sourceHandle
30
+ outputs = {}
31
+ ts = graphlib.TopologicalSorter(dependencies)
32
+ for node_id in ts.static_order():
33
+ node = nodes[node_id]
34
+ op = catalog[node.data.title]
35
+ params = {**node.data.params}
36
+ node.publish_started()
37
+ try:
38
+ inputs = []
39
+ missing = []
40
+ for i in op.inputs.values():
41
+ edges = in_edges[node_id]
42
+ if i.name in edges and edges[i.name] in outputs:
43
+ inputs.append(outputs[edges[i.name]])
44
+ else:
45
+ missing.append(i.name)
46
+ if missing:
47
+ node.publish_error(f"Missing input: {', '.join(missing)}")
48
+ continue
49
+ result = op(*inputs, **params)
50
+ result.output = await await_if_needed(result.output)
51
+ result.display = await await_if_needed(result.display)
52
+ if len(op.outputs) == 1:
53
+ [output] = list(op.outputs.values())
54
+ outputs[node_id, output.name] = result.output
55
+ elif len(op.outputs) > 1:
56
+ assert type(result.output) is dict, "An op with multiple outputs must return a dict"
57
+ for output in op.outputs.values():
58
+ outputs[node_id, output.name] = result.output[output.name]
59
+ node.publish_result(result)
60
+ except Exception as e:
61
+ if not os.environ.get("LYNXKITE_SUPPRESS_OP_ERRORS"):
62
+ traceback.print_exc()
63
+ node.publish_error(e)
64
+ return outputs
lynxkite-core/src/lynxkite/core/ops.py CHANGED
@@ -144,6 +144,7 @@ def _param_to_type(name, value, type):
144
  assert value != "", f"{name} is unset."
145
  return float(value)
146
  if isinstance(type, enum.EnumMeta):
 
147
  return type[value]
148
  if isinstance(type, types.UnionType):
149
  match type.__args__:
 
144
  assert value != "", f"{name} is unset."
145
  return float(value)
146
  if isinstance(type, enum.EnumMeta):
147
+ assert value in type.__members__, f"{value} is not an option for {name}."
148
  return type[value]
149
  if isinstance(type, types.UnionType):
150
  match type.__args__:
lynxkite-core/src/lynxkite/core/workspace.py CHANGED
@@ -97,6 +97,25 @@ class Workspace(BaseConfig):
97
  edges: list[WorkspaceEdge] = dataclasses.field(default_factory=list)
98
  _crdt: pycrdt.Map
99
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
100
 
101
  async def execute(ws: Workspace):
102
  if ws.env in ops.EXECUTORS:
 
97
  edges: list[WorkspaceEdge] = dataclasses.field(default_factory=list)
98
  _crdt: pycrdt.Map
99
 
100
+ def normalize(self):
101
+ if self.env not in ops.CATALOGS:
102
+ return self
103
+ catalog = ops.CATALOGS[self.env]
104
+ _ops = {n.id: catalog[n.data.title] for n in self.nodes if n.data.title in catalog}
105
+ valid_targets = set(
106
+ (n.id, h) for n in self.nodes for h in _ops[n.id].inputs if n.id in _ops
107
+ )
108
+ valid_sources = set(
109
+ (n.id, h) for n in self.nodes for h in _ops[n.id].outputs if n.id in _ops
110
+ )
111
+ edges = [
112
+ edge
113
+ for edge in self.edges
114
+ if (edge.source, edge.sourceHandle) in valid_sources
115
+ and (edge.target, edge.targetHandle) in valid_targets
116
+ ]
117
+ return self.model_copy(update={"edges": edges})
118
+
119
 
120
  async def execute(ws: Workspace):
121
  if ws.env in ops.EXECUTORS:
lynxkite-lynxscribe/src/lynxkite_lynxscribe/lynxscribe_ops.py CHANGED
@@ -28,7 +28,7 @@ from lynxscribe.components.chat.processors import (
28
  TruncateHistory,
29
  )
30
  from lynxscribe.components.chat.api import ChatAPI
31
- from lynxscribe.core.models.prompts import ChatCompletionPrompt
32
  from lynxscribe.components.rag.loaders import FAQTemplateLoader
33
 
34
  from lynxkite.core import ops
@@ -56,6 +56,11 @@ class RAGVersion(Enum):
56
  V2 = "v2"
57
 
58
 
 
 
 
 
 
59
  class RAGTemplate(BaseModel):
60
  """
61
  Model for RAG templates consisting of three tables: they are connected via scenario names.
@@ -365,6 +370,10 @@ def ls_save_rag_graph(
365
  @ops.input_position(rag_graph="bottom")
366
  @op("LynxScribe Image RAG Query")
367
  async def search_context(rag_graph, text, *, top_k=3):
 
 
 
 
368
  message = text["text"]
369
  rag_graph = rag_graph[0]["rag_graph"]
370
 
@@ -382,7 +391,9 @@ async def search_context(rag_graph, text, *, top_k=3):
382
  description = emb_sim.embedding.document
383
  result_list.append({"image_url": image_url, "score": score, "description": description})
384
 
385
- return {"embedding_similarities": result_list}
 
 
386
 
387
 
388
  @op("LynxScribe Image Result Viewer", view="image")
@@ -672,6 +683,116 @@ def chat_processor(processor, *, _ctx: one_by_one.Context):
672
  return {"chat_processor": chat_processor, **cfg}
673
 
674
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
675
  @output_on_top
676
  @op("Truncate history")
677
  def truncate_history(*, max_tokens=10000):
@@ -718,6 +839,21 @@ def input_chat(*, chat: str):
718
  return {"text": chat}
719
 
720
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
721
  @op("View", view="table_view")
722
  def view(input):
723
  columns = [str(c) for c in input.keys() if not str(c).startswith("_")]
 
28
  TruncateHistory,
29
  )
30
  from lynxscribe.components.chat.api import ChatAPI
31
+ from lynxscribe.core.models.prompts import ChatCompletionPrompt, Message
32
  from lynxscribe.components.rag.loaders import FAQTemplateLoader
33
 
34
  from lynxkite.core import ops
 
56
  V2 = "v2"
57
 
58
 
59
+ class MessageRole(Enum):
60
+ SYSTEM = "system"
61
+ USER = "user"
62
+
63
+
64
  class RAGTemplate(BaseModel):
65
  """
66
  Model for RAG templates consisting of three tables: they are connected via scenario names.
 
370
  @ops.input_position(rag_graph="bottom")
371
  @op("LynxScribe Image RAG Query")
372
  async def search_context(rag_graph, text, *, top_k=3):
373
+ """
374
+ top_k: which results we are showing (TODO: when the image viewer is
375
+ updated w pager, change back to top k)
376
+ """
377
  message = text["text"]
378
  rag_graph = rag_graph[0]["rag_graph"]
379
 
 
391
  description = emb_sim.embedding.document
392
  result_list.append({"image_url": image_url, "score": score, "description": description})
393
 
394
+ real_k = min(top_k, len(result_list) - 1)
395
+
396
+ return {"embedding_similarities": [result_list[real_k]]}
397
 
398
 
399
  @op("LynxScribe Image Result Viewer", view="image")
 
683
  return {"chat_processor": chat_processor, **cfg}
684
 
685
 
686
+ @output_on_top
687
+ @op("LynxScribe Message")
688
+ def lynxscribe_message(
689
+ *, prompt_role: MessageRole = MessageRole.SYSTEM, prompt_content: ops.LongStr
690
+ ):
691
+ return_message = Message(role=prompt_role.value, content=prompt_content.strip())
692
+ return {"prompt_message": return_message}
693
+
694
+
695
+ @op("Read Excel")
696
+ def read_excel(*, file_path: str, sheet_name: str = "Sheet1", columns: str = ""):
697
+ """
698
+ Reads an Excel file and returns the content of the specified sheet.
699
+ The columns parameter can be used to specify which columns to include in the output.
700
+ If not specified, all columns will be included (separate the values by comma).
701
+
702
+ TODO: more general: several input/output versions.
703
+ """
704
+ df = pd.read_excel(file_path, sheet_name=sheet_name)
705
+ if columns:
706
+ columns = [c.strip() for c in columns.split(",") if c.strip()]
707
+ columns = [c for c in columns if c in df.columns]
708
+ if len(columns) == 0:
709
+ raise ValueError("No valid columns specified.")
710
+ df = df[columns].copy()
711
+ return {"dataframe": df}
712
+
713
+
714
+ @ops.input_position(system_prompt="bottom", instruction_prompt="bottom", dataframe="left")
715
+ @op("LynxScribe Task Solver")
716
+ @mem.cache
717
+ async def ls_task_solver(
718
+ system_prompt,
719
+ instruction_prompt,
720
+ dataframe,
721
+ *,
722
+ llm_interface: str = "openai",
723
+ llm_model_name: str = "gpt-4o",
724
+ new_column_names: str = "processed_field",
725
+ # api_key_name: str = "OPENAI_API_KEY",
726
+ ):
727
+ """
728
+ Solving the described task on a data frame and put the results into a new column.
729
+
730
+ If there are multiple new_column_names provided, the structured dictionary output
731
+ will be split into multiple columns.
732
+ """
733
+
734
+ # handling inputs
735
+ system_message = system_prompt[0]["prompt_message"]
736
+ instruction_message = instruction_prompt[0]["prompt_message"]
737
+ df = dataframe["dataframe"]
738
+
739
+ # preparing output
740
+ out_df = df.copy()
741
+
742
+ # connecting to the LLM
743
+ llm_params = {"name": llm_interface}
744
+ # if api_key_name:
745
+ # llm_params["api_key"] = os.getenv(api_key_name)
746
+ llm = get_llm_engine(**llm_params)
747
+
748
+ # getting the list of fieldnames used in the instruction message
749
+ fieldnames = []
750
+ for pot_fieldname in df.columns:
751
+ if "{" + pot_fieldname + "}" in instruction_message.content:
752
+ fieldnames.append(pot_fieldname)
753
+
754
+ # generate a list of instruction messages (from fieldnames)
755
+ # each row of the df is a separate instruction message
756
+ # TODO: make it fast for large dataframes
757
+ instruction_messages = []
758
+ for i in range(len(df)):
759
+ instruction_message_i = deepcopy(instruction_message)
760
+ for fieldname in fieldnames:
761
+ instruction_message_i.content = instruction_message_i.content.replace(
762
+ "{" + fieldname + "}", str(df.iloc[i][fieldname])
763
+ )
764
+ instruction_messages.append(instruction_message_i)
765
+
766
+ # generate completition prompt
767
+ completion_prompts = [
768
+ ChatCompletionPrompt(
769
+ model=llm_model_name,
770
+ messages=[system_message, instruction_message_j],
771
+ )
772
+ for instruction_message_j in instruction_messages
773
+ ]
774
+
775
+ # get the answers
776
+ tasks = [llm.acreate_completion(completion_prompt=_prompt) for _prompt in completion_prompts]
777
+ out_completions = await asyncio.gather(*tasks)
778
+
779
+ # answer post-processing: 1 vs more columns
780
+ col_list = [_c.strip() for _c in new_column_names.split(",") if _c.strip()]
781
+ if len(col_list) == 0:
782
+ raise ValueError("No valid column names specified.")
783
+ elif len(col_list) == 1:
784
+ out_df[col_list[0]] = [result.choices[0].message.content for result in out_completions]
785
+ else:
786
+ answers = [
787
+ dictionary_corrector(result.choices[0].message.content, expected_keys=col_list)
788
+ for result in out_completions
789
+ ]
790
+ for i, col in enumerate(col_list):
791
+ out_df[col] = [answer[col] for answer in answers]
792
+
793
+ return {"dataframe": out_df}
794
+
795
+
796
  @output_on_top
797
  @op("Truncate history")
798
  def truncate_history(*, max_tokens=10000):
 
839
  return {"text": chat}
840
 
841
 
842
+ @ops.input_position(input="bottom")
843
+ @op("View DataFrame", view="table_view")
844
+ def view_df(input):
845
+ df = input[0]["dataframe"]
846
+ v = {
847
+ "dataframes": {
848
+ "df": {
849
+ "columns": [str(c) for c in df.columns],
850
+ "data": df.values.tolist(),
851
+ }
852
+ }
853
+ }
854
+ return v
855
+
856
+
857
  @op("View", view="table_view")
858
  def view(input):
859
  columns = [str(c) for c in input.keys() if not str(c).startswith("_")]
lynxkite-pillow-example/src/lynxkite_pillow_example/__init__.py CHANGED
@@ -1,7 +1,7 @@
1
  """Demo for how easily we can provide a UI for popular open-source tools."""
2
 
3
  from lynxkite.core import ops
4
- from lynxkite.core.executors import one_by_one
5
  from PIL import Image, ImageFilter
6
  import base64
7
  import fsspec
@@ -9,7 +9,7 @@ import io
9
 
10
  ENV = "Pillow"
11
  op = ops.op_registration(ENV)
12
- one_by_one.register(ENV, cache=False)
13
 
14
 
15
  @op("Open image")
 
1
  """Demo for how easily we can provide a UI for popular open-source tools."""
2
 
3
  from lynxkite.core import ops
4
+ from lynxkite.core.executors import simple
5
  from PIL import Image, ImageFilter
6
  import base64
7
  import fsspec
 
9
 
10
  ENV = "Pillow"
11
  op = ops.op_registration(ENV)
12
+ simple.register(ENV)
13
 
14
 
15
  @op("Open image")