darabos commited on
Commit
2148b2a
·
unverified ·
2 Parent(s): edf6482 b9f38c7

Merge pull request #129 from biggraph/feature/image-search-new

Browse files
examples/LynxScribe FAQ Chatbot Builder.lynxkite.json ADDED
@@ -0,0 +1,605 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "edges": [
3
+ {
4
+ "id": "LynxScribe FAQ to RAG 1 LynxScribe RAG Graph Chatbot Builder 1",
5
+ "source": "LynxScribe FAQ to RAG 1",
6
+ "sourceHandle": "output",
7
+ "target": "LynxScribe RAG Graph Chatbot Builder 1",
8
+ "targetHandle": "rag_graph"
9
+ },
10
+ {
11
+ "id": "LynxScribe RAG Graph Chatbot Builder 1 LynxScribe RAG Graph Chatbot Backend 1",
12
+ "source": "LynxScribe RAG Graph Chatbot Builder 1",
13
+ "sourceHandle": "output",
14
+ "target": "LynxScribe RAG Graph Chatbot Backend 1",
15
+ "targetHandle": "knowledge_base"
16
+ },
17
+ {
18
+ "id": "Chat processor 1 LynxScribe RAG Graph Chatbot Backend 1",
19
+ "source": "Chat processor 1",
20
+ "sourceHandle": "output",
21
+ "target": "LynxScribe RAG Graph Chatbot Backend 1",
22
+ "targetHandle": "chat_processor"
23
+ },
24
+ {
25
+ "id": "Truncate history 1 Chat processor 1",
26
+ "source": "Truncate history 1",
27
+ "sourceHandle": "output",
28
+ "target": "Chat processor 1",
29
+ "targetHandle": "processor"
30
+ },
31
+ {
32
+ "id": "LynxScribe RAG Graph Chatbot Backend 1 Test Chat API 1",
33
+ "source": "LynxScribe RAG Graph Chatbot Backend 1",
34
+ "sourceHandle": "output",
35
+ "target": "Test Chat API 1",
36
+ "targetHandle": "chat_api"
37
+ },
38
+ {
39
+ "id": "Input chat 1 Test Chat API 1",
40
+ "source": "Input chat 1",
41
+ "sourceHandle": "output",
42
+ "target": "Test Chat API 1",
43
+ "targetHandle": "message"
44
+ },
45
+ {
46
+ "id": "Test Chat API 1 View 1",
47
+ "source": "Test Chat API 1",
48
+ "sourceHandle": "output",
49
+ "target": "View 1",
50
+ "targetHandle": "input"
51
+ }
52
+ ],
53
+ "env": "LynxScribe",
54
+ "nodes": [
55
+ {
56
+ "data": {
57
+ "__execution_delay": 0.0,
58
+ "collapsed": false,
59
+ "display": null,
60
+ "error": null,
61
+ "input_metadata": null,
62
+ "meta": {
63
+ "inputs": {},
64
+ "name": "LynxScribe FAQ to RAG",
65
+ "outputs": {
66
+ "output": {
67
+ "name": "output",
68
+ "position": "right",
69
+ "type": {
70
+ "type": "None"
71
+ }
72
+ }
73
+ },
74
+ "params": {
75
+ "faq_excel_path": {
76
+ "default": "uploads/organon_demo/organon_en_copy.xlsx",
77
+ "name": "faq_excel_path",
78
+ "type": {
79
+ "type": "<class 'str'>"
80
+ }
81
+ },
82
+ "scenario_cluster_distance_pct": {
83
+ "default": 30.0,
84
+ "name": "scenario_cluster_distance_pct",
85
+ "type": {
86
+ "type": "<class 'float'>"
87
+ }
88
+ },
89
+ "text_embedder_interface": {
90
+ "default": "openai",
91
+ "name": "text_embedder_interface",
92
+ "type": {
93
+ "type": "<class 'str'>"
94
+ }
95
+ },
96
+ "text_embedder_model_name_or_path": {
97
+ "default": "text-embedding-3-large",
98
+ "name": "text_embedder_model_name_or_path",
99
+ "type": {
100
+ "type": "<class 'str'>"
101
+ }
102
+ },
103
+ "vdb_collection_name": {
104
+ "default": "lynx",
105
+ "name": "vdb_collection_name",
106
+ "type": {
107
+ "type": "<class 'str'>"
108
+ }
109
+ },
110
+ "vdb_num_dimensions": {
111
+ "default": 3072.0,
112
+ "name": "vdb_num_dimensions",
113
+ "type": {
114
+ "type": "<class 'int'>"
115
+ }
116
+ },
117
+ "vdb_provider_name": {
118
+ "default": "faiss",
119
+ "name": "vdb_provider_name",
120
+ "type": {
121
+ "type": "<class 'str'>"
122
+ }
123
+ }
124
+ },
125
+ "type": "basic"
126
+ },
127
+ "params": {
128
+ "faq_excel_path": "uploads/organon_demo/organon_en_copy.xlsx",
129
+ "scenario_cluster_distance_pct": "30",
130
+ "text_embedder_interface": "openai",
131
+ "text_embedder_model_name_or_path": "text-embedding-3-large",
132
+ "vdb_collection_name": "lynx",
133
+ "vdb_num_dimensions": 3072.0,
134
+ "vdb_provider_name": "faiss"
135
+ },
136
+ "status": "done",
137
+ "title": "LynxScribe FAQ to RAG"
138
+ },
139
+ "dragHandle": ".bg-primary",
140
+ "height": 620.0,
141
+ "id": "LynxScribe FAQ to RAG 1",
142
+ "position": {
143
+ "x": -1180.0,
144
+ "y": -76.0
145
+ },
146
+ "type": "basic",
147
+ "width": 415.0
148
+ },
149
+ {
150
+ "data": {
151
+ "__execution_delay": 0.0,
152
+ "collapsed": false,
153
+ "display": null,
154
+ "error": null,
155
+ "input_metadata": null,
156
+ "meta": {
157
+ "inputs": {
158
+ "rag_graph": {
159
+ "name": "rag_graph",
160
+ "position": "left",
161
+ "type": {
162
+ "type": "<class 'inspect._empty'>"
163
+ }
164
+ }
165
+ },
166
+ "name": "LynxScribe RAG Graph Chatbot Builder",
167
+ "outputs": {
168
+ "output": {
169
+ "name": "output",
170
+ "position": "top",
171
+ "type": {
172
+ "type": "None"
173
+ }
174
+ }
175
+ },
176
+ "params": {
177
+ "node_types": {
178
+ "default": "intent_cluster",
179
+ "name": "node_types",
180
+ "type": {
181
+ "type": "<class 'str'>"
182
+ }
183
+ },
184
+ "scenario_file": {
185
+ "default": "uploads/lynx_chatbot_scenario_selector.yaml",
186
+ "name": "scenario_file",
187
+ "type": {
188
+ "type": "<class 'str'>"
189
+ }
190
+ },
191
+ "scenario_meta_name": {
192
+ "default": "scenario_name",
193
+ "name": "scenario_meta_name",
194
+ "type": {
195
+ "type": "<class 'str'>"
196
+ }
197
+ }
198
+ },
199
+ "position": {
200
+ "x": 1569.0,
201
+ "y": 528.0
202
+ },
203
+ "type": "basic"
204
+ },
205
+ "params": {
206
+ "node_types": "intent_cluster",
207
+ "scenario_file": "uploads/organon_demo/backend-scenarios-en.yaml",
208
+ "scenario_meta_name": "scenario_name"
209
+ },
210
+ "status": "done",
211
+ "title": "LynxScribe RAG Graph Chatbot Builder"
212
+ },
213
+ "dragHandle": ".bg-primary",
214
+ "height": 296.0,
215
+ "id": "LynxScribe RAG Graph Chatbot Builder 1",
216
+ "position": {
217
+ "x": -591.0,
218
+ "y": 86.0
219
+ },
220
+ "type": "basic",
221
+ "width": 547.0
222
+ },
223
+ {
224
+ "data": {
225
+ "__execution_delay": 0.0,
226
+ "collapsed": null,
227
+ "display": null,
228
+ "error": null,
229
+ "input_metadata": null,
230
+ "meta": {
231
+ "inputs": {
232
+ "chat_processor": {
233
+ "name": "chat_processor",
234
+ "position": "bottom",
235
+ "type": {
236
+ "type": "<class 'inspect._empty'>"
237
+ }
238
+ },
239
+ "knowledge_base": {
240
+ "name": "knowledge_base",
241
+ "position": "bottom",
242
+ "type": {
243
+ "type": "<class 'inspect._empty'>"
244
+ }
245
+ }
246
+ },
247
+ "name": "LynxScribe RAG Graph Chatbot Backend",
248
+ "outputs": {
249
+ "output": {
250
+ "name": "output",
251
+ "position": "top",
252
+ "type": {
253
+ "type": "None"
254
+ }
255
+ }
256
+ },
257
+ "params": {
258
+ "llm_interface": {
259
+ "default": "openai",
260
+ "name": "llm_interface",
261
+ "type": {
262
+ "type": "<class 'str'>"
263
+ }
264
+ },
265
+ "llm_model_name": {
266
+ "default": "gpt-4o",
267
+ "name": "llm_model_name",
268
+ "type": {
269
+ "type": "<class 'str'>"
270
+ }
271
+ },
272
+ "negative_answer": {
273
+ "default": "I'm sorry, but the data I've been trained on does not contain any information related to your question.",
274
+ "name": "negative_answer",
275
+ "type": {
276
+ "type": "<class 'str'>"
277
+ }
278
+ },
279
+ "retriever_limits_by_type": {
280
+ "default": "{}",
281
+ "name": "retriever_limits_by_type",
282
+ "type": {
283
+ "type": "<class 'str'>"
284
+ }
285
+ },
286
+ "retriever_max_iterations": {
287
+ "default": 3.0,
288
+ "name": "retriever_max_iterations",
289
+ "type": {
290
+ "type": "<class 'int'>"
291
+ }
292
+ },
293
+ "retriever_overall_chunk_limit": {
294
+ "default": 20.0,
295
+ "name": "retriever_overall_chunk_limit",
296
+ "type": {
297
+ "type": "<class 'int'>"
298
+ }
299
+ },
300
+ "retriever_overall_token_limit": {
301
+ "default": 3000.0,
302
+ "name": "retriever_overall_token_limit",
303
+ "type": {
304
+ "type": "<class 'int'>"
305
+ }
306
+ },
307
+ "retriever_strict_limits": {
308
+ "default": true,
309
+ "name": "retriever_strict_limits",
310
+ "type": {
311
+ "type": "<class 'bool'>"
312
+ }
313
+ }
314
+ },
315
+ "position": {
316
+ "x": 1280.0,
317
+ "y": 450.0
318
+ },
319
+ "type": "basic"
320
+ },
321
+ "params": {
322
+ "llm_interface": "openai",
323
+ "llm_model_name": "gpt-4o",
324
+ "negative_answer": "I'm sorry, but the data I've been trained on does not contain any information related to your question.",
325
+ "retriever_limits_by_type": "{\"faq_question\": [0, 0], \"faq_answer\": [3, 3]}",
326
+ "retriever_max_iterations": "3",
327
+ "retriever_overall_chunk_limit": "3",
328
+ "retriever_overall_token_limit": "30000",
329
+ "retriever_strict_limits": true
330
+ },
331
+ "status": "done",
332
+ "title": "LynxScribe RAG Graph Chatbot Backend"
333
+ },
334
+ "dragHandle": ".bg-primary",
335
+ "height": 382.0,
336
+ "id": "LynxScribe RAG Graph Chatbot Backend 1",
337
+ "position": {
338
+ "x": -427.131476508498,
339
+ "y": -465.1194966607713
340
+ },
341
+ "type": "basic",
342
+ "width": 791.0
343
+ },
344
+ {
345
+ "data": {
346
+ "display": null,
347
+ "error": null,
348
+ "input_metadata": null,
349
+ "meta": {
350
+ "inputs": {
351
+ "processor": {
352
+ "name": "processor",
353
+ "position": "bottom",
354
+ "type": {
355
+ "type": "<class 'inspect._empty'>"
356
+ }
357
+ }
358
+ },
359
+ "name": "Chat processor",
360
+ "outputs": {
361
+ "output": {
362
+ "name": "output",
363
+ "position": "top",
364
+ "type": {
365
+ "type": "None"
366
+ }
367
+ }
368
+ },
369
+ "params": {},
370
+ "position": {
371
+ "x": 1291.0,
372
+ "y": 718.0
373
+ },
374
+ "type": "basic"
375
+ },
376
+ "params": {},
377
+ "status": "done",
378
+ "title": "Chat processor"
379
+ },
380
+ "dragHandle": ".bg-primary",
381
+ "height": 200.0,
382
+ "id": "Chat processor 1",
383
+ "position": {
384
+ "x": 252.7291107206022,
385
+ "y": 81.86852349150202
386
+ },
387
+ "type": "basic",
388
+ "width": 200.0
389
+ },
390
+ {
391
+ "data": {
392
+ "display": null,
393
+ "error": null,
394
+ "input_metadata": null,
395
+ "meta": {
396
+ "inputs": {},
397
+ "name": "Truncate history",
398
+ "outputs": {
399
+ "output": {
400
+ "name": "output",
401
+ "position": "top",
402
+ "type": {
403
+ "type": "None"
404
+ }
405
+ }
406
+ },
407
+ "params": {
408
+ "max_tokens": {
409
+ "default": 10000.0,
410
+ "name": "max_tokens",
411
+ "type": {
412
+ "type": "<class 'int'>"
413
+ }
414
+ }
415
+ },
416
+ "position": {
417
+ "x": 1440.0,
418
+ "y": 936.0
419
+ },
420
+ "type": "basic"
421
+ },
422
+ "params": {
423
+ "max_tokens": 10000.0
424
+ },
425
+ "status": "done",
426
+ "title": "Truncate history"
427
+ },
428
+ "dragHandle": ".bg-primary",
429
+ "height": 200.0,
430
+ "id": "Truncate history 1",
431
+ "position": {
432
+ "x": 253.59374153502728,
433
+ "y": 386.4661577036063
434
+ },
435
+ "type": "basic",
436
+ "width": 200.0
437
+ },
438
+ {
439
+ "data": {
440
+ "__execution_delay": 0.0,
441
+ "collapsed": null,
442
+ "display": null,
443
+ "error": null,
444
+ "input_metadata": null,
445
+ "meta": {
446
+ "inputs": {},
447
+ "name": "Input chat",
448
+ "outputs": {
449
+ "output": {
450
+ "name": "output",
451
+ "position": "right",
452
+ "type": {
453
+ "type": "None"
454
+ }
455
+ }
456
+ },
457
+ "params": {
458
+ "chat": {
459
+ "default": null,
460
+ "name": "chat",
461
+ "type": {
462
+ "type": "<class 'str'>"
463
+ }
464
+ }
465
+ },
466
+ "position": {
467
+ "x": 449.0,
468
+ "y": 172.0
469
+ },
470
+ "type": "basic"
471
+ },
472
+ "params": {
473
+ "chat": "I had headache after taking the pill"
474
+ },
475
+ "status": "done",
476
+ "title": "Input chat"
477
+ },
478
+ "dragHandle": ".bg-primary",
479
+ "height": 204.0,
480
+ "id": "Input chat 1",
481
+ "position": {
482
+ "x": -1115.7774404622555,
483
+ "y": -747.1320865489535
484
+ },
485
+ "type": "basic",
486
+ "width": 552.0
487
+ },
488
+ {
489
+ "data": {
490
+ "__execution_delay": 0.0,
491
+ "collapsed": null,
492
+ "display": null,
493
+ "error": null,
494
+ "input_metadata": null,
495
+ "meta": {
496
+ "inputs": {
497
+ "chat_api": {
498
+ "name": "chat_api",
499
+ "position": "bottom",
500
+ "type": {
501
+ "type": "<class 'inspect._empty'>"
502
+ }
503
+ },
504
+ "message": {
505
+ "name": "message",
506
+ "position": "left",
507
+ "type": {
508
+ "type": "<class 'inspect._empty'>"
509
+ }
510
+ }
511
+ },
512
+ "name": "Test Chat API",
513
+ "outputs": {
514
+ "output": {
515
+ "name": "output",
516
+ "position": "right",
517
+ "type": {
518
+ "type": "None"
519
+ }
520
+ }
521
+ },
522
+ "params": {
523
+ "show_details": {
524
+ "default": false,
525
+ "name": "show_details",
526
+ "type": {
527
+ "type": "<class 'bool'>"
528
+ }
529
+ }
530
+ },
531
+ "position": {
532
+ "x": 937.0,
533
+ "y": 213.0
534
+ },
535
+ "type": "basic"
536
+ },
537
+ "params": {
538
+ "show_details": false
539
+ },
540
+ "status": "done",
541
+ "title": "Test Chat API"
542
+ },
543
+ "dragHandle": ".bg-primary",
544
+ "height": 200.0,
545
+ "id": "Test Chat API 1",
546
+ "position": {
547
+ "x": -131.54900620226195,
548
+ "y": -745.4660726292032
549
+ },
550
+ "type": "basic",
551
+ "width": 200.0
552
+ },
553
+ {
554
+ "data": {
555
+ "display": {
556
+ "dataframes": {
557
+ "df": {
558
+ "columns": [
559
+ "answer"
560
+ ],
561
+ "data": [
562
+ [
563
+ "I'm not equipped to handle adverse events or other product-related queries. Your safety is important to us, and we want to ensure you receive the appropriate support. Please report any adverse events or concerns to our dedicated support team. They can be reached at [email protected]. If you have any questions related to contraceptives or women's health, please feel free to ask, and I'll provide you with the information you need.\n"
564
+ ]
565
+ ]
566
+ }
567
+ }
568
+ },
569
+ "error": null,
570
+ "input_metadata": null,
571
+ "meta": {
572
+ "inputs": {
573
+ "input": {
574
+ "name": "input",
575
+ "position": "left",
576
+ "type": {
577
+ "type": "<class 'inspect._empty'>"
578
+ }
579
+ }
580
+ },
581
+ "name": "View",
582
+ "outputs": {},
583
+ "params": {},
584
+ "position": {
585
+ "x": 1547.0,
586
+ "y": 222.0
587
+ },
588
+ "type": "table_view"
589
+ },
590
+ "params": {},
591
+ "status": "done",
592
+ "title": "View"
593
+ },
594
+ "dragHandle": ".bg-primary",
595
+ "height": 483.0,
596
+ "id": "View 1",
597
+ "position": {
598
+ "x": 540.6544350347407,
599
+ "y": -886.065865503576
600
+ },
601
+ "type": "table_view",
602
+ "width": 707.0
603
+ }
604
+ ]
605
+ }
examples/LynxScribe Image Search.lynxkite.json ADDED
@@ -0,0 +1,419 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "edges": [
3
+ {
4
+ "id": "Cloud-sourced File Listing 1 LynxScribe Image Describer 1",
5
+ "source": "Cloud-sourced File Listing 1",
6
+ "sourceHandle": "output",
7
+ "target": "LynxScribe Image Describer 1",
8
+ "targetHandle": "file_urls"
9
+ },
10
+ {
11
+ "id": "LynxScribe Image Describer 1 LynxScribe Image RAG Builder 1",
12
+ "source": "LynxScribe Image Describer 1",
13
+ "sourceHandle": "output",
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",
20
+ "sourceHandle": "output",
21
+ "target": "LynxScribe Image RAG Query 1",
22
+ "targetHandle": "text"
23
+ },
24
+ {
25
+ "id": "LynxScribe Image RAG Builder 1 LynxScribe Image RAG Query 1",
26
+ "source": "LynxScribe Image RAG Builder 1",
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",
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": "Cloud-sourced File Listing",
51
+ "outputs": {
52
+ "output": {
53
+ "name": "output",
54
+ "position": "right",
55
+ "type": {
56
+ "type": "None"
57
+ }
58
+ }
59
+ },
60
+ "params": {
61
+ "accepted_file_types": {
62
+ "default": ".jpg, .jpeg, .png",
63
+ "name": "accepted_file_types",
64
+ "type": {
65
+ "type": "<class 'str'>"
66
+ }
67
+ },
68
+ "cloud_provider": {
69
+ "default": "gcp",
70
+ "name": "cloud_provider",
71
+ "type": {
72
+ "enum": [
73
+ "GCP",
74
+ "AWS",
75
+ "AZURE"
76
+ ]
77
+ }
78
+ },
79
+ "folder_URL": {
80
+ "default": "https://storage.googleapis.com/lynxkite_public_data/lynxscribe-images/image-rag-test",
81
+ "name": "folder_URL",
82
+ "type": {
83
+ "type": "<class 'str'>"
84
+ }
85
+ }
86
+ },
87
+ "type": "basic"
88
+ },
89
+ "params": {
90
+ "accepted_file_types": ".jpg, .jpeg, .png",
91
+ "cloud_provider": "GCP",
92
+ "folder_URL": "https://storage.googleapis.com/lynxkite_public_data/lynxscribe-images/image-rag-test"
93
+ },
94
+ "status": "done",
95
+ "title": "Cloud-sourced File Listing"
96
+ },
97
+ "dragHandle": ".bg-primary",
98
+ "height": 353.0,
99
+ "id": "Cloud-sourced File Listing 1",
100
+ "position": {
101
+ "x": -365.0,
102
+ "y": 302.0
103
+ },
104
+ "type": "basic",
105
+ "width": 430.0
106
+ },
107
+ {
108
+ "data": {
109
+ "display": null,
110
+ "error": null,
111
+ "input_metadata": null,
112
+ "meta": {
113
+ "inputs": {
114
+ "file_urls": {
115
+ "name": "file_urls",
116
+ "position": "left",
117
+ "type": {
118
+ "type": "<class 'inspect._empty'>"
119
+ }
120
+ }
121
+ },
122
+ "name": "LynxScribe Image Describer",
123
+ "outputs": {
124
+ "output": {
125
+ "name": "output",
126
+ "position": "right",
127
+ "type": {
128
+ "type": "None"
129
+ }
130
+ }
131
+ },
132
+ "params": {
133
+ "llm_interface": {
134
+ "default": "openai",
135
+ "name": "llm_interface",
136
+ "type": {
137
+ "type": "<class 'str'>"
138
+ }
139
+ },
140
+ "llm_prompt_name": {
141
+ "default": "cot_picture_descriptor",
142
+ "name": "llm_prompt_name",
143
+ "type": {
144
+ "type": "<class 'str'>"
145
+ }
146
+ },
147
+ "llm_prompt_path": {
148
+ "default": "uploads/image_description_prompts.yaml",
149
+ "name": "llm_prompt_path",
150
+ "type": {
151
+ "type": "<class 'str'>"
152
+ }
153
+ },
154
+ "llm_visual_model": {
155
+ "default": "gpt-4o",
156
+ "name": "llm_visual_model",
157
+ "type": {
158
+ "type": "<class 'str'>"
159
+ }
160
+ }
161
+ },
162
+ "type": "basic"
163
+ },
164
+ "params": {
165
+ "llm_interface": "openai",
166
+ "llm_prompt_name": "cot_picture_descriptor",
167
+ "llm_prompt_path": "uploads/image_description_prompts.yaml",
168
+ "llm_visual_model": "gpt-4o"
169
+ },
170
+ "status": "done",
171
+ "title": "LynxScribe Image Describer"
172
+ },
173
+ "dragHandle": ".bg-primary",
174
+ "height": 361.0,
175
+ "id": "LynxScribe Image Describer 1",
176
+ "position": {
177
+ "x": 159.0,
178
+ "y": 298.0
179
+ },
180
+ "type": "basic",
181
+ "width": 371.0
182
+ },
183
+ {
184
+ "data": {
185
+ "display": null,
186
+ "error": null,
187
+ "input_metadata": null,
188
+ "meta": {
189
+ "inputs": {
190
+ "image_descriptions": {
191
+ "name": "image_descriptions",
192
+ "position": "left",
193
+ "type": {
194
+ "type": "<class 'inspect._empty'>"
195
+ }
196
+ }
197
+ },
198
+ "name": "LynxScribe Image RAG Builder",
199
+ "outputs": {
200
+ "output": {
201
+ "name": "output",
202
+ "position": "right",
203
+ "type": {
204
+ "type": "None"
205
+ }
206
+ }
207
+ },
208
+ "params": {
209
+ "text_embedder_interface": {
210
+ "default": "openai",
211
+ "name": "text_embedder_interface",
212
+ "type": {
213
+ "type": "<class 'str'>"
214
+ }
215
+ },
216
+ "text_embedder_model_name_or_path": {
217
+ "default": "text-embedding-3-large",
218
+ "name": "text_embedder_model_name_or_path",
219
+ "type": {
220
+ "type": "<class 'str'>"
221
+ }
222
+ },
223
+ "vdb_collection_name": {
224
+ "default": "lynx",
225
+ "name": "vdb_collection_name",
226
+ "type": {
227
+ "type": "<class 'str'>"
228
+ }
229
+ },
230
+ "vdb_num_dimensions": {
231
+ "default": 3072.0,
232
+ "name": "vdb_num_dimensions",
233
+ "type": {
234
+ "type": "<class 'int'>"
235
+ }
236
+ },
237
+ "vdb_provider_name": {
238
+ "default": "faiss",
239
+ "name": "vdb_provider_name",
240
+ "type": {
241
+ "type": "<class 'str'>"
242
+ }
243
+ }
244
+ },
245
+ "type": "basic"
246
+ },
247
+ "params": {
248
+ "text_embedder_interface": "openai",
249
+ "text_embedder_model_name_or_path": "text-embedding-3-large",
250
+ "vdb_collection_name": "lynx",
251
+ "vdb_num_dimensions": 3072.0,
252
+ "vdb_provider_name": "faiss"
253
+ },
254
+ "status": "done",
255
+ "title": "LynxScribe Image RAG Builder"
256
+ },
257
+ "dragHandle": ".bg-primary",
258
+ "height": 441.0,
259
+ "id": "LynxScribe Image RAG Builder 1",
260
+ "position": {
261
+ "x": 644.0,
262
+ "y": 259.0
263
+ },
264
+ "type": "basic",
265
+ "width": 291.0
266
+ },
267
+ {
268
+ "data": {
269
+ "__execution_delay": 0.0,
270
+ "collapsed": null,
271
+ "display": null,
272
+ "error": null,
273
+ "input_metadata": null,
274
+ "meta": {
275
+ "inputs": {},
276
+ "name": "Input chat",
277
+ "outputs": {
278
+ "output": {
279
+ "name": "output",
280
+ "position": "right",
281
+ "type": {
282
+ "type": "None"
283
+ }
284
+ }
285
+ },
286
+ "params": {
287
+ "chat": {
288
+ "default": null,
289
+ "name": "chat",
290
+ "type": {
291
+ "type": "<class 'str'>"
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"
306
+ },
307
+ "dragHandle": ".bg-primary",
308
+ "height": 218.0,
309
+ "id": "Input chat 1",
310
+ "position": {
311
+ "x": 153.0,
312
+ "y": -47.0
313
+ },
314
+ "type": "basic",
315
+ "width": 776.0
316
+ },
317
+ {
318
+ "data": {
319
+ "display": null,
320
+ "error": null,
321
+ "input_metadata": null,
322
+ "meta": {
323
+ "inputs": {
324
+ "rag_graph": {
325
+ "name": "rag_graph",
326
+ "position": "bottom",
327
+ "type": {
328
+ "type": "<class 'inspect._empty'>"
329
+ }
330
+ },
331
+ "text": {
332
+ "name": "text",
333
+ "position": "left",
334
+ "type": {
335
+ "type": "<class 'inspect._empty'>"
336
+ }
337
+ }
338
+ },
339
+ "name": "LynxScribe Image RAG Query",
340
+ "outputs": {
341
+ "output": {
342
+ "name": "output",
343
+ "position": "right",
344
+ "type": {
345
+ "type": "None"
346
+ }
347
+ }
348
+ },
349
+ "params": {
350
+ "top_k": {
351
+ "default": 3.0,
352
+ "name": "top_k",
353
+ "type": {
354
+ "type": "<class 'int'>"
355
+ }
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
+ }
examples/{LynxScribe demo.lynxkite.json → LynxScribe RAG Chatbot.lynxkite.json} RENAMED
@@ -1,217 +1,243 @@
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": "who is the CTO of Lynx?"
11
- },
12
  "display": null,
13
  "error": null,
14
- "collapsed": null,
15
  "meta": {
16
  "inputs": {},
17
- "params": {
18
- "chat": {
19
- "default": null,
20
- "type": {
21
- "type": "<class 'str'>"
22
- },
23
- "name": "chat"
24
- }
25
- },
26
  "outputs": {
27
  "output": {
28
  "name": "output",
 
29
  "type": {
30
  "type": "None"
31
- },
32
- "position": "right"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
33
  }
34
  },
35
- "name": "Input chat",
36
  "type": "basic"
37
  },
38
- "__execution_delay": 0.0
 
 
 
 
 
 
39
  },
 
 
 
40
  "position": {
41
- "x": -493.5496596237119,
42
- "y": 20.90123252513356
43
  },
44
- "height": 186.0,
45
- "width": 259.0,
46
- "parentId": null
47
  },
48
  {
49
- "id": "View 1",
50
- "type": "table_view",
51
  "data": {
52
- "title": "View",
53
- "params": {},
54
- "display": {
55
- "dataframes": {
56
- "df": {
57
- "columns": [
58
- "answer"
59
- ],
60
- "data": [
61
- [
62
- "TheThe Chief Technology Officer (CTO) of Lynx Analytics is Chema Lizano. He leads the technology strategy and roadmap at the company, overseeing the vision, development, and implementation of solutions across various clients and environments. If you have any more questions regarding our team or services, feel free to ask!\n\nPlease visit https://www.lynxanalytics.com/board for further information."
63
- ]
64
- ]
65
- }
66
- }
67
- },
68
  "error": null,
 
69
  "meta": {
70
- "type": "table_view",
71
- "name": "View",
72
  "inputs": {
73
- "input": {
74
- "name": "input",
 
75
  "type": {
76
  "type": "<class 'inspect._empty'>"
77
- },
78
- "position": "left"
79
  }
80
  },
81
- "outputs": {},
82
- "params": {}
83
- }
84
- },
85
- "position": {
86
- "x": 731.7440706129762,
87
- "y": -716.4943976910913
88
- },
89
- "width": 1256.0,
90
- "parentId": null,
91
- "height": 950.0
92
- },
93
- {
94
- "id": "LLM 1",
95
- "type": "basic",
96
- "data": {
97
- "title": "LLM",
98
- "params": {
99
- "name": "openai"
100
- },
101
- "display": null,
102
- "error": null,
103
- "meta": {
104
- "inputs": {},
105
  "outputs": {
106
  "output": {
 
 
107
  "type": {
108
  "type": "None"
109
- },
110
- "name": "output",
111
- "position": "top"
112
  }
113
  },
114
- "type": "basic",
115
  "params": {
116
- "name": {
 
 
 
 
 
 
 
 
 
 
117
  "default": "openai",
118
- "name": "name",
119
  "type": {
120
  "type": "<class 'str'>"
121
  }
122
- }
123
- },
124
- "name": "LLM"
125
- }
126
- },
127
- "position": {
128
- "x": -312.5774211084781,
129
- "y": 1093.4019527511366
130
- },
131
- "parentId": null,
132
- "width": 200.0,
133
- "height": 200.0
134
- },
135
- {
136
- "id": "Scenario selector 1",
137
- "type": "basic",
138
- "data": {
139
- "title": "Scenario selector",
140
- "params": {
141
- "scenario_file": "uploads/chat_api/scenarios.yaml",
142
- "node_types": "intent_cluster"
143
- },
144
- "display": null,
145
- "error": null,
146
- "meta": {
147
- "params": {
148
- "scenario_file": {
149
  "type": {
150
  "type": "<class 'str'>"
151
- },
152
- "name": "scenario_file",
153
- "default": null
154
  },
155
- "node_types": {
156
- "default": "intent_cluster",
 
157
  "type": {
158
  "type": "<class 'str'>"
159
- },
160
- "name": "node_types"
161
- }
162
- },
163
- "inputs": {},
164
- "outputs": {
165
- "output": {
166
- "position": "top",
167
- "name": "output",
168
  "type": {
169
- "type": "None"
 
 
 
 
 
 
 
170
  }
171
  }
172
  },
173
- "type": "basic",
174
- "name": "Scenario selector"
175
- }
 
 
 
 
 
 
 
 
 
176
  },
 
 
 
177
  "position": {
178
- "x": -549.1300345090008,
179
- "y": 1086.4852248156676
180
  },
181
- "parentId": null,
182
- "height": 200.0,
183
- "width": 200.0
184
  },
185
  {
186
- "id": "Chat API 1",
187
- "type": "basic",
188
  "data": {
189
- "title": "Chat API",
190
- "params": {
191
- "model": "gpt-4o-mini"
192
- },
193
  "display": null,
194
  "error": null,
 
195
  "meta": {
196
- "name": "Chat API",
197
- "type": "basic",
198
- "outputs": {
199
- "output": {
200
- "type": {
201
- "type": "None"
202
- },
203
- "position": "top",
204
- "name": "output"
205
- }
206
- },
207
  "inputs": {
208
- "chatbot": {
209
- "name": "chatbot",
210
- "type": {
211
- "type": "<class 'inspect._empty'>"
212
- },
213
- "position": "bottom"
214
- },
215
  "chat_processor": {
216
  "name": "chat_processor",
217
  "position": "bottom",
@@ -220,126 +246,36 @@
220
  }
221
  },
222
  "knowledge_base": {
223
- "type": {
224
- "type": "<class 'inspect._empty'>"
225
- },
226
  "position": "bottom",
227
- "name": "knowledge_base"
228
- }
229
- },
230
- "params": {
231
- "model": {
232
- "default": "gpt-4o-mini",
233
- "type": {
234
- "type": "<class 'str'>"
235
- },
236
- "name": "model"
237
- }
238
- }
239
- }
240
- },
241
- "position": {
242
- "x": -22.866663363810787,
243
- "y": 258.20943122219336
244
- },
245
- "parentId": null,
246
- "width": 200.0,
247
- "height": 200.0
248
- },
249
- {
250
- "id": "Knowledge base 1",
251
- "type": "basic",
252
- "data": {
253
- "title": "Knowledge base",
254
- "params": {
255
- "template_cluster_path": "uploads/chat_api/data/lynx/tempclusters.pickle",
256
- "edges_path": "uploads/chat_api/data/lynx/edges.pickle",
257
- "nodes_path": "uploads/chat_api/data/lynx/nodes.pickle"
258
- },
259
- "display": null,
260
- "error": null,
261
- "meta": {
262
- "name": "Knowledge base",
263
- "type": "basic",
264
- "params": {
265
- "nodes_path": {
266
- "name": "nodes_path",
267
- "default": "nodes.pickle",
268
- "type": {
269
- "type": "<class 'str'>"
270
- }
271
- },
272
- "template_cluster_path": {
273
- "type": {
274
- "type": "<class 'str'>"
275
- },
276
- "name": "template_cluster_path",
277
- "default": "tempclusters.pickle"
278
- },
279
- "edges_path": {
280
- "name": "edges_path",
281
- "default": "edges.pickle",
282
  "type": {
283
- "type": "<class 'str'>"
284
  }
285
  }
286
  },
287
- "inputs": {},
288
  "outputs": {
289
  "output": {
290
- "position": "top",
291
  "name": "output",
292
- "type": {
293
- "type": "None"
294
- }
295
- }
296
- }
297
- }
298
- },
299
- "position": {
300
- "x": 598.8683124946176,
301
- "y": 609.9499973808545
302
- },
303
- "width": 336.0,
304
- "height": 320.0,
305
- "parentId": null
306
- },
307
- {
308
- "id": "RAG chatbot 1",
309
- "type": "basic",
310
- "data": {
311
- "title": "RAG chatbot",
312
- "params": {
313
- "limits_by_type": "{\"information\": [2, 3], \"summary\": [2, 3]}",
314
- "max_results": "5",
315
- "negative_answer": "I'm sorry, but the data I've been trained on does not contain any information related to your question.",
316
- "strict_limits": true
317
- },
318
- "display": null,
319
- "error": null,
320
- "meta": {
321
- "outputs": {
322
- "output": {
323
  "position": "top",
324
- "name": "output",
325
  "type": {
326
  "type": "None"
327
  }
328
  }
329
  },
330
  "params": {
331
- "max_results": {
332
- "default": 5.0,
 
333
  "type": {
334
- "type": "<class 'int'>"
335
- },
336
- "name": "max_results"
337
  },
338
- "strict_limits": {
339
- "name": "strict_limits",
340
- "default": true,
341
  "type": {
342
- "type": "<class 'bool'>"
343
  }
344
  },
345
  "negative_answer": {
@@ -349,404 +285,395 @@
349
  "type": "<class 'str'>"
350
  }
351
  },
352
- "limits_by_type": {
353
  "default": "{}",
354
- "name": "limits_by_type",
355
  "type": {
356
  "type": "<class 'str'>"
357
  }
358
- }
359
- },
360
- "name": "RAG chatbot",
361
- "type": "basic",
362
- "inputs": {
363
- "rag_graph": {
364
- "type": {
365
- "type": "<class 'inspect._empty'>"
366
- },
367
- "name": "rag_graph",
368
- "position": "bottom"
369
  },
370
- "llm": {
371
- "name": "llm",
372
- "position": "bottom",
373
  "type": {
374
- "type": "<class 'inspect._empty'>"
375
  }
376
  },
377
- "scenario_selector": {
 
 
378
  "type": {
379
- "type": "<class 'inspect._empty'>"
380
- },
381
- "name": "scenario_selector",
382
- "position": "bottom"
 
 
 
 
 
 
 
 
 
 
 
 
383
  }
384
- }
 
385
  },
386
- "beingResized": false
 
 
 
 
 
 
 
 
 
 
 
387
  },
 
 
 
388
  "position": {
389
- "x": -533.1301830766971,
390
- "y": 547.294980747757
391
  },
392
- "parentId": null,
393
- "height": 399.0,
394
- "width": 339.0
395
  },
396
  {
397
- "id": "RAG graph 1",
398
- "type": "basic",
399
  "data": {
400
- "title": "RAG graph",
401
- "params": {},
402
  "display": null,
403
  "error": null,
 
404
  "meta": {
405
- "type": "basic",
406
  "inputs": {
407
- "text_embedder": {
408
- "type": {
409
- "type": "<class 'inspect._empty'>"
410
- },
411
- "position": "bottom",
412
- "name": "text_embedder"
413
- },
414
- "vector_store": {
415
  "position": "bottom",
416
  "type": {
417
  "type": "<class 'inspect._empty'>"
418
- },
419
- "name": "vector_store"
420
  }
421
  },
422
- "name": "RAG graph",
423
- "params": {},
424
  "outputs": {
425
  "output": {
 
426
  "position": "top",
427
  "type": {
428
  "type": "None"
429
- },
430
- "name": "output"
431
  }
432
- }
433
- }
 
 
 
 
 
434
  },
 
 
 
435
  "position": {
436
- "x": -817.8208895639339,
437
- "y": 1014.836542916127
438
  },
439
- "parentId": null,
440
- "width": 200.0,
441
- "height": 200.0
442
  },
443
  {
444
- "id": "Vector store 1",
445
- "type": "basic",
446
  "data": {
447
- "title": "Vector store",
448
- "params": {
449
- "name": "chromadb",
450
- "collection_name": "lynx"
451
- },
452
  "display": null,
453
  "error": null,
454
- "beingResized": false,
455
  "meta": {
456
- "params": {
457
- "collection_name": {
458
- "type": {
459
- "type": "<class 'str'>"
460
- },
461
- "default": "lynx",
462
- "name": "collection_name"
463
- },
464
- "name": {
465
- "default": "chromadb",
466
- "type": {
467
- "type": "<class 'str'>"
468
- },
469
- "name": "name"
470
- }
471
- },
472
- "type": "basic",
473
- "name": "Vector store",
474
  "outputs": {
475
  "output": {
 
 
476
  "type": {
477
  "type": "None"
478
- },
479
- "position": "top",
480
- "name": "output"
481
  }
482
  },
483
- "inputs": {}
484
- }
485
- },
486
- "position": {
487
- "x": -1053.794625339574,
488
- "y": 1347.7711940497127
489
- },
490
- "height": 227.0,
491
- "parentId": null,
492
- "width": 275.0
493
- },
494
- {
495
- "id": "Text embedder 2",
496
- "type": "basic",
497
- "data": {
498
- "title": "Text embedder",
499
- "params": {
500
- "model": "text-embedding-ada-002"
501
- },
502
- "display": null,
503
- "error": null,
504
- "meta": {
505
  "params": {
506
- "model": {
507
- "default": "text-embedding-ada-002",
508
- "type": {
509
- "type": "<class 'str'>"
510
- },
511
- "name": "model"
512
- }
513
- },
514
- "name": "Text embedder",
515
- "outputs": {
516
- "output": {
517
  "type": {
518
- "type": "None"
519
- },
520
- "position": "top",
521
- "name": "output"
522
  }
523
  },
524
- "type": "basic",
525
- "inputs": {
526
- "llm": {
527
- "type": {
528
- "type": "<class 'inspect._empty'>"
529
- },
530
- "name": "llm",
531
- "position": "bottom"
532
- }
533
- }
534
- }
535
  },
 
 
 
536
  "position": {
537
- "x": -719.98604638686,
538
- "y": 1343.5978526690794
539
  },
540
- "width": 200.0,
541
- "height": 200.0,
542
- "parentId": null
543
  },
544
  {
545
- "id": "LLM 2",
546
- "type": "basic",
547
  "data": {
548
- "title": "LLM",
549
- "params": {
550
- "name": "openai"
551
- },
552
  "display": null,
553
  "error": null,
 
554
  "meta": {
 
 
555
  "outputs": {
556
  "output": {
557
- "position": "top",
558
  "name": "output",
 
559
  "type": {
560
  "type": "None"
561
  }
562
  }
563
  },
564
- "name": "LLM",
565
- "type": "basic",
566
- "inputs": {},
567
  "params": {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
568
  "name": {
569
- "default": "openai",
570
  "name": "name",
571
  "type": {
572
  "type": "<class 'str'>"
573
  }
 
 
 
 
 
 
 
574
  }
575
- }
576
- }
 
 
 
 
 
 
 
 
 
577
  },
 
 
 
578
  "position": {
579
- "x": -727.6171373682814,
580
- "y": 1649.7242636905507
581
  },
582
- "width": 200.0,
583
- "parentId": null,
584
- "height": 200.0
585
  },
586
  {
587
- "id": "Truncate history 1",
588
- "type": "basic",
589
  "data": {
590
- "title": "Truncate history",
591
- "params": {
592
- "max_tokens": 10000.0
593
- },
594
  "display": null,
595
  "error": null,
 
596
  "meta": {
 
 
597
  "outputs": {
598
  "output": {
 
 
599
  "type": {
600
  "type": "None"
601
- },
602
- "name": "output",
603
- "position": "top"
604
  }
605
  },
606
- "type": "basic",
607
  "params": {
608
- "max_tokens": {
609
- "default": 10000.0,
610
- "name": "max_tokens",
611
  "type": {
612
- "type": "<class 'int'>"
613
  }
614
  }
615
  },
616
- "name": "Truncate history",
617
- "inputs": {}
618
- }
 
 
 
 
619
  },
 
 
 
620
  "position": {
621
- "x": 0.08889822620079713,
622
- "y": 1044.7639853229612
623
  },
624
- "height": 200.0,
625
- "width": 200.0,
626
- "parentId": null
627
  },
628
  {
629
- "id": "Chat processor 1",
630
- "type": "basic",
631
  "data": {
632
- "title": "Chat processor",
633
- "params": {},
634
  "display": null,
635
  "error": null,
636
- "__execution_delay": null,
637
- "collapsed": true,
638
  "meta": {
639
- "name": "Chat processor",
640
  "inputs": {
641
- "processor": {
642
- "name": "processor",
643
  "position": "bottom",
644
  "type": {
645
  "type": "<class 'inspect._empty'>"
646
  }
 
 
 
 
 
 
 
647
  }
648
  },
649
- "params": {},
650
- "type": "basic",
651
  "outputs": {
652
  "output": {
 
 
653
  "type": {
654
  "type": "None"
655
- },
656
- "position": "top",
657
- "name": "output"
658
  }
659
- }
660
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
661
  },
 
 
 
662
  "position": {
663
- "x": 182.89729246405872,
664
- "y": 778.546274223181
665
  },
666
- "parentId": null,
667
- "width": 200.0,
668
- "height": 200.0
669
  },
670
  {
671
- "id": "Mask 1",
672
- "type": "basic",
673
  "data": {
674
- "title": "Mask",
675
- "params": {
676
- "mask_pattern": "masked_email_address_{}",
677
- "name": "email",
678
679
- "regex": "([a-z0-9!#$%&'*+\\/=?^_`{|.}~-]+@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?)"
 
 
 
 
 
 
 
680
  },
681
- "display": null,
682
  "error": null,
 
683
  "meta": {
684
- "inputs": {},
685
- "outputs": {
686
- "output": {
687
- "position": "top",
688
- "name": "output",
689
  "type": {
690
- "type": "None"
691
  }
692
  }
693
  },
694
- "type": "basic",
695
- "name": "Mask",
696
- "params": {
697
- "name": {
698
- "default": "",
699
- "name": "name",
700
- "type": {
701
- "type": "<class 'str'>"
702
- }
703
- },
704
- "exceptions": {
705
- "name": "exceptions",
706
- "type": {
707
- "type": "<class 'str'>"
708
- },
709
- "default": ""
710
- },
711
- "regex": {
712
- "type": {
713
- "type": "<class 'str'>"
714
- },
715
- "name": "regex",
716
- "default": ""
717
- },
718
- "mask_pattern": {
719
- "default": "",
720
- "type": {
721
- "type": "<class 'str'>"
722
- },
723
- "name": "mask_pattern"
724
- }
725
- }
726
- }
727
  },
 
 
 
728
  "position": {
729
- "x": 233.69759202223884,
730
- "y": 1041.6145468043276
731
  },
732
- "height": 200.0,
733
- "parentId": null,
734
- "width": 200.0
735
  },
736
  {
737
- "id": "Mask 2",
738
- "type": "basic",
739
  "data": {
740
- "title": "Mask",
741
- "params": {
742
- "regex": "((?:(?:\\\\d{4}[- ]?){3}\\\\d{4}|\\\\d{15,16}))(?![\\\\d])",
743
- "exceptions": "",
744
- "name": "credit_card",
745
- "mask_pattern": "masked_credit_card_number_{}"
746
- },
747
  "display": null,
748
  "error": null,
 
749
  "meta": {
 
 
 
 
 
 
 
 
 
 
750
  "outputs": {
751
  "output": {
752
  "name": "output",
@@ -756,214 +683,52 @@
756
  }
757
  }
758
  },
759
- "inputs": {},
760
- "name": "Mask",
761
- "type": "basic",
762
  "params": {
763
- "exceptions": {
764
- "type": {
765
- "type": "<class 'str'>"
766
- },
767
- "default": "",
768
- "name": "exceptions"
769
- },
770
- "regex": {
771
- "default": "",
772
  "type": {
773
  "type": "<class 'str'>"
774
- },
775
- "name": "regex"
776
  },
777
- "mask_pattern": {
778
- "name": "mask_pattern",
 
779
  "type": {
780
  "type": "<class 'str'>"
781
- },
782
- "default": ""
783
  },
784
- "name": {
785
- "name": "name",
786
- "default": "",
787
  "type": {
788
  "type": "<class 'str'>"
789
  }
790
  }
791
- }
792
- }
793
- },
794
- "position": {
795
- "x": 513.2761671440603,
796
- "y": 1034.8547191984255
797
- },
798
- "width": 200.0,
799
- "parentId": null,
800
- "height": 200.0
801
- },
802
- {
803
- "id": "Test Chat API 2",
804
- "type": "basic",
805
- "data": {
806
- "title": "Test Chat API",
807
- "params": {
808
- "show_details": false
809
- },
810
- "display": null,
811
- "error": null,
812
- "collapsed": false,
813
- "__execution_delay": 0.0,
814
- "meta": {
815
- "params": {
816
- "show_details": {
817
- "default": false,
818
- "type": {
819
- "type": "<class 'bool'>"
820
- },
821
- "name": "show_details"
822
- }
823
- },
824
- "inputs": {
825
- "message": {
826
- "name": "message",
827
- "position": "left",
828
- "type": {
829
- "type": "<class 'inspect._empty'>"
830
- }
831
- },
832
- "chat_api": {
833
- "position": "bottom",
834
- "type": {
835
- "type": "<class 'inspect._empty'>"
836
- },
837
- "name": "chat_api"
838
- }
839
  },
840
- "outputs": {
841
- "output": {
842
- "position": "right",
843
- "type": {
844
- "type": "None"
845
- },
846
- "name": "output"
847
- }
848
  },
849
- "name": "Test Chat API",
850
  "type": "basic"
851
- }
 
 
 
 
 
 
 
852
  },
 
 
 
853
  "position": {
854
- "x": -57.377776548056346,
855
- "y": -16.924593985348814
856
  },
857
- "width": 376.0,
858
- "parentId": null,
859
- "height": 225.0
860
- }
861
- ],
862
- "edges": [
863
- {
864
- "id": "xy-edge__Knowledge base 1output-Chat API 1knowledge_base",
865
- "source": "Knowledge base 1",
866
- "target": "Chat API 1",
867
- "sourceHandle": "output",
868
- "targetHandle": "knowledge_base"
869
- },
870
- {
871
- "id": "xy-edge__RAG chatbot 1output-Chat API 1chatbot",
872
- "source": "RAG chatbot 1",
873
- "target": "Chat API 1",
874
- "sourceHandle": "output",
875
- "targetHandle": "chatbot"
876
- },
877
- {
878
- "id": "xy-edge__LLM 1output-RAG chatbot 1llm",
879
- "source": "LLM 1",
880
- "target": "RAG chatbot 1",
881
- "sourceHandle": "output",
882
- "targetHandle": "llm"
883
- },
884
- {
885
- "id": "xy-edge__Scenario selector 1output-RAG chatbot 1scenario_selector",
886
- "source": "Scenario selector 1",
887
- "target": "RAG chatbot 1",
888
- "sourceHandle": "output",
889
- "targetHandle": "scenario_selector"
890
- },
891
- {
892
- "id": "xy-edge__RAG graph 1output-RAG chatbot 1rag_graph",
893
- "source": "RAG graph 1",
894
- "target": "RAG chatbot 1",
895
- "sourceHandle": "output",
896
- "targetHandle": "rag_graph"
897
- },
898
- {
899
- "id": "xy-edge__Vector store 1output-RAG graph 1vector_store",
900
- "source": "Vector store 1",
901
- "target": "RAG graph 1",
902
- "sourceHandle": "output",
903
- "targetHandle": "vector_store"
904
- },
905
- {
906
- "id": "xy-edge__Text embedder 2output-RAG graph 1text_embedder",
907
- "source": "Text embedder 2",
908
- "target": "RAG graph 1",
909
- "sourceHandle": "output",
910
- "targetHandle": "text_embedder"
911
- },
912
- {
913
- "id": "xy-edge__LLM 2output-Text embedder 2llm",
914
- "source": "LLM 2",
915
- "target": "Text embedder 2",
916
- "sourceHandle": "output",
917
- "targetHandle": "llm"
918
- },
919
- {
920
- "id": "xy-edge__Truncate history 1output-Chat processor 1processor",
921
- "source": "Truncate history 1",
922
- "target": "Chat processor 1",
923
- "sourceHandle": "output",
924
- "targetHandle": "processor"
925
- },
926
- {
927
- "id": "xy-edge__Chat processor 1output-Chat API 1chat_processor",
928
- "source": "Chat processor 1",
929
- "target": "Chat API 1",
930
- "sourceHandle": "output",
931
- "targetHandle": "chat_processor"
932
- },
933
- {
934
- "id": "xy-edge__Mask 1output-Chat processor 1processor",
935
- "source": "Mask 1",
936
- "target": "Chat processor 1",
937
- "sourceHandle": "output",
938
- "targetHandle": "processor"
939
- },
940
- {
941
- "id": "xy-edge__Mask 2output-Chat processor 1processor",
942
- "source": "Mask 2",
943
- "target": "Chat processor 1",
944
- "sourceHandle": "output",
945
- "targetHandle": "processor"
946
- },
947
- {
948
- "id": "xy-edge__Input chat 1output-Test Chat API 2message",
949
- "source": "Input chat 1",
950
- "target": "Test Chat API 2",
951
- "sourceHandle": "output",
952
- "targetHandle": "message"
953
- },
954
- {
955
- "id": "xy-edge__Test Chat API 2output-View 1input",
956
- "source": "Test Chat API 2",
957
- "target": "View 1",
958
- "sourceHandle": "output",
959
- "targetHandle": "input"
960
- },
961
- {
962
- "id": "xy-edge__Chat API 1output-Test Chat API 2chat_api",
963
- "source": "Chat API 1",
964
- "target": "Test Chat API 2",
965
- "sourceHandle": "output",
966
- "targetHandle": "chat_api"
967
  }
968
  ]
969
  }
 
1
  {
2
+ "edges": [
3
+ {
4
+ "id": "Cloud-sourced File Listing 1 LynxScribe Text RAG Loader 1",
5
+ "source": "Cloud-sourced File Listing 1",
6
+ "sourceHandle": "output",
7
+ "target": "LynxScribe Text RAG Loader 1",
8
+ "targetHandle": "file_urls"
9
+ },
10
+ {
11
+ "id": "Truncate history 1 Chat processor 1",
12
+ "source": "Truncate history 1",
13
+ "sourceHandle": "output",
14
+ "target": "Chat processor 1",
15
+ "targetHandle": "processor"
16
+ },
17
+ {
18
+ "id": "Chat processor 1 LynxScribe RAG Graph Chatbot Backend 1",
19
+ "source": "Chat processor 1",
20
+ "sourceHandle": "output",
21
+ "target": "LynxScribe RAG Graph Chatbot Backend 1",
22
+ "targetHandle": "chat_processor"
23
+ },
24
+ {
25
+ "id": "Mask 1 Chat processor 1",
26
+ "source": "Mask 1",
27
+ "sourceHandle": "output",
28
+ "target": "Chat processor 1",
29
+ "targetHandle": "processor"
30
+ },
31
+ {
32
+ "id": "Input chat 1 Test Chat API 1",
33
+ "source": "Input chat 1",
34
+ "sourceHandle": "output",
35
+ "target": "Test Chat API 1",
36
+ "targetHandle": "message"
37
+ },
38
+ {
39
+ "id": "LynxScribe RAG Graph Chatbot Backend 1 Test Chat API 1",
40
+ "source": "LynxScribe RAG Graph Chatbot Backend 1",
41
+ "sourceHandle": "output",
42
+ "target": "Test Chat API 1",
43
+ "targetHandle": "chat_api"
44
+ },
45
+ {
46
+ "id": "Test Chat API 1 View 1",
47
+ "source": "Test Chat API 1",
48
+ "sourceHandle": "output",
49
+ "target": "View 1",
50
+ "targetHandle": "input"
51
+ },
52
+ {
53
+ "id": "LynxScribe Text RAG Loader 1 LynxScribe RAG Graph Chatbot Builder 1",
54
+ "source": "LynxScribe Text RAG Loader 1",
55
+ "sourceHandle": "output",
56
+ "target": "LynxScribe RAG Graph Chatbot Builder 1",
57
+ "targetHandle": "rag_graph"
58
+ },
59
+ {
60
+ "id": "LynxScribe RAG Graph Chatbot Builder 1 LynxScribe RAG Graph Chatbot Backend 1",
61
+ "source": "LynxScribe RAG Graph Chatbot Builder 1",
62
+ "sourceHandle": "output",
63
+ "target": "LynxScribe RAG Graph Chatbot Backend 1",
64
+ "targetHandle": "knowledge_base"
65
+ }
66
+ ],
67
  "env": "LynxScribe",
68
  "nodes": [
69
  {
 
 
70
  "data": {
71
+ "__execution_delay": 0.0,
72
+ "collapsed": null,
 
 
73
  "display": null,
74
  "error": null,
75
+ "input_metadata": null,
76
  "meta": {
77
  "inputs": {},
78
+ "name": "Cloud-sourced File Listing",
 
 
 
 
 
 
 
 
79
  "outputs": {
80
  "output": {
81
  "name": "output",
82
+ "position": "right",
83
  "type": {
84
  "type": "None"
85
+ }
86
+ }
87
+ },
88
+ "params": {
89
+ "accepted_file_types": {
90
+ "default": ".jpg, .jpeg, .png",
91
+ "name": "accepted_file_types",
92
+ "type": {
93
+ "type": "<class 'str'>"
94
+ }
95
+ },
96
+ "cloud_provider": {
97
+ "default": "gcp",
98
+ "name": "cloud_provider",
99
+ "type": {
100
+ "enum": [
101
+ "GCP",
102
+ "AWS",
103
+ "AZURE"
104
+ ]
105
+ }
106
+ },
107
+ "folder_URL": {
108
+ "default": "https://storage.googleapis.com/lynxkite_public_data/lynxscribe-images/image-rag-test",
109
+ "name": "folder_URL",
110
+ "type": {
111
+ "type": "<class 'str'>"
112
+ }
113
  }
114
  },
 
115
  "type": "basic"
116
  },
117
+ "params": {
118
+ "accepted_file_types": ".pickle",
119
+ "cloud_provider": "GCP",
120
+ "folder_URL": "https://storage.googleapis.com/lynxkite_public_data/lynxscribe-knowledge-graphs/lynx-chatbot"
121
+ },
122
+ "status": "done",
123
+ "title": "Cloud-sourced File Listing"
124
  },
125
+ "dragHandle": ".bg-primary",
126
+ "height": 286.0,
127
+ "id": "Cloud-sourced File Listing 1",
128
  "position": {
129
+ "x": -827.0,
130
+ "y": 382.0
131
  },
132
+ "type": "basic",
133
+ "width": 515.0
 
134
  },
135
  {
 
 
136
  "data": {
137
+ "__execution_delay": 0.0,
138
+ "collapsed": null,
139
+ "display": null,
 
 
 
 
 
 
 
 
 
 
 
 
 
140
  "error": null,
141
+ "input_metadata": null,
142
  "meta": {
 
 
143
  "inputs": {
144
+ "file_urls": {
145
+ "name": "file_urls",
146
+ "position": "left",
147
  "type": {
148
  "type": "<class 'inspect._empty'>"
149
+ }
 
150
  }
151
  },
152
+ "name": "LynxScribe Text RAG Loader",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
153
  "outputs": {
154
  "output": {
155
+ "name": "output",
156
+ "position": "right",
157
  "type": {
158
  "type": "None"
159
+ }
 
 
160
  }
161
  },
 
162
  "params": {
163
+ "input_type": {
164
+ "default": "v1",
165
+ "name": "input_type",
166
+ "type": {
167
+ "enum": [
168
+ "V1",
169
+ "V2"
170
+ ]
171
+ }
172
+ },
173
+ "text_embedder_interface": {
174
  "default": "openai",
175
+ "name": "text_embedder_interface",
176
  "type": {
177
  "type": "<class 'str'>"
178
  }
179
+ },
180
+ "text_embedder_model_name_or_path": {
181
+ "default": "text-embedding-3-large",
182
+ "name": "text_embedder_model_name_or_path",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
183
  "type": {
184
  "type": "<class 'str'>"
185
+ }
 
 
186
  },
187
+ "vdb_collection_name": {
188
+ "default": "lynx",
189
+ "name": "vdb_collection_name",
190
  "type": {
191
  "type": "<class 'str'>"
192
+ }
193
+ },
194
+ "vdb_num_dimensions": {
195
+ "default": 3072.0,
196
+ "name": "vdb_num_dimensions",
 
 
 
 
197
  "type": {
198
+ "type": "<class 'int'>"
199
+ }
200
+ },
201
+ "vdb_provider_name": {
202
+ "default": "faiss",
203
+ "name": "vdb_provider_name",
204
+ "type": {
205
+ "type": "<class 'str'>"
206
  }
207
  }
208
  },
209
+ "type": "basic"
210
+ },
211
+ "params": {
212
+ "input_type": "V1",
213
+ "text_embedder_interface": "openai",
214
+ "text_embedder_model_name_or_path": "text-embedding-ada-002",
215
+ "vdb_collection_name": "lynx",
216
+ "vdb_num_dimensions": "1536",
217
+ "vdb_provider_name": "faiss"
218
+ },
219
+ "status": "done",
220
+ "title": "LynxScribe Text RAG Loader"
221
  },
222
+ "dragHandle": ".bg-primary",
223
+ "height": 515.0,
224
+ "id": "LynxScribe Text RAG Loader 1",
225
  "position": {
226
+ "x": -173.0,
227
+ "y": 268.0
228
  },
229
+ "type": "basic",
230
+ "width": 290.0
 
231
  },
232
  {
 
 
233
  "data": {
234
+ "__execution_delay": 0.0,
235
+ "collapsed": null,
 
 
236
  "display": null,
237
  "error": null,
238
+ "input_metadata": null,
239
  "meta": {
 
 
 
 
 
 
 
 
 
 
 
240
  "inputs": {
 
 
 
 
 
 
 
241
  "chat_processor": {
242
  "name": "chat_processor",
243
  "position": "bottom",
 
246
  }
247
  },
248
  "knowledge_base": {
249
+ "name": "knowledge_base",
 
 
250
  "position": "bottom",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
251
  "type": {
252
+ "type": "<class 'inspect._empty'>"
253
  }
254
  }
255
  },
256
+ "name": "LynxScribe RAG Graph Chatbot Backend",
257
  "outputs": {
258
  "output": {
 
259
  "name": "output",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
260
  "position": "top",
 
261
  "type": {
262
  "type": "None"
263
  }
264
  }
265
  },
266
  "params": {
267
+ "llm_interface": {
268
+ "default": "openai",
269
+ "name": "llm_interface",
270
  "type": {
271
+ "type": "<class 'str'>"
272
+ }
 
273
  },
274
+ "llm_model_name": {
275
+ "default": "gpt-4o",
276
+ "name": "llm_model_name",
277
  "type": {
278
+ "type": "<class 'str'>"
279
  }
280
  },
281
  "negative_answer": {
 
285
  "type": "<class 'str'>"
286
  }
287
  },
288
+ "retriever_limits_by_type": {
289
  "default": "{}",
290
+ "name": "retriever_limits_by_type",
291
  "type": {
292
  "type": "<class 'str'>"
293
  }
 
 
 
 
 
 
 
 
 
 
 
294
  },
295
+ "retriever_max_iterations": {
296
+ "default": 3.0,
297
+ "name": "retriever_max_iterations",
298
  "type": {
299
+ "type": "<class 'int'>"
300
  }
301
  },
302
+ "retriever_overall_chunk_limit": {
303
+ "default": 20.0,
304
+ "name": "retriever_overall_chunk_limit",
305
  "type": {
306
+ "type": "<class 'int'>"
307
+ }
308
+ },
309
+ "retriever_overall_token_limit": {
310
+ "default": 3000.0,
311
+ "name": "retriever_overall_token_limit",
312
+ "type": {
313
+ "type": "<class 'int'>"
314
+ }
315
+ },
316
+ "retriever_strict_limits": {
317
+ "default": true,
318
+ "name": "retriever_strict_limits",
319
+ "type": {
320
+ "type": "<class 'bool'>"
321
+ }
322
  }
323
+ },
324
+ "type": "basic"
325
  },
326
+ "params": {
327
+ "llm_interface": "openai",
328
+ "llm_model_name": "gpt-4o",
329
+ "negative_answer": "I'm sorry, but the data I've been trained on does not contain any information related to your question.",
330
+ "retriever_limits_by_type": "{\"information\": [1, 5], \"summary\": [0, 2], \"template_qna\": [1, 3], \"QnA question\": [0, 0]}",
331
+ "retriever_max_iterations": "3",
332
+ "retriever_overall_chunk_limit": 20.0,
333
+ "retriever_overall_token_limit": 3000.0,
334
+ "retriever_strict_limits": true
335
+ },
336
+ "status": "done",
337
+ "title": "LynxScribe RAG Graph Chatbot Backend"
338
  },
339
+ "dragHandle": ".bg-primary",
340
+ "height": 697.0,
341
+ "id": "LynxScribe RAG Graph Chatbot Backend 1",
342
  "position": {
343
+ "x": 356.69268530841373,
344
+ "y": -467.49315862719016
345
  },
346
+ "type": "basic",
347
+ "width": 821.0
 
348
  },
349
  {
 
 
350
  "data": {
 
 
351
  "display": null,
352
  "error": null,
353
+ "input_metadata": null,
354
  "meta": {
 
355
  "inputs": {
356
+ "processor": {
357
+ "name": "processor",
 
 
 
 
 
 
358
  "position": "bottom",
359
  "type": {
360
  "type": "<class 'inspect._empty'>"
361
+ }
 
362
  }
363
  },
364
+ "name": "Chat processor",
 
365
  "outputs": {
366
  "output": {
367
+ "name": "output",
368
  "position": "top",
369
  "type": {
370
  "type": "None"
371
+ }
 
372
  }
373
+ },
374
+ "params": {},
375
+ "type": "basic"
376
+ },
377
+ "params": {},
378
+ "status": "done",
379
+ "title": "Chat processor"
380
  },
381
+ "dragHandle": ".bg-primary",
382
+ "height": 220.0,
383
+ "id": "Chat processor 1",
384
  "position": {
385
+ "x": 907.3546850533578,
386
+ "y": 381.09754180073975
387
  },
388
+ "type": "basic",
389
+ "width": 387.0
 
390
  },
391
  {
 
 
392
  "data": {
 
 
 
 
 
393
  "display": null,
394
  "error": null,
395
+ "input_metadata": null,
396
  "meta": {
397
+ "inputs": {},
398
+ "name": "Truncate history",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
399
  "outputs": {
400
  "output": {
401
+ "name": "output",
402
+ "position": "top",
403
  "type": {
404
  "type": "None"
405
+ }
 
 
406
  }
407
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
408
  "params": {
409
+ "max_tokens": {
410
+ "default": 10000.0,
411
+ "name": "max_tokens",
 
 
 
 
 
 
 
 
412
  "type": {
413
+ "type": "<class 'int'>"
414
+ }
 
 
415
  }
416
  },
417
+ "type": "basic"
418
+ },
419
+ "params": {
420
+ "max_tokens": 10000.0
421
+ },
422
+ "status": "done",
423
+ "title": "Truncate history"
 
 
 
 
424
  },
425
+ "dragHandle": ".bg-primary",
426
+ "height": 200.0,
427
+ "id": "Truncate history 1",
428
  "position": {
429
+ "x": 931.4096899549071,
430
+ "y": 703.2861674410822
431
  },
432
+ "type": "basic",
433
+ "width": 200.0
 
434
  },
435
  {
 
 
436
  "data": {
437
+ "__execution_delay": 0.0,
438
+ "collapsed": false,
 
 
439
  "display": null,
440
  "error": null,
441
+ "input_metadata": null,
442
  "meta": {
443
+ "inputs": {},
444
+ "name": "Mask",
445
  "outputs": {
446
  "output": {
 
447
  "name": "output",
448
+ "position": "top",
449
  "type": {
450
  "type": "None"
451
  }
452
  }
453
  },
 
 
 
454
  "params": {
455
+ "exceptions": {
456
+ "default": "",
457
+ "name": "exceptions",
458
+ "type": {
459
+ "type": "<class 'str'>"
460
+ }
461
+ },
462
+ "mask_pattern": {
463
+ "default": "",
464
+ "name": "mask_pattern",
465
+ "type": {
466
+ "type": "<class 'str'>"
467
+ }
468
+ },
469
  "name": {
470
+ "default": "",
471
  "name": "name",
472
  "type": {
473
  "type": "<class 'str'>"
474
  }
475
+ },
476
+ "regex": {
477
+ "default": "",
478
+ "name": "regex",
479
+ "type": {
480
+ "type": "<class 'str'>"
481
+ }
482
  }
483
+ },
484
+ "type": "basic"
485
+ },
486
+ "params": {
487
488
+ "mask_pattern": "masked_email_address_{}",
489
+ "name": "email",
490
+ "regex": "([a-z0-9!#$%&'*+\\/=?^_`{|.}~-]+@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?)"
491
+ },
492
+ "status": "done",
493
+ "title": "Mask"
494
  },
495
+ "dragHandle": ".bg-primary",
496
+ "height": 366.0,
497
+ "id": "Mask 1",
498
  "position": {
499
+ "x": 1163.4170914925835,
500
+ "y": 712.6187958660655
501
  },
502
+ "type": "basic",
503
+ "width": 281.0
 
504
  },
505
  {
 
 
506
  "data": {
507
+ "__execution_delay": 0.0,
508
+ "collapsed": null,
 
 
509
  "display": null,
510
  "error": null,
511
+ "input_metadata": null,
512
  "meta": {
513
+ "inputs": {},
514
+ "name": "Input chat",
515
  "outputs": {
516
  "output": {
517
+ "name": "output",
518
+ "position": "right",
519
  "type": {
520
  "type": "None"
521
+ }
 
 
522
  }
523
  },
 
524
  "params": {
525
+ "chat": {
526
+ "default": null,
527
+ "name": "chat",
528
  "type": {
529
+ "type": "<class 'str'>"
530
  }
531
  }
532
  },
533
+ "type": "basic"
534
+ },
535
+ "params": {
536
+ "chat": "Who is the CEO of Lynx?"
537
+ },
538
+ "status": "done",
539
+ "title": "Input chat"
540
  },
541
+ "dragHandle": ".bg-primary",
542
+ "height": 198.0,
543
+ "id": "Input chat 1",
544
  "position": {
545
+ "x": -854.3584473819146,
546
+ "y": -770.2371549901112
547
  },
548
+ "type": "basic",
549
+ "width": 910.0
 
550
  },
551
  {
 
 
552
  "data": {
553
+ "__execution_delay": 0.0,
554
+ "collapsed": null,
555
  "display": null,
556
  "error": null,
557
+ "input_metadata": null,
 
558
  "meta": {
 
559
  "inputs": {
560
+ "chat_api": {
561
+ "name": "chat_api",
562
  "position": "bottom",
563
  "type": {
564
  "type": "<class 'inspect._empty'>"
565
  }
566
+ },
567
+ "message": {
568
+ "name": "message",
569
+ "position": "left",
570
+ "type": {
571
+ "type": "<class 'inspect._empty'>"
572
+ }
573
  }
574
  },
575
+ "name": "Test Chat API",
 
576
  "outputs": {
577
  "output": {
578
+ "name": "output",
579
+ "position": "right",
580
  "type": {
581
  "type": "None"
582
+ }
 
 
583
  }
584
+ },
585
+ "params": {
586
+ "show_details": {
587
+ "default": false,
588
+ "name": "show_details",
589
+ "type": {
590
+ "type": "<class 'bool'>"
591
+ }
592
+ }
593
+ },
594
+ "type": "basic"
595
+ },
596
+ "params": {
597
+ "show_details": false
598
+ },
599
+ "status": "done",
600
+ "title": "Test Chat API"
601
  },
602
+ "dragHandle": ".bg-primary",
603
+ "height": 260.0,
604
+ "id": "Test Chat API 1",
605
  "position": {
606
+ "x": 356.57819670145534,
607
+ "y": -803.3229228909706
608
  },
609
+ "type": "basic",
610
+ "width": 820.0
 
611
  },
612
  {
 
 
613
  "data": {
614
+ "display": {
615
+ "dataframes": {
616
+ "df": {
617
+ "columns": [
618
+ "answer"
619
+ ],
620
+ "data": [
621
+ [
622
+ "The CEO of Lynx Analytics is Gyorgy Lajtai. He is also a co-founder of the company and has a rich background in CRM, marketing automation, and systems."
623
+ ]
624
+ ]
625
+ }
626
+ }
627
  },
 
628
  "error": null,
629
+ "input_metadata": null,
630
  "meta": {
631
+ "inputs": {
632
+ "input": {
633
+ "name": "input",
634
+ "position": "left",
 
635
  "type": {
636
+ "type": "<class 'inspect._empty'>"
637
  }
638
  }
639
  },
640
+ "name": "View",
641
+ "outputs": {},
642
+ "params": {},
643
+ "type": "table_view"
644
+ },
645
+ "params": {},
646
+ "status": "done",
647
+ "title": "View"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
648
  },
649
+ "dragHandle": ".bg-primary",
650
+ "height": 627.0,
651
+ "id": "View 1",
652
  "position": {
653
+ "x": 1497.8623453933426,
654
+ "y": -810.6258103791108
655
  },
656
+ "type": "table_view",
657
+ "width": 995.0
 
658
  },
659
  {
 
 
660
  "data": {
661
+ "__execution_delay": 0.0,
662
+ "collapsed": null,
 
 
 
 
 
663
  "display": null,
664
  "error": null,
665
+ "input_metadata": null,
666
  "meta": {
667
+ "inputs": {
668
+ "rag_graph": {
669
+ "name": "rag_graph",
670
+ "position": "left",
671
+ "type": {
672
+ "type": "<class 'inspect._empty'>"
673
+ }
674
+ }
675
+ },
676
+ "name": "LynxScribe RAG Graph Chatbot Builder",
677
  "outputs": {
678
  "output": {
679
  "name": "output",
 
683
  }
684
  }
685
  },
 
 
 
686
  "params": {
687
+ "node_types": {
688
+ "default": "intent_cluster",
689
+ "name": "node_types",
 
 
 
 
 
 
690
  "type": {
691
  "type": "<class 'str'>"
692
+ }
 
693
  },
694
+ "scenario_file": {
695
+ "default": "uploads/lynx_chatbot_scenario_selector.yaml",
696
+ "name": "scenario_file",
697
  "type": {
698
  "type": "<class 'str'>"
699
+ }
 
700
  },
701
+ "scenario_meta_name": {
702
+ "default": "scenario_name",
703
+ "name": "scenario_meta_name",
704
  "type": {
705
  "type": "<class 'str'>"
706
  }
707
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
708
  },
709
+ "position": {
710
+ "x": 1121.0,
711
+ "y": 813.0
 
 
 
 
 
712
  },
 
713
  "type": "basic"
714
+ },
715
+ "params": {
716
+ "node_types": "intent_cluster",
717
+ "scenario_file": "uploads/lynx_chatbot_scenario_selector.yaml",
718
+ "scenario_meta_name": ""
719
+ },
720
+ "status": "done",
721
+ "title": "LynxScribe RAG Graph Chatbot Builder"
722
  },
723
+ "dragHandle": ".bg-primary",
724
+ "height": 297.0,
725
+ "id": "LynxScribe RAG Graph Chatbot Builder 1",
726
  "position": {
727
+ "x": 328.41755532473496,
728
+ "y": 378.2277574498554
729
  },
730
+ "type": "basic",
731
+ "width": 396.0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
732
  }
733
  ]
734
  }
examples/uploads/image_description_prompts.yaml ADDED
@@ -0,0 +1,90 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ cot_picture_descriptor:
2
+ - role: system
3
+ content: &cot_starter >
4
+ You are an advanced AI specializing in structured image descriptions using a Chain-of-Thought (CoT) approach.
5
+ Your goal is to analyze an image and return a detailed dictionary containing relevant details categorized by elements.
6
+
7
+ - role: system
8
+ content: &cot_details >
9
+ You should always return a dictionary with the following main keys:
10
+ - "image type": Identify whether the image is a "picture", "diagram", "flowchart", "advertisement", or "other".
11
+ - "overall description": A concise but clear summary of the entire image.
12
+ - "details": A dictionary containing all significant elements in the image, where:
13
+ * Each key represents a major object or entity in the image.
14
+ * Each value is a detailed description of that entity.
15
+
16
+ - role: system
17
+ content: &cot_normal_pic >
18
+ If the image is a normal picture (e.g., a scene with people, animals, landscapes, or objects in a real-world setting),
19
+ follow these steps:
20
+ 1. Identify and describe the background (e.g., sky, buildings, landscape).
21
+ 2. Identify the main action happening (e.g., a dog chasing a ball).
22
+ 3. Break down individual objects and provide a description for each, including attributes like color, size, texture, and their relationship with other objects.
23
+ In this case, the sub-dictionary under the "details" key should contain the following keys:
24
+ * "background": A description of the background elements.
25
+ * "main scene": A summary of the primary action taking place.
26
+ * Individual keys for all identified objects, each with a detailed description.
27
+ While describing the objects, be very detailed. Not just mention person, but mention: middle-aged women with brown curly hair, ...
28
+
29
+ - role: system
30
+ content: &cot_diagrams >
31
+ If the image is a diagram, identify key labeled components and describe their meaning.
32
+ - Describe the meaning of the diagram, and if there are axes, explain their purpose.
33
+ - Provide an interpretation of the overall meaning and takeaway from the chart, including relationships between elements if applicable.
34
+ In this case, the sub-dictionary under the "details" key should contain the following keys:
35
+ * "x-axis", "y-axis" (or variations like "y1-axis" and "y2-axis") if applicable.
36
+ * "legend": A description of the plotted data, including sources if available.
37
+ * "takeaway": A summary of the main insights derived from the chart.
38
+ * Additional structured details, such as grouped data (e.g., individual timelines in a line chart).
39
+
40
+ - role: system
41
+ content: &cot_flowcharts >
42
+ If the image is a flowchart:
43
+ - Identify the start and end points.
44
+ - List key process steps and decision nodes.
45
+ - Describe directional flows and relationships between components.
46
+ In this case, the sub-dictionary under the "details" key should contain the following keys:
47
+ * "start points": The identified starting nodes of the flowchart.
48
+ * "end points": The final outcome(s) of the flowchart.
49
+ * "detailed description": A natural language explanation of the entire flow.
50
+ * Additional keys for each process step and decision point, described in detail.
51
+
52
+ - role: system
53
+ content: &cot_ads >
54
+ If the image is an advertisement:
55
+ - Describe the main subject and any branding elements.
56
+ - Identify slogans, logos, and promotional text.
57
+ - Analyze the visual strategy used (e.g., color scheme, emotional appeal, focal points).
58
+ In this case, the sub-dictionary under the "details" key should contain the following keys:
59
+ * "advertised brand": The brand being promoted.
60
+ * "advertised product": The product or service being advertised.
61
+ * "background": The background setting of the advertisement.
62
+ * "main scene": The primary subject or action depicted.
63
+ * "used slogans": Any slogans or catchphrases appearing in the advertisement.
64
+ * "visual strategy": An analysis of the design and emotional impact.
65
+ * Additional keys for individual objects, just like in the case of normal pictures.
66
+
67
+ - role: system
68
+ content: &cot_output_example >
69
+ Example output for a normal picture:
70
+
71
+ ```json
72
+ {
73
+ "image type": "picture",
74
+ "overall description": "A peaceful rural landscape featuring a cow chained to a tree in a field with mountains in the background.",
75
+ "details": {
76
+ "background": "A large open field with patches of grass and dirt, surrounded by distant mountains under a clear blue sky.",
77
+ "main scene": "A cow chained to a tree in the middle of a grassy field.",
78
+ "cow": "A brown and white cow standing near the tree, appearing calm.",
79
+ "tree": "A sturdy oak tree with green leaves and a metal chain wrapped around its trunk.",
80
+ "mountain": "Tall, rocky mountains stretching across the horizon.",
81
+ "chain": "A shiny metal chain, slightly rusty in some places."
82
+ }
83
+ }
84
+ ```
85
+ - role: user
86
+ content:
87
+ - type: text
88
+ text: "Describe this image as you trained. Only output the dictionary add nothing else."
89
+ - type: "image_url"
90
+ image_url: {image_address}
examples/uploads/lynx_chatbot_scenario_selector.yaml ADDED
@@ -0,0 +1,302 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ - name: general_interest
2
+ mode: retrieve_llm # Literal[retrieve_llm, retrieve_only, llm_only, fixed_answer, sticky_answer]
3
+ prompt_messages: # Answer prompts in [role, content] format, should contain {context}
4
+ - role: system # Literal[system, assistant, user, tool]
5
+ content: &role >
6
+ You are LynxScribe, a chatbot representing Lynx Analytics, a leading Singaporean analytics
7
+ company specializing in pharma, life sciences, generative AI, and graph AI. Your role is to
8
+ respond to inquiries on the Lynx Analytics website. To better understand the visitors'
9
+ needs, you may ask follow-up questions as detailed in subsequent instructions.
10
+ - role: system
11
+ content: &preferences >
12
+ Lynx Analytics specializes in a range of areas including pharma (with a focus on marketing
13
+ support), life sciences, graph AI, and generative AI solutions. When responding to inquiries
14
+ about our solutions or products, give priority to those related to generative AI (chatbots
15
+ for pharma and service providers), graph AI (graph reasoning), and pharma (research, key
16
+ opinion leaders, brand adoption ladder). Also, briefly touch upon our offerings in retail
17
+ (price AI, assort AI, promo AI) and finance (digital banking, Customer Happiness Index), as
18
+ these are areas of secondary priority. Additionally, although telecommunication is worth
19
+ mentioning briefly to highlight our comprehensive range of expertise and solutions.
20
+ - role: system
21
+ content: &context >
22
+ Respond to questions solely based on the context outlined below:\n\n{context}
23
+ - role: system
24
+ content: &instr_prices >
25
+ If inquiries about pricing arise, suggest contacting Lynx Analytics for detailed
26
+ information. Additionally, emphasize that Lynx Analytics offers solutions at competitive
27
+ prices without compromising on quality.
28
+ - role: system
29
+ content: &ask_industry >
30
+ If it's not mentioned in the chat history, include a question at the end of your response
31
+ to inquire about their industry interest or employment. For example: 'May I know which
32
+ specific domain or industry you are interested in or work in?'
33
+ - role: system
34
+ content: &ask_visit_reason >
35
+ If the chat history does not reveal it, ask about their reason for visiting the website. For
36
+ instance, you might say: 'Could you share what prompted your visit to our website today?'
37
+ - role: system
38
+ content: &final_instr >
39
+ Carefully answer questions based on the provided context. Refrain from introducing new
40
+ names; use only those within your context. Respond in the language of the question. If
41
+ necessary, ask follow-up questions. Ensure your answers are clear, utilizing bullet points
42
+ where appropriate. Avoid phrases like 'According to this article' to maintain a natural
43
+ tone.
44
+ link_answer: &link # When present, formatted node link appends to answer, should contain {link}
45
+ "\n\nPlease visit <a href='{link}' target='_blank'>{link}</a> for further information."
46
+ min_similarity_score: -1 # Only need to specify if > -1 and in RETRIEVE_LLM or RETRIEVE_ONLY mode
47
+ - name: life_sciences_interest
48
+ mode: retrieve_llm
49
+ prompt_messages:
50
+ - role: system
51
+ content: *role
52
+ - role: system
53
+ content: *preferences
54
+ - role: system
55
+ content: *context
56
+ - role: system
57
+ content: *instr_prices
58
+ - role: system
59
+ content: &ask_profession >
60
+ If their job is not mentioned in the chat history, add a question at the end of your answer
61
+ about their profession. For example: 'Could you please tell me about your current profession
62
+ or occupation?'
63
+ - role: system
64
+ content: *ask_visit_reason
65
+ - role: system
66
+ content: &ask_email >
67
+ If their email is not already in the chat history, suggest that they can provide their email
68
+ address for further contact. For instance: 'Should you wish for further communication
69
+ regarding your queries, feel free to provide your email address.'
70
+ - role: system
71
+ content: *final_instr
72
+ link_answer: *link
73
+ min_similarity_score: -1
74
+ - name: finance_interest
75
+ mode: retrieve_llm
76
+ prompt_messages:
77
+ - role: system
78
+ content: *role
79
+ - role: system
80
+ content: *context
81
+ - role: system
82
+ content: *instr_prices
83
+ - role: system
84
+ content: &ask_responsibilities >
85
+ If their job or responsibilities are not detailed in the chat history, include a question
86
+ at the end of your response. For example: 'Would you mind sharing some details about the
87
+ specific responsibilities you manage in your role?'
88
+ - role: system
89
+ content: *ask_visit_reason
90
+ - role: system
91
+ content: *ask_email
92
+ - role: system
93
+ content: *final_instr
94
+ link_answer: *link
95
+ min_similarity_score: -1
96
+ - name: telco_interest
97
+ mode: retrieve_llm
98
+ prompt_messages:
99
+ - role: system
100
+ content: *role
101
+ - role: system
102
+ content: *context
103
+ - role: system
104
+ content: *instr_prices
105
+ - role: system
106
+ content: *ask_responsibilities
107
+ - role: system
108
+ content: *ask_visit_reason
109
+ - role: system
110
+ content: *ask_email
111
+ - role: system
112
+ content: *final_instr
113
+ link_answer: *link
114
+ min_similarity_score: -1
115
+ - name: retail_interest
116
+ mode: retrieve_llm
117
+ prompt_messages:
118
+ - role: system
119
+ content: *role
120
+ - role: system
121
+ content: *context
122
+ - role: system
123
+ content: *instr_prices
124
+ - role: system
125
+ content: *ask_responsibilities
126
+ - role: system
127
+ content: *ask_visit_reason
128
+ - role: system
129
+ content: *ask_email
130
+ - role: system
131
+ content: *final_instr
132
+ link_answer: *link
133
+ min_similarity_score: -1
134
+ - name: lynx_kite
135
+ mode: retrieve_llm
136
+ prompt_messages:
137
+ - role: system
138
+ content: *role
139
+ - role: system
140
+ content: *preferences
141
+ - role: system
142
+ content: *context
143
+ - role: system
144
+ content: *instr_prices
145
+ - role: system
146
+ content: *ask_industry
147
+ - role: system
148
+ content: &ask_graph >
149
+ If it's not mentioned in the chat history, include a question at the end of your response to
150
+ inquire about their specific needs related to graph analytics. For example: 'May I know
151
+ which particular graph-related problem you are looking to solve with graph analytics?'
152
+ - role: system
153
+ content: *ask_email
154
+ - role: system
155
+ content: *final_instr
156
+ link_answer: *link
157
+ min_similarity_score: -1
158
+ - name: lynx_team
159
+ mode: retrieve_llm
160
+ prompt_messages:
161
+ - role: system
162
+ content: *role
163
+ - role: system
164
+ content: *context
165
+ - role: system
166
+ content: *instr_prices
167
+ - role: system
168
+ content: *ask_visit_reason
169
+ - role: system
170
+ content: >
171
+ When they inquire about names that could refer to multiple individuals, provide the names
172
+ along with a brief description of each. Then, ask for clarification on which specific
173
+ individual they are referring to.
174
+ - role: system
175
+ content: *final_instr
176
+ link_answer: *link
177
+ min_similarity_score: -1
178
+ - name: lynx_career
179
+ mode: retrieve_llm
180
+ prompt_messages:
181
+ - role: system
182
+ content: *role
183
+ - role: system
184
+ content: *context
185
+ - role: system
186
+ content: *instr_prices
187
+ - role: system
188
+ content: *ask_responsibilities
189
+ - role: system
190
+ content: >
191
+ If it's not already mentioned in the chat history, include a question at the end of your
192
+ response to inquire about their motivation for wanting to work with us. For example: 'Could
193
+ you share what motivates you to seek a position with our team?'
194
+ - role: system
195
+ content: *ask_email
196
+ - role: system
197
+ content: *final_instr
198
+ link_answer: *link
199
+ min_similarity_score: -1
200
+ - name: lynxscribe
201
+ mode: retrieve_llm
202
+ prompt_messages:
203
+ - role: system
204
+ content: *role
205
+ - role: system
206
+ content: *preferences
207
+ - role: system
208
+ content: *context
209
+ - role: system
210
+ content: *instr_prices
211
+ - role: system
212
+ content: *ask_industry
213
+ - role: system
214
+ content: >
215
+ If the chat history does not already include this information, add a question at the end of
216
+ your response to identify their specific needs in generative AI. For example: 'Could you
217
+ please specify the problem you are aiming to address using generative AI?'
218
+ - role: system
219
+ content: *ask_email
220
+ - role: system
221
+ content: *final_instr
222
+ link_answer: *link
223
+ min_similarity_score: -1
224
+ - name: general_ds
225
+ mode: retrieve_llm
226
+ prompt_messages:
227
+ - role: system
228
+ content: *role
229
+ - role: system
230
+ content: *context
231
+ - role: system
232
+ content: *instr_prices
233
+ - role: system
234
+ content: *ask_industry
235
+ - role: system
236
+ content: *ask_visit_reason
237
+ - role: system
238
+ content: *ask_email
239
+ - role: system
240
+ content: *final_instr
241
+ link_answer: *link
242
+ min_similarity_score: -1
243
+ - name: general_graph
244
+ mode: retrieve_llm
245
+ prompt_messages:
246
+ - role: system
247
+ content: *role
248
+ - role: system
249
+ content: *preferences
250
+ - role: system
251
+ content: *context
252
+ - role: system
253
+ content: *instr_prices
254
+ - role: system
255
+ content: *ask_graph
256
+ - role: system
257
+ content: *ask_industry
258
+ - role: system
259
+ content: *ask_email
260
+ - role: system
261
+ content: *final_instr
262
+ link_answer: *link
263
+ min_similarity_score: -1
264
+ - name: other_okay
265
+ mode: retrieve_llm
266
+ prompt_messages:
267
+ - role: system
268
+ content: *role
269
+ - role: system
270
+ content: *preferences
271
+ - role: system
272
+ content: *context
273
+ - role: system
274
+ content: *instr_prices
275
+ - role: system
276
+ content: *ask_industry
277
+ - role: system
278
+ content: *ask_visit_reason
279
+ - role: system
280
+ content: *final_instr
281
+ link_answer: *link
282
+ min_similarity_score: -1
283
+ - name: contact_us
284
+ mode: retrieve_llm
285
+ prompt_messages:
286
+ - role: system
287
+ content: *role
288
+ - role: system
289
+ content: *context
290
+ - role: system
291
+ content: *instr_prices
292
+ - role: system
293
+ content: *ask_email
294
+ - role: system
295
+ content: *final_instr
296
+ link_answer: *link
297
+ min_similarity_score: -1
298
+ - name: malicious
299
+ mode: fixed_answer # Could be sticky, but if we want the user to rephrase, let's give 2nd chance
300
+ fixed_answer: >
301
+ I am sorry, but I feel you want me use in a wrong way. If I feel it wrong, please try to
302
+ rephrase your question.
examples/uploads/organon_demo/backend-scenarios-en.yaml ADDED
@@ -0,0 +1,92 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ - name: general_contraception_information # Must match the scenario name found in RAG graph
2
+ mode: retrieve_only # Literal[retrieve_llm, retrieve_only, llm_only, fixed_answer, sticky_answer]
3
+ min_pass_similarity_score: 0.55 # Custom value: if any answer is above, give three results
4
+ single_similarity_score: 0.86 # Custom value: is best answer is above, give single result
5
+ # min_similarity_score not used (stays -1.0) because it ditches all lower-score docs from results
6
+ - name: intrauterine_devices
7
+ mode: retrieve_only
8
+ min_pass_similarity_score: 0.55 # Sim Score = 2 * (1 - Distance) - 1
9
+ single_similarity_score: 0.86 # Sim Score = 2 * (1 - Distance) - 1
10
+ - name: contraceptive_injection
11
+ mode: retrieve_only
12
+ min_pass_similarity_score: 0.55
13
+ single_similarity_score: 0.86
14
+ - name: emergency_contraceptive_pills
15
+ mode: retrieve_only
16
+ min_pass_similarity_score: 0.55
17
+ single_similarity_score: 0.86
18
+ - name: contraceptive_implant
19
+ mode: retrieve_only
20
+ min_pass_similarity_score: 0.55
21
+ single_similarity_score: 0.86
22
+ - name: combined_contraceptive_pills
23
+ mode: retrieve_only
24
+ min_pass_similarity_score: 0.55
25
+ single_similarity_score: 0.86
26
+ - name: other_okay
27
+ mode: retrieve_only
28
+ min_pass_similarity_score: 0.55
29
+ single_similarity_score: 0.86
30
+ - name: own_side_effect
31
+ mode: fixed_answer
32
+ fixed_answer:
33
+ &abort_side_eff > # Anchors are added only for reference to old V1 template file
34
+ I'm not equipped to handle adverse events or other product-related queries. Your safety is
35
+ important to us, and we want to ensure you receive the appropriate support. Please report any
36
+ adverse events or concerns to our dedicated support team.
37
+ They can be reached at [email protected].
38
+ If you have any questions related to contraceptives or women's health, please feel free to ask,
39
+ and I'll provide you with the information you need.
40
+ keywords: [Mercilon, Marvelon, Implanon]
41
+ keyword_answer: &alternative_side_eff >
42
+ I'm not equipped to handle adverse events or other product-related queries. Your safety is
43
+ important to us, and we want to ensure you receive the appropriate support. Please report any
44
+ adverse events or concerns related to Organon products to our dedicated support team.
45
+ They can be reached at [email protected].
46
+ If you have any questions related to contraceptives or women's health, please feel free to ask,
47
+ and I'll provide you with the information you need.
48
+ - name: own_dosage
49
+ mode: fixed_answer
50
+ fixed_answer: &abort_dosage >
51
+ I'm not equipped to handle adverse events, dosage-related questions or other product-related
52
+ queries. Your safety is important to us, and we want to ensure you receive the appropriate
53
+ support. Please consult with the prescribing doctor about the details on how to use the
54
+ medication. For further information, please write a message to our dedicated support team.
55
+ They can be reached at [email protected].
56
+ If you have any questions related to contraceptives or women's health, please feel free to ask,
57
+ and I'll provide you with the information you need.
58
+ - name: brand_specific_information
59
+ mode: fixed_answer
60
+ fixed_answer: &brand_selection >
61
+ I appreciate your question about brand recommendations. For personalized advice related to
62
+ healthcare or specific products, it's always best to consult with a healthcare professional
63
+ who can consider your individual needs and provide tailored recommendations. They have the
64
+ expertise to guide you in the right direction.
65
+ If you have any questions related to contraceptives or women's health, please feel free to ask,
66
+ and I'll provide you with the information you need.
67
+ - name: greetings_hay
68
+ mode: fixed_answer
69
+ fixed_answer: &how_are_you_hi >
70
+ I am fine thanks, hope you feel the same! Feel free to ask any contraception related question.
71
+ - name: greetings_hi
72
+ mode: fixed_answer
73
+ fixed_answer: &say_hi >
74
+ Hi, nice to meet you! Feel free to ask any contraception related question.
75
+ - name: emailthanks
76
+ mode: fixed_answer
77
+ fixed_answer: &email_thanks >
78
+ Thank you. Could I help with something else?
79
+ - name: thanks
80
+ mode: fixed_answer
81
+ fixed_answer: &thanks_answer >
82
+ Not at all. Could I help with something else?
83
+ - name: confirmation
84
+ mode: fixed_answer
85
+ fixed_answer: &confirmation_answer >
86
+ Thank you. Could I help with something else?
87
+ - name: malicious
88
+ mode: sticky_answer
89
+ fixed_answer: &malicious_message >
90
+ I am sorry, but I feel you want me use in a wrong way.
91
+ If I feel it wrong, please try to rephrase your question.
92
+ Refresh your browser if you'd like to ask more questions.
examples/uploads/organon_demo/organon_en_copy.xlsx ADDED
Binary file (66.9 kB). View file
 
lynxkite-lynxscribe/pyproject.toml CHANGED
@@ -6,7 +6,7 @@ readme = "README.md"
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]
 
6
  requires-python = ">=3.11"
7
  dependencies = [
8
  "lynxkite-core",
9
+ "lynxscribe[openai,faiss-cpu,litellm,google] @ git+ssh://[email protected]/biggraph/lynxscribe@main",
10
  ]
11
 
12
  [tool.uv.sources]
lynxkite-lynxscribe/src/lynxkite_lynxscribe/lynxscribe_ops.py CHANGED
@@ -1,12 +1,24 @@
1
  """
2
  LynxScribe configuration and testing in LynxKite.
 
3
  """
4
 
 
 
 
 
 
 
 
 
5
  import pathlib
6
  from lynxscribe.core.llm.base import get_llm_engine
7
  from lynxscribe.core.vector_store.base import get_vector_store
8
  from lynxscribe.common.config import load_config
9
  from lynxscribe.components.text.embedder import TextEmbedder
 
 
 
10
  from lynxscribe.components.rag.rag_graph import RAGGraph
11
  from lynxscribe.components.rag.knowledge_base_graph import PandasKnowledgeBaseGraph
12
  from lynxscribe.components.rag.rag_chatbot import Scenario, ScenarioSelector, RAGChatbot
@@ -17,94 +29,621 @@ from lynxscribe.components.chat.processors import (
17
  )
18
  from lynxscribe.components.chat.api import ChatAPI
19
  from lynxscribe.core.models.prompts import ChatCompletionPrompt
 
20
 
21
  from lynxkite.core import ops
22
  import json
23
  from lynxkite.core.executors import one_by_one
24
 
 
 
25
  ENV = "LynxScribe"
26
  one_by_one.register(ENV)
 
27
  op = ops.op_registration(ENV)
28
  output_on_top = ops.output_position(output="top")
29
 
30
 
31
- @output_on_top
32
- @op("Vector store")
33
- def vector_store(*, name="chromadb", collection_name="lynx"):
34
- vector_store = get_vector_store(name=name, collection_name=collection_name)
35
- return {"vector_store": vector_store}
36
 
37
 
38
- @output_on_top
39
- @op("LLM")
40
- def llm(*, name="openai"):
41
- llm = get_llm_engine(name=name)
42
- return {"llm": llm}
43
 
44
 
45
- @output_on_top
46
- @ops.input_position(llm="bottom")
47
- @op("Text embedder")
48
- def text_embedder(llm, *, model="text-embedding-ada-002"):
49
- llm = llm[0]["llm"]
50
- text_embedder = TextEmbedder(llm=llm, model=model)
51
- return {"text_embedder": text_embedder}
 
 
 
 
 
 
 
 
 
 
 
52
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
53
 
54
- @output_on_top
55
- @ops.input_position(vector_store="bottom", text_embedder="bottom")
56
- @op("RAG graph")
57
- def rag_graph(vector_store, text_embedder):
58
- vector_store = vector_store[0]["vector_store"]
59
- text_embedder = text_embedder[0]["text_embedder"]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
60
  rag_graph = RAGGraph(
61
  PandasKnowledgeBaseGraph(vector_store=vector_store, text_embedder=text_embedder)
62
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
63
  return {"rag_graph": rag_graph}
64
 
65
 
66
  @output_on_top
67
- @op("Scenario selector")
68
- def scenario_selector(*, scenario_file: str, node_types="intent_cluster"):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
69
  scenarios = load_config(scenario_file)
70
  node_types = [t.strip() for t in node_types.split(",")]
71
- scenario_selector = ScenarioSelector(
72
- scenarios=[Scenario(**scenario) for scenario in scenarios],
73
- node_types=node_types,
74
- )
75
- return {"scenario_selector": scenario_selector}
76
 
 
 
77
 
78
- DEFAULT_NEGATIVE_ANSWER = "I'm sorry, but the data I've been trained on does not contain any information related to your question."
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
79
 
80
 
81
  @output_on_top
82
- @ops.input_position(rag_graph="bottom", scenario_selector="bottom", llm="bottom")
83
- @op("RAG chatbot")
84
- def rag_chatbot(
85
- rag_graph,
86
- scenario_selector,
87
- llm,
88
  *,
89
  negative_answer=DEFAULT_NEGATIVE_ANSWER,
90
- limits_by_type="{}",
91
- strict_limits=True,
92
- max_results=5,
 
 
 
 
 
93
  ):
94
- rag_graph = rag_graph[0]["rag_graph"]
95
- scenario_selector = scenario_selector[0]["scenario_selector"]
96
- llm = llm[0]["llm"]
97
- limits_by_type = json.loads(limits_by_type)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
98
  rag_chatbot = RAGChatbot(
99
  rag_graph=rag_graph,
100
  scenario_selector=scenario_selector,
101
  llm=llm,
102
  negative_answer=negative_answer,
103
- limits_by_type=limits_by_type,
104
- strict_limits=strict_limits,
105
- max_results=max_results,
 
 
 
 
 
106
  )
107
- return {"chatbot": rag_chatbot}
 
108
 
109
 
110
  @output_on_top
@@ -162,7 +701,12 @@ async def test_chat_api(message, chat_api, *, show_details=False):
162
  messages=[{"role": "user", "content": message["text"]}],
163
  )
164
  response = await chat_api.answer(request, stream=False)
165
- answer = response.choices[0].message.content
 
 
 
 
 
166
  if show_details:
167
  return {"answer": answer, **response.__dict__}
168
  else:
@@ -174,39 +718,6 @@ def input_chat(*, chat: str):
174
  return {"text": chat}
175
 
176
 
177
- @output_on_top
178
- @ops.input_position(chatbot="bottom", chat_processor="bottom", knowledge_base="bottom")
179
- @op("Chat API")
180
- def chat_api(chatbot, chat_processor, knowledge_base, *, model="gpt-4o-mini"):
181
- chatbot = chatbot[0]["chatbot"]
182
- chat_processor = chat_processor[0]["chat_processor"]
183
- knowledge_base = knowledge_base[0]
184
- c = ChatAPI(
185
- chatbot=chatbot,
186
- chat_processor=chat_processor,
187
- model=model,
188
- )
189
- if knowledge_base:
190
- c.chatbot.rag_graph.kg_base.load_v1_knowledge_base(**knowledge_base)
191
- c.chatbot.scenario_selector.check_compatibility(c.chatbot.rag_graph)
192
- return {"chat_api": c}
193
-
194
-
195
- @output_on_top
196
- @op("Knowledge base")
197
- def knowledge_base(
198
- *,
199
- nodes_path="nodes.pickle",
200
- edges_path="edges.pickle",
201
- template_cluster_path="tempclusters.pickle",
202
- ):
203
- return {
204
- "nodes_path": nodes_path,
205
- "edges_path": edges_path,
206
- "template_cluster_path": template_cluster_path,
207
- }
208
-
209
-
210
  @op("View", view="table_view")
211
  def view(input):
212
  columns = [str(c) for c in input.keys() if not str(c).startswith("_")]
@@ -230,7 +741,7 @@ async def get_chat_api(ws: str):
230
  assert path.exists(), f"Workspace {path} does not exist"
231
  ws = workspace.load(path)
232
  contexts = await ops.EXECUTORS[ENV](ws)
233
- nodes = [op for op in ws.nodes if op.data.title == "Chat API"]
234
  [node] = nodes
235
  context = contexts[node.id]
236
  return context.last_result["chat_api"]
@@ -299,3 +810,43 @@ def get_lynxscribe_workspaces():
299
  pass # Ignore files that are not valid workspaces.
300
  workspaces.sort()
301
  return workspaces
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  """
2
  LynxScribe configuration and testing in LynxKite.
3
+ TODO: all these outputs should contain metadata. So the next task can check the input type, etc.
4
  """
5
 
6
+ from google.cloud import storage
7
+ from copy import deepcopy
8
+ from enum import Enum
9
+ import asyncio
10
+ import pandas as pd
11
+ import joblib
12
+ from pydantic import BaseModel, ConfigDict
13
+
14
  import pathlib
15
  from lynxscribe.core.llm.base import get_llm_engine
16
  from lynxscribe.core.vector_store.base import get_vector_store
17
  from lynxscribe.common.config import load_config
18
  from lynxscribe.components.text.embedder import TextEmbedder
19
+ from lynxscribe.core.models.embedding import Embedding
20
+ from lynxscribe.components.embedding_clustering import FclusterBasedClustering
21
+
22
  from lynxscribe.components.rag.rag_graph import RAGGraph
23
  from lynxscribe.components.rag.knowledge_base_graph import PandasKnowledgeBaseGraph
24
  from lynxscribe.components.rag.rag_chatbot import Scenario, ScenarioSelector, RAGChatbot
 
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
35
  import json
36
  from lynxkite.core.executors import one_by_one
37
 
38
+ DEFAULT_NEGATIVE_ANSWER = "I'm sorry, but the data I've been trained on does not contain any information related to your question."
39
+
40
  ENV = "LynxScribe"
41
  one_by_one.register(ENV)
42
+ mem = joblib.Memory("joblib-cache")
43
  op = ops.op_registration(ENV)
44
  output_on_top = ops.output_position(output="top")
45
 
46
 
47
+ # defining the cloud provider enum
48
+ class CloudProvider(Enum):
49
+ GCP = "gcp"
50
+ AWS = "aws"
51
+ AZURE = "azure"
52
 
53
 
54
+ class RAGVersion(Enum):
55
+ V1 = "v1"
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.
62
+ One table (FAQs) contains scenario-denoted nodes to upsert into the knowledge base, the other
63
+ two tables serve as the configuration for the scenario selector.
64
+ Attributes:
65
+ faq_data:
66
+ Table where each row is an FAQ question, and possibly its answer pair. Will be fed into
67
+ `FAQTemplateLoader.load_nodes_and_edges()`. For configuration of this table see the
68
+ loader's init arguments.
69
+ scenario_data:
70
+ Table where each row is a Scenario, column names are thus scenario attributes. Will be
71
+ fed into `ScenarioSelector.from_data()`.
72
+ prompt_codes:
73
+ Optional helper for the scenario table, may contain prompt code mappings to real prompt
74
+ messages. It's enough then to use the codes instead of the full messages in the
75
+ scenarios table. Will be fed into `ScenarioSelector.from_data()`.
76
+ """
77
 
78
+ model_config = ConfigDict(arbitrary_types_allowed=True)
79
+
80
+ faq_data: pd.DataFrame
81
+ scenario_data: pd.DataFrame
82
+ prompt_codes: dict[str, str] = {}
83
+
84
+ @classmethod
85
+ def from_excel_path(
86
+ cls,
87
+ path: str,
88
+ faq_data_sheet_name: str,
89
+ scenario_data_sheet_name: str,
90
+ prompt_codes_sheet_name: str | None = None,
91
+ ) -> "RAGTemplate":
92
+ """Spawn a RAGTemplate from an Excel file containing the two needed (plus one optional) sheets."""
93
+
94
+ def transform_codes(prompt_codes: pd.DataFrame) -> dict[str, str]:
95
+ """Check and transform prompt codes table into a code dictionary."""
96
+ if (len_columns := len(prompt_codes.columns)) != 2:
97
+ raise ValueError(
98
+ f"Prompt codes should contain exactly 2 columns, {len_columns} found."
99
+ )
100
+ return prompt_codes.set_index(prompt_codes.columns[0])[
101
+ prompt_codes.columns[1]
102
+ ].to_dict()
103
+
104
+ return cls(
105
+ faq_data=pd.read_excel(path, sheet_name=faq_data_sheet_name),
106
+ scenario_data=pd.read_excel(path, sheet_name=scenario_data_sheet_name),
107
+ prompt_codes=transform_codes(pd.read_excel(path, sheet_name=prompt_codes_sheet_name))
108
+ if prompt_codes_sheet_name
109
+ else {},
110
+ )
111
+
112
+
113
+ @op("Cloud-sourced File Listing")
114
+ def cloud_file_loader(
115
+ *,
116
+ cloud_provider: CloudProvider = CloudProvider.GCP,
117
+ folder_URL: str = "https://storage.googleapis.com/lynxkite_public_data/lynxscribe-images/image-rag-test",
118
+ accepted_file_types: str = ".jpg, .jpeg, .png",
119
+ ):
120
+ """
121
+ Gives back the list of URLs of all the images from a cloud-based folder.
122
+ Currently only supports GCP storage.
123
+ """
124
+ if folder_URL[-1].endswith("/"):
125
+ folder_URL = folder_URL[:-1]
126
+
127
+ accepted_file_types = tuple([t.strip() for t in accepted_file_types.split(",")])
128
+
129
+ if cloud_provider == CloudProvider.GCP:
130
+ client = storage.Client()
131
+ url_useful_part = folder_URL.split(".com/")[-1]
132
+ bucket_name = url_useful_part.split("/")[0]
133
+ if bucket_name == url_useful_part:
134
+ prefix = ""
135
+ else:
136
+ prefix = url_useful_part.split(bucket_name + "/")[-1]
137
+
138
+ bucket = client.bucket(bucket_name)
139
+ blobs = bucket.list_blobs(prefix=prefix)
140
+ file_urls = [blob.public_url for blob in blobs if blob.name.endswith(accepted_file_types)]
141
+ return {"file_urls": file_urls}
142
+ else:
143
+ raise ValueError(f"Cloud provider '{cloud_provider}' is not supported.")
144
+
145
+
146
+ # @output_on_top
147
+ # @op("LynxScribe RAG Graph Vector Store")
148
+ # @mem.cache
149
+ # def ls_rag_graph(
150
+ # *,
151
+ # name: str = "faiss",
152
+ # num_dimensions: int = 3072,
153
+ # collection_name: str = "lynx",
154
+ # text_embedder_interface: str = "openai",
155
+ # text_embedder_model_name_or_path: str = "text-embedding-3-large",
156
+ # # api_key_name: str = "OPENAI_API_KEY",
157
+ # ):
158
+ # """
159
+ # Returns with a vector store instance.
160
+ # """
161
+
162
+ # # getting the text embedder instance
163
+ # llm_params = {"name": text_embedder_interface}
164
+ # # if api_key_name:
165
+ # # llm_params["api_key"] = os.getenv(api_key_name)
166
+ # llm = get_llm_engine(**llm_params)
167
+ # text_embedder = TextEmbedder(llm=llm, model=text_embedder_model_name_or_path)
168
+
169
+ # # getting the vector store
170
+ # if name == "chromadb":
171
+ # vector_store = get_vector_store(name=name, collection_name=collection_name)
172
+ # elif name == "faiss":
173
+ # vector_store = get_vector_store(name=name, num_dimensions=num_dimensions)
174
+ # else:
175
+ # raise ValueError(f"Vector store name '{name}' is not supported.")
176
+
177
+ # # building up the RAG graph
178
+ # rag_graph = RAGGraph(
179
+ # PandasKnowledgeBaseGraph(vector_store=vector_store, text_embedder=text_embedder)
180
+ # )
181
+
182
+ # return {"rag_graph": rag_graph}
183
+
184
+
185
+ @op("LynxScribe Image Describer")
186
+ @mem.cache
187
+ async def ls_image_describer(
188
+ file_urls,
189
+ *,
190
+ llm_interface: str = "openai",
191
+ llm_visual_model: str = "gpt-4o",
192
+ llm_prompt_path: str = "uploads/image_description_prompts.yaml",
193
+ llm_prompt_name: str = "cot_picture_descriptor",
194
+ # api_key_name: str = "OPENAI_API_KEY",
195
+ ):
196
+ """
197
+ Returns with image descriptions from a list of image URLs.
198
 
199
+ TODO: making the inputs more flexible (e.g. accepting file locations, URLs, binaries, etc.).
200
+ the input dictionary should contain some meta info: e.g., what is in the list...
201
+ """
202
+
203
+ # handling inputs
204
+ image_urls = file_urls["file_urls"]
205
+
206
+ # loading the LLM
207
+ llm_params = {"name": llm_interface}
208
+ # if api_key_name:
209
+ # llm_params["api_key"] = os.getenv(api_key_name)
210
+ llm = get_llm_engine(**llm_params)
211
+
212
+ # preparing the prompts
213
+ prompt_base = load_config(llm_prompt_path)[llm_prompt_name]
214
+ prompt_list = []
215
+
216
+ for i in range(len(image_urls)):
217
+ image = image_urls[i]
218
+
219
+ _prompt = deepcopy(prompt_base)
220
+ for message in _prompt:
221
+ if isinstance(message["content"], list):
222
+ for _message_part in message["content"]:
223
+ if "image_url" in _message_part:
224
+ _message_part["image_url"] = {"url": image}
225
+
226
+ prompt_list.append(_prompt)
227
+
228
+ # creating the prompt objects
229
+ ch_prompt_list = [
230
+ ChatCompletionPrompt(model=llm_visual_model, messages=prompt) for prompt in prompt_list
231
+ ]
232
+
233
+ # get the image descriptions
234
+ tasks = [llm.acreate_completion(completion_prompt=_prompt) for _prompt in ch_prompt_list]
235
+ out_completions = await asyncio.gather(*tasks)
236
+ results = [
237
+ dictionary_corrector(result.choices[0].message.content) for result in out_completions
238
+ ]
239
+
240
+ # getting the image descriptions (list of dictionaries {image_url: URL, description: description})
241
+ # TODO: some result class could be a better idea (will be developed in LynxScribe)
242
+ image_descriptions = [
243
+ {"image_url": image_urls[i], "description": results[i]} for i in range(len(image_urls))
244
+ ]
245
+
246
+ return {"image_descriptions": image_descriptions}
247
+
248
+
249
+ @op("LynxScribe Image RAG Builder")
250
+ @mem.cache
251
+ async def ls_image_rag_builder(
252
+ image_descriptions,
253
+ *,
254
+ vdb_provider_name: str = "faiss",
255
+ vdb_num_dimensions: int = 3072,
256
+ vdb_collection_name: str = "lynx",
257
+ text_embedder_interface: str = "openai",
258
+ text_embedder_model_name_or_path: str = "text-embedding-3-large",
259
+ # api_key_name: str = "OPENAI_API_KEY",
260
+ ):
261
+ """
262
+ Based on image descriptions, and embedding/VDB parameters,
263
+ the function builds up an image RAG graph, where the nodes are the
264
+ descriptions of the images (and of all image objects).
265
+
266
+ In a later phase, synthetic questions and "named entities" will also
267
+ be added to the graph.
268
+ """
269
+
270
+ # handling inputs
271
+ image_descriptions = image_descriptions["image_descriptions"]
272
+
273
+ # Building up the empty RAG graph
274
+
275
+ # a) Define LLM interface and get a text embedder
276
+ llm_params = {"name": text_embedder_interface}
277
+ # if api_key_name:
278
+ # llm_params["api_key"] = os.getenv(api_key_name)
279
+ llm = get_llm_engine(**llm_params)
280
+ text_embedder = TextEmbedder(llm=llm, model=text_embedder_model_name_or_path)
281
+
282
+ # b) getting the vector store
283
+ # TODO: vdb_provider_name should be ENUM, and other parameters should appear accordingly
284
+ if vdb_provider_name == "chromadb":
285
+ vector_store = get_vector_store(name=vdb_provider_name, collection_name=vdb_collection_name)
286
+ elif vdb_provider_name == "faiss":
287
+ vector_store = get_vector_store(name=vdb_provider_name, num_dimensions=vdb_num_dimensions)
288
+ else:
289
+ raise ValueError(f"Vector store name '{vdb_provider_name}' is not supported.")
290
+
291
+ # c) building up the RAG graph
292
  rag_graph = RAGGraph(
293
  PandasKnowledgeBaseGraph(vector_store=vector_store, text_embedder=text_embedder)
294
  )
295
+
296
+ dict_list_df = []
297
+ for image_description_tuple in image_descriptions:
298
+ image_url = image_description_tuple["image_url"]
299
+ image_description = image_description_tuple["description"]
300
+
301
+ if "overall description" in image_description:
302
+ dict_list_df.append(
303
+ {
304
+ "image_url": image_url,
305
+ "description": image_description["overall description"],
306
+ "source": "overall description",
307
+ }
308
+ )
309
+
310
+ if "details" in image_description:
311
+ for dkey in image_description["details"].keys():
312
+ text = f"The picture's description is: {image_description['overall description']}\n\nThe description of the {dkey} is: {image_description['details'][dkey]}"
313
+ dict_list_df.append(
314
+ {"image_url": image_url, "description": text, "source": "details"}
315
+ )
316
+
317
+ pdf_descriptions = pd.DataFrame(dict_list_df)
318
+ pdf_descriptions["embedding_values"] = await text_embedder.acreate_embedding(
319
+ pdf_descriptions["description"].to_list()
320
+ )
321
+ pdf_descriptions["id"] = "im_" + pdf_descriptions.index.astype(str)
322
+
323
+ # adding the embeddings to the RAG graph with metadata
324
+ pdf_descriptions["embedding"] = pdf_descriptions.apply(
325
+ lambda row: Embedding(
326
+ id=row["id"],
327
+ value=row["embedding_values"],
328
+ metadata={
329
+ "image_url": row["image_url"],
330
+ "image_part": row["source"],
331
+ "type": "image_description",
332
+ },
333
+ document=row["description"],
334
+ ),
335
+ axis=1,
336
+ )
337
+ embedding_list = pdf_descriptions["embedding"].tolist()
338
+
339
+ # adding the embeddings to the RAG graph
340
+ rag_graph.kg_base.vector_store.upsert(embedding_list)
341
+
342
+ # # saving the RAG graph
343
+ # rag_graph.kg_base.save(image_rag_out_path)
344
+
345
+ return {"rag_graph": rag_graph}
346
+
347
+
348
+ @op("LynxScribe RAG Graph Saver")
349
+ def ls_save_rag_graph(
350
+ rag_graph,
351
+ *,
352
+ image_rag_out_path: str = "image_test_rag_graph.pickle",
353
+ ):
354
+ """
355
+ Saves the RAG graph to a pickle file.
356
+ """
357
+
358
+ # reading inputs
359
+ rag_graph = rag_graph[0]["rag_graph"]
360
+
361
+ rag_graph.kg_base.save(image_rag_out_path)
362
+ return None
363
+
364
+
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
+
371
+ # get all similarities
372
+ emb_similarities = await rag_graph.search_context(
373
+ message, max_results=top_k, unique_metadata_key="image_url"
374
+ )
375
+
376
+ # get the image urls, scores and descriptions
377
+ result_list = []
378
+
379
+ for emb_sim in emb_similarities:
380
+ image_url = emb_sim.embedding.metadata["image_url"]
381
+ score = emb_sim.score
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")
389
+ def view_image(embedding_similarities):
390
+ """
391
+ Plotting the TOP images (from embedding similarities).
392
+
393
+ TODO: later on, the user can scroll the images and send feedbacks
394
+ """
395
+ embedding_similarities = embedding_similarities["embedding_similarities"]
396
+ return embedding_similarities[0]["image_url"]
397
+
398
+
399
+ @op("LynxScribe Text RAG Loader")
400
+ @mem.cache
401
+ def ls_text_rag_loader(
402
+ file_urls,
403
+ *,
404
+ input_type: RAGVersion = RAGVersion.V1,
405
+ vdb_provider_name: str = "faiss",
406
+ vdb_num_dimensions: int = 3072,
407
+ vdb_collection_name: str = "lynx",
408
+ text_embedder_interface: str = "openai",
409
+ text_embedder_model_name_or_path: str = "text-embedding-3-large",
410
+ # api_key_name: str = "OPENAI_API_KEY",
411
+ ):
412
+ """
413
+ Loading a text-based RAG graph from saved files (getting pandas readable links).
414
+ """
415
+
416
+ # handling inputs
417
+ file_urls = file_urls["file_urls"]
418
+
419
+ # getting the text embedder instance
420
+ llm_params = {"name": text_embedder_interface}
421
+ # if api_key_name:
422
+ # llm_params["api_key"] = os.getenv(api_key_name)
423
+ llm = get_llm_engine(**llm_params)
424
+ text_embedder = TextEmbedder(llm=llm, model=text_embedder_model_name_or_path)
425
+
426
+ # getting the vector store
427
+ if vdb_provider_name == "chromadb":
428
+ vector_store = get_vector_store(name=vdb_provider_name, collection_name=vdb_collection_name)
429
+ elif vdb_provider_name == "faiss":
430
+ vector_store = get_vector_store(name=vdb_provider_name, num_dimensions=vdb_num_dimensions)
431
+ else:
432
+ raise ValueError(f"Vector store name '{vdb_provider_name}' is not supported.")
433
+
434
+ # building up the RAG graph
435
+ rag_graph = RAGGraph(
436
+ PandasKnowledgeBaseGraph(vector_store=vector_store, text_embedder=text_embedder)
437
+ )
438
+
439
+ # loading the knowledge base (temporary + TODO: adding v2)
440
+ if input_type == RAGVersion.V1:
441
+ node_file = [f for f in file_urls if "nodes.p" in f][0]
442
+ edge_file = [f for f in file_urls if "edges.p" in f][0]
443
+ tempcluster_file = [f for f in file_urls if "clusters.p" in f][0]
444
+ rag_graph.kg_base.load_v1_knowledge_base(
445
+ nodes_path=node_file,
446
+ edges_path=edge_file,
447
+ template_cluster_path=tempcluster_file,
448
+ )
449
+ elif input_type == RAGVersion.V2:
450
+ raise ValueError("Currently only v1 input type is supported.")
451
+ else:
452
+ raise ValueError(f"Input type '{input_type}' is not supported.")
453
+
454
+ return {"rag_graph": rag_graph}
455
+
456
+
457
+ @op("LynxScribe FAQ to RAG")
458
+ @mem.cache
459
+ async def ls_faq_to_rag(
460
+ *,
461
+ faq_excel_path: str = "",
462
+ vdb_provider_name: str = "faiss",
463
+ vdb_num_dimensions: int = 3072,
464
+ vdb_collection_name: str = "lynx",
465
+ text_embedder_interface: str = "openai",
466
+ text_embedder_model_name_or_path: str = "text-embedding-3-large",
467
+ scenario_cluster_distance_pct: int = 30,
468
+ ):
469
+ """
470
+ Loading a text-based RAG graph from saved files (getting pandas readable links).
471
+ """
472
+
473
+ # getting the text embedder instance
474
+ llm_params = {"name": text_embedder_interface}
475
+ llm = get_llm_engine(**llm_params)
476
+ text_embedder = TextEmbedder(llm=llm, model=text_embedder_model_name_or_path)
477
+
478
+ # getting the vector store
479
+ if vdb_provider_name == "chromadb":
480
+ vector_store = get_vector_store(name=vdb_provider_name, collection_name=vdb_collection_name)
481
+ elif vdb_provider_name == "faiss":
482
+ vector_store = get_vector_store(name=vdb_provider_name, num_dimensions=vdb_num_dimensions)
483
+ else:
484
+ raise ValueError(f"Vector store name '{vdb_provider_name}' is not supported.")
485
+
486
+ # building up the RAG graph
487
+ rag_graph = RAGGraph(
488
+ PandasKnowledgeBaseGraph(vector_store=vector_store, text_embedder=text_embedder)
489
+ )
490
+
491
+ # loading the knowledge base from the FAQ file
492
+ rag_template = RAGTemplate.from_excel_path(
493
+ path=faq_excel_path,
494
+ faq_data_sheet_name="scenario_examples",
495
+ scenario_data_sheet_name="scenario_scripts",
496
+ prompt_codes_sheet_name="prompt_dictionary",
497
+ )
498
+
499
+ faq_loader_params = {
500
+ "id_column": "scenario_example_ID",
501
+ "timestamp_column": "last_modified_timestamp",
502
+ "validity_column": "valid_flg",
503
+ "question_type_contents_id": ["faq_question", "faq_question", "q_{id}"],
504
+ "answer_type_contents_id": ["faq_answer", "{faq_question}\n\n{faq_answer}", "a_{id}"],
505
+ "question_to_answer_edge_type_weight": ["qna", 1.0],
506
+ }
507
+
508
+ nodes, edges = FAQTemplateLoader(**faq_loader_params).load_nodes_and_edges(
509
+ rag_template.faq_data
510
+ )
511
+
512
+ await rag_graph.kg_base.upsert_nodes(*nodes)
513
+ rag_graph.kg_base.upsert_edges(edges)
514
+
515
+ # Generating scenario clusters
516
+ question_ids = [_id for _id in nodes[0] if _id.startswith("q_")]
517
+ stored_embeddings = rag_graph.kg_base.vector_store.get(
518
+ question_ids, include=["embeddings", "metadatas"]
519
+ )
520
+ embedding_vals = pd.Series([_emb.value for _emb in stored_embeddings], index=question_ids)
521
+ labels = pd.Series(
522
+ [_emb.metadata["scenario_name"] for _emb in stored_embeddings], index=question_ids
523
+ )
524
+ temp_cls = FclusterBasedClustering(distance_percentile=scenario_cluster_distance_pct)
525
+ temp_cls.fit(embedding_vals, labels)
526
+ df_tempclusters = temp_cls.get_cluster_centers()
527
+
528
+ # Adding the scenario clusters to the RAG Graph
529
+ df_tempclusters["template_id"] = "t_" + df_tempclusters.index.astype(str)
530
+ df_tempclusters["embedding"] = df_tempclusters.apply(
531
+ lambda row: Embedding(
532
+ id=row["template_id"],
533
+ value=row["cluster_center"],
534
+ metadata={"scenario_name": row["control_label"], "type": "intent_cluster"},
535
+ ),
536
+ axis=1,
537
+ )
538
+ embedding_list = df_tempclusters["embedding"].tolist()
539
+ rag_graph.kg_base.vector_store.upsert(embedding_list)
540
+
541
  return {"rag_graph": rag_graph}
542
 
543
 
544
  @output_on_top
545
+ @op("LynxScribe RAG Graph Chatbot Builder")
546
+ def ls_rag_chatbot_builder(
547
+ rag_graph,
548
+ *,
549
+ scenario_file: str = "uploads/lynx_chatbot_scenario_selector.yaml",
550
+ node_types: str = "intent_cluster",
551
+ scenario_meta_name: str = "",
552
+ ):
553
+ """
554
+ Builds up a RAG Graph-based chatbot (basically the loaded RAG graph +
555
+ a scenario selector).
556
+
557
+ TODO: Later, the scenario selector can be built up synthetically from
558
+ the input documents - or semi-automated, not just from the scenario
559
+ yaml.
560
+ """
561
+
562
  scenarios = load_config(scenario_file)
563
  node_types = [t.strip() for t in node_types.split(",")]
 
 
 
 
 
564
 
565
+ # handling inputs
566
+ rag_graph = rag_graph["rag_graph"]
567
 
568
+ parameters = {
569
+ "scenarios": [Scenario(**scenario) for scenario in scenarios],
570
+ "node_types": node_types,
571
+ }
572
+ if len(scenario_meta_name) > 0:
573
+ parameters["get_scenario_name"] = lambda node: node.metadata[scenario_meta_name]
574
+
575
+ # loading the scenarios
576
+ scenario_selector = ScenarioSelector(**parameters)
577
+
578
+ # TODO: later we should unify this "knowledge base" object across the functions
579
+ # this could be always an input of a RAG Chatbot, but also for other apps.
580
+ return {
581
+ "knowledge_base": {
582
+ "rag_graph": rag_graph,
583
+ "scenario_selector": scenario_selector,
584
+ }
585
+ }
586
 
587
 
588
  @output_on_top
589
+ @ops.input_position(knowledge_base="bottom", chat_processor="bottom")
590
+ @op("LynxScribe RAG Graph Chatbot Backend")
591
+ def ls_rag_chatbot_backend(
592
+ knowledge_base,
593
+ chat_processor,
 
594
  *,
595
  negative_answer=DEFAULT_NEGATIVE_ANSWER,
596
+ retriever_limits_by_type="{}",
597
+ retriever_strict_limits=True,
598
+ retriever_overall_chunk_limit=20,
599
+ retriever_overall_token_limit=3000,
600
+ retriever_max_iterations=3,
601
+ llm_interface: str = "openai",
602
+ llm_model_name: str = "gpt-4o",
603
+ # api_key_name: str = "OPENAI_API_KEY",
604
  ):
605
+ """
606
+ Returns with a chatbot instance.
607
+ """
608
+
609
+ # handling_inputs
610
+ rag_graph = knowledge_base[0]["knowledge_base"]["rag_graph"]
611
+ scenario_selector = knowledge_base[0]["knowledge_base"]["scenario_selector"]
612
+ chat_processor = chat_processor[0]["chat_processor"]
613
+ limits_by_type = json.loads(retriever_limits_by_type)
614
+
615
+ # connecting to the LLM
616
+ llm_params = {"name": llm_interface}
617
+ # if api_key_name:
618
+ # llm_params["api_key"] = os.getenv(api_key_name)
619
+ llm = get_llm_engine(**llm_params)
620
+
621
+ # setting the parameters
622
+ params = {
623
+ "limits_by_type": limits_by_type,
624
+ "strict_limits": retriever_strict_limits,
625
+ "max_results": retriever_overall_chunk_limit,
626
+ "token_limit": retriever_overall_token_limit,
627
+ "max_iterations": retriever_max_iterations,
628
+ }
629
+
630
+ # generating the RAG Chatbot
631
  rag_chatbot = RAGChatbot(
632
  rag_graph=rag_graph,
633
  scenario_selector=scenario_selector,
634
  llm=llm,
635
  negative_answer=negative_answer,
636
+ **params,
637
+ )
638
+
639
+ # generating the chatbot back-end
640
+ c = ChatAPI(
641
+ chatbot=rag_chatbot,
642
+ chat_processor=chat_processor,
643
+ model=llm_model_name,
644
  )
645
+
646
+ return {"chat_api": c}
647
 
648
 
649
  @output_on_top
 
701
  messages=[{"role": "user", "content": message["text"]}],
702
  )
703
  response = await chat_api.answer(request, stream=False)
704
+ if len(response.choices) == 0:
705
+ answer = "The following FAQ items are similar to the question:\n"
706
+ for item in response.sources:
707
+ answer += f"------------------------------------------------------ \n{item.body}\n\n"
708
+ else:
709
+ answer = response.choices[0].message.content
710
  if show_details:
711
  return {"answer": answer, **response.__dict__}
712
  else:
 
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("_")]
 
741
  assert path.exists(), f"Workspace {path} does not exist"
742
  ws = workspace.load(path)
743
  contexts = await ops.EXECUTORS[ENV](ws)
744
+ nodes = [op for op in ws.nodes if op.data.title == "LynxScribe RAG Graph Chatbot Backend"]
745
  [node] = nodes
746
  context = contexts[node.id]
747
  return context.last_result["chat_api"]
 
810
  pass # Ignore files that are not valid workspaces.
811
  workspaces.sort()
812
  return workspaces
813
+
814
+
815
+ def dictionary_corrector(dict_string: str, expected_keys: list | None = None) -> dict:
816
+ """
817
+ Processing LLM outputs: when the LLM returns with a dictionary (in a string format). It optionally
818
+ crosschecks the input with the expected keys and return a dictionary with the expected keys and their
819
+ values ('unknown' if not present). If there is an error during the processing, it will return with
820
+ a dictionary of the expected keys, all with 'error' as a value (or with an empty dictionary).
821
+
822
+ Currently the function does not delete the extra key-value pairs.
823
+ """
824
+
825
+ out_dict = {}
826
+
827
+ if len(dict_string) == 0:
828
+ return out_dict
829
+
830
+ # deleting the optional text before the first and after the last curly brackets
831
+ dstring_prc = dict_string
832
+ if dstring_prc[0] != "{":
833
+ dstring_prc = "{" + "{".join(dstring_prc.split("{")[1:])
834
+ if dstring_prc[-1] != "}":
835
+ dstring_prc = "}".join(dstring_prc.split("}")[:-1]) + "}"
836
+
837
+ try:
838
+ trf_dict = json.loads(dstring_prc)
839
+ if expected_keys:
840
+ for _key in expected_keys:
841
+ if _key in trf_dict:
842
+ out_dict[_key] = trf_dict[_key]
843
+ else:
844
+ out_dict[_key] = "unknown"
845
+ else:
846
+ out_dict = trf_dict
847
+ except Exception:
848
+ if expected_keys:
849
+ for _key in expected_keys:
850
+ out_dict[_key] = "error"
851
+
852
+ return out_dict