darabos commited on
Commit
204116f
·
1 Parent(s): 83b1026

Update the LynxScribe ops with OpenAI API change.

Browse files
lynxkite-app/.gitignore ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ /src/lynxkite/app/web_assets
2
+ !/src/lynxkite/app/web_assets/__init__.py
3
+ !/src/lynxkite/app/web_assets/assets/__init__.py
lynxkite-app/data/LynxScribe demo CHANGED
@@ -14,24 +14,24 @@
14
  "meta": {
15
  "outputs": {
16
  "output": {
17
- "name": "output",
18
  "type": {
19
  "type": "None"
20
  },
21
- "position": "right"
 
22
  }
23
  },
24
- "name": "Input chat",
25
  "inputs": {},
26
  "params": {
27
  "chat": {
28
- "name": "chat",
29
- "default": null,
30
  "type": {
31
  "type": "<class 'str'>"
32
- }
 
 
33
  }
34
  },
 
35
  "type": "basic"
36
  }
37
  },
@@ -39,8 +39,8 @@
39
  "x": -493.5496596237119,
40
  "y": 20.90123252513356
41
  },
42
- "parentId": null,
43
  "height": 186.0,
 
44
  "width": 259.0
45
  },
46
  {
@@ -52,13 +52,13 @@
52
  "display": {
53
  "dataframes": {
54
  "df": {
 
 
 
55
  "data": [
56
  [
57
- "The Chief Innovation Officer (CIO) of Lynx Analytics is Gabor Benedek. He is responsible for defining the methodological roadmap for the company's predictive analytics consulting and product development departments. If you need more specific information about his role, feel free to ask!\n\nPlease visit <a href='https://www.lynxanalytics.com/team-leadership' target='_blank'>https://www.lynxanalytics.com/team-leadership</a> for further information."
58
  ]
59
- ],
60
- "columns": [
61
- "answer"
62
  ]
63
  }
64
  }
@@ -72,10 +72,10 @@
72
  "inputs": {
73
  "input": {
74
  "position": "left",
 
75
  "type": {
76
  "type": "<class 'inspect._empty'>"
77
- },
78
- "name": "input"
79
  }
80
  }
81
  }
@@ -84,9 +84,9 @@
84
  "x": 722.1450069267316,
85
  "y": -785.6076562320527
86
  },
 
87
  "parentId": null,
88
- "height": 200.0,
89
- "width": 200.0
90
  },
91
  {
92
  "id": "LLM 1",
@@ -99,8 +99,9 @@
99
  "display": null,
100
  "error": null,
101
  "meta": {
102
- "name": "LLM",
103
  "inputs": {},
 
 
104
  "params": {
105
  "name": {
106
  "name": "name",
@@ -112,14 +113,13 @@
112
  },
113
  "outputs": {
114
  "output": {
115
- "position": "top",
116
  "type": {
117
  "type": "None"
118
  },
119
- "name": "output"
120
  }
121
- },
122
- "type": "basic"
123
  }
124
  },
125
  "position": {
@@ -142,32 +142,32 @@
142
  "display": null,
143
  "error": null,
144
  "meta": {
 
145
  "name": "Scenario selector",
146
  "inputs": {},
 
 
 
 
 
 
 
 
 
147
  "params": {
148
  "scenario_file": {
149
- "default": null,
150
  "type": {
151
  "type": "<class 'str'>"
152
  },
 
153
  "name": "scenario_file"
154
  },
155
  "node_types": {
156
- "name": "node_types",
157
- "default": "intent_cluster",
158
  "type": {
159
  "type": "<class 'str'>"
160
- }
161
- }
162
- },
163
- "type": "basic",
164
- "outputs": {
165
- "output": {
166
- "type": {
167
- "type": "None"
168
  },
169
- "name": "output",
170
- "position": "top"
171
  }
172
  }
173
  }
@@ -176,8 +176,8 @@
176
  "x": -549.1300345090008,
177
  "y": 1086.4852248156676
178
  },
179
- "height": 200.0,
180
  "width": 200.0,
 
181
  "parentId": null
182
  },
183
  {
@@ -192,30 +192,15 @@
192
  "error": null,
193
  "meta": {
194
  "name": "Chat API",
195
- "inputs": {
196
- "chatbot": {
197
- "position": "bottom",
198
- "name": "chatbot",
199
- "type": {
200
- "type": "<class 'inspect._empty'>"
201
- }
202
- },
203
- "knowledge_base": {
204
- "type": {
205
- "type": "<class 'inspect._empty'>"
206
- },
207
- "position": "bottom",
208
- "name": "knowledge_base"
209
- },
210
- "chat_processor": {
211
- "position": "bottom",
212
- "name": "chat_processor",
213
  "type": {
214
- "type": "<class 'inspect._empty'>"
215
  }
216
  }
217
  },
218
- "type": "basic",
219
  "params": {
220
  "model": {
221
  "type": {
@@ -225,15 +210,30 @@
225
  "name": "model"
226
  }
227
  },
228
- "outputs": {
229
- "output": {
230
- "position": "top",
231
  "type": {
232
- "type": "None"
233
  },
234
- "name": "output"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
235
  }
236
- }
 
237
  }
238
  },
239
  "position": {
@@ -251,55 +251,55 @@
251
  "title": "Knowledge base",
252
  "params": {
253
  "nodes_path": "/home/darabos/nvme/lynxscribe/examples/chat_api/data/lynx/nodes.pickle",
254
- "template_cluster_path": "/home/darabos/nvme/lynxscribe/examples/chat_api/data/lynx/tempclusters.pickle",
255
- "edges_path": "/home/darabos/nvme/lynxscribe/examples/chat_api/data/lynx/edges.pickle"
256
  },
257
  "display": null,
258
  "error": null,
259
  "meta": {
260
  "params": {
261
- "edges_path": {
262
- "name": "edges_path",
263
- "default": "edges.pickle",
264
  "type": {
265
  "type": "<class 'str'>"
266
  }
267
  },
268
- "template_cluster_path": {
269
- "name": "template_cluster_path",
270
- "default": "tempclusters.pickle",
271
  "type": {
272
  "type": "<class 'str'>"
273
- }
 
 
274
  },
275
- "nodes_path": {
276
- "name": "nodes_path",
277
- "default": "nodes.pickle",
278
  "type": {
279
  "type": "<class 'str'>"
280
  }
281
  }
282
  },
283
  "name": "Knowledge base",
 
 
284
  "outputs": {
285
  "output": {
 
286
  "type": {
287
  "type": "None"
288
  },
289
- "position": "top",
290
- "name": "output"
291
  }
292
- },
293
- "inputs": {},
294
- "type": "basic"
295
  }
296
  },
297
  "position": {
298
  "x": 598.8683124946176,
299
  "y": 609.9499973808545
300
  },
301
- "parentId": null,
302
  "height": 320.0,
 
303
  "width": 336.0
304
  },
305
  {
@@ -308,47 +308,22 @@
308
  "data": {
309
  "title": "RAG chatbot",
310
  "params": {
 
311
  "negative_answer": "I'm sorry, but the data I've been trained on does not contain any information related to your question.",
312
  "max_results": "5",
313
- "limits_by_type": "{\"information\": [2, 3], \"summary\": [2, 3]}",
314
  "strict_limits": true
315
  },
316
  "display": null,
317
  "error": null,
318
  "meta": {
319
- "outputs": {
320
- "output": {
321
- "position": "top",
322
- "type": {
323
- "type": "None"
324
- },
325
- "name": "output"
326
- }
327
- },
328
- "inputs": {
329
- "llm": {
330
- "type": {
331
- "type": "<class 'inspect._empty'>"
332
- },
333
- "position": "bottom",
334
- "name": "llm"
335
- },
336
- "rag_graph": {
337
- "name": "rag_graph",
338
- "position": "bottom",
339
  "type": {
340
- "type": "<class 'inspect._empty'>"
341
  }
342
  },
343
- "scenario_selector": {
344
- "position": "bottom",
345
- "type": {
346
- "type": "<class 'inspect._empty'>"
347
- },
348
- "name": "scenario_selector"
349
- }
350
- },
351
- "params": {
352
  "max_results": {
353
  "name": "max_results",
354
  "type": {
@@ -356,30 +331,55 @@
356
  },
357
  "default": 5.0
358
  },
359
- "negative_answer": {
360
- "type": {
361
- "type": "<class 'str'>"
362
- },
363
- "name": "negative_answer",
364
- "default": "I'm sorry, but the data I've been trained on does not contain any information related to your question."
365
- },
366
  "strict_limits": {
367
  "default": true,
 
368
  "type": {
369
  "type": "<class 'bool'>"
370
- },
371
- "name": "strict_limits"
372
  },
373
  "limits_by_type": {
374
- "default": "{}",
375
  "name": "limits_by_type",
 
376
  "type": {
377
  "type": "<class 'str'>"
378
  }
379
  }
380
  },
 
 
 
 
 
 
 
 
 
 
381
  "name": "RAG chatbot",
382
- "type": "basic"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
383
  },
384
  "beingResized": false
385
  },
@@ -387,8 +387,8 @@
387
  "x": -533.1301830766971,
388
  "y": 547.294980747757
389
  },
390
- "parentId": null,
391
  "width": 339.0,
 
392
  "height": 399.0
393
  },
394
  {
@@ -401,8 +401,23 @@
401
  "error": null,
402
  "meta": {
403
  "name": "RAG graph",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
404
  "params": {},
405
- "type": "basic",
406
  "outputs": {
407
  "output": {
408
  "position": "top",
@@ -412,22 +427,7 @@
412
  }
413
  }
414
  },
415
- "inputs": {
416
- "text_embedder": {
417
- "position": "bottom",
418
- "name": "text_embedder",
419
- "type": {
420
- "type": "<class 'inspect._empty'>"
421
- }
422
- },
423
- "vector_store": {
424
- "name": "vector_store",
425
- "position": "bottom",
426
- "type": {
427
- "type": "<class 'inspect._empty'>"
428
- }
429
- }
430
- }
431
  }
432
  },
433
  "position": {
@@ -449,25 +449,25 @@
449
  },
450
  "display": null,
451
  "error": null,
 
452
  "meta": {
453
- "name": "Vector store",
454
  "params": {
455
- "name": {
456
- "name": "name",
457
- "default": "chromadb",
458
  "type": {
459
  "type": "<class 'str'>"
460
- }
 
461
  },
462
- "collection_name": {
463
- "default": "lynx",
464
- "name": "collection_name",
465
  "type": {
466
  "type": "<class 'str'>"
467
  }
468
  }
469
  },
470
- "inputs": {},
471
  "outputs": {
472
  "output": {
473
  "name": "output",
@@ -477,17 +477,17 @@
477
  "position": "top"
478
  }
479
  },
480
- "type": "basic"
481
- },
482
- "beingResized": false
483
  },
484
  "position": {
485
  "x": -1053.794625339574,
486
  "y": 1347.7711940497127
487
  },
488
- "width": 275.0,
489
  "parentId": null,
490
- "height": 227.0
491
  },
492
  {
493
  "id": "Text embedder 2",
@@ -500,33 +500,33 @@
500
  "display": null,
501
  "error": null,
502
  "meta": {
503
- "name": "Text embedder",
504
  "params": {
505
  "model": {
506
- "default": "text-embedding-ada-002",
507
  "type": {
508
  "type": "<class 'str'>"
509
  },
510
- "name": "model"
 
511
  }
512
  },
513
- "outputs": {
514
- "output": {
515
- "type": {
516
- "type": "None"
517
- },
518
- "name": "output",
519
- "position": "top"
520
- }
521
- },
522
- "type": "basic",
523
  "inputs": {
524
  "llm": {
525
  "name": "llm",
 
526
  "type": {
527
  "type": "<class 'inspect._empty'>"
 
 
 
 
 
 
 
 
 
528
  },
529
- "position": "bottom"
530
  }
531
  }
532
  }
@@ -535,9 +535,9 @@
535
  "x": -719.98604638686,
536
  "y": 1343.5978526690794
537
  },
 
538
  "parentId": null,
539
- "height": 200.0,
540
- "width": 200.0
541
  },
542
  {
543
  "id": "LLM 2",
@@ -550,15 +550,8 @@
550
  "display": null,
551
  "error": null,
552
  "meta": {
553
- "outputs": {
554
- "output": {
555
- "type": {
556
- "type": "None"
557
- },
558
- "name": "output",
559
- "position": "top"
560
- }
561
- },
562
  "params": {
563
  "name": {
564
  "name": "name",
@@ -568,9 +561,16 @@
568
  }
569
  }
570
  },
571
- "inputs": {},
572
- "name": "LLM",
573
- "type": "basic"
 
 
 
 
 
 
 
574
  }
575
  },
576
  "position": {
@@ -592,27 +592,27 @@
592
  "display": null,
593
  "error": null,
594
  "meta": {
 
595
  "inputs": {},
 
596
  "params": {
597
  "max_tokens": {
598
- "default": 10000.0,
599
  "type": {
600
  "type": "<class 'int'>"
601
  },
 
602
  "name": "max_tokens"
603
  }
604
  },
605
  "outputs": {
606
  "output": {
607
- "position": "top",
608
- "name": "output",
609
  "type": {
610
  "type": "None"
611
- }
 
 
612
  }
613
- },
614
- "type": "basic",
615
- "name": "Truncate history"
616
  }
617
  },
618
  "position": {
@@ -620,8 +620,8 @@
620
  "y": 1044.7639853229612
621
  },
622
  "height": 200.0,
623
- "width": 200.0,
624
- "parentId": null
625
  },
626
  {
627
  "id": "Chat processor 1",
@@ -631,30 +631,30 @@
631
  "params": {},
632
  "display": null,
633
  "error": null,
634
- "__execution_delay": null,
635
  "collapsed": true,
 
636
  "meta": {
637
- "outputs": {
638
- "output": {
639
- "type": {
640
- "type": "None"
641
- },
642
- "position": "top",
643
- "name": "output"
644
- }
645
- },
646
- "type": "basic",
647
  "inputs": {
648
  "processor": {
 
649
  "type": {
650
  "type": "<class 'inspect._empty'>"
651
  },
652
- "position": "bottom",
653
  "name": "processor"
654
  }
655
  },
656
  "params": {},
657
- "name": "Chat processor"
 
 
 
 
 
 
 
 
 
 
658
  }
659
  },
660
  "position": {
@@ -662,8 +662,8 @@
662
  "y": 778.546274223181
663
  },
664
  "width": 200.0,
665
- "height": 200.0,
666
- "parentId": null
667
  },
668
  {
669
  "id": "Mask 1",
@@ -671,55 +671,55 @@
671
  "data": {
672
  "title": "Mask",
673
  "params": {
674
- "regex": "([a-z0-9!#$%&'*+\\/=?^_`{|.}~-]+@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?)",
675
676
- "name": "email",
677
- "mask_pattern": "masked_email_address_{}"
 
678
  },
679
  "display": null,
680
  "error": null,
681
  "meta": {
682
- "inputs": {},
683
  "outputs": {
684
  "output": {
685
- "name": "output",
686
  "type": {
687
  "type": "None"
688
  },
689
- "position": "top"
690
  }
691
  },
692
- "type": "basic",
693
  "params": {
694
- "mask_pattern": {
695
  "default": "",
 
696
  "type": {
697
  "type": "<class 'str'>"
698
- },
699
- "name": "mask_pattern"
700
  },
701
- "regex": {
702
- "name": "regex",
703
  "default": "",
704
  "type": {
705
  "type": "<class 'str'>"
706
  }
707
  },
708
- "name": {
709
- "name": "name",
710
  "default": "",
711
  "type": {
712
  "type": "<class 'str'>"
713
- }
 
714
  },
715
- "exceptions": {
716
- "name": "exceptions",
717
- "default": "",
718
  "type": {
719
  "type": "<class 'str'>"
720
- }
 
 
721
  }
722
  },
 
 
723
  "name": "Mask"
724
  }
725
  },
@@ -727,8 +727,8 @@
727
  "x": 233.69759202223884,
728
  "y": 1041.6145468043276
729
  },
730
- "parentId": null,
731
  "width": 200.0,
 
732
  "height": 200.0
733
  },
734
  {
@@ -738,64 +738,64 @@
738
  "title": "Mask",
739
  "params": {
740
  "mask_pattern": "masked_credit_card_number_{}",
741
- "name": "credit_card",
742
  "regex": "((?:(?:\\\\d{4}[- ]?){3}\\\\d{4}|\\\\d{15,16}))(?![\\\\d])",
743
- "exceptions": ""
744
  },
745
  "display": null,
746
  "error": null,
747
  "meta": {
748
- "inputs": {},
 
 
 
 
 
 
 
 
 
749
  "type": "basic",
 
750
  "params": {
751
- "exceptions": {
752
- "name": "exceptions",
753
  "default": "",
754
  "type": {
755
  "type": "<class 'str'>"
756
- }
 
757
  },
758
- "mask_pattern": {
759
- "name": "mask_pattern",
760
  "default": "",
 
761
  "type": {
762
  "type": "<class 'str'>"
763
  }
764
  },
765
- "regex": {
766
- "name": "regex",
767
  "type": {
768
  "type": "<class 'str'>"
769
  },
770
- "default": ""
771
  },
772
- "name": {
 
773
  "type": {
774
  "type": "<class 'str'>"
775
  },
776
- "default": "",
777
- "name": "name"
778
- }
779
- },
780
- "outputs": {
781
- "output": {
782
- "position": "top",
783
- "name": "output",
784
- "type": {
785
- "type": "None"
786
- }
787
  }
788
- },
789
- "name": "Mask"
790
  }
791
  },
792
  "position": {
793
  "x": 513.2761671440603,
794
  "y": 1034.8547191984255
795
  },
796
- "width": 200.0,
797
  "parentId": null,
798
- "height": 200.0
799
  },
800
  {
801
  "id": "Test Chat API 2",
@@ -803,58 +803,58 @@
803
  "data": {
804
  "title": "Test Chat API",
805
  "params": {
806
- "show_details": true
807
  },
808
  "display": null,
809
  "error": null,
810
- "__execution_delay": 0.0,
811
- "collapsed": false,
812
  "meta": {
813
- "params": {
814
- "show_details": {
815
- "name": "show_details",
816
  "type": {
817
- "type": "<class 'bool'>"
818
  },
819
- "default": false
820
  }
821
  },
 
822
  "inputs": {
823
- "message": {
824
- "name": "message",
825
  "type": {
826
  "type": "<class 'inspect._empty'>"
827
  },
828
- "position": "left"
829
  },
830
- "chat_api": {
831
- "position": "bottom",
832
- "name": "chat_api",
833
  "type": {
834
  "type": "<class 'inspect._empty'>"
835
- }
 
 
836
  }
837
  },
838
  "name": "Test Chat API",
839
- "outputs": {
840
- "output": {
841
- "position": "right",
842
- "name": "output",
843
  "type": {
844
- "type": "None"
845
- }
 
 
846
  }
847
- },
848
- "type": "basic"
849
- }
 
850
  },
851
  "position": {
852
- "x": -11.653333595036415,
853
- "y": -0.9210389517918429
854
  },
 
855
  "parentId": null,
856
- "width": 200.0,
857
- "height": 200.0
858
  }
859
  ],
860
  "edges": [
 
14
  "meta": {
15
  "outputs": {
16
  "output": {
 
17
  "type": {
18
  "type": "None"
19
  },
20
+ "position": "right",
21
+ "name": "output"
22
  }
23
  },
 
24
  "inputs": {},
25
  "params": {
26
  "chat": {
 
 
27
  "type": {
28
  "type": "<class 'str'>"
29
+ },
30
+ "name": "chat",
31
+ "default": null
32
  }
33
  },
34
+ "name": "Input chat",
35
  "type": "basic"
36
  }
37
  },
 
39
  "x": -493.5496596237119,
40
  "y": 20.90123252513356
41
  },
 
42
  "height": 186.0,
43
+ "parentId": null,
44
  "width": 259.0
45
  },
46
  {
 
52
  "display": {
53
  "dataframes": {
54
  "df": {
55
+ "columns": [
56
+ "answer"
57
+ ],
58
  "data": [
59
  [
60
+ "The Chief Innovation Officer (CIO) of Lynx Analytics is Gabor Benedek. He is responsible for defining the methodological roadmap for Lynx's predictive analytics consulting and product development departments. If you need more specific information or have follow-up questions, feel free to ask!\n\nPlease visit https://www.lynxanalytics.com/team-leadership for further information."
61
  ]
 
 
 
62
  ]
63
  }
64
  }
 
72
  "inputs": {
73
  "input": {
74
  "position": "left",
75
+ "name": "input",
76
  "type": {
77
  "type": "<class 'inspect._empty'>"
78
+ }
 
79
  }
80
  }
81
  }
 
84
  "x": 722.1450069267316,
85
  "y": -785.6076562320527
86
  },
87
+ "height": 950.0,
88
  "parentId": null,
89
+ "width": 1256.0
 
90
  },
91
  {
92
  "id": "LLM 1",
 
99
  "display": null,
100
  "error": null,
101
  "meta": {
 
102
  "inputs": {},
103
+ "type": "basic",
104
+ "name": "LLM",
105
  "params": {
106
  "name": {
107
  "name": "name",
 
113
  },
114
  "outputs": {
115
  "output": {
116
+ "name": "output",
117
  "type": {
118
  "type": "None"
119
  },
120
+ "position": "top"
121
  }
122
+ }
 
123
  }
124
  },
125
  "position": {
 
142
  "display": null,
143
  "error": null,
144
  "meta": {
145
+ "type": "basic",
146
  "name": "Scenario selector",
147
  "inputs": {},
148
+ "outputs": {
149
+ "output": {
150
+ "type": {
151
+ "type": "None"
152
+ },
153
+ "position": "top",
154
+ "name": "output"
155
+ }
156
+ },
157
  "params": {
158
  "scenario_file": {
 
159
  "type": {
160
  "type": "<class 'str'>"
161
  },
162
+ "default": null,
163
  "name": "scenario_file"
164
  },
165
  "node_types": {
 
 
166
  "type": {
167
  "type": "<class 'str'>"
 
 
 
 
 
 
 
 
168
  },
169
+ "name": "node_types",
170
+ "default": "intent_cluster"
171
  }
172
  }
173
  }
 
176
  "x": -549.1300345090008,
177
  "y": 1086.4852248156676
178
  },
 
179
  "width": 200.0,
180
+ "height": 200.0,
181
  "parentId": null
182
  },
183
  {
 
192
  "error": null,
193
  "meta": {
194
  "name": "Chat API",
195
+ "outputs": {
196
+ "output": {
197
+ "position": "top",
198
+ "name": "output",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
199
  "type": {
200
+ "type": "None"
201
  }
202
  }
203
  },
 
204
  "params": {
205
  "model": {
206
  "type": {
 
210
  "name": "model"
211
  }
212
  },
213
+ "inputs": {
214
+ "knowledge_base": {
 
215
  "type": {
216
+ "type": "<class 'inspect._empty'>"
217
  },
218
+ "name": "knowledge_base",
219
+ "position": "bottom"
220
+ },
221
+ "chatbot": {
222
+ "type": {
223
+ "type": "<class 'inspect._empty'>"
224
+ },
225
+ "position": "bottom",
226
+ "name": "chatbot"
227
+ },
228
+ "chat_processor": {
229
+ "position": "bottom",
230
+ "type": {
231
+ "type": "<class 'inspect._empty'>"
232
+ },
233
+ "name": "chat_processor"
234
  }
235
+ },
236
+ "type": "basic"
237
  }
238
  },
239
  "position": {
 
251
  "title": "Knowledge base",
252
  "params": {
253
  "nodes_path": "/home/darabos/nvme/lynxscribe/examples/chat_api/data/lynx/nodes.pickle",
254
+ "edges_path": "/home/darabos/nvme/lynxscribe/examples/chat_api/data/lynx/edges.pickle",
255
+ "template_cluster_path": "/home/darabos/nvme/lynxscribe/examples/chat_api/data/lynx/tempclusters.pickle"
256
  },
257
  "display": null,
258
  "error": null,
259
  "meta": {
260
  "params": {
261
+ "nodes_path": {
262
+ "name": "nodes_path",
263
+ "default": "nodes.pickle",
264
  "type": {
265
  "type": "<class 'str'>"
266
  }
267
  },
268
+ "edges_path": {
 
 
269
  "type": {
270
  "type": "<class 'str'>"
271
+ },
272
+ "name": "edges_path",
273
+ "default": "edges.pickle"
274
  },
275
+ "template_cluster_path": {
276
+ "name": "template_cluster_path",
277
+ "default": "tempclusters.pickle",
278
  "type": {
279
  "type": "<class 'str'>"
280
  }
281
  }
282
  },
283
  "name": "Knowledge base",
284
+ "inputs": {},
285
+ "type": "basic",
286
  "outputs": {
287
  "output": {
288
+ "name": "output",
289
  "type": {
290
  "type": "None"
291
  },
292
+ "position": "top"
 
293
  }
294
+ }
 
 
295
  }
296
  },
297
  "position": {
298
  "x": 598.8683124946176,
299
  "y": 609.9499973808545
300
  },
 
301
  "height": 320.0,
302
+ "parentId": null,
303
  "width": 336.0
304
  },
305
  {
 
308
  "data": {
309
  "title": "RAG chatbot",
310
  "params": {
311
+ "limits_by_type": "{\"information\": [2, 3], \"summary\": [2, 3]}",
312
  "negative_answer": "I'm sorry, but the data I've been trained on does not contain any information related to your question.",
313
  "max_results": "5",
 
314
  "strict_limits": true
315
  },
316
  "display": null,
317
  "error": null,
318
  "meta": {
319
+ "params": {
320
+ "negative_answer": {
321
+ "default": "I'm sorry, but the data I've been trained on does not contain any information related to your question.",
322
+ "name": "negative_answer",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
323
  "type": {
324
+ "type": "<class 'str'>"
325
  }
326
  },
 
 
 
 
 
 
 
 
 
327
  "max_results": {
328
  "name": "max_results",
329
  "type": {
 
331
  },
332
  "default": 5.0
333
  },
 
 
 
 
 
 
 
334
  "strict_limits": {
335
  "default": true,
336
+ "name": "strict_limits",
337
  "type": {
338
  "type": "<class 'bool'>"
339
+ }
 
340
  },
341
  "limits_by_type": {
 
342
  "name": "limits_by_type",
343
+ "default": "{}",
344
  "type": {
345
  "type": "<class 'str'>"
346
  }
347
  }
348
  },
349
+ "outputs": {
350
+ "output": {
351
+ "type": {
352
+ "type": "None"
353
+ },
354
+ "position": "top",
355
+ "name": "output"
356
+ }
357
+ },
358
+ "type": "basic",
359
  "name": "RAG chatbot",
360
+ "inputs": {
361
+ "rag_graph": {
362
+ "name": "rag_graph",
363
+ "type": {
364
+ "type": "<class 'inspect._empty'>"
365
+ },
366
+ "position": "bottom"
367
+ },
368
+ "scenario_selector": {
369
+ "name": "scenario_selector",
370
+ "type": {
371
+ "type": "<class 'inspect._empty'>"
372
+ },
373
+ "position": "bottom"
374
+ },
375
+ "llm": {
376
+ "name": "llm",
377
+ "type": {
378
+ "type": "<class 'inspect._empty'>"
379
+ },
380
+ "position": "bottom"
381
+ }
382
+ }
383
  },
384
  "beingResized": false
385
  },
 
387
  "x": -533.1301830766971,
388
  "y": 547.294980747757
389
  },
 
390
  "width": 339.0,
391
+ "parentId": null,
392
  "height": 399.0
393
  },
394
  {
 
401
  "error": null,
402
  "meta": {
403
  "name": "RAG graph",
404
+ "inputs": {
405
+ "vector_store": {
406
+ "name": "vector_store",
407
+ "type": {
408
+ "type": "<class 'inspect._empty'>"
409
+ },
410
+ "position": "bottom"
411
+ },
412
+ "text_embedder": {
413
+ "type": {
414
+ "type": "<class 'inspect._empty'>"
415
+ },
416
+ "position": "bottom",
417
+ "name": "text_embedder"
418
+ }
419
+ },
420
  "params": {},
 
421
  "outputs": {
422
  "output": {
423
  "position": "top",
 
427
  }
428
  }
429
  },
430
+ "type": "basic"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
431
  }
432
  },
433
  "position": {
 
449
  },
450
  "display": null,
451
  "error": null,
452
+ "beingResized": false,
453
  "meta": {
 
454
  "params": {
455
+ "collection_name": {
456
+ "default": "lynx",
 
457
  "type": {
458
  "type": "<class 'str'>"
459
+ },
460
+ "name": "collection_name"
461
  },
462
+ "name": {
463
+ "default": "chromadb",
464
+ "name": "name",
465
  "type": {
466
  "type": "<class 'str'>"
467
  }
468
  }
469
  },
470
+ "name": "Vector store",
471
  "outputs": {
472
  "output": {
473
  "name": "output",
 
477
  "position": "top"
478
  }
479
  },
480
+ "type": "basic",
481
+ "inputs": {}
482
+ }
483
  },
484
  "position": {
485
  "x": -1053.794625339574,
486
  "y": 1347.7711940497127
487
  },
488
+ "height": 227.0,
489
  "parentId": null,
490
+ "width": 275.0
491
  },
492
  {
493
  "id": "Text embedder 2",
 
500
  "display": null,
501
  "error": null,
502
  "meta": {
503
+ "type": "basic",
504
  "params": {
505
  "model": {
 
506
  "type": {
507
  "type": "<class 'str'>"
508
  },
509
+ "name": "model",
510
+ "default": "text-embedding-ada-002"
511
  }
512
  },
 
 
 
 
 
 
 
 
 
 
513
  "inputs": {
514
  "llm": {
515
  "name": "llm",
516
+ "position": "bottom",
517
  "type": {
518
  "type": "<class 'inspect._empty'>"
519
+ }
520
+ }
521
+ },
522
+ "name": "Text embedder",
523
+ "outputs": {
524
+ "output": {
525
+ "name": "output",
526
+ "type": {
527
+ "type": "None"
528
  },
529
+ "position": "top"
530
  }
531
  }
532
  }
 
535
  "x": -719.98604638686,
536
  "y": 1343.5978526690794
537
  },
538
+ "width": 200.0,
539
  "parentId": null,
540
+ "height": 200.0
 
541
  },
542
  {
543
  "id": "LLM 2",
 
550
  "display": null,
551
  "error": null,
552
  "meta": {
553
+ "inputs": {},
554
+ "type": "basic",
 
 
 
 
 
 
 
555
  "params": {
556
  "name": {
557
  "name": "name",
 
561
  }
562
  }
563
  },
564
+ "outputs": {
565
+ "output": {
566
+ "name": "output",
567
+ "type": {
568
+ "type": "None"
569
+ },
570
+ "position": "top"
571
+ }
572
+ },
573
+ "name": "LLM"
574
  }
575
  },
576
  "position": {
 
592
  "display": null,
593
  "error": null,
594
  "meta": {
595
+ "name": "Truncate history",
596
  "inputs": {},
597
+ "type": "basic",
598
  "params": {
599
  "max_tokens": {
 
600
  "type": {
601
  "type": "<class 'int'>"
602
  },
603
+ "default": 10000.0,
604
  "name": "max_tokens"
605
  }
606
  },
607
  "outputs": {
608
  "output": {
 
 
609
  "type": {
610
  "type": "None"
611
+ },
612
+ "name": "output",
613
+ "position": "top"
614
  }
615
+ }
 
 
616
  }
617
  },
618
  "position": {
 
620
  "y": 1044.7639853229612
621
  },
622
  "height": 200.0,
623
+ "parentId": null,
624
+ "width": 200.0
625
  },
626
  {
627
  "id": "Chat processor 1",
 
631
  "params": {},
632
  "display": null,
633
  "error": null,
 
634
  "collapsed": true,
635
+ "__execution_delay": null,
636
  "meta": {
 
 
 
 
 
 
 
 
 
 
637
  "inputs": {
638
  "processor": {
639
+ "position": "bottom",
640
  "type": {
641
  "type": "<class 'inspect._empty'>"
642
  },
 
643
  "name": "processor"
644
  }
645
  },
646
  "params": {},
647
+ "name": "Chat processor",
648
+ "outputs": {
649
+ "output": {
650
+ "position": "top",
651
+ "name": "output",
652
+ "type": {
653
+ "type": "None"
654
+ }
655
+ }
656
+ },
657
+ "type": "basic"
658
  }
659
  },
660
  "position": {
 
662
  "y": 778.546274223181
663
  },
664
  "width": 200.0,
665
+ "parentId": null,
666
+ "height": 200.0
667
  },
668
  {
669
  "id": "Mask 1",
 
671
  "data": {
672
  "title": "Mask",
673
  "params": {
 
674
675
+ "regex": "([a-z0-9!#$%&'*+\\/=?^_`{|.}~-]+@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?)",
676
+ "mask_pattern": "masked_email_address_{}",
677
+ "name": "email"
678
  },
679
  "display": null,
680
  "error": null,
681
  "meta": {
 
682
  "outputs": {
683
  "output": {
684
+ "position": "top",
685
  "type": {
686
  "type": "None"
687
  },
688
+ "name": "output"
689
  }
690
  },
 
691
  "params": {
692
+ "name": {
693
  "default": "",
694
+ "name": "name",
695
  "type": {
696
  "type": "<class 'str'>"
697
+ }
 
698
  },
699
+ "exceptions": {
700
+ "name": "exceptions",
701
  "default": "",
702
  "type": {
703
  "type": "<class 'str'>"
704
  }
705
  },
706
+ "mask_pattern": {
 
707
  "default": "",
708
  "type": {
709
  "type": "<class 'str'>"
710
+ },
711
+ "name": "mask_pattern"
712
  },
713
+ "regex": {
 
 
714
  "type": {
715
  "type": "<class 'str'>"
716
+ },
717
+ "name": "regex",
718
+ "default": ""
719
  }
720
  },
721
+ "type": "basic",
722
+ "inputs": {},
723
  "name": "Mask"
724
  }
725
  },
 
727
  "x": 233.69759202223884,
728
  "y": 1041.6145468043276
729
  },
 
730
  "width": 200.0,
731
+ "parentId": null,
732
  "height": 200.0
733
  },
734
  {
 
738
  "title": "Mask",
739
  "params": {
740
  "mask_pattern": "masked_credit_card_number_{}",
741
+ "exceptions": "",
742
  "regex": "((?:(?:\\\\d{4}[- ]?){3}\\\\d{4}|\\\\d{15,16}))(?![\\\\d])",
743
+ "name": "credit_card"
744
  },
745
  "display": null,
746
  "error": null,
747
  "meta": {
748
+ "name": "Mask",
749
+ "outputs": {
750
+ "output": {
751
+ "name": "output",
752
+ "type": {
753
+ "type": "None"
754
+ },
755
+ "position": "top"
756
+ }
757
+ },
758
  "type": "basic",
759
+ "inputs": {},
760
  "params": {
761
+ "mask_pattern": {
 
762
  "default": "",
763
  "type": {
764
  "type": "<class 'str'>"
765
+ },
766
+ "name": "mask_pattern"
767
  },
768
+ "name": {
 
769
  "default": "",
770
+ "name": "name",
771
  "type": {
772
  "type": "<class 'str'>"
773
  }
774
  },
775
+ "exceptions": {
776
+ "default": "",
777
  "type": {
778
  "type": "<class 'str'>"
779
  },
780
+ "name": "exceptions"
781
  },
782
+ "regex": {
783
+ "default": "",
784
  "type": {
785
  "type": "<class 'str'>"
786
  },
787
+ "name": "regex"
 
 
 
 
 
 
 
 
 
 
788
  }
789
+ }
 
790
  }
791
  },
792
  "position": {
793
  "x": 513.2761671440603,
794
  "y": 1034.8547191984255
795
  },
796
+ "height": 200.0,
797
  "parentId": null,
798
+ "width": 200.0
799
  },
800
  {
801
  "id": "Test Chat API 2",
 
803
  "data": {
804
  "title": "Test Chat API",
805
  "params": {
806
+ "show_details": false
807
  },
808
  "display": null,
809
  "error": null,
 
 
810
  "meta": {
811
+ "outputs": {
812
+ "output": {
813
+ "position": "right",
814
  "type": {
815
+ "type": "None"
816
  },
817
+ "name": "output"
818
  }
819
  },
820
+ "type": "basic",
821
  "inputs": {
822
+ "chat_api": {
823
+ "name": "chat_api",
824
  "type": {
825
  "type": "<class 'inspect._empty'>"
826
  },
827
+ "position": "bottom"
828
  },
829
+ "message": {
 
 
830
  "type": {
831
  "type": "<class 'inspect._empty'>"
832
+ },
833
+ "name": "message",
834
+ "position": "left"
835
  }
836
  },
837
  "name": "Test Chat API",
838
+ "params": {
839
+ "show_details": {
 
 
840
  "type": {
841
+ "type": "<class 'bool'>"
842
+ },
843
+ "name": "show_details",
844
+ "default": false
845
  }
846
+ }
847
+ },
848
+ "__execution_delay": 0.0,
849
+ "collapsed": false
850
  },
851
  "position": {
852
+ "x": -57.377776548056346,
853
+ "y": -16.924593985348814
854
  },
855
+ "width": 376.0,
856
  "parentId": null,
857
+ "height": 225.0
 
858
  }
859
  ],
860
  "edges": [
lynxkite-app/src/lynxkite/app/main.py CHANGED
@@ -20,7 +20,6 @@ def detect_plugins():
20
  print("No modules found in lynxkite_plugins. Be sure to install some plugins.")
21
  return {}
22
 
23
- print(list(pkgutil.iter_modules(lynxkite_plugins.__path__)))
24
  plugins = {}
25
  for _, name, _ in pkgutil.iter_modules(lynxkite_plugins.__path__):
26
  name = f"lynxkite_plugins.{name}"
 
20
  print("No modules found in lynxkite_plugins. Be sure to install some plugins.")
21
  return {}
22
 
 
23
  plugins = {}
24
  for _, name, _ in pkgutil.iter_modules(lynxkite_plugins.__path__):
25
  name = f"lynxkite_plugins.{name}"
lynxkite-core/build/lib/lynxkite/executors/one_by_one.py DELETED
@@ -1,175 +0,0 @@
1
- """A LynxKite executor that assumes most operations operate on their input one by one."""
2
-
3
- from .. import ops
4
- from .. import workspace
5
- import orjson
6
- import pandas as pd
7
- import pydantic
8
- import traceback
9
- import inspect
10
- import typing
11
-
12
-
13
- class Context(ops.BaseConfig):
14
- """Passed to operation functions as "_ctx" if they have such a parameter."""
15
-
16
- node: workspace.WorkspaceNode
17
- last_result: typing.Any = None
18
-
19
-
20
- class Output(ops.BaseConfig):
21
- """Return this to send values to specific outputs of a node."""
22
-
23
- output_handle: str
24
- value: dict
25
-
26
-
27
- def df_to_list(df):
28
- return df.to_dict(orient="records")
29
-
30
-
31
- def has_ctx(op):
32
- sig = inspect.signature(op.func)
33
- return "_ctx" in sig.parameters
34
-
35
-
36
- CACHES = {}
37
-
38
-
39
- def register(env: str, cache: bool = True):
40
- """Registers the one-by-one executor."""
41
- if cache:
42
- CACHES[env] = {}
43
- cache = CACHES[env]
44
- else:
45
- cache = None
46
- ops.EXECUTORS[env] = lambda ws: execute(ws, ops.CATALOGS[env], cache=cache)
47
-
48
-
49
- def get_stages(ws, catalog):
50
- """Inputs on top/bottom are batch inputs. We decompose the graph into a DAG of components along these edges."""
51
- nodes = {n.id: n for n in ws.nodes}
52
- batch_inputs = {}
53
- inputs = {}
54
- for edge in ws.edges:
55
- inputs.setdefault(edge.target, []).append(edge.source)
56
- node = nodes[edge.target]
57
- op = catalog[node.data.title]
58
- i = op.inputs[edge.targetHandle]
59
- if i.position in "top or bottom":
60
- batch_inputs.setdefault(edge.target, []).append(edge.source)
61
- stages = []
62
- for bt, bss in batch_inputs.items():
63
- upstream = set(bss)
64
- new = set(bss)
65
- while new:
66
- n = new.pop()
67
- for i in inputs.get(n, []):
68
- if i not in upstream:
69
- upstream.add(i)
70
- new.add(i)
71
- stages.append(upstream)
72
- stages.sort(key=lambda s: len(s))
73
- stages.append(set(nodes))
74
- return stages
75
-
76
-
77
- def _default_serializer(obj):
78
- if isinstance(obj, pydantic.BaseModel):
79
- return obj.dict()
80
- return {"__nonserializable__": id(obj)}
81
-
82
-
83
- def make_cache_key(obj):
84
- return orjson.dumps(obj, default=_default_serializer)
85
-
86
-
87
- EXECUTOR_OUTPUT_CACHE = {}
88
-
89
-
90
- async def await_if_needed(obj):
91
- if inspect.isawaitable(obj):
92
- return await obj
93
- return obj
94
-
95
-
96
- async def execute(ws, catalog, cache=None):
97
- nodes = {n.id: n for n in ws.nodes}
98
- contexts = {n.id: Context(node=n) for n in ws.nodes}
99
- edges = {n.id: [] for n in ws.nodes}
100
- for e in ws.edges:
101
- edges[e.source].append(e)
102
- tasks = {}
103
- NO_INPUT = object() # Marker for initial tasks.
104
- for node in ws.nodes:
105
- node.data.error = None
106
- op = catalog.get(node.data.title)
107
- if op is None:
108
- node.data.error = f'Operation "{node.data.title}" not found.'
109
- continue
110
- # Start tasks for nodes that have no non-batch inputs.
111
- if all([i.position in "top or bottom" for i in op.inputs.values()]):
112
- tasks[node.id] = [NO_INPUT]
113
- batch_inputs = {}
114
- # Run the rest until we run out of tasks.
115
- stages = get_stages(ws, catalog)
116
- for stage in stages:
117
- next_stage = {}
118
- while tasks:
119
- n, ts = tasks.popitem()
120
- if n not in stage:
121
- next_stage.setdefault(n, []).extend(ts)
122
- continue
123
- node = nodes[n]
124
- data = node.data
125
- op = catalog[data.title]
126
- params = {**data.params}
127
- if has_ctx(op):
128
- params["_ctx"] = contexts[node.id]
129
- results = []
130
- for task in ts:
131
- try:
132
- inputs = []
133
- for i in op.inputs.values():
134
- if i.position in "top or bottom":
135
- assert (n, i.name) in batch_inputs, f"{i.name} is missing"
136
- inputs.append(batch_inputs[(n, i.name)])
137
- else:
138
- inputs.append(task)
139
- if cache is not None:
140
- key = make_cache_key((inputs, params))
141
- if key not in cache:
142
- cache[key] = await await_if_needed(op(*inputs, **params))
143
- result = cache[key]
144
- else:
145
- result = await await_if_needed(op(*inputs, **params))
146
- except Exception as e:
147
- traceback.print_exc()
148
- data.error = str(e)
149
- break
150
- contexts[node.id].last_result = result
151
- # Returned lists and DataFrames are considered multiple tasks.
152
- if isinstance(result, pd.DataFrame):
153
- result = df_to_list(result)
154
- elif not isinstance(result, list):
155
- result = [result]
156
- results.extend(result)
157
- else: # Finished all tasks without errors.
158
- if (
159
- op.type == "visualization"
160
- or op.type == "table_view"
161
- or op.type == "image"
162
- ):
163
- data.display = results[0]
164
- for edge in edges[node.id]:
165
- t = nodes[edge.target]
166
- op = catalog[t.data.title]
167
- i = op.inputs[edge.targetHandle]
168
- if i.position in "top or bottom":
169
- batch_inputs.setdefault(
170
- (edge.target, edge.targetHandle), []
171
- ).extend(results)
172
- else:
173
- tasks.setdefault(edge.target, []).extend(results)
174
- tasks = next_stage
175
- return contexts
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
lynxkite-core/build/lib/lynxkite/ops.py DELETED
@@ -1,224 +0,0 @@
1
- """API for implementing LynxKite operations."""
2
-
3
- from __future__ import annotations
4
- import enum
5
- import functools
6
- import inspect
7
- import pydantic
8
- import typing
9
- from typing_extensions import Annotated
10
-
11
- CATALOGS = {}
12
- EXECUTORS = {}
13
-
14
- typeof = type # We have some arguments called "type".
15
-
16
-
17
- def type_to_json(t):
18
- if isinstance(t, type) and issubclass(t, enum.Enum):
19
- return {"enum": list(t.__members__.keys())}
20
- if getattr(t, "__metadata__", None):
21
- return t.__metadata__[-1]
22
- return {"type": str(t)}
23
-
24
-
25
- Type = Annotated[typing.Any, pydantic.PlainSerializer(type_to_json, return_type=dict)]
26
- LongStr = Annotated[str, {"format": "textarea"}]
27
- PathStr = Annotated[str, {"format": "path"}]
28
- CollapsedStr = Annotated[str, {"format": "collapsed"}]
29
- NodeAttribute = Annotated[str, {"format": "node attribute"}]
30
- EdgeAttribute = Annotated[str, {"format": "edge attribute"}]
31
-
32
-
33
- class BaseConfig(pydantic.BaseModel):
34
- model_config = pydantic.ConfigDict(
35
- arbitrary_types_allowed=True,
36
- )
37
-
38
-
39
- class Parameter(BaseConfig):
40
- """Defines a parameter for an operation."""
41
-
42
- name: str
43
- default: typing.Any
44
- type: Type = None
45
-
46
- @staticmethod
47
- def options(name, options, default=None):
48
- e = enum.Enum(f"OptionsFor_{name}", options)
49
- return Parameter.basic(name, e[default or options[0]], e)
50
-
51
- @staticmethod
52
- def collapsed(name, default, type=None):
53
- return Parameter.basic(name, default, CollapsedStr)
54
-
55
- @staticmethod
56
- def basic(name, default=None, type=None):
57
- if default is inspect._empty:
58
- default = None
59
- if type is None or type is inspect._empty:
60
- type = typeof(default) if default is not None else None
61
- return Parameter(name=name, default=default, type=type)
62
-
63
-
64
- class Input(BaseConfig):
65
- name: str
66
- type: Type
67
- position: str = "left"
68
-
69
-
70
- class Output(BaseConfig):
71
- name: str
72
- type: Type
73
- position: str = "right"
74
-
75
-
76
- MULTI_INPUT = Input(name="multi", type="*")
77
-
78
-
79
- def basic_inputs(*names):
80
- return {name: Input(name=name, type=None) for name in names}
81
-
82
-
83
- def basic_outputs(*names):
84
- return {name: Output(name=name, type=None) for name in names}
85
-
86
-
87
- class Op(BaseConfig):
88
- func: typing.Callable = pydantic.Field(exclude=True)
89
- name: str
90
- params: dict[str, Parameter]
91
- inputs: dict[str, Input]
92
- outputs: dict[str, Output]
93
- type: str = "basic" # The UI to use for this operation.
94
-
95
- def __call__(self, *inputs, **params):
96
- # Convert parameters.
97
- for p in params:
98
- if p in self.params:
99
- if self.params[p].type == int:
100
- params[p] = int(params[p])
101
- elif self.params[p].type == float:
102
- params[p] = float(params[p])
103
- elif isinstance(self.params[p].type, enum.EnumMeta):
104
- params[p] = self.params[p].type[params[p]]
105
- res = self.func(*inputs, **params)
106
- return res
107
-
108
-
109
- def op(env: str, name: str, *, view="basic", outputs=None):
110
- """Decorator for defining an operation."""
111
-
112
- def decorator(func):
113
- sig = inspect.signature(func)
114
- # Positional arguments are inputs.
115
- inputs = {
116
- name: Input(name=name, type=param.annotation)
117
- for name, param in sig.parameters.items()
118
- if param.kind != param.KEYWORD_ONLY
119
- }
120
- params = {}
121
- for n, param in sig.parameters.items():
122
- if param.kind == param.KEYWORD_ONLY and not n.startswith("_"):
123
- params[n] = Parameter.basic(n, param.default, param.annotation)
124
- if outputs:
125
- _outputs = {name: Output(name=name, type=None) for name in outputs}
126
- else:
127
- _outputs = (
128
- {"output": Output(name="output", type=None)} if view == "basic" else {}
129
- )
130
- op = Op(
131
- func=func,
132
- name=name,
133
- params=params,
134
- inputs=inputs,
135
- outputs=_outputs,
136
- type=view,
137
- )
138
- CATALOGS.setdefault(env, {})
139
- CATALOGS[env][name] = op
140
- func.__op__ = op
141
- return func
142
-
143
- return decorator
144
-
145
-
146
- def input_position(**kwargs):
147
- """Decorator for specifying unusual positions for the inputs."""
148
-
149
- def decorator(func):
150
- op = func.__op__
151
- for k, v in kwargs.items():
152
- op.inputs[k].position = v
153
- return func
154
-
155
- return decorator
156
-
157
-
158
- def output_position(**kwargs):
159
- """Decorator for specifying unusual positions for the outputs."""
160
-
161
- def decorator(func):
162
- op = func.__op__
163
- for k, v in kwargs.items():
164
- op.outputs[k].position = v
165
- return func
166
-
167
- return decorator
168
-
169
-
170
- def no_op(*args, **kwargs):
171
- if args:
172
- return args[0]
173
- return None
174
-
175
-
176
- def register_passive_op(env: str, name: str, inputs=[], outputs=["output"], params=[]):
177
- """A passive operation has no associated code."""
178
- op = Op(
179
- func=no_op,
180
- name=name,
181
- params={p.name: p for p in params},
182
- inputs=dict(
183
- (i, Input(name=i, type=None)) if isinstance(i, str) else (i.name, i)
184
- for i in inputs
185
- ),
186
- outputs=dict(
187
- (o, Output(name=o, type=None)) if isinstance(o, str) else (o.name, o)
188
- for o in outputs
189
- ),
190
- )
191
- CATALOGS.setdefault(env, {})
192
- CATALOGS[env][name] = op
193
- return op
194
-
195
-
196
- def register_executor(env: str):
197
- """Decorator for registering an executor."""
198
-
199
- def decorator(func):
200
- EXECUTORS[env] = func
201
- return func
202
-
203
- return decorator
204
-
205
-
206
- def op_registration(env: str):
207
- return functools.partial(op, env)
208
-
209
-
210
- def passive_op_registration(env: str):
211
- return functools.partial(register_passive_op, env)
212
-
213
-
214
- def register_area(env, name, params=[]):
215
- """A node that represents an area. It can contain other nodes, but does not restrict movement in any way."""
216
- op = Op(
217
- func=no_op,
218
- name=name,
219
- params={p.name: p for p in params},
220
- inputs={},
221
- outputs={},
222
- type="area",
223
- )
224
- CATALOGS[env][name] = op
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
lynxkite-core/build/lib/lynxkite/workspace.py DELETED
@@ -1,96 +0,0 @@
1
- """For working with LynxKite workspaces."""
2
-
3
- from typing import Optional
4
- import dataclasses
5
- import os
6
- import pydantic
7
- import tempfile
8
- from . import ops
9
-
10
-
11
- class BaseConfig(pydantic.BaseModel):
12
- model_config = pydantic.ConfigDict(
13
- extra="allow",
14
- )
15
-
16
-
17
- class Position(BaseConfig):
18
- x: float
19
- y: float
20
-
21
-
22
- class WorkspaceNodeData(BaseConfig):
23
- title: str
24
- params: dict
25
- display: Optional[object] = None
26
- error: Optional[str] = None
27
- # Also contains a "meta" field when going out.
28
- # This is ignored when coming back from the frontend.
29
-
30
-
31
- class WorkspaceNode(BaseConfig):
32
- id: str
33
- type: str
34
- data: WorkspaceNodeData
35
- position: Position
36
-
37
-
38
- class WorkspaceEdge(BaseConfig):
39
- id: str
40
- source: str
41
- target: str
42
- sourceHandle: str
43
- targetHandle: str
44
-
45
-
46
- class Workspace(BaseConfig):
47
- env: str = ""
48
- nodes: list[WorkspaceNode] = dataclasses.field(default_factory=list)
49
- edges: list[WorkspaceEdge] = dataclasses.field(default_factory=list)
50
-
51
-
52
- async def execute(ws: Workspace):
53
- if ws.env in ops.EXECUTORS:
54
- await ops.EXECUTORS[ws.env](ws)
55
-
56
-
57
- def save(ws: Workspace, path: str):
58
- j = ws.model_dump_json(indent=2)
59
- dirname, basename = os.path.split(path)
60
- # Create temp file in the same directory to make sure it's on the same filesystem.
61
- with tempfile.NamedTemporaryFile(
62
- "w", prefix=f".{basename}.", dir=dirname, delete=False
63
- ) as f:
64
- temp_name = f.name
65
- f.write(j)
66
- os.replace(temp_name, path)
67
-
68
-
69
- def load(path: str):
70
- with open(path) as f:
71
- j = f.read()
72
- ws = Workspace.model_validate_json(j)
73
- # Metadata is added after loading. This way code changes take effect on old boxes too.
74
- _update_metadata(ws)
75
- return ws
76
-
77
-
78
- def _update_metadata(ws):
79
- catalog = ops.CATALOGS.get(ws.env, {})
80
- nodes = {node.id: node for node in ws.nodes}
81
- done = set()
82
- while len(done) < len(nodes):
83
- for node in ws.nodes:
84
- if node.id in done:
85
- continue
86
- data = node.data
87
- op = catalog.get(data.title)
88
- if op:
89
- data.meta = op
90
- node.type = op.type
91
- if data.error == "Unknown operation.":
92
- data.error = None
93
- else:
94
- data.error = "Unknown operation."
95
- done.add(node.id)
96
- return ws
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
lynxkite-lynxscribe/pyproject.toml CHANGED
@@ -6,6 +6,7 @@ readme = "README.md"
6
  requires-python = ">=3.11"
7
  dependencies = [
8
  "lynxkite-core",
 
9
  ]
10
 
11
  [tool.uv.sources]
 
6
  requires-python = ">=3.11"
7
  dependencies = [
8
  "lynxkite-core",
9
+ "lynxscribe[openai] @ git+ssh://[email protected]/biggraph/lynxscribe@main",
10
  ]
11
 
12
  [tool.uv.sources]
lynxkite-lynxscribe/src/lynxkite_plugins/lynxscribe/lynxscribe_ops.py CHANGED
@@ -14,7 +14,8 @@ from lynxscribe.components.chat.processors import (
14
  MaskTemplate,
15
  TruncateHistory,
16
  )
17
- from lynxscribe.components.chat.api import ChatAPI, ChatAPIRequest, ChatAPIResponse
 
18
 
19
  from lynxkite.core import ops
20
  import json
@@ -155,16 +156,15 @@ def mask(*, name="", regex="", exceptions="", mask_pattern=""):
155
  @op("Test Chat API")
156
  async def test_chat_api(message, chat_api, *, show_details=False):
157
  chat_api = chat_api[0]["chat_api"]
158
- request = ChatAPIRequest(
159
- session_id="b43215a0-428f-11ef-9454-0242ac120002",
160
- question=message["text"],
161
- history=[],
162
  )
163
  response = await chat_api.answer(request)
164
  if show_details:
165
  return {**response.__dict__}
166
  else:
167
- return {"answer": response.answer}
168
 
169
 
170
  @op("Input chat")
@@ -237,12 +237,7 @@ async def get_chat_api(ws):
237
 
238
  async def stream_chat_api_response(request):
239
  chat_api = await get_chat_api(request["model"])
240
- chat_api_request = ChatAPIRequest(
241
- session_id=request.get("session_id", "00000000-0000-0000-0000-000000000000"),
242
- history=request["messages"][:-1],
243
- question=request["messages"][-1]["content"],
244
- )
245
- response = await chat_api.answer(chat_api_request)
246
  response = response.model_dump()
247
  yield json.dumps(
248
  {
 
14
  MaskTemplate,
15
  TruncateHistory,
16
  )
17
+ from lynxscribe.components.chat.api import ChatAPI
18
+ from lynxscribe.core.models.prompts import ChatCompletionPrompt
19
 
20
  from lynxkite.core import ops
21
  import json
 
156
  @op("Test Chat API")
157
  async def test_chat_api(message, chat_api, *, show_details=False):
158
  chat_api = chat_api[0]["chat_api"]
159
+ request = ChatCompletionPrompt(
160
+ model="",
161
+ messages=[{"role": "user", "content": message["text"]}],
 
162
  )
163
  response = await chat_api.answer(request)
164
  if show_details:
165
  return {**response.__dict__}
166
  else:
167
+ return {"answer": response.choices[0].message.content}
168
 
169
 
170
  @op("Input chat")
 
237
 
238
  async def stream_chat_api_response(request):
239
  chat_api = await get_chat_api(request["model"])
240
+ response = await chat_api.answer(request)
 
 
 
 
 
241
  response = response.model_dump()
242
  yield json.dumps(
243
  {