darabos commited on
Commit
c964cd4
·
unverified ·
2 Parent(s): 29d6ac1 b34d742

Merge pull request #1 from biggraph/darabos-lynxscribe

Browse files
data/LynxScribe demo ADDED
@@ -0,0 +1,949 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "env": "LynxScribe",
3
+ "nodes": [
4
+ {
5
+ "id": "Input chat 1",
6
+ "type": "basic",
7
+ "data": {
8
+ "title": "Input chat",
9
+ "params": {
10
+ "chat": "mi az elet titka"
11
+ },
12
+ "display": null,
13
+ "error": null,
14
+ "meta": {
15
+ "name": "Input chat",
16
+ "params": {
17
+ "chat": {
18
+ "name": "chat",
19
+ "default": null,
20
+ "type": {
21
+ "type": "<class 'str'>"
22
+ }
23
+ }
24
+ },
25
+ "inputs": {},
26
+ "outputs": {
27
+ "output": {
28
+ "name": "output",
29
+ "type": {
30
+ "type": "None"
31
+ },
32
+ "position": "right"
33
+ }
34
+ },
35
+ "type": "basic",
36
+ "sub_nodes": null
37
+ }
38
+ },
39
+ "position": {
40
+ "x": -292.9043729325095,
41
+ "y": 59.46898452253504
42
+ },
43
+ "parentId": null
44
+ },
45
+ {
46
+ "id": "View 1",
47
+ "type": "table_view",
48
+ "data": {
49
+ "title": "View",
50
+ "params": {},
51
+ "display": {
52
+ "dataframes": {
53
+ "df": {
54
+ "columns": [
55
+ "response"
56
+ ],
57
+ "data": [
58
+ [
59
+ "Az élet titka sok ember számára különböző lehet, és sok tényezőtől függ, mint például a személyes értékek, tapasztalatok és célok. Néhány általános gondolat az élet titkairól:\n\n- **Kapcsolatok**: A szeretet és az emberi kapcsolatok nagyon fontosak, hiszen ezek adhatják az élet értelmét.\n- **Önmegvalósítás**: Az, hogy megtaláljuk a szenvedélyeinket és céljainkat, segíthet abban, hogy boldogan éljünk.\n- **Folyamatos tanulás**: Az élet folyamatos tanulás, amely segít fejlődni és alkalmazkodni a változásokhoz.\n- **Egészség**: A fizikai és mentális egészség megőrzése alapvető az életminőség szempontjából.\n\nEzek persze csak általános nézőpontok, és mindenki másképp találhatja meg a saját életének a titkát. Te mivel kapcsolatban keresed az élet titkát?\n\nPlease visit <a href='https://www.linkedin.com/in/g%c3%a1bor-benedek-95578717' target='_blank'>https://www.linkedin.com/in/g%c3%a1bor-benedek-95578717</a> for further information."
60
+ ]
61
+ ]
62
+ }
63
+ }
64
+ },
65
+ "error": null,
66
+ "meta": {
67
+ "name": "View",
68
+ "params": {},
69
+ "inputs": {
70
+ "input": {
71
+ "name": "input",
72
+ "type": {
73
+ "type": "<class 'inspect._empty'>"
74
+ },
75
+ "position": "left"
76
+ }
77
+ },
78
+ "outputs": {},
79
+ "type": "table_view",
80
+ "sub_nodes": null
81
+ }
82
+ },
83
+ "position": {
84
+ "x": 472.96030572661607,
85
+ "y": 44.15182379992555
86
+ },
87
+ "parentId": null
88
+ },
89
+ {
90
+ "id": "LLM 1",
91
+ "type": "basic",
92
+ "data": {
93
+ "title": "LLM",
94
+ "params": {
95
+ "name": "openai"
96
+ },
97
+ "display": null,
98
+ "error": null,
99
+ "meta": {
100
+ "name": "LLM",
101
+ "params": {
102
+ "name": {
103
+ "name": "name",
104
+ "default": "openai",
105
+ "type": {
106
+ "type": "<class 'str'>"
107
+ }
108
+ }
109
+ },
110
+ "inputs": {},
111
+ "outputs": {
112
+ "output": {
113
+ "name": "output",
114
+ "type": {
115
+ "type": "None"
116
+ },
117
+ "position": "top"
118
+ }
119
+ },
120
+ "type": "basic",
121
+ "sub_nodes": null
122
+ }
123
+ },
124
+ "position": {
125
+ "x": -312.5774211084781,
126
+ "y": 1093.4019527511366
127
+ },
128
+ "parentId": null
129
+ },
130
+ {
131
+ "id": "Scenario selector 1",
132
+ "type": "basic",
133
+ "data": {
134
+ "title": "Scenario selector",
135
+ "params": {
136
+ "scenario_file": "/home/darabos/nvme/lynxscribe/examples/chat_api/scenarios.yaml",
137
+ "node_types": "intent_cluster"
138
+ },
139
+ "display": null,
140
+ "error": null,
141
+ "meta": {
142
+ "name": "Scenario selector",
143
+ "params": {
144
+ "scenario_file": {
145
+ "name": "scenario_file",
146
+ "default": null,
147
+ "type": {
148
+ "type": "<class 'str'>"
149
+ }
150
+ },
151
+ "node_types": {
152
+ "name": "node_types",
153
+ "default": "intent_cluster",
154
+ "type": {
155
+ "type": "<class 'str'>"
156
+ }
157
+ }
158
+ },
159
+ "inputs": {},
160
+ "outputs": {
161
+ "output": {
162
+ "name": "output",
163
+ "type": {
164
+ "type": "None"
165
+ },
166
+ "position": "top"
167
+ }
168
+ },
169
+ "type": "basic",
170
+ "sub_nodes": null
171
+ }
172
+ },
173
+ "position": {
174
+ "x": -549.1300345090008,
175
+ "y": 1086.4852248156676
176
+ },
177
+ "parentId": null
178
+ },
179
+ {
180
+ "id": "Chat API 1",
181
+ "type": "basic",
182
+ "data": {
183
+ "title": "Chat API",
184
+ "params": {
185
+ "model": "gpt-4o-mini"
186
+ },
187
+ "display": null,
188
+ "error": null,
189
+ "meta": {
190
+ "name": "Chat API",
191
+ "params": {
192
+ "model": {
193
+ "name": "model",
194
+ "default": "gpt-4o-mini",
195
+ "type": {
196
+ "type": "<class 'str'>"
197
+ }
198
+ }
199
+ },
200
+ "inputs": {
201
+ "chatbot": {
202
+ "name": "chatbot",
203
+ "type": {
204
+ "type": "<class 'inspect._empty'>"
205
+ },
206
+ "position": "bottom"
207
+ },
208
+ "chat_processor": {
209
+ "name": "chat_processor",
210
+ "type": {
211
+ "type": "<class 'inspect._empty'>"
212
+ },
213
+ "position": "bottom"
214
+ },
215
+ "knowledge_base": {
216
+ "name": "knowledge_base",
217
+ "type": {
218
+ "type": "<class 'inspect._empty'>"
219
+ },
220
+ "position": "bottom"
221
+ }
222
+ },
223
+ "outputs": {
224
+ "output": {
225
+ "name": "output",
226
+ "type": {
227
+ "type": "None"
228
+ },
229
+ "position": "top"
230
+ }
231
+ },
232
+ "type": "basic",
233
+ "sub_nodes": null
234
+ }
235
+ },
236
+ "position": {
237
+ "x": -46.94726514341976,
238
+ "y": 235.19823621492515
239
+ },
240
+ "parentId": null
241
+ },
242
+ {
243
+ "id": "Knowledge base 1",
244
+ "type": "basic",
245
+ "data": {
246
+ "title": "Knowledge base",
247
+ "params": {
248
+ "nodes_path": "/home/darabos/nvme/lynxscribe/examples/chat_api/data/lynx/nodes.pickle",
249
+ "edges_path": "/home/darabos/nvme/lynxscribe/examples/chat_api/data/lynx/edges.pickle",
250
+ "template_cluster_path": "/home/darabos/nvme/lynxscribe/examples/chat_api/data/lynx/tempclusters.pickle"
251
+ },
252
+ "display": null,
253
+ "error": null,
254
+ "meta": {
255
+ "name": "Knowledge base",
256
+ "params": {
257
+ "nodes_path": {
258
+ "name": "nodes_path",
259
+ "default": "nodes.pickle",
260
+ "type": {
261
+ "type": "<class 'str'>"
262
+ }
263
+ },
264
+ "edges_path": {
265
+ "name": "edges_path",
266
+ "default": "edges.pickle",
267
+ "type": {
268
+ "type": "<class 'str'>"
269
+ }
270
+ },
271
+ "template_cluster_path": {
272
+ "name": "template_cluster_path",
273
+ "default": "tempclusters.pickle",
274
+ "type": {
275
+ "type": "<class 'str'>"
276
+ }
277
+ }
278
+ },
279
+ "inputs": {},
280
+ "outputs": {
281
+ "output": {
282
+ "name": "output",
283
+ "type": {
284
+ "type": "None"
285
+ },
286
+ "position": "top"
287
+ }
288
+ },
289
+ "type": "basic",
290
+ "sub_nodes": null
291
+ }
292
+ },
293
+ "position": {
294
+ "x": 382.20164582795104,
295
+ "y": 533.2833307141879
296
+ },
297
+ "parentId": null
298
+ },
299
+ {
300
+ "id": "RAG chatbot 1",
301
+ "type": "basic",
302
+ "data": {
303
+ "title": "RAG chatbot",
304
+ "params": {
305
+ "negative_answer": "I'm sorry, but the data I've been trained on does not contain any information related to your question.",
306
+ "limits_by_type": "{\"information\": [2, 3], \"summary\": [2, 3]}",
307
+ "strict_limits": true,
308
+ "max_results": 5
309
+ },
310
+ "display": null,
311
+ "error": null,
312
+ "meta": {
313
+ "name": "RAG chatbot",
314
+ "params": {
315
+ "negative_answer": {
316
+ "name": "negative_answer",
317
+ "default": "I'm sorry, but the data I've been trained on does not contain any information related to your question.",
318
+ "type": {
319
+ "type": "<class 'str'>"
320
+ }
321
+ },
322
+ "limits_by_type": {
323
+ "name": "limits_by_type",
324
+ "default": "{}",
325
+ "type": {
326
+ "type": "<class 'str'>"
327
+ }
328
+ },
329
+ "strict_limits": {
330
+ "name": "strict_limits",
331
+ "default": true,
332
+ "type": {
333
+ "type": "<class 'bool'>"
334
+ }
335
+ },
336
+ "max_results": {
337
+ "name": "max_results",
338
+ "default": 5,
339
+ "type": {
340
+ "type": "<class 'int'>"
341
+ }
342
+ }
343
+ },
344
+ "inputs": {
345
+ "rag_graph": {
346
+ "name": "rag_graph",
347
+ "type": {
348
+ "type": "<class 'inspect._empty'>"
349
+ },
350
+ "position": "bottom"
351
+ },
352
+ "scenario_selector": {
353
+ "name": "scenario_selector",
354
+ "type": {
355
+ "type": "<class 'inspect._empty'>"
356
+ },
357
+ "position": "bottom"
358
+ },
359
+ "llm": {
360
+ "name": "llm",
361
+ "type": {
362
+ "type": "<class 'inspect._empty'>"
363
+ },
364
+ "position": "bottom"
365
+ }
366
+ },
367
+ "outputs": {
368
+ "output": {
369
+ "name": "output",
370
+ "type": {
371
+ "type": "None"
372
+ },
373
+ "position": "top"
374
+ }
375
+ },
376
+ "type": "basic",
377
+ "sub_nodes": null
378
+ },
379
+ "beingResized": false
380
+ },
381
+ "position": {
382
+ "x": -521.6507639530705,
383
+ "y": 547.294980747757
384
+ },
385
+ "parentId": null,
386
+ "width": 336,
387
+ "height": 349
388
+ },
389
+ {
390
+ "id": "RAG graph 1",
391
+ "type": "basic",
392
+ "data": {
393
+ "title": "RAG graph",
394
+ "params": {},
395
+ "display": null,
396
+ "error": null,
397
+ "meta": {
398
+ "name": "RAG graph",
399
+ "params": {},
400
+ "inputs": {
401
+ "vector_store": {
402
+ "name": "vector_store",
403
+ "type": {
404
+ "type": "<class 'inspect._empty'>"
405
+ },
406
+ "position": "bottom"
407
+ },
408
+ "text_embedder": {
409
+ "name": "text_embedder",
410
+ "type": {
411
+ "type": "<class 'inspect._empty'>"
412
+ },
413
+ "position": "bottom"
414
+ }
415
+ },
416
+ "outputs": {
417
+ "output": {
418
+ "name": "output",
419
+ "type": {
420
+ "type": "None"
421
+ },
422
+ "position": "top"
423
+ }
424
+ },
425
+ "type": "basic",
426
+ "sub_nodes": null
427
+ }
428
+ },
429
+ "position": {
430
+ "x": -817.8208895639339,
431
+ "y": 1014.836542916127
432
+ },
433
+ "parentId": null
434
+ },
435
+ {
436
+ "id": "Vector store 1",
437
+ "type": "basic",
438
+ "data": {
439
+ "title": "Vector store",
440
+ "params": {
441
+ "name": "chromadb",
442
+ "collection_name": "lynx"
443
+ },
444
+ "display": null,
445
+ "error": null,
446
+ "meta": {
447
+ "name": "Vector store",
448
+ "params": {
449
+ "name": {
450
+ "name": "name",
451
+ "default": "chromadb",
452
+ "type": {
453
+ "type": "<class 'str'>"
454
+ }
455
+ },
456
+ "collection_name": {
457
+ "name": "collection_name",
458
+ "default": "lynx",
459
+ "type": {
460
+ "type": "<class 'str'>"
461
+ }
462
+ }
463
+ },
464
+ "inputs": {},
465
+ "outputs": {
466
+ "output": {
467
+ "name": "output",
468
+ "type": {
469
+ "type": "None"
470
+ },
471
+ "position": "top"
472
+ }
473
+ },
474
+ "type": "basic",
475
+ "sub_nodes": null
476
+ },
477
+ "beingResized": false
478
+ },
479
+ "position": {
480
+ "x": -1053.794625339574,
481
+ "y": 1347.7711940497127
482
+ },
483
+ "parentId": null,
484
+ "width": 275,
485
+ "height": 227
486
+ },
487
+ {
488
+ "id": "Text embedder 2",
489
+ "type": "basic",
490
+ "data": {
491
+ "title": "Text embedder",
492
+ "params": {
493
+ "model": "text-embedding-ada-002"
494
+ },
495
+ "display": null,
496
+ "error": null,
497
+ "meta": {
498
+ "name": "Text embedder",
499
+ "params": {
500
+ "model": {
501
+ "name": "model",
502
+ "default": "text-embedding-ada-002",
503
+ "type": {
504
+ "type": "<class 'str'>"
505
+ }
506
+ }
507
+ },
508
+ "inputs": {
509
+ "llm": {
510
+ "name": "llm",
511
+ "type": {
512
+ "type": "<class 'inspect._empty'>"
513
+ },
514
+ "position": "bottom"
515
+ }
516
+ },
517
+ "outputs": {
518
+ "output": {
519
+ "name": "output",
520
+ "type": {
521
+ "type": "None"
522
+ },
523
+ "position": "top"
524
+ }
525
+ },
526
+ "type": "basic",
527
+ "sub_nodes": null
528
+ }
529
+ },
530
+ "position": {
531
+ "x": -749.98604638686,
532
+ "y": 1293.5978526690794
533
+ },
534
+ "parentId": null
535
+ },
536
+ {
537
+ "id": "LLM 2",
538
+ "type": "basic",
539
+ "data": {
540
+ "title": "LLM",
541
+ "params": {
542
+ "name": "openai"
543
+ },
544
+ "display": null,
545
+ "error": null,
546
+ "meta": {
547
+ "name": "LLM",
548
+ "params": {
549
+ "name": {
550
+ "name": "name",
551
+ "default": "openai",
552
+ "type": {
553
+ "type": "<class 'str'>"
554
+ }
555
+ }
556
+ },
557
+ "inputs": {},
558
+ "outputs": {
559
+ "output": {
560
+ "name": "output",
561
+ "type": {
562
+ "type": "None"
563
+ },
564
+ "position": "top"
565
+ }
566
+ },
567
+ "type": "basic",
568
+ "sub_nodes": null
569
+ }
570
+ },
571
+ "position": {
572
+ "x": -714.2838040349482,
573
+ "y": 1469.7242636905507
574
+ },
575
+ "parentId": null
576
+ },
577
+ {
578
+ "id": "Truncate history 1",
579
+ "type": "basic",
580
+ "data": {
581
+ "title": "Truncate history",
582
+ "params": {
583
+ "max_tokens": 10000,
584
+ "language": "English"
585
+ },
586
+ "display": null,
587
+ "error": null,
588
+ "meta": {
589
+ "name": "Truncate history",
590
+ "params": {
591
+ "max_tokens": {
592
+ "name": "max_tokens",
593
+ "default": 10000,
594
+ "type": {
595
+ "type": "<class 'int'>"
596
+ }
597
+ },
598
+ "language": {
599
+ "name": "language",
600
+ "default": "English",
601
+ "type": {
602
+ "type": "<class 'str'>"
603
+ }
604
+ }
605
+ },
606
+ "inputs": {},
607
+ "outputs": {
608
+ "output": {
609
+ "name": "output",
610
+ "type": {
611
+ "type": "None"
612
+ },
613
+ "position": "top"
614
+ }
615
+ },
616
+ "type": "basic",
617
+ "sub_nodes": null
618
+ }
619
+ },
620
+ "position": {
621
+ "x": 0.08889822620079713,
622
+ "y": 1044.7639853229612
623
+ },
624
+ "parentId": null
625
+ },
626
+ {
627
+ "id": "Chat processor 1",
628
+ "type": "basic",
629
+ "data": {
630
+ "title": "Chat processor",
631
+ "params": {},
632
+ "display": null,
633
+ "error": null,
634
+ "meta": {
635
+ "name": "Chat processor",
636
+ "params": {},
637
+ "inputs": {
638
+ "processor": {
639
+ "name": "processor",
640
+ "type": {
641
+ "type": "<class 'inspect._empty'>"
642
+ },
643
+ "position": "bottom"
644
+ }
645
+ },
646
+ "outputs": {
647
+ "output": {
648
+ "name": "output",
649
+ "type": {
650
+ "type": "None"
651
+ },
652
+ "position": "top"
653
+ }
654
+ },
655
+ "type": "basic",
656
+ "sub_nodes": null
657
+ }
658
+ },
659
+ "position": {
660
+ "x": 266.23062579739207,
661
+ "y": 931.8796075565143
662
+ },
663
+ "parentId": null
664
+ },
665
+ {
666
+ "id": "Test Chat API 1",
667
+ "type": "basic",
668
+ "data": {
669
+ "title": "Test Chat API",
670
+ "params": {},
671
+ "display": null,
672
+ "error": null,
673
+ "meta": {
674
+ "name": "Test Chat API",
675
+ "params": {},
676
+ "inputs": {
677
+ "message": {
678
+ "name": "message",
679
+ "type": {
680
+ "type": "<class 'inspect._empty'>"
681
+ },
682
+ "position": "left"
683
+ },
684
+ "chat_api": {
685
+ "name": "chat_api",
686
+ "type": {
687
+ "type": "<class 'inspect._empty'>"
688
+ },
689
+ "position": "bottom"
690
+ }
691
+ },
692
+ "outputs": {
693
+ "output": {
694
+ "name": "output",
695
+ "type": {
696
+ "type": "None"
697
+ },
698
+ "position": "right"
699
+ }
700
+ },
701
+ "type": "basic",
702
+ "sub_nodes": null
703
+ }
704
+ },
705
+ "position": {
706
+ "x": 30.070483767962628,
707
+ "y": 30.871931330853627
708
+ },
709
+ "parentId": null
710
+ },
711
+ {
712
+ "id": "Mask 1",
713
+ "type": "basic",
714
+ "data": {
715
+ "title": "Mask",
716
+ "params": {
717
+ "name": "email",
718
+ "regex": "([a-z0-9!#$%&'*+\\/=?^_`{|.}~-]+@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?)",
719
720
+ "mask_pattern": "masked_email_address_{}"
721
+ },
722
+ "display": null,
723
+ "error": null,
724
+ "meta": {
725
+ "name": "Mask",
726
+ "params": {
727
+ "name": {
728
+ "name": "name",
729
+ "default": "",
730
+ "type": {
731
+ "type": "None"
732
+ }
733
+ },
734
+ "regex": {
735
+ "name": "regex",
736
+ "default": "",
737
+ "type": {
738
+ "type": "None"
739
+ }
740
+ },
741
+ "exceptions": {
742
+ "name": "exceptions",
743
+ "default": "",
744
+ "type": {
745
+ "type": "None"
746
+ }
747
+ },
748
+ "mask_pattern": {
749
+ "name": "mask_pattern",
750
+ "default": "",
751
+ "type": {
752
+ "type": "None"
753
+ }
754
+ }
755
+ },
756
+ "inputs": {},
757
+ "outputs": {
758
+ "output": {
759
+ "name": "output",
760
+ "type": {
761
+ "type": "None"
762
+ },
763
+ "position": "top"
764
+ }
765
+ },
766
+ "type": "basic",
767
+ "sub_nodes": null
768
+ }
769
+ },
770
+ "position": {
771
+ "x": 233.69759202223884,
772
+ "y": 1041.6145468043276
773
+ },
774
+ "parentId": null
775
+ },
776
+ {
777
+ "id": "Mask 2",
778
+ "type": "basic",
779
+ "data": {
780
+ "title": "Mask",
781
+ "params": {
782
+ "name": "credit_card",
783
+ "regex": "((?:(?:\\\\d{4}[- ]?){3}\\\\d{4}|\\\\d{15,16}))(?![\\\\d])",
784
+ "exceptions": "",
785
+ "mask_pattern": "masked_credit_card_number_{}"
786
+ },
787
+ "display": null,
788
+ "error": null,
789
+ "meta": {
790
+ "name": "Mask",
791
+ "params": {
792
+ "name": {
793
+ "name": "name",
794
+ "default": "",
795
+ "type": {
796
+ "type": "None"
797
+ }
798
+ },
799
+ "regex": {
800
+ "name": "regex",
801
+ "default": "",
802
+ "type": {
803
+ "type": "None"
804
+ }
805
+ },
806
+ "exceptions": {
807
+ "name": "exceptions",
808
+ "default": "",
809
+ "type": {
810
+ "type": "None"
811
+ }
812
+ },
813
+ "mask_pattern": {
814
+ "name": "mask_pattern",
815
+ "default": "",
816
+ "type": {
817
+ "type": "None"
818
+ }
819
+ }
820
+ },
821
+ "inputs": {},
822
+ "outputs": {
823
+ "output": {
824
+ "name": "output",
825
+ "type": {
826
+ "type": "None"
827
+ },
828
+ "position": "top"
829
+ }
830
+ },
831
+ "type": "basic",
832
+ "sub_nodes": null
833
+ }
834
+ },
835
+ "position": {
836
+ "x": 513.2761671440603,
837
+ "y": 1034.8547191984255
838
+ },
839
+ "parentId": null
840
+ }
841
+ ],
842
+ "edges": [
843
+ {
844
+ "id": "xy-edge__Knowledge base 1output-Chat API 1knowledge_base",
845
+ "source": "Knowledge base 1",
846
+ "target": "Chat API 1",
847
+ "sourceHandle": "output",
848
+ "targetHandle": "knowledge_base"
849
+ },
850
+ {
851
+ "id": "xy-edge__RAG chatbot 1output-Chat API 1chatbot",
852
+ "source": "RAG chatbot 1",
853
+ "target": "Chat API 1",
854
+ "sourceHandle": "output",
855
+ "targetHandle": "chatbot"
856
+ },
857
+ {
858
+ "id": "xy-edge__LLM 1output-RAG chatbot 1llm",
859
+ "source": "LLM 1",
860
+ "target": "RAG chatbot 1",
861
+ "sourceHandle": "output",
862
+ "targetHandle": "llm"
863
+ },
864
+ {
865
+ "id": "xy-edge__Scenario selector 1output-RAG chatbot 1scenario_selector",
866
+ "source": "Scenario selector 1",
867
+ "target": "RAG chatbot 1",
868
+ "sourceHandle": "output",
869
+ "targetHandle": "scenario_selector"
870
+ },
871
+ {
872
+ "id": "xy-edge__RAG graph 1output-RAG chatbot 1rag_graph",
873
+ "source": "RAG graph 1",
874
+ "target": "RAG chatbot 1",
875
+ "sourceHandle": "output",
876
+ "targetHandle": "rag_graph"
877
+ },
878
+ {
879
+ "id": "xy-edge__Vector store 1output-RAG graph 1vector_store",
880
+ "source": "Vector store 1",
881
+ "target": "RAG graph 1",
882
+ "sourceHandle": "output",
883
+ "targetHandle": "vector_store"
884
+ },
885
+ {
886
+ "id": "xy-edge__Text embedder 2output-RAG graph 1text_embedder",
887
+ "source": "Text embedder 2",
888
+ "target": "RAG graph 1",
889
+ "sourceHandle": "output",
890
+ "targetHandle": "text_embedder"
891
+ },
892
+ {
893
+ "id": "xy-edge__LLM 2output-Text embedder 2llm",
894
+ "source": "LLM 2",
895
+ "target": "Text embedder 2",
896
+ "sourceHandle": "output",
897
+ "targetHandle": "llm"
898
+ },
899
+ {
900
+ "id": "xy-edge__Truncate history 1output-Chat processor 1processor",
901
+ "source": "Truncate history 1",
902
+ "target": "Chat processor 1",
903
+ "sourceHandle": "output",
904
+ "targetHandle": "processor"
905
+ },
906
+ {
907
+ "id": "xy-edge__Chat processor 1output-Chat API 1chat_processor",
908
+ "source": "Chat processor 1",
909
+ "target": "Chat API 1",
910
+ "sourceHandle": "output",
911
+ "targetHandle": "chat_processor"
912
+ },
913
+ {
914
+ "id": "xy-edge__Chat API 1output-Test Chat API 1chat_api",
915
+ "source": "Chat API 1",
916
+ "target": "Test Chat API 1",
917
+ "sourceHandle": "output",
918
+ "targetHandle": "chat_api"
919
+ },
920
+ {
921
+ "id": "xy-edge__Test Chat API 1output-View 1input",
922
+ "source": "Test Chat API 1",
923
+ "target": "View 1",
924
+ "sourceHandle": "output",
925
+ "targetHandle": "input"
926
+ },
927
+ {
928
+ "id": "xy-edge__Input chat 1output-Test Chat API 1message",
929
+ "source": "Input chat 1",
930
+ "target": "Test Chat API 1",
931
+ "sourceHandle": "output",
932
+ "targetHandle": "message"
933
+ },
934
+ {
935
+ "id": "xy-edge__Mask 1output-Chat processor 1processor",
936
+ "source": "Mask 1",
937
+ "target": "Chat processor 1",
938
+ "sourceHandle": "output",
939
+ "targetHandle": "processor"
940
+ },
941
+ {
942
+ "id": "xy-edge__Mask 2output-Chat processor 1processor",
943
+ "source": "Mask 2",
944
+ "target": "Chat processor 1",
945
+ "sourceHandle": "output",
946
+ "targetHandle": "processor"
947
+ }
948
+ ]
949
+ }
requirements.txt CHANGED
@@ -2,6 +2,7 @@ fastapi
2
  matplotlib
3
  networkx
4
  numpy
 
5
  pandas
6
  scipy
7
  uvicorn[standard]
 
2
  matplotlib
3
  networkx
4
  numpy
5
+ orjson
6
  pandas
7
  scipy
8
  uvicorn[standard]
server/executors/one_by_one.py CHANGED
@@ -1,8 +1,8 @@
1
  from .. import ops
2
  from .. import workspace
3
- import fastapi
4
- import json
5
  import pandas as pd
 
6
  import traceback
7
  import inspect
8
  import typing
@@ -37,7 +37,7 @@ def register(env: str, cache: bool = True):
37
  ops.EXECUTORS[env] = lambda ws: execute(ws, ops.CATALOGS[env], cache=cache)
38
 
39
  def get_stages(ws, catalog):
40
- '''Inputs on top are batch inputs. We decompose the graph into a DAG of components along these edges.'''
41
  nodes = {n.id: n for n in ws.nodes}
42
  batch_inputs = {}
43
  inputs = {}
@@ -46,7 +46,7 @@ def get_stages(ws, catalog):
46
  node = nodes[edge.target]
47
  op = catalog[node.data.title]
48
  i = op.inputs[edge.targetHandle]
49
- if i.position == 'top':
50
  batch_inputs.setdefault(edge.target, []).append(edge.source)
51
  stages = []
52
  for bt, bss in batch_inputs.items():
@@ -63,6 +63,15 @@ def get_stages(ws, catalog):
63
  stages.append(set(nodes))
64
  return stages
65
 
 
 
 
 
 
 
 
 
 
66
  EXECUTOR_OUTPUT_CACHE = {}
67
 
68
  def execute(ws, catalog, cache=None):
@@ -77,7 +86,7 @@ def execute(ws, catalog, cache=None):
77
  node.data.error = None
78
  op = catalog[node.data.title]
79
  # Start tasks for nodes that have no non-batch inputs.
80
- if all([i.position == 'top' for i in op.inputs.values()]):
81
  tasks[node.id] = [NO_INPUT]
82
  batch_inputs = {}
83
  # Run the rest until we run out of tasks.
@@ -99,12 +108,12 @@ def execute(ws, catalog, cache=None):
99
  for task in ts:
100
  try:
101
  inputs = [
102
- batch_inputs[(n, i.name)] if i.position == 'top' else task
103
  for i in op.inputs.values()]
104
- if cache:
105
- key = json.dumps(fastapi.encoders.jsonable_encoder((inputs, params)))
106
  if key not in cache:
107
- cache[key] = op.func(*inputs, **params)
108
  result = cache[key]
109
  else:
110
  result = op(*inputs, **params)
@@ -126,8 +135,9 @@ def execute(ws, catalog, cache=None):
126
  t = nodes[edge.target]
127
  op = catalog[t.data.title]
128
  i = op.inputs[edge.targetHandle]
129
- if i.position == 'top':
130
  batch_inputs.setdefault((edge.target, edge.targetHandle), []).extend(results)
131
  else:
132
  tasks.setdefault(edge.target, []).extend(results)
133
  tasks = next_stage
 
 
1
  from .. import ops
2
  from .. import workspace
3
+ import orjson
 
4
  import pandas as pd
5
+ import pydantic
6
  import traceback
7
  import inspect
8
  import typing
 
37
  ops.EXECUTORS[env] = lambda ws: execute(ws, ops.CATALOGS[env], cache=cache)
38
 
39
  def get_stages(ws, catalog):
40
+ '''Inputs on top/bottom are batch inputs. We decompose the graph into a DAG of components along these edges.'''
41
  nodes = {n.id: n for n in ws.nodes}
42
  batch_inputs = {}
43
  inputs = {}
 
46
  node = nodes[edge.target]
47
  op = catalog[node.data.title]
48
  i = op.inputs[edge.targetHandle]
49
+ if i.position in 'top or bottom':
50
  batch_inputs.setdefault(edge.target, []).append(edge.source)
51
  stages = []
52
  for bt, bss in batch_inputs.items():
 
63
  stages.append(set(nodes))
64
  return stages
65
 
66
+
67
+ def _default_serializer(obj):
68
+ if isinstance(obj, pydantic.BaseModel):
69
+ return obj.dict()
70
+ return {"__nonserializable__": id(obj)}
71
+
72
+ def make_cache_key(obj):
73
+ return orjson.dumps(obj, default=_default_serializer)
74
+
75
  EXECUTOR_OUTPUT_CACHE = {}
76
 
77
  def execute(ws, catalog, cache=None):
 
86
  node.data.error = None
87
  op = catalog[node.data.title]
88
  # Start tasks for nodes that have no non-batch inputs.
89
+ if all([i.position in 'top or bottom' for i in op.inputs.values()]):
90
  tasks[node.id] = [NO_INPUT]
91
  batch_inputs = {}
92
  # Run the rest until we run out of tasks.
 
108
  for task in ts:
109
  try:
110
  inputs = [
111
+ batch_inputs[(n, i.name)] if i.position in 'top or bottom' else task
112
  for i in op.inputs.values()]
113
+ if cache is not None:
114
+ key = make_cache_key((inputs, params))
115
  if key not in cache:
116
+ cache[key] = op(*inputs, **params)
117
  result = cache[key]
118
  else:
119
  result = op(*inputs, **params)
 
135
  t = nodes[edge.target]
136
  op = catalog[t.data.title]
137
  i = op.inputs[edge.targetHandle]
138
+ if i.position in 'top or bottom':
139
  batch_inputs.setdefault((edge.target, edge.targetHandle), []).extend(results)
140
  else:
141
  tasks.setdefault(edge.target, []).extend(results)
142
  tasks = next_stage
143
+ return contexts
server/lynxscribe_ops.py CHANGED
@@ -1,52 +1,200 @@
1
- '''An example of passive ops. Just using LynxKite to describe the configuration of a complex system.'''
2
- from .ops import passive_op_registration, Parameter as P, MULTI_INPUT
3
-
4
- reg = passive_op_registration('LynxScribe')
5
- reg('Scrape documents', params=[P.basic('url', '')])
6
- reg('Conversation logs')
7
- reg('Extract graph', inputs=['input'])
8
- reg('Compute embeddings', inputs=['input'], params=[P.options('method', ['OpenAI', 'graph', 'Yi-34b']), P.basic('dimensions', 1234)])
9
- reg('Vector DB', inputs=[MULTI_INPUT], params=[P.options('backend', ['FAISS', 'ANN', 'HNSW'])])
10
- reg('Chat UI', outputs=[], inputs=['input'])
11
- reg('Chat backend')
12
- reg('WhatsApp')
13
- reg('PII removal', inputs=['input'])
14
- reg('Intent classification', inputs=['input'])
15
- reg('System prompt', params=[P.basic('prompt', 'You are a helpful chatbot.')])
16
- reg('LLM', inputs=[MULTI_INPUT], params=[P.options('backend', ['GPT-4', 'Yi-34b', 'Claude 3 Opus', 'Google Gemini'])])
17
-
18
- # From Marton's mock-up.
19
- yi = 'Yi-34B (triton)'
20
- reg('Chat Input', params=[
21
- P.options('load_mode', ['augmented']),
22
- P.options('model', [yi]),
23
- P.options('embedder', ['GritLM-7b (triton)']),
24
- ])
25
- reg('k-NN Intent Classifier', inputs=['qa_embs', 'rag_graph'], params=[
26
- P.options('distance', ['cosine', 'euclidean']),
27
- P.basic('max_dist', 0.3),
28
- P.basic('k', 3),
29
- P.options('voting', ['most common', 'weighted']),
30
- ])
31
- reg('Chroma Graph RAG Loader', inputs=[], params=[
32
- P.options('location', ['GCP']),
33
- P.collapsed('bucket', ''),
34
- P.collapsed('folder', ''),
35
- P.options('embedder', ['GritLM-7b (triton)']),
36
- ])
37
- reg('Scenario Builder', inputs=['input'], params=[
38
- P.collapsed('scenario', ''),
39
- ])
40
- reg('Graph RAG Answer', inputs=['qa_embs', 'intent', 'rag_graph', 'prompt_dict'], params=[
41
- P.options('answer_llm', [yi]),
42
- P.basic('faq_dist', 0.12),
43
- P.basic('max_dist', 0.25),
44
- P.basic('ctx_tokens', 2800),
45
- P.options('distance', ['cosine', 'euclidean']),
46
- P.collapsed('graph_rag_params', ''),
47
- ])
48
- reg('Answer Post Processing', inputs=['qa_embs', 'rag_graph'], params=[
49
- P.options('distance', ['cosine', 'euclidean']),
50
- P.basic('min_conf', 0.78),
51
- ])
52
- reg('Chat Output', inputs=['input'], outputs=[])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ LynxScribe configuration and testing in LynxKite.
3
+ """
4
+ from lynxscribe.core.llm.base import get_llm_engine
5
+ from lynxscribe.core.vector_store.base import get_vector_store
6
+ from lynxscribe.common.config import load_config
7
+ from lynxscribe.components.text_embedder import TextEmbedder
8
+ from lynxscribe.components.rag.rag_graph import RAGGraph
9
+ from lynxscribe.components.rag.knowledge_base_graph import PandasKnowledgeBaseGraph
10
+ from lynxscribe.components.rag.rag_chatbot import Scenario, ScenarioSelector, RAGChatbot
11
+ from lynxscribe.components.chat_processor.base import ChatProcessor
12
+ from lynxscribe.components.chat_processor.processors import MaskTemplate, TruncateHistory
13
+ from lynxscribe.components.chat_api import ChatAPI, ChatAPIRequest, ChatAPIResponse
14
+
15
+ from . import ops
16
+ import asyncio
17
+ import json
18
+ from .executors import one_by_one
19
+
20
+ ENV = 'LynxScribe'
21
+ one_by_one.register(ENV)
22
+ op = ops.op_registration(ENV)
23
+ output_on_top = ops.output_position(output="top")
24
+
25
+ @output_on_top
26
+ @op("Vector store")
27
+ def vector_store(*, name='chromadb', collection_name='lynx'):
28
+ vector_store = get_vector_store(name=name, collection_name=collection_name)
29
+ return {'vector_store': vector_store}
30
+
31
+ @output_on_top
32
+ @op("LLM")
33
+ def llm(*, name='openai'):
34
+ llm = get_llm_engine(name=name)
35
+ return {'llm': llm}
36
+
37
+ @output_on_top
38
+ @ops.input_position(llm="bottom")
39
+ @op("Text embedder")
40
+ def text_embedder(llm, *, model='text-embedding-ada-002'):
41
+ llm = llm[0]['llm']
42
+ text_embedder = TextEmbedder(llm=llm, model=model)
43
+ return {'text_embedder': text_embedder}
44
+
45
+ @output_on_top
46
+ @ops.input_position(vector_store="bottom", text_embedder="bottom")
47
+ @op("RAG graph")
48
+ def rag_graph(vector_store, text_embedder):
49
+ vector_store = vector_store[0]['vector_store']
50
+ text_embedder = text_embedder[0]['text_embedder']
51
+ rag_graph = RAGGraph(
52
+ PandasKnowledgeBaseGraph(vector_store=vector_store, text_embedder=text_embedder)
53
+ )
54
+ return {'rag_graph': rag_graph}
55
+
56
+ @output_on_top
57
+ @op("Scenario selector")
58
+ def scenario_selector(*, scenario_file: str, node_types='intent_cluster'):
59
+ scenarios = load_config(scenario_file)
60
+ node_types = [t.strip() for t in node_types.split(',')]
61
+ scenario_selector = ScenarioSelector(
62
+ scenarios=[Scenario(**scenario) for scenario in scenarios],
63
+ node_types=node_types,
64
+ )
65
+ return {'scenario_selector': scenario_selector}
66
+
67
+ DEFAULT_NEGATIVE_ANSWER = "I'm sorry, but the data I've been trained on does not contain any information related to your question."
68
+
69
+ @output_on_top
70
+ @ops.input_position(rag_graph="bottom", scenario_selector="bottom", llm="bottom")
71
+ @op("RAG chatbot")
72
+ def rag_chatbot(
73
+ rag_graph, scenario_selector, llm, *,
74
+ negative_answer=DEFAULT_NEGATIVE_ANSWER,
75
+ limits_by_type='{}',
76
+ strict_limits=True, max_results=5):
77
+ rag_graph = rag_graph[0]['rag_graph']
78
+ scenario_selector = scenario_selector[0]['scenario_selector']
79
+ llm = llm[0]['llm']
80
+ limits_by_type = json.loads(limits_by_type)
81
+ rag_chatbot = RAGChatbot(
82
+ rag_graph=rag_graph,
83
+ scenario_selector=scenario_selector,
84
+ llm=llm,
85
+ negative_answer=negative_answer,
86
+ limits_by_type=limits_by_type,
87
+ strict_limits=strict_limits,
88
+ max_results=max_results,
89
+ )
90
+ return {'chatbot': rag_chatbot}
91
+
92
+ @output_on_top
93
+ @ops.input_position(processor="bottom")
94
+ @op("Chat processor")
95
+ def chat_processor(processor, *, _ctx: one_by_one.Context):
96
+ cfg = _ctx.last_result or {'question_processors': [], 'answer_processors': [], 'masks': []}
97
+ for f in ['question_processor', 'answer_processor', 'mask']:
98
+ if f in processor:
99
+ cfg[f + 's'].append(processor[f])
100
+ question_processors = cfg['question_processors'][:]
101
+ answer_processors = cfg['answer_processors'][:]
102
+ masking_templates = {}
103
+ for mask in cfg['masks']:
104
+ masking_templates[mask['name']] = mask
105
+ if masking_templates:
106
+ question_processors.append(MaskTemplate(masking_templates=masking_templates))
107
+ answer_processors.append(MaskTemplate(masking_templates=masking_templates))
108
+ chat_processor = ChatProcessor(question_processors=question_processors, answer_processors=answer_processors)
109
+ return {'chat_processor': chat_processor, **cfg}
110
+
111
+ @output_on_top
112
+ @op("Truncate history")
113
+ def truncate_history(*, max_tokens=10000, language='English'):
114
+ return {'question_processor': TruncateHistory(max_tokens=max_tokens, language=language.lower())}
115
+
116
+ @output_on_top
117
+ @op("Mask")
118
+ def mask(*, name='', regex='', exceptions='', mask_pattern=''):
119
+ exceptions = [e.strip() for e in exceptions.split(',') if e.strip()]
120
+ return {'mask': {'name': name, 'regex': regex, 'exceptions': exceptions, 'mask_pattern': mask_pattern}}
121
+
122
+ @ops.input_position(chat_api="bottom")
123
+ @op("Test Chat API")
124
+ def test_chat_api(message, chat_api):
125
+ chat_api = chat_api[0]['chat_api']
126
+ request = ChatAPIRequest(session_id="b43215a0-428f-11ef-9454-0242ac120002", question=message['text'], history=[])
127
+ response = asyncio.run(chat_api.answer(request))
128
+ return {'response': response.answer}
129
+
130
+ @op("Input chat")
131
+ def input_chat(*, chat: str):
132
+ return {'text': chat}
133
+
134
+ @output_on_top
135
+ @ops.input_position(chatbot="bottom", chat_processor="bottom", knowledge_base="bottom")
136
+ @op("Chat API")
137
+ def chat_api(chatbot, chat_processor, knowledge_base, *, model='gpt-4o-mini'):
138
+ chatbot = chatbot[0]['chatbot']
139
+ chat_processor = chat_processor[0]['chat_processor']
140
+ knowledge_base = knowledge_base[0]
141
+ c = ChatAPI(
142
+ chatbot=chatbot,
143
+ chat_processor=chat_processor,
144
+ model=model,
145
+ )
146
+ if knowledge_base:
147
+ c.chatbot.rag_graph.kg_base.load_v1_knowledge_base(**knowledge_base)
148
+ c.chatbot.scenario_selector.check_compatibility(c.chatbot.rag_graph)
149
+ return {'chat_api': c}
150
+
151
+ @output_on_top
152
+ @op("Knowledge base")
153
+ def knowledge_base(*, nodes_path='nodes.pickle', edges_path='edges.pickle', template_cluster_path='tempclusters.pickle'):
154
+ return {'nodes_path': nodes_path, 'edges_path': edges_path, 'template_cluster_path': template_cluster_path}
155
+
156
+ @op("View", view="table_view")
157
+ def view(input):
158
+ columns = [str(c) for c in input.keys() if not str(c).startswith('_')]
159
+ v = {
160
+ 'dataframes': { 'df': {
161
+ 'columns': columns,
162
+ 'data': [[input[c] for c in columns]],
163
+ }}
164
+ }
165
+ return v
166
+
167
+ async def api_service(request):
168
+ '''
169
+ Serves a chat endpoint that matches LynxScribe's interface.
170
+ To access it you need to add the "module" and "workspace"
171
+ parameters.
172
+ The workspace must contain exactly one "Chat API" node.
173
+
174
+ curl -X POST ${LYNXKITE_URL}/api/service \
175
+ -H "Content-Type: application/json" \
176
+ -d '{
177
+ "module": "server.lynxscribe_ops",
178
+ "workspace": "LynxScribe demo",
179
+ "session_id": "b43215a0-428f-11ef-9454-0242ac120002",
180
+ "question": "what does the fox say",
181
+ "history": [],
182
+ "user_id": "x",
183
+ "meta_inputs": {}
184
+ }'
185
+ '''
186
+ import pathlib
187
+ from . import workspace
188
+ DATA_PATH = pathlib.Path.cwd() / 'data'
189
+ path = DATA_PATH / request['workspace']
190
+ assert path.is_relative_to(DATA_PATH)
191
+ assert path.exists(), f'Workspace {path} does not exist'
192
+ ws = workspace.load(path)
193
+ contexts = ops.EXECUTORS[ENV](ws)
194
+ nodes = [op for op in ws.nodes if op.data.title == 'Chat API']
195
+ [node] = nodes
196
+ context = contexts[node.id]
197
+ chat_api = context.last_result['chat_api']
198
+ request = ChatAPIRequest(session_id=request['session_id'], question=request['question'], history=request['history'])
199
+ response = await chat_api.answer(request)
200
+ return response
server/main.py CHANGED
@@ -1,15 +1,18 @@
1
  import dataclasses
2
  import fastapi
 
3
  import pathlib
4
  import pkgutil
5
  from . import ops
6
  from . import workspace
7
 
8
  here = pathlib.Path(__file__).parent
 
9
  for _, name, _ in pkgutil.iter_modules([str(here)]):
10
  if name.endswith('_ops') and not name.startswith('test_'):
11
  print(f'Importing {name}')
12
- __import__(f'server.{name}')
 
13
 
14
  app = fastapi.FastAPI()
15
 
@@ -67,3 +70,9 @@ def make_dir(req: dict):
67
  assert not path.exists()
68
  path.mkdir()
69
  return list_dir(path.parent)
 
 
 
 
 
 
 
1
  import dataclasses
2
  import fastapi
3
+ import importlib
4
  import pathlib
5
  import pkgutil
6
  from . import ops
7
  from . import workspace
8
 
9
  here = pathlib.Path(__file__).parent
10
+ lynxkite_modules = {}
11
  for _, name, _ in pkgutil.iter_modules([str(here)]):
12
  if name.endswith('_ops') and not name.startswith('test_'):
13
  print(f'Importing {name}')
14
+ name = f'server.{name}'
15
+ lynxkite_modules[name] = importlib.import_module(name)
16
 
17
  app = fastapi.FastAPI()
18
 
 
70
  assert not path.exists()
71
  path.mkdir()
72
  return list_dir(path.parent)
73
+
74
+ @app.post("/api/service")
75
+ async def service(req: dict):
76
+ '''Executors can provide extra HTTP APIs through the /api/service endpoint.'''
77
+ module = lynxkite_modules[req['module']]
78
+ return await module.api_service(req)
web/package-lock.json CHANGED
@@ -25,6 +25,7 @@
25
  "sass": "^1.77.2",
26
  "svelte": "^4.2.12",
27
  "svelte-check": "^3.6.9",
 
28
  "tslib": "^2.6.2",
29
  "typescript": "^5.4.4",
30
  "unplugin-icons": "^0.18.5",
@@ -1081,6 +1082,13 @@
1081
  "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.14.tgz",
1082
  "integrity": "sha512-WCfD5Ht3ZesJUsONdhvm84dmzWOiOzOAqOncN0++w0lBw1o8OuDNJF2McvvCef/yBqb/HYRahp1BYtODFQ8bRg=="
1083
  },
 
 
 
 
 
 
 
1084
  "node_modules/@types/pug": {
1085
  "version": "2.0.10",
1086
  "resolved": "https://registry.npmjs.org/@types/pug/-/pug-2.0.10.tgz",
@@ -1844,6 +1852,19 @@
1844
  "@jridgewell/sourcemap-codec": "^1.4.15"
1845
  }
1846
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
1847
  "node_modules/mdn-data": {
1848
  "version": "2.0.30",
1849
  "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz",
@@ -2460,6 +2481,20 @@
2460
  "svelte": "^3.19.0 || ^4.0.0"
2461
  }
2462
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2463
  "node_modules/svelte-preprocess": {
2464
  "version": "5.1.4",
2465
  "resolved": "https://registry.npmjs.org/svelte-preprocess/-/svelte-preprocess-5.1.4.tgz",
 
25
  "sass": "^1.77.2",
26
  "svelte": "^4.2.12",
27
  "svelte-check": "^3.6.9",
28
+ "svelte-markdown": "^0.4.1",
29
  "tslib": "^2.6.2",
30
  "typescript": "^5.4.4",
31
  "unplugin-icons": "^0.18.5",
 
1082
  "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.14.tgz",
1083
  "integrity": "sha512-WCfD5Ht3ZesJUsONdhvm84dmzWOiOzOAqOncN0++w0lBw1o8OuDNJF2McvvCef/yBqb/HYRahp1BYtODFQ8bRg=="
1084
  },
1085
+ "node_modules/@types/marked": {
1086
+ "version": "5.0.2",
1087
+ "resolved": "https://registry.npmjs.org/@types/marked/-/marked-5.0.2.tgz",
1088
+ "integrity": "sha512-OucS4KMHhFzhz27KxmWg7J+kIYqyqoW5kdIEI319hqARQQUTqhao3M/F+uFnDXD0Rg72iDDZxZNxq5gvctmLlg==",
1089
+ "dev": true,
1090
+ "license": "MIT"
1091
+ },
1092
  "node_modules/@types/pug": {
1093
  "version": "2.0.10",
1094
  "resolved": "https://registry.npmjs.org/@types/pug/-/pug-2.0.10.tgz",
 
1852
  "@jridgewell/sourcemap-codec": "^1.4.15"
1853
  }
1854
  },
1855
+ "node_modules/marked": {
1856
+ "version": "5.1.2",
1857
+ "resolved": "https://registry.npmjs.org/marked/-/marked-5.1.2.tgz",
1858
+ "integrity": "sha512-ahRPGXJpjMjwSOlBoTMZAK7ATXkli5qCPxZ21TG44rx1KEo44bii4ekgTDQPNRQ4Kh7JMb9Ub1PVk1NxRSsorg==",
1859
+ "dev": true,
1860
+ "license": "MIT",
1861
+ "bin": {
1862
+ "marked": "bin/marked.js"
1863
+ },
1864
+ "engines": {
1865
+ "node": ">= 16"
1866
+ }
1867
+ },
1868
  "node_modules/mdn-data": {
1869
  "version": "2.0.30",
1870
  "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz",
 
2481
  "svelte": "^3.19.0 || ^4.0.0"
2482
  }
2483
  },
2484
+ "node_modules/svelte-markdown": {
2485
+ "version": "0.4.1",
2486
+ "resolved": "https://registry.npmjs.org/svelte-markdown/-/svelte-markdown-0.4.1.tgz",
2487
+ "integrity": "sha512-pOlLY6EruKJaWI9my/2bKX8PdTeP5CM0s4VMmwmC2prlOkjAf+AOmTM4wW/l19Y6WZ87YmP8+ZCJCCwBChWjYw==",
2488
+ "dev": true,
2489
+ "license": "MIT",
2490
+ "dependencies": {
2491
+ "@types/marked": "^5.0.1",
2492
+ "marked": "^5.1.2"
2493
+ },
2494
+ "peerDependencies": {
2495
+ "svelte": "^4.0.0"
2496
+ }
2497
+ },
2498
  "node_modules/svelte-preprocess": {
2499
  "version": "5.1.4",
2500
  "resolved": "https://registry.npmjs.org/svelte-preprocess/-/svelte-preprocess-5.1.4.tgz",
web/package.json CHANGED
@@ -15,6 +15,7 @@
15
  "sass": "^1.77.2",
16
  "svelte": "^4.2.12",
17
  "svelte-check": "^3.6.9",
 
18
  "tslib": "^2.6.2",
19
  "typescript": "^5.4.4",
20
  "unplugin-icons": "^0.18.5",
 
15
  "sass": "^1.77.2",
16
  "svelte": "^4.2.12",
17
  "svelte-check": "^3.6.9",
18
+ "svelte-markdown": "^0.4.1",
19
  "tslib": "^2.6.2",
20
  "typescript": "^5.4.4",
21
  "unplugin-icons": "^0.18.5",