david-oplatka commited on
Commit
757fbed
1 Parent(s): 86fb75b

Reorganize Files

Browse files
.env ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ VECTARA_CUSTOMER_ID="1526022105"
2
+ VECTARA_API_KEY="zqt_WvU_2ZD8owebGIuCtv_aWivgVyPGiH3pyF57bg"
3
+ VECTARA_CORPUS_ID="279"
4
+ VECTARA_AGENT_TYPE="OPENAI"
5
+ VECTARA_AGENT_MAIN_LLM_PROVIDER="OPENAI"
6
+ VECTARA_AGENT_TOOL_LLM_PROVIDER="OPENAI"
7
+ OPENAI_API_KEY="sk-proj-ecGoQqchQtEztxM1oeTfT3BlbkFJ0sDJUagzIEUlW7JSK9tf"
8
+ QUERY_EXAMPLES="Please explain the current eminent domain policies in Alaska; What are the elements of permitting requirements and notice?; What is adverse possession? What are the elements of adverse possession in Alaska?"
9
+ DEMO_NAME="Legal Assistant"
10
+ SHORT_DESCRIPTION="This demo can help you prepare for a court case by providing you information about past court cases in Alaska."
11
+ EXTRA_INFO="You can ask about specific topics or rulings and get related case info, including the official case document and other cases cited in the final opinion. You can also ask the assistant to critique your argument to enhance your preparation."
Dockerfile CHANGED
@@ -1,14 +1,14 @@
1
- ARG VECTARA_CUSTOMER_ID
2
- ARG VECTARA_API_KEY
3
- ARG VECTARA_CORPUS_ID
4
- ARG VECTARA_AGENT_TYPE
5
- ARG VECTARA_AGENT_MAIN_LLM_PROVIDER
6
- ARG VECTARA_AGENT_TOOL_LLM_PROVIDE
7
- ARG OPENAI_API_KEY
8
- ARG QUERY_EXAMPLES
9
- ARG DEMO_NAME
10
- ARG SHORT_DESCRIPTION
11
- ARG EXTRA_INFO
12
 
13
  FROM python:3.10
14
 
 
1
+ # ARG VECTARA_CUSTOMER_ID
2
+ # ARG VECTARA_API_KEY
3
+ # ARG VECTARA_CORPUS_ID
4
+ # ARG VECTARA_AGENT_TYPE
5
+ # ARG VECTARA_AGENT_MAIN_LLM_PROVIDER
6
+ # ARG VECTARA_AGENT_TOOL_LLM_PROVIDE
7
+ # ARG OPENAI_API_KEY
8
+ # ARG QUERY_EXAMPLES
9
+ # ARG DEMO_NAME
10
+ # ARG SHORT_DESCRIPTION
11
+ # ARG EXTRA_INFO
12
 
13
  FROM python:3.10
14
 
__pycache__/agent.cpython-310.pyc ADDED
Binary file (10.3 kB). View file
 
__pycache__/chatbox.cpython-310.pyc ADDED
Binary file (1.44 kB). View file
 
__pycache__/chatui.cpython-310.pyc ADDED
Binary file (4.76 kB). View file
 
__pycache__/footer.cpython-310.pyc ADDED
Binary file (1.85 kB). View file
 
__pycache__/header.cpython-310.pyc ADDED
Binary file (2.33 kB). View file
 
__pycache__/utils.cpython-310.pyc ADDED
Binary file (195 Bytes). View file
 
app.py CHANGED
@@ -1,39 +1,27 @@
1
  import asyncio
2
  import threading
3
- from markdown_it import MarkdownIt
4
- from mdit_py_plugins import front_matter
5
 
6
- from reactpy import component, html, hooks, use_effect, svg
7
  from reactpy.backend.starlette import configure
8
  from starlette.applications import Starlette
9
  from vectara_agentic.agent import AgentStatusType
10
  from agent import initialize_agent, get_agent_config
11
 
12
- wait_message = "Please wait. Assistant at work..."
 
 
 
 
13
 
14
  @component
15
  def App():
16
-
17
- # Used for showing/hiding logs
18
  show_logs, set_show_logs = hooks.use_state(False)
19
-
20
- # Determining when the "Show Logs" button should appear
21
  show_logs_button, set_show_logs_button = hooks.use_state(False)
22
-
23
- # Used for resetting chat bot with "Start Over" button.
24
  first_turn, set_first_turn = hooks.use_state(True)
25
-
26
- # Tracks the state of whether the header is collapsed or exapnded
27
- more_info, set_more_info = hooks.use_state(False)
28
-
29
- # Tracks the state of whether the footer is collapsed or expanded
30
- collapsed, set_collapse = hooks.use_state(False)
31
-
32
- # Record of Chat Messages
33
  messages, set_messages = hooks.use_state([])
34
-
35
  message, set_message = hooks.use_state("")
36
- input_value, set_input_value = hooks.use_state("")
37
 
38
  def use_agent_logger():
39
  agent_log_entries, set_agent_log_entries = hooks.use_state([])
@@ -46,6 +34,7 @@ def App():
46
 
47
  return agent_log_entries, add_log_entry, reset_log_entries
48
 
 
49
  log_entries, add_log_entry, reset_log_entries = use_agent_logger()
50
 
51
  def update_func(status_type: AgentStatusType, msg: str):
@@ -53,16 +42,11 @@ def App():
53
  output = f"{status_type.value} - {msg}"
54
  add_log_entry(output)
55
 
 
56
  cfg, _ = hooks.use_state(get_agent_config())
57
  agent, _ = hooks.use_state(initialize_agent(cfg, update_func))
58
 
59
- def toggle_header(event=None):
60
- set_more_info(not more_info)
61
-
62
- def toggle_footer(event=None):
63
- set_collapse(not collapsed)
64
-
65
- # Clears all messages and resets chat interface
66
  def start_over(event=None):
67
  set_messages([])
68
  set_first_turn(True)
@@ -70,7 +54,6 @@ def App():
70
  def display_message(new_messages):
71
  if first_turn:
72
  set_first_turn(False)
73
-
74
  set_messages(messages + list(new_messages))
75
 
76
  async def chat_response(user_message):
@@ -89,17 +72,14 @@ def App():
89
  set_show_logs_button(True)
90
 
91
  def send_message(event=None):
92
- print(f"DEBUG: SEND MESSAGE CALLED FOR MESSAGE {message}")
93
  if message.strip():
94
  sent_message = message
95
  set_message("")
96
- set_input_value("")
97
  set_show_logs_button(False)
98
  set_show_logs(False)
99
  reset_log_entries()
100
  display_message([{"user": "human", "message": sent_message}])
101
  display_message([{"user": "bot", "message": wait_message}])
102
-
103
  asyncio.create_task(send_message_async(sent_message))
104
 
105
  def send_example(ex_prompt):
@@ -112,576 +92,13 @@ def App():
112
 
113
  asyncio.create_task(send_message_async(sent_message))
114
 
115
- # # New Code Suggestion from Claude
116
- # def handle_input(event):
117
- # set_message(event['target']['value'])
118
-
119
- # def handle_key_down(event):
120
- # if event['key'] == 'Enter':
121
- # send_message()
122
-
123
-
124
- def handle_input(event):
125
- new_value = event['target']['value']
126
- set_input_value(new_value)
127
-
128
- def handle_key_down(event):
129
- if event['key'] == 'Enter':
130
- send_message()
131
-
132
- use_effect(lambda: set_message(input_value), [input_value])
133
-
134
- @component
135
- def Header(demo_name: str, short_description: str, extra_info: str):
136
- return html.header(
137
- {
138
- "style": {
139
- "backgroundColor": "#FFFFFF",
140
- "display": "flex",
141
- "justifyContent": "space-between",
142
- "alignItems": "center",
143
- }
144
- },
145
- html.div(
146
- {
147
- "style": {
148
- "display": "flex",
149
- "alignItems": "center",
150
- "flex": 2,
151
- "textAlign": "left",
152
- "padding": "10px",
153
- }
154
- },
155
- html.img(
156
- {
157
- "src": "https://avatars.githubusercontent.com/u/108304503?s=200&v=4",
158
- "style": {
159
- "height": "30px",
160
- "marginRight": "15px",
161
- "marginLeft": "5px",
162
- "transform": "translateY(-2px)",
163
- }
164
- }
165
- ),
166
- html.p(
167
- {
168
- "style": {
169
- "fontSize": "25px",
170
- "fontFamily": "Georgia, 'Times New Roman', Times, serif",
171
- }
172
- },
173
- f"{demo_name}"
174
- ),
175
- ),
176
- html.div(
177
- {
178
- "style": {
179
- "flex": 5,
180
- "textAlign": "center",
181
- "padding": "10px 0px 15px 0px",
182
- "fontFamily": "Lato",
183
- "position": "relative",
184
- }
185
- },
186
- html.h3(f"Welcome to the {demo_name} Demo"),
187
- html.p(
188
- short_description,
189
- html.button(
190
- {
191
- "style": {
192
- "display": "inline" if not more_info else "none",
193
- "backgroundColor": "#FFFFFF",
194
- "color": "#757575",
195
- "fontSize": "13px",
196
- "cursor": "pointer",
197
- "border": "none",
198
- "padding": "0px 0px 0px 5px",
199
- },
200
- "type": "button",
201
- "onClick": toggle_header,
202
- },
203
- html.u(
204
- {
205
- "style": {
206
- "flex": 2,
207
- "textAlign": "right",
208
- "padding": "10px",
209
- }
210
- },
211
- "Learn More"
212
- )
213
- ),
214
- f" {extra_info}" if more_info else ""
215
- ),
216
- html.button(
217
- {
218
- "style": {
219
- "display": "block" if more_info else "none",
220
- "background": "none",
221
- "border": "none",
222
- "color": "#757575",
223
- "cursor": "pointer",
224
- "position": "absolute",
225
- "left": "50%",
226
- "transform": "translateX(-50%)",
227
- "bottom": "1px",
228
- },
229
- "type": "button",
230
- "on_click": toggle_header,
231
- },
232
- svg.svg(
233
- {
234
- "width": "20",
235
- "height": "20",
236
- "viewBox": "0 0 20 20",
237
- "fill": "none",
238
- "stroke": "black",
239
- "strokeWidth": "2",
240
- "strokeLinecap": "round",
241
- "strokeLinejoin": "round",
242
- },
243
- svg.path(
244
- {
245
- "d": "M12 19V5M5 12l7-7 7 7",
246
- "stroke": "currentColor",
247
- }
248
- )
249
- )
250
- )
251
- ),
252
- html.div(
253
- {
254
- "style": {
255
- "flex": 2,
256
- "textAlign": "right",
257
- "padding": "10px",
258
- }
259
- },
260
- html.button(
261
- {
262
- "style": {
263
- "backgroundColor": "#FFFFFF",
264
- "color": "#757575",
265
- "fontSize": "14px",
266
- "cursor": "pointer",
267
- "border": "1px solid #e2dfdf",
268
- "borderRadius": "5px",
269
- "padding": "6px 20px",
270
- "marginRight": "15px",
271
- },
272
- "type": "button",
273
- "onClick": start_over,
274
- },
275
- "Start Over?"
276
- )
277
- )
278
- )
279
-
280
- def markdown_to_html(markdown_text):
281
- md = (
282
- MarkdownIt("commonmark", {"breaks": True, "html": True})
283
- .use(front_matter.front_matter_plugin)
284
- )
285
- return md.render(markdown_text)
286
-
287
- @component
288
- def MarkdownRenderer(content):
289
- html_content = markdown_to_html(content)
290
- return html.div(
291
- {
292
- "style": {
293
- "fontFamily": "Arial",
294
- "color": "#49454F",
295
- "fontSize": "14px",
296
- },
297
- "dangerouslySetInnerHTML": {"__html": html_content}
298
- }
299
- )
300
-
301
- @component
302
- def ExamplePrompts():
303
- example_questions = [example.strip() for example in cfg['examples'].split(";")] if cfg.examples else []
304
-
305
- def create_prompt_button(question):
306
- return html.button(
307
- {
308
- "style": {
309
- "backgroundColor": "#FFFFFF",
310
- "borderWidth": "1px",
311
- "borderColor": "#65558F",
312
- "borderRadius": "20px",
313
- "padding": "5px 7px",
314
- "margin": "5px",
315
- "cursor": "pointer",
316
- "color": "#21005D",
317
- "fontSize": "13px",
318
- },
319
- "type": "button",
320
- "onClick": lambda _: send_example(question),
321
- },
322
- question
323
- )
324
-
325
- if first_turn:
326
- return html.div(
327
- {
328
- "style": {
329
- "display": "flex",
330
- "transform": "translate(4%, 100%)",
331
- "flexDirection": "column",
332
- "justifyContent": "center",
333
- "width": "90%",
334
- }
335
- },
336
- html.p(
337
- {
338
- "style": {
339
- "fontSize": "16px",
340
- "fontFamily": "Arial",
341
- "color": "#49454F",
342
- "marginBottom": "5px",
343
- "transform": "translateX(3%)",
344
- "textAlign": "left",
345
- }
346
- },
347
- "Queries to try:"
348
- ),
349
- html.div(
350
- {
351
- "style": {
352
- "display": "flex",
353
- "flexWrap": "wrap",
354
- "justifyContent": "center",
355
- }
356
- },
357
- *[create_prompt_button(q) for q in example_questions]
358
- )
359
- )
360
-
361
- return None
362
-
363
- @component
364
- def ChatBox():
365
- return html.div(
366
- {
367
- "style": {
368
- "position": "fixed",
369
- "bottom": "70px" if collapsed else "140px",
370
- "left": "50%",
371
- "transform": "translateX(-50%)",
372
- "width": "80%",
373
- "display": "flex",
374
- "alignItems": "center",
375
- "backgroundColor": "#FFFFFF",
376
- "borderRadius": "50px",
377
- "zIndex": "1000",
378
- }
379
- },
380
- html.input(
381
- {
382
- "type": "text",
383
- "value": message,
384
- "placeholder": "Your Message",
385
- "onInput": handle_input,
386
- "onKeyDown": handle_key_down,
387
- "style": {
388
- "width": "100%",
389
- "padding": "10px 50px 10px 20px",
390
- "border": "none",
391
- "borderRadius": "50px",
392
- "color": "#65558F",
393
- }
394
- }
395
- ),
396
- html.button(
397
- {
398
- "type": "button",
399
- "onClick": send_message,
400
- "style": {
401
- "position": "absolute",
402
- "right": "8px",
403
- "top": "50%",
404
- "transform":"translateY(-50%)",
405
- "background": "none",
406
- "border": "none",
407
- "cursor": "pointer",
408
- "padding": "8px",
409
- "display": "flex",
410
- "alignItems": "center",
411
- "justifyContent": "center",
412
- }
413
- },
414
- svg.svg(
415
- {
416
- "xmlns": "http://www.w3.org/2000/svg",
417
- "width": "20",
418
- "height": "20",
419
- "fill": "none",
420
- "stroke": "currentColor",
421
- "stroke-linecap": "round",
422
- "stroke-linejoin": "round",
423
- "stroke-width": "2",
424
- "viewBox": "0 0 24 24",
425
- "class": "feather feather-send",
426
- },
427
- svg.path({"d": "M22 2 11 13"}),
428
- svg.path({"d": "M22 2 15 22 11 13 2 9z"})
429
- )
430
- )
431
- )
432
-
433
-
434
- @component
435
- def ChatMessage(user, message):
436
-
437
- return html.div(
438
- {
439
- "style": {
440
- "display": "flex",
441
- "width": "75%",
442
- "transform": "translateX(20%)",
443
- "justifyContent": "flex-end" if user == "human" else "flex-start",
444
- "margin": "20px 0px 10px 0px"
445
- }
446
- },
447
- html.div(
448
- {
449
- "style": {
450
- "maxWidth": "50%",
451
- "padding": "0px 15px 0px 10px",
452
- "borderRadius": "15px",
453
- "backgroundColor": "#E8DEF8" if user == "human" else "#ECE6F0",
454
- "color": "#000000",
455
- }
456
- },
457
- MarkdownRenderer(message)
458
- )
459
- )
460
-
461
- @component
462
- def Logs():
463
- if (len(messages) > 0) and (len(log_entries) > 0) and (messages[-1]["message"] != wait_message) and (show_logs_button):
464
- if not show_logs:
465
- return html.div(
466
- {
467
- "style": {
468
- "display": "flex",
469
- "transform": "translateX(75%)",
470
- "width": "20%",
471
- "margin": "0px",
472
- }
473
- },
474
- html.button(
475
- {
476
- "style": {
477
- "position": "flex",
478
- "background": "none",
479
- "color": "#757575",
480
- "border": "none",
481
- "cursor": "pointer",
482
- "fontFamily": "Arial",
483
- "fontSize": "14px",
484
- },
485
- "type": "button",
486
- "onClick": lambda _: set_show_logs(True)
487
- },
488
- "Show Logs"
489
- )
490
- )
491
- else:
492
- return html.div(
493
- {
494
- "style": {
495
- "display": "flex",
496
- "transform": "translateX(20%)",
497
- "border": "2px solid #e2dfdf",
498
- "borderRadius": "10px",
499
- "width": "75%",
500
- }
501
- },
502
- html.div(
503
- [
504
- html.p(
505
- {
506
- "style": {
507
- "fontSize": "14px",
508
- "marginLeft": "10px",
509
- }
510
- },
511
- entry
512
- ) for entry in log_entries
513
- ],
514
- html.button(
515
- {
516
- "style": {
517
- "position": "flex",
518
- "background": "none",
519
- "color": "#757575",
520
- "border": "none",
521
- "cursor": "pointer",
522
- "fontFamily": "Arial",
523
- "fontSize": "14px",
524
- "marginBottom": "10px",
525
- "marginLeft": "5px",
526
- },
527
- "type": "button",
528
- "onClick": lambda _: set_show_logs(False)
529
- },
530
- "Hide Logs"
531
- )
532
- )
533
- )
534
 
535
- return None
536
-
537
- @component
538
- def ChatUI():
539
-
540
- def render_chat_content():
541
- if first_turn:
542
- return ExamplePrompts()
543
- else:
544
- return [ChatMessage(msg["user"], msg["message"]) for msg in messages]
545
-
546
- return html.div(
547
- {
548
- "style": {
549
- "display": "flex",
550
- "flexDirection": "column",
551
- "padding": "10px",
552
- "backgroundColor": "#F4F1F4",
553
- "overflowY": "auto",
554
- "height": f"calc(100vh - {265 if collapsed else 335}px)",
555
- "marginBottom": "15px",
556
- "paddingBottom": "15px",
557
- },
558
- },
559
- render_chat_content(),
560
- Logs()
561
- )
562
-
563
-
564
- @component
565
- def Footer():
566
-
567
- if collapsed:
568
- return html.footer(
569
- {
570
- "style": {
571
- "backgroundColor": "#FFFFFF",
572
- "position": "fixed",
573
- "bottom": 0,
574
- "width": "100%",
575
- "height": "50px",
576
- }
577
- },
578
- html.button(
579
- {
580
- "style": {
581
- "background": "none",
582
- "border": "none",
583
- "color": "#757575",
584
- "cursor": "pointer",
585
- "position": "absolute",
586
- "bottom": "5px",
587
- "right": "10px",
588
- },
589
- "type": "button",
590
- "on_click": toggle_footer,
591
- },
592
- svg.svg(
593
- {
594
- "xmlns": "http://www.w3.org/2000/svg",
595
- "width": "24",
596
- "height": "24",
597
- "fill": "none",
598
- "stroke": "currentColor",
599
- "stroke-linecap": "round",
600
- "stroke-linejoin": "round",
601
- "stroke-width": "3",
602
- "viewBox": "0 0 24 24",
603
- },
604
- svg.path({"d": "M19 14l-7-7-7 7"})
605
- )
606
- )
607
- )
608
-
609
- else:
610
- return html.footer(
611
- {
612
- "style": {
613
- "backgroundColor": "#FFFFFF",
614
- "position": "fixed",
615
- "bottom": 0,
616
- "width": "100%",
617
- }
618
- },
619
- html.div(
620
- {
621
- "style": {
622
- "backgroundColor": "#FFFFFF",
623
- "padding": "0px 20px 0px 50px",
624
- "position": "relative",
625
- "display": "block",
626
- }
627
- },
628
- html.button(
629
- {
630
- "style": {
631
- "position": "absolute",
632
- "right": "10px",
633
- "background": "none",
634
- "border": "none",
635
- "color": "#757575",
636
- "cursor": "pointer",
637
- },
638
- "type": "button",
639
- "on_click": toggle_footer,
640
- },
641
- svg.svg(
642
- {
643
- "xmlns": "http://www.w3.org/2000/svg",
644
- "width": "24",
645
- "height": "24",
646
- "fill": "none",
647
- "stroke": "currentColor",
648
- "stroke-linecap": "round",
649
- "stroke-linejoin": "round",
650
- "stroke-width": "3",
651
- "viewBox": "0 0 24 24",
652
- },
653
- svg.path({"d": "M18 6L6 18"}),
654
- svg.path({"d": "M6 6l12 12"})
655
- )
656
- ),
657
- html.p(
658
- {
659
- "style": {
660
- "fontSize": "20px",
661
- "color": "#4b4851"
662
- }
663
- },
664
- "How this works?",
665
- ),
666
- html.p(
667
- {
668
- "style": {
669
- "color": "#757575",
670
- }
671
- },
672
- "This app was built with ",
673
- html.a(
674
- {
675
- "href": "https://vectara.com/",
676
- "target": "_blank",
677
- },
678
- "Vectara",
679
- ),
680
- html.br(),
681
- "It demonstrates the use of Agentic-RAG functionality with Vectara",
682
- )
683
- )
684
- )
685
 
686
  return html.div(
687
  {
@@ -698,29 +115,38 @@ def App():
698
  }
699
  },
700
  html.head(
701
- html.style("""
 
702
  body {
703
  margin: 0;
704
  padding: 0;
705
  }
706
- """)
 
707
  ),
708
  Header(
709
  demo_name=cfg['demo_name'],
710
  short_description=cfg['short_description'],
711
- extra_info=cfg['extra_info']
 
712
  ),
713
- ChatUI(),
714
- ChatBox(),
715
- Footer(),
 
 
 
 
 
 
 
 
 
 
716
  )
717
 
718
  app = Starlette()
719
-
720
  configure(app, App)
721
 
722
- # Should only need for local testing
723
-
724
  # if __name__ == "__main__":
725
- # import uvicorn
726
- # uvicorn.run(app, host="0.0.0.0", port=8000)
 
1
  import asyncio
2
  import threading
 
 
3
 
4
+ from reactpy import component, html, hooks, use_effect, run
5
  from reactpy.backend.starlette import configure
6
  from starlette.applications import Starlette
7
  from vectara_agentic.agent import AgentStatusType
8
  from agent import initialize_agent, get_agent_config
9
 
10
+ from header import Header
11
+ from footer import Footer
12
+ from chatbox import ChatBox
13
+ from chatui import ChatUI
14
+ from utils import wait_message
15
 
16
  @component
17
  def App():
18
+ # State hooks
 
19
  show_logs, set_show_logs = hooks.use_state(False)
 
 
20
  show_logs_button, set_show_logs_button = hooks.use_state(False)
21
+ collapsed, set_collapsed = hooks.use_state(False) # Whether or not the footer is collapsed
 
22
  first_turn, set_first_turn = hooks.use_state(True)
 
 
 
 
 
 
 
 
23
  messages, set_messages = hooks.use_state([])
 
24
  message, set_message = hooks.use_state("")
 
25
 
26
  def use_agent_logger():
27
  agent_log_entries, set_agent_log_entries = hooks.use_state([])
 
34
 
35
  return agent_log_entries, add_log_entry, reset_log_entries
36
 
37
+ # Logger setup
38
  log_entries, add_log_entry, reset_log_entries = use_agent_logger()
39
 
40
  def update_func(status_type: AgentStatusType, msg: str):
 
42
  output = f"{status_type.value} - {msg}"
43
  add_log_entry(output)
44
 
45
+ # Agent setup
46
  cfg, _ = hooks.use_state(get_agent_config())
47
  agent, _ = hooks.use_state(initialize_agent(cfg, update_func))
48
 
49
+
 
 
 
 
 
 
50
  def start_over(event=None):
51
  set_messages([])
52
  set_first_turn(True)
 
54
  def display_message(new_messages):
55
  if first_turn:
56
  set_first_turn(False)
 
57
  set_messages(messages + list(new_messages))
58
 
59
  async def chat_response(user_message):
 
72
  set_show_logs_button(True)
73
 
74
  def send_message(event=None):
 
75
  if message.strip():
76
  sent_message = message
77
  set_message("")
 
78
  set_show_logs_button(False)
79
  set_show_logs(False)
80
  reset_log_entries()
81
  display_message([{"user": "human", "message": sent_message}])
82
  display_message([{"user": "bot", "message": wait_message}])
 
83
  asyncio.create_task(send_message_async(sent_message))
84
 
85
  def send_example(ex_prompt):
 
92
 
93
  asyncio.create_task(send_message_async(sent_message))
94
 
95
+ async def handle_key_down(event):
96
+ if event['key'] == 'Enter' and message.strip():
97
+ await send_message()
98
+ set_message("")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
99
 
100
+ def handle_input_change(event):
101
+ set_message(event['target']['value'])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
102
 
103
  return html.div(
104
  {
 
115
  }
116
  },
117
  html.head(
118
+ html.style(
119
+ """
120
  body {
121
  margin: 0;
122
  padding: 0;
123
  }
124
+ """
125
+ )
126
  ),
127
  Header(
128
  demo_name=cfg['demo_name'],
129
  short_description=cfg['short_description'],
130
+ extra_info=cfg['extra_info'],
131
+ start_over=start_over
132
  ),
133
+ ChatUI(
134
+ messages=messages,
135
+ first_turn=first_turn,
136
+ examples=cfg['examples'],
137
+ send_example=send_example,
138
+ log_entries=log_entries,
139
+ show_logs=show_logs,
140
+ set_show_logs=set_show_logs,
141
+ show_logs_button=show_logs_button,
142
+ collapsed=collapsed
143
+ ),
144
+ ChatBox(message, handle_input_change, handle_key_down, send_message, collapsed),
145
+ Footer(collapsed, set_collapsed),
146
  )
147
 
148
  app = Starlette()
 
149
  configure(app, App)
150
 
 
 
151
  # if __name__ == "__main__":
152
+ # run(App)
 
app2.py ADDED
@@ -0,0 +1,733 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import asyncio
2
+ import threading
3
+ from markdown_it import MarkdownIt
4
+ from mdit_py_plugins import front_matter
5
+
6
+ from reactpy import component, html, hooks, use_effect, svg, run # DELETE run FOR DEPLOYMENT
7
+ from reactpy.backend.starlette import configure
8
+ from starlette.applications import Starlette
9
+ from vectara_agentic.agent import AgentStatusType
10
+ from agent import initialize_agent, get_agent_config
11
+
12
+ wait_message = "Please wait. Assistant at work..."
13
+
14
+ @component
15
+ def App():
16
+
17
+ # Used for showing/hiding logs
18
+ show_logs, set_show_logs = hooks.use_state(False)
19
+
20
+ # Determining when the "Show Logs" button should appear
21
+ show_logs_button, set_show_logs_button = hooks.use_state(False)
22
+
23
+ # Used for resetting chat bot with "Start Over" button.
24
+ first_turn, set_first_turn = hooks.use_state(True)
25
+
26
+ # Tracks the state of whether the header is collapsed or exapnded
27
+ more_info, set_more_info = hooks.use_state(False)
28
+
29
+ # Tracks the state of whether the footer is collapsed or expanded
30
+ collapsed, set_collapse = hooks.use_state(False)
31
+
32
+ # Record of Chat Messages
33
+ messages, set_messages = hooks.use_state([])
34
+
35
+ message, set_message = hooks.use_state("")
36
+ # input_value, set_input_value = hooks.use_state("")
37
+
38
+ def use_agent_logger():
39
+ agent_log_entries, set_agent_log_entries = hooks.use_state([])
40
+
41
+ def reset_log_entries():
42
+ set_agent_log_entries([])
43
+
44
+ def add_log_entry(new_log_entry):
45
+ set_agent_log_entries(lambda previous_entries: previous_entries + [new_log_entry])
46
+
47
+ return agent_log_entries, add_log_entry, reset_log_entries
48
+
49
+ log_entries, add_log_entry, reset_log_entries = use_agent_logger()
50
+
51
+ def update_func(status_type: AgentStatusType, msg: str):
52
+ if status_type != AgentStatusType.AGENT_UPDATE:
53
+ output = f"{status_type.value} - {msg}"
54
+ add_log_entry(output)
55
+
56
+ cfg, _ = hooks.use_state(get_agent_config())
57
+ agent, _ = hooks.use_state(initialize_agent(cfg, update_func))
58
+
59
+ def toggle_header(event=None):
60
+ set_more_info(not more_info)
61
+
62
+ def toggle_footer(event=None):
63
+ set_collapse(not collapsed)
64
+
65
+ # Clears all messages and resets chat interface
66
+ def start_over(event=None):
67
+ set_messages([])
68
+ set_first_turn(True)
69
+
70
+ def display_message(new_messages):
71
+ if first_turn:
72
+ set_first_turn(False)
73
+
74
+ set_messages(messages + list(new_messages))
75
+
76
+ async def chat_response(user_message):
77
+ response = await asyncio.to_thread(agent.chat, user_message)
78
+ return response
79
+
80
+ async def send_message_async(sent_message):
81
+ response = await chat_response(sent_message)
82
+ set_messages(messages[:-1])
83
+ display_message(
84
+ [
85
+ {"user": "human", "message": sent_message},
86
+ {"user": "bot", "message": response},
87
+ ]
88
+ )
89
+ set_show_logs_button(True)
90
+
91
+ def send_message(event=None):
92
+ print(f"DEBUG: SEND MESSAGE CALLED FOR MESSAGE {message}")
93
+ if message.strip():
94
+ sent_message = message
95
+ set_message("")
96
+ # set_input_value("")
97
+ set_show_logs_button(False)
98
+ set_show_logs(False)
99
+ reset_log_entries()
100
+ display_message([{"user": "human", "message": sent_message}])
101
+ display_message([{"user": "bot", "message": wait_message}])
102
+
103
+ asyncio.create_task(send_message_async(sent_message))
104
+
105
+ def send_example(ex_prompt):
106
+ if ex_prompt.strip():
107
+ sent_message = ex_prompt
108
+ set_message("")
109
+ reset_log_entries()
110
+ display_message([{"user": "human", "message": sent_message}])
111
+ display_message([{"user": "bot", "message": wait_message}])
112
+
113
+ asyncio.create_task(send_message_async(sent_message))
114
+
115
+ # # New Code Suggestion from Claude
116
+ # def handle_input(event):
117
+ # set_message(event['target']['value'])
118
+
119
+ # def handle_key_down(event):
120
+ # if event['key'] == 'Enter':
121
+ # send_message()
122
+
123
+
124
+ # def handle_input(event):
125
+ # new_value = event['target']['value']
126
+ # set_input_value(new_value)
127
+
128
+ # def handle_key_down(event):
129
+ # if event['key'] == 'Enter':
130
+ # send_message()
131
+
132
+ # use_effect(lambda: set_message(input_value), [input_value])
133
+
134
+ async def handle_key_down(event):
135
+ if event['key'] == 'Enter' and message.strip():
136
+ await send_message()
137
+ set_message("")
138
+
139
+ def handle_input_change(event):
140
+ set_message(event['target']['value'])
141
+
142
+ @component
143
+ def Header(demo_name: str, short_description: str, extra_info: str):
144
+ return html.header(
145
+ {
146
+ "style": {
147
+ "backgroundColor": "#FFFFFF",
148
+ "display": "flex",
149
+ "justifyContent": "space-between",
150
+ "alignItems": "center",
151
+ }
152
+ },
153
+ html.div(
154
+ {
155
+ "style": {
156
+ "display": "flex",
157
+ "alignItems": "center",
158
+ "flex": 2,
159
+ "textAlign": "left",
160
+ "padding": "10px",
161
+ }
162
+ },
163
+ html.img(
164
+ {
165
+ "src": "https://avatars.githubusercontent.com/u/108304503?s=200&v=4",
166
+ "style": {
167
+ "height": "30px",
168
+ "marginRight": "15px",
169
+ "marginLeft": "5px",
170
+ "transform": "translateY(-2px)",
171
+ }
172
+ }
173
+ ),
174
+ html.p(
175
+ {
176
+ "style": {
177
+ "fontSize": "25px",
178
+ "fontFamily": "Georgia, 'Times New Roman', Times, serif",
179
+ }
180
+ },
181
+ f"{demo_name}"
182
+ ),
183
+ ),
184
+ html.div(
185
+ {
186
+ "style": {
187
+ "flex": 5,
188
+ "textAlign": "center",
189
+ "padding": "10px 0px 15px 0px",
190
+ "fontFamily": "Lato",
191
+ "position": "relative",
192
+ }
193
+ },
194
+ html.h3(f"Welcome to the {demo_name} Demo"),
195
+ html.p(
196
+ short_description,
197
+ html.button(
198
+ {
199
+ "style": {
200
+ "display": "inline" if not more_info else "none",
201
+ "backgroundColor": "#FFFFFF",
202
+ "color": "#757575",
203
+ "fontSize": "13px",
204
+ "cursor": "pointer",
205
+ "border": "none",
206
+ "padding": "0px 0px 0px 5px",
207
+ },
208
+ "type": "button",
209
+ "onClick": toggle_header,
210
+ },
211
+ html.u(
212
+ {
213
+ "style": {
214
+ "flex": 2,
215
+ "textAlign": "right",
216
+ "padding": "10px",
217
+ }
218
+ },
219
+ "Learn More"
220
+ )
221
+ ),
222
+ f" {extra_info}" if more_info else ""
223
+ ),
224
+ html.button(
225
+ {
226
+ "style": {
227
+ "display": "block" if more_info else "none",
228
+ "background": "none",
229
+ "border": "none",
230
+ "color": "#757575",
231
+ "cursor": "pointer",
232
+ "position": "absolute",
233
+ "left": "50%",
234
+ "transform": "translateX(-50%)",
235
+ "bottom": "1px",
236
+ },
237
+ "type": "button",
238
+ "on_click": toggle_header,
239
+ },
240
+ svg.svg(
241
+ {
242
+ "width": "20",
243
+ "height": "20",
244
+ "viewBox": "0 0 20 20",
245
+ "fill": "none",
246
+ "stroke": "black",
247
+ "strokeWidth": "2",
248
+ "strokeLinecap": "round",
249
+ "strokeLinejoin": "round",
250
+ },
251
+ svg.path(
252
+ {
253
+ "d": "M12 19V5M5 12l7-7 7 7",
254
+ "stroke": "currentColor",
255
+ }
256
+ )
257
+ )
258
+ )
259
+ ),
260
+ html.div(
261
+ {
262
+ "style": {
263
+ "flex": 2,
264
+ "textAlign": "right",
265
+ "padding": "10px",
266
+ }
267
+ },
268
+ html.button(
269
+ {
270
+ "style": {
271
+ "backgroundColor": "#FFFFFF",
272
+ "color": "#757575",
273
+ "fontSize": "14px",
274
+ "cursor": "pointer",
275
+ "border": "1px solid #e2dfdf",
276
+ "borderRadius": "5px",
277
+ "padding": "6px 20px",
278
+ "marginRight": "15px",
279
+ },
280
+ "type": "button",
281
+ "onClick": start_over,
282
+ },
283
+ "Start Over?"
284
+ )
285
+ )
286
+ )
287
+
288
+ def markdown_to_html(markdown_text):
289
+ md = (
290
+ MarkdownIt("commonmark", {"breaks": True, "html": True})
291
+ .use(front_matter.front_matter_plugin)
292
+ )
293
+ return md.render(markdown_text)
294
+
295
+ @component
296
+ def MarkdownRenderer(content):
297
+ html_content = markdown_to_html(content)
298
+ return html.div(
299
+ {
300
+ "style": {
301
+ "fontFamily": "Arial",
302
+ "color": "#49454F",
303
+ "fontSize": "14px",
304
+ },
305
+ "dangerouslySetInnerHTML": {"__html": html_content}
306
+ }
307
+ )
308
+
309
+ @component
310
+ def ExamplePrompts():
311
+ example_questions = [example.strip() for example in cfg['examples'].split(";")] if cfg.examples else []
312
+
313
+ def create_prompt_button(question):
314
+ return html.button(
315
+ {
316
+ "style": {
317
+ "backgroundColor": "#FFFFFF",
318
+ "borderWidth": "1px",
319
+ "borderColor": "#65558F",
320
+ "borderRadius": "20px",
321
+ "padding": "5px 7px",
322
+ "margin": "5px",
323
+ "cursor": "pointer",
324
+ "color": "#21005D",
325
+ "fontSize": "13px",
326
+ },
327
+ "type": "button",
328
+ "onClick": lambda _: send_example(question),
329
+ },
330
+ question
331
+ )
332
+
333
+ if first_turn:
334
+ return html.div(
335
+ {
336
+ "style": {
337
+ "display": "flex",
338
+ "transform": "translate(4%, 100%)",
339
+ "flexDirection": "column",
340
+ "justifyContent": "center",
341
+ "width": "90%",
342
+ }
343
+ },
344
+ html.p(
345
+ {
346
+ "style": {
347
+ "fontSize": "16px",
348
+ "fontFamily": "Arial",
349
+ "color": "#49454F",
350
+ "marginBottom": "5px",
351
+ "transform": "translateX(3%)",
352
+ "textAlign": "left",
353
+ }
354
+ },
355
+ "Queries to try:"
356
+ ),
357
+ html.div(
358
+ {
359
+ "style": {
360
+ "display": "flex",
361
+ "flexWrap": "wrap",
362
+ "justifyContent": "center",
363
+ }
364
+ },
365
+ *[create_prompt_button(q) for q in example_questions]
366
+ )
367
+ )
368
+
369
+ return None
370
+
371
+ @component
372
+ def ChatBox():
373
+ return html.div(
374
+ {
375
+ "style": {
376
+ "position": "fixed",
377
+ "bottom": "70px" if collapsed else "140px",
378
+ "left": "50%",
379
+ "transform": "translateX(-50%)",
380
+ "width": "80%",
381
+ "display": "flex",
382
+ "alignItems": "center",
383
+ "backgroundColor": "#FFFFFF",
384
+ "borderRadius": "50px",
385
+ "zIndex": "1000",
386
+ }
387
+ },
388
+ html.input(
389
+ {
390
+ "type": "text",
391
+ "value": message,
392
+ "placeholder": "Your Message",
393
+ "onChange": handle_input_change,
394
+ "onKeyDown": handle_key_down,
395
+ "style": {
396
+ "width": "100%",
397
+ "padding": "10px 50px 10px 20px",
398
+ "border": "none",
399
+ "borderRadius": "50px",
400
+ "color": "#65558F",
401
+ }
402
+ }
403
+ ),
404
+ html.button(
405
+ {
406
+ "type": "button",
407
+ "onClick": send_message,
408
+ "style": {
409
+ "position": "absolute",
410
+ "right": "8px",
411
+ "top": "50%",
412
+ "transform":"translateY(-50%)",
413
+ "background": "none",
414
+ "border": "none",
415
+ "cursor": "pointer",
416
+ "padding": "8px",
417
+ "display": "flex",
418
+ "alignItems": "center",
419
+ "justifyContent": "center",
420
+ }
421
+ },
422
+ svg.svg(
423
+ {
424
+ "xmlns": "http://www.w3.org/2000/svg",
425
+ "width": "20",
426
+ "height": "20",
427
+ "fill": "none",
428
+ "stroke": "currentColor",
429
+ "stroke-linecap": "round",
430
+ "stroke-linejoin": "round",
431
+ "stroke-width": "2",
432
+ "viewBox": "0 0 24 24",
433
+ "class": "feather feather-send",
434
+ },
435
+ svg.path({"d": "M22 2 11 13"}),
436
+ svg.path({"d": "M22 2 15 22 11 13 2 9z"})
437
+ )
438
+ )
439
+ )
440
+
441
+
442
+ @component
443
+ def ChatMessage(user, message):
444
+
445
+ return html.div(
446
+ {
447
+ "style": {
448
+ "display": "flex",
449
+ "width": "75%",
450
+ "transform": "translateX(20%)",
451
+ "justifyContent": "flex-end" if user == "human" else "flex-start",
452
+ "margin": "20px 0px 10px 0px"
453
+ }
454
+ },
455
+ html.div(
456
+ {
457
+ "style": {
458
+ "maxWidth": "50%",
459
+ "padding": "0px 15px 0px 10px",
460
+ "borderRadius": "15px",
461
+ "backgroundColor": "#E8DEF8" if user == "human" else "#ECE6F0",
462
+ "color": "#000000",
463
+ }
464
+ },
465
+ MarkdownRenderer(message)
466
+ )
467
+ )
468
+
469
+ @component
470
+ def Logs():
471
+ if (len(messages) > 0) and (len(log_entries) > 0) and (messages[-1]["message"] != wait_message) and (show_logs_button):
472
+ if not show_logs:
473
+ return html.div(
474
+ {
475
+ "style": {
476
+ "display": "flex",
477
+ "transform": "translateX(75%)",
478
+ "width": "20%",
479
+ "margin": "0px",
480
+ }
481
+ },
482
+ html.button(
483
+ {
484
+ "style": {
485
+ "position": "flex",
486
+ "background": "none",
487
+ "color": "#757575",
488
+ "border": "none",
489
+ "cursor": "pointer",
490
+ "fontFamily": "Arial",
491
+ "fontSize": "14px",
492
+ },
493
+ "type": "button",
494
+ "onClick": lambda _: set_show_logs(True)
495
+ },
496
+ "Show Logs"
497
+ )
498
+ )
499
+ else:
500
+ return html.div(
501
+ {
502
+ "style": {
503
+ "display": "flex",
504
+ "transform": "translateX(20%)",
505
+ "border": "2px solid #e2dfdf",
506
+ "borderRadius": "10px",
507
+ "width": "75%",
508
+ }
509
+ },
510
+ html.div(
511
+ [
512
+ html.p(
513
+ {
514
+ "style": {
515
+ "fontSize": "14px",
516
+ "marginLeft": "10px",
517
+ }
518
+ },
519
+ entry
520
+ ) for entry in log_entries
521
+ ],
522
+ html.button(
523
+ {
524
+ "style": {
525
+ "position": "flex",
526
+ "background": "none",
527
+ "color": "#757575",
528
+ "border": "none",
529
+ "cursor": "pointer",
530
+ "fontFamily": "Arial",
531
+ "fontSize": "14px",
532
+ "marginBottom": "10px",
533
+ "marginLeft": "5px",
534
+ },
535
+ "type": "button",
536
+ "onClick": lambda _: set_show_logs(False)
537
+ },
538
+ "Hide Logs"
539
+ )
540
+ )
541
+ )
542
+
543
+ return None
544
+
545
+ @component
546
+ def ChatUI():
547
+
548
+ def render_chat_content():
549
+ if first_turn:
550
+ return ExamplePrompts()
551
+ else:
552
+ return [ChatMessage(msg["user"], msg["message"]) for msg in messages]
553
+
554
+ return html.div(
555
+ {
556
+ "style": {
557
+ "display": "flex",
558
+ "flexDirection": "column",
559
+ "padding": "10px",
560
+ "backgroundColor": "#F4F1F4",
561
+ "overflowY": "auto",
562
+ "height": f"calc(100vh - {265 if collapsed else 335}px)",
563
+ "marginBottom": "15px",
564
+ "paddingBottom": "15px",
565
+ },
566
+ },
567
+ render_chat_content(),
568
+ Logs()
569
+ )
570
+
571
+
572
+ @component
573
+ def Footer():
574
+
575
+ if collapsed:
576
+ return html.footer(
577
+ {
578
+ "style": {
579
+ "backgroundColor": "#FFFFFF",
580
+ "position": "fixed",
581
+ "bottom": 0,
582
+ "width": "100%",
583
+ "height": "50px",
584
+ }
585
+ },
586
+ html.button(
587
+ {
588
+ "style": {
589
+ "background": "none",
590
+ "border": "none",
591
+ "color": "#757575",
592
+ "cursor": "pointer",
593
+ "position": "absolute",
594
+ "bottom": "5px",
595
+ "right": "10px",
596
+ },
597
+ "type": "button",
598
+ "on_click": toggle_footer,
599
+ },
600
+ svg.svg(
601
+ {
602
+ "xmlns": "http://www.w3.org/2000/svg",
603
+ "width": "24",
604
+ "height": "24",
605
+ "fill": "none",
606
+ "stroke": "currentColor",
607
+ "stroke-linecap": "round",
608
+ "stroke-linejoin": "round",
609
+ "stroke-width": "3",
610
+ "viewBox": "0 0 24 24",
611
+ },
612
+ svg.path({"d": "M19 14l-7-7-7 7"})
613
+ )
614
+ )
615
+ )
616
+
617
+ else:
618
+ return html.footer(
619
+ {
620
+ "style": {
621
+ "backgroundColor": "#FFFFFF",
622
+ "position": "fixed",
623
+ "bottom": 0,
624
+ "width": "100%",
625
+ }
626
+ },
627
+ html.div(
628
+ {
629
+ "style": {
630
+ "backgroundColor": "#FFFFFF",
631
+ "padding": "0px 20px 0px 50px",
632
+ "position": "relative",
633
+ "display": "block",
634
+ }
635
+ },
636
+ html.button(
637
+ {
638
+ "style": {
639
+ "position": "absolute",
640
+ "right": "10px",
641
+ "background": "none",
642
+ "border": "none",
643
+ "color": "#757575",
644
+ "cursor": "pointer",
645
+ },
646
+ "type": "button",
647
+ "on_click": toggle_footer,
648
+ },
649
+ svg.svg(
650
+ {
651
+ "xmlns": "http://www.w3.org/2000/svg",
652
+ "width": "24",
653
+ "height": "24",
654
+ "fill": "none",
655
+ "stroke": "currentColor",
656
+ "stroke-linecap": "round",
657
+ "stroke-linejoin": "round",
658
+ "stroke-width": "3",
659
+ "viewBox": "0 0 24 24",
660
+ },
661
+ svg.path({"d": "M18 6L6 18"}),
662
+ svg.path({"d": "M6 6l12 12"})
663
+ )
664
+ ),
665
+ html.p(
666
+ {
667
+ "style": {
668
+ "fontSize": "20px",
669
+ "color": "#4b4851"
670
+ }
671
+ },
672
+ "How this works?",
673
+ ),
674
+ html.p(
675
+ {
676
+ "style": {
677
+ "color": "#757575",
678
+ }
679
+ },
680
+ "This app was built with ",
681
+ html.a(
682
+ {
683
+ "href": "https://vectara.com/",
684
+ "target": "_blank",
685
+ },
686
+ "Vectara",
687
+ ),
688
+ html.br(),
689
+ "It demonstrates the use of Agentic-RAG functionality with Vectara",
690
+ )
691
+ )
692
+ )
693
+
694
+ return html.div(
695
+ {
696
+ "style": {
697
+ "backgroundColor": "#F4F1F4",
698
+ "margin": "0",
699
+ "padding": "0",
700
+ "minHeight": "100vh",
701
+ "display": "flex",
702
+ "boxSizing": "border-box",
703
+ "flexDirection": "column",
704
+ "overflowX": "hidden",
705
+ "position": "relative",
706
+ }
707
+ },
708
+ html.head(
709
+ html.style("""
710
+ body {
711
+ margin: 0;
712
+ padding: 0;
713
+ }
714
+ """)
715
+ ),
716
+ Header(
717
+ demo_name=cfg['demo_name'],
718
+ short_description=cfg['short_description'],
719
+ extra_info=cfg['extra_info']
720
+ ),
721
+ ChatUI(),
722
+ ChatBox(),
723
+ Footer(),
724
+ )
725
+
726
+ # app = Starlette()
727
+
728
+ # configure(app, App)
729
+
730
+ # Should only need for local testing
731
+
732
+ if __name__ == "__main__":
733
+ run(App)
app_full.py ADDED
@@ -0,0 +1,714 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import asyncio
2
+ import threading
3
+ from markdown_it import MarkdownIt
4
+ from mdit_py_plugins import front_matter
5
+
6
+ from reactpy import component, html, hooks, use_effect, svg, run # DELETE run FOR DEPLOYMENT
7
+ from reactpy.backend.starlette import configure
8
+ from starlette.applications import Starlette
9
+ from vectara_agentic.agent import AgentStatusType
10
+ from agent import initialize_agent, get_agent_config
11
+
12
+ wait_message = "Please wait. Assistant at work..."
13
+
14
+ @component
15
+ def App():
16
+
17
+ # Used for showing/hiding logs
18
+ show_logs, set_show_logs = hooks.use_state(False)
19
+
20
+ # Determining when the "Show Logs" button should appear
21
+ show_logs_button, set_show_logs_button = hooks.use_state(False)
22
+
23
+ # Used for resetting chat bot with "Start Over" button.
24
+ first_turn, set_first_turn = hooks.use_state(True)
25
+
26
+ # Tracks the state of whether the header is collapsed or exapnded
27
+ more_info, set_more_info = hooks.use_state(False)
28
+
29
+ # Tracks the state of whether the footer is collapsed or expanded
30
+ collapsed, set_collapse = hooks.use_state(False)
31
+
32
+ # Record of Chat Messages
33
+ messages, set_messages = hooks.use_state([])
34
+
35
+ message, set_message = hooks.use_state("")
36
+ # input_value, set_input_value = hooks.use_state("")
37
+
38
+ def use_agent_logger():
39
+ agent_log_entries, set_agent_log_entries = hooks.use_state([])
40
+
41
+ def reset_log_entries():
42
+ set_agent_log_entries([])
43
+
44
+ def add_log_entry(new_log_entry):
45
+ set_agent_log_entries(lambda previous_entries: previous_entries + [new_log_entry])
46
+
47
+ return agent_log_entries, add_log_entry, reset_log_entries
48
+
49
+ log_entries, add_log_entry, reset_log_entries = use_agent_logger()
50
+
51
+ def update_func(status_type: AgentStatusType, msg: str):
52
+ if status_type != AgentStatusType.AGENT_UPDATE:
53
+ output = f"{status_type.value} - {msg}"
54
+ add_log_entry(output)
55
+
56
+ cfg, _ = hooks.use_state(get_agent_config())
57
+ agent, _ = hooks.use_state(initialize_agent(cfg, update_func))
58
+
59
+ def toggle_header(event=None):
60
+ set_more_info(not more_info)
61
+
62
+ def toggle_footer(event=None):
63
+ set_collapse(not collapsed)
64
+
65
+ # Clears all messages and resets chat interface
66
+ def start_over(event=None):
67
+ set_messages([])
68
+ set_first_turn(True)
69
+
70
+ def display_message(new_messages):
71
+ if first_turn:
72
+ set_first_turn(False)
73
+
74
+ set_messages(messages + list(new_messages))
75
+
76
+ async def chat_response(user_message):
77
+ response = await asyncio.to_thread(agent.chat, user_message)
78
+ return response
79
+
80
+ async def send_message_async(sent_message):
81
+ response = await chat_response(sent_message)
82
+ set_messages(messages[:-1])
83
+ display_message(
84
+ [
85
+ {"user": "human", "message": sent_message},
86
+ {"user": "bot", "message": response},
87
+ ]
88
+ )
89
+ set_show_logs_button(True)
90
+
91
+ def send_message(event=None):
92
+ print(f"DEBUG: SEND MESSAGE CALLED FOR MESSAGE {message}")
93
+ if message.strip():
94
+ sent_message = message
95
+ set_message("")
96
+ # set_input_value("")
97
+ set_show_logs_button(False)
98
+ set_show_logs(False)
99
+ reset_log_entries()
100
+ display_message([{"user": "human", "message": sent_message}])
101
+ display_message([{"user": "bot", "message": wait_message}])
102
+
103
+ asyncio.create_task(send_message_async(sent_message))
104
+
105
+ def send_example(ex_prompt):
106
+ if ex_prompt.strip():
107
+ sent_message = ex_prompt
108
+ set_message("")
109
+ reset_log_entries()
110
+ display_message([{"user": "human", "message": sent_message}])
111
+ display_message([{"user": "bot", "message": wait_message}])
112
+
113
+ asyncio.create_task(send_message_async(sent_message))
114
+
115
+ async def handle_key_down(event):
116
+ if event['key'] == 'Enter' and message.strip():
117
+ await send_message()
118
+ set_message("")
119
+
120
+ def handle_input_change(event):
121
+ set_message(event['target']['value'])
122
+
123
+ @component
124
+ def Header(demo_name: str, short_description: str, extra_info: str):
125
+ return html.header(
126
+ {
127
+ "style": {
128
+ "backgroundColor": "#FFFFFF",
129
+ "display": "flex",
130
+ "justifyContent": "space-between",
131
+ "alignItems": "center",
132
+ }
133
+ },
134
+ html.div(
135
+ {
136
+ "style": {
137
+ "display": "flex",
138
+ "alignItems": "center",
139
+ "flex": 2,
140
+ "textAlign": "left",
141
+ "padding": "10px",
142
+ }
143
+ },
144
+ html.img(
145
+ {
146
+ "src": "https://avatars.githubusercontent.com/u/108304503?s=200&v=4",
147
+ "style": {
148
+ "height": "30px",
149
+ "marginRight": "15px",
150
+ "marginLeft": "5px",
151
+ "transform": "translateY(-2px)",
152
+ }
153
+ }
154
+ ),
155
+ html.p(
156
+ {
157
+ "style": {
158
+ "fontSize": "25px",
159
+ "fontFamily": "Georgia, 'Times New Roman', Times, serif",
160
+ }
161
+ },
162
+ f"{demo_name}"
163
+ ),
164
+ ),
165
+ html.div(
166
+ {
167
+ "style": {
168
+ "flex": 5,
169
+ "textAlign": "center",
170
+ "padding": "10px 0px 15px 0px",
171
+ "fontFamily": "Lato",
172
+ "position": "relative",
173
+ }
174
+ },
175
+ html.h3(f"Welcome to the {demo_name} Demo"),
176
+ html.p(
177
+ short_description,
178
+ html.button(
179
+ {
180
+ "style": {
181
+ "display": "inline" if not more_info else "none",
182
+ "backgroundColor": "#FFFFFF",
183
+ "color": "#757575",
184
+ "fontSize": "13px",
185
+ "cursor": "pointer",
186
+ "border": "none",
187
+ "padding": "0px 0px 0px 5px",
188
+ },
189
+ "type": "button",
190
+ "onClick": toggle_header,
191
+ },
192
+ html.u(
193
+ {
194
+ "style": {
195
+ "flex": 2,
196
+ "textAlign": "right",
197
+ "padding": "10px",
198
+ }
199
+ },
200
+ "Learn More"
201
+ )
202
+ ),
203
+ f" {extra_info}" if more_info else ""
204
+ ),
205
+ html.button(
206
+ {
207
+ "style": {
208
+ "display": "block" if more_info else "none",
209
+ "background": "none",
210
+ "border": "none",
211
+ "color": "#757575",
212
+ "cursor": "pointer",
213
+ "position": "absolute",
214
+ "left": "50%",
215
+ "transform": "translateX(-50%)",
216
+ "bottom": "1px",
217
+ },
218
+ "type": "button",
219
+ "on_click": toggle_header,
220
+ },
221
+ svg.svg(
222
+ {
223
+ "width": "20",
224
+ "height": "20",
225
+ "viewBox": "0 0 20 20",
226
+ "fill": "none",
227
+ "stroke": "black",
228
+ "strokeWidth": "2",
229
+ "strokeLinecap": "round",
230
+ "strokeLinejoin": "round",
231
+ },
232
+ svg.path(
233
+ {
234
+ "d": "M12 19V5M5 12l7-7 7 7",
235
+ "stroke": "currentColor",
236
+ }
237
+ )
238
+ )
239
+ )
240
+ ),
241
+ html.div(
242
+ {
243
+ "style": {
244
+ "flex": 2,
245
+ "textAlign": "right",
246
+ "padding": "10px",
247
+ }
248
+ },
249
+ html.button(
250
+ {
251
+ "style": {
252
+ "backgroundColor": "#FFFFFF",
253
+ "color": "#757575",
254
+ "fontSize": "14px",
255
+ "cursor": "pointer",
256
+ "border": "1px solid #e2dfdf",
257
+ "borderRadius": "5px",
258
+ "padding": "6px 20px",
259
+ "marginRight": "15px",
260
+ },
261
+ "type": "button",
262
+ "onClick": start_over,
263
+ },
264
+ "Start Over?"
265
+ )
266
+ )
267
+ )
268
+
269
+ def markdown_to_html(markdown_text):
270
+ md = (
271
+ MarkdownIt("commonmark", {"breaks": True, "html": True})
272
+ .use(front_matter.front_matter_plugin)
273
+ )
274
+ return md.render(markdown_text)
275
+
276
+ @component
277
+ def MarkdownRenderer(content):
278
+ html_content = markdown_to_html(content)
279
+ return html.div(
280
+ {
281
+ "style": {
282
+ "fontFamily": "Arial",
283
+ "color": "#49454F",
284
+ "fontSize": "14px",
285
+ },
286
+ "dangerouslySetInnerHTML": {"__html": html_content}
287
+ }
288
+ )
289
+
290
+ @component
291
+ def ExamplePrompts():
292
+ example_questions = [example.strip() for example in cfg['examples'].split(";")] if cfg.examples else []
293
+
294
+ def create_prompt_button(question):
295
+ return html.button(
296
+ {
297
+ "style": {
298
+ "backgroundColor": "#FFFFFF",
299
+ "borderWidth": "1px",
300
+ "borderColor": "#65558F",
301
+ "borderRadius": "20px",
302
+ "padding": "5px 7px",
303
+ "margin": "5px",
304
+ "cursor": "pointer",
305
+ "color": "#21005D",
306
+ "fontSize": "13px",
307
+ },
308
+ "type": "button",
309
+ "onClick": lambda _: send_example(question),
310
+ },
311
+ question
312
+ )
313
+
314
+ if first_turn:
315
+ return html.div(
316
+ {
317
+ "style": {
318
+ "display": "flex",
319
+ "transform": "translate(4%, 100%)",
320
+ "flexDirection": "column",
321
+ "justifyContent": "center",
322
+ "width": "90%",
323
+ }
324
+ },
325
+ html.p(
326
+ {
327
+ "style": {
328
+ "fontSize": "16px",
329
+ "fontFamily": "Arial",
330
+ "color": "#49454F",
331
+ "marginBottom": "5px",
332
+ "transform": "translateX(3%)",
333
+ "textAlign": "left",
334
+ }
335
+ },
336
+ "Queries to try:"
337
+ ),
338
+ html.div(
339
+ {
340
+ "style": {
341
+ "display": "flex",
342
+ "flexWrap": "wrap",
343
+ "justifyContent": "center",
344
+ }
345
+ },
346
+ *[create_prompt_button(q) for q in example_questions]
347
+ )
348
+ )
349
+
350
+ return None
351
+
352
+ @component
353
+ def ChatBox():
354
+ return html.div(
355
+ {
356
+ "style": {
357
+ "position": "fixed",
358
+ "bottom": "70px" if collapsed else "140px",
359
+ "left": "50%",
360
+ "transform": "translateX(-50%)",
361
+ "width": "80%",
362
+ "display": "flex",
363
+ "alignItems": "center",
364
+ "backgroundColor": "#FFFFFF",
365
+ "borderRadius": "50px",
366
+ "zIndex": "1000",
367
+ }
368
+ },
369
+ html.input(
370
+ {
371
+ "type": "text",
372
+ "value": message,
373
+ "placeholder": "Your Message",
374
+ "onChange": handle_input_change,
375
+ "onKeyDown": handle_key_down,
376
+ "style": {
377
+ "width": "100%",
378
+ "padding": "10px 50px 10px 20px",
379
+ "border": "none",
380
+ "borderRadius": "50px",
381
+ "color": "#65558F",
382
+ }
383
+ }
384
+ ),
385
+ html.button(
386
+ {
387
+ "type": "button",
388
+ "onClick": send_message,
389
+ "style": {
390
+ "position": "absolute",
391
+ "right": "8px",
392
+ "top": "50%",
393
+ "transform":"translateY(-50%)",
394
+ "background": "none",
395
+ "border": "none",
396
+ "cursor": "pointer",
397
+ "padding": "8px",
398
+ "display": "flex",
399
+ "alignItems": "center",
400
+ "justifyContent": "center",
401
+ }
402
+ },
403
+ svg.svg(
404
+ {
405
+ "xmlns": "http://www.w3.org/2000/svg",
406
+ "width": "20",
407
+ "height": "20",
408
+ "fill": "none",
409
+ "stroke": "currentColor",
410
+ "stroke-linecap": "round",
411
+ "stroke-linejoin": "round",
412
+ "stroke-width": "2",
413
+ "viewBox": "0 0 24 24",
414
+ "class": "feather feather-send",
415
+ },
416
+ svg.path({"d": "M22 2 11 13"}),
417
+ svg.path({"d": "M22 2 15 22 11 13 2 9z"})
418
+ )
419
+ )
420
+ )
421
+
422
+
423
+ @component
424
+ def ChatMessage(user, message):
425
+
426
+ return html.div(
427
+ {
428
+ "style": {
429
+ "display": "flex",
430
+ "width": "75%",
431
+ "transform": "translateX(20%)",
432
+ "justifyContent": "flex-end" if user == "human" else "flex-start",
433
+ "margin": "20px 0px 10px 0px"
434
+ }
435
+ },
436
+ html.div(
437
+ {
438
+ "style": {
439
+ "maxWidth": "50%",
440
+ "padding": "0px 15px 0px 10px",
441
+ "borderRadius": "15px",
442
+ "backgroundColor": "#E8DEF8" if user == "human" else "#ECE6F0",
443
+ "color": "#000000",
444
+ }
445
+ },
446
+ MarkdownRenderer(message)
447
+ )
448
+ )
449
+
450
+ @component
451
+ def Logs():
452
+ if (len(messages) > 0) and (len(log_entries) > 0) and (messages[-1]["message"] != wait_message) and (show_logs_button):
453
+ if not show_logs:
454
+ return html.div(
455
+ {
456
+ "style": {
457
+ "display": "flex",
458
+ "transform": "translateX(75%)",
459
+ "width": "20%",
460
+ "margin": "0px",
461
+ }
462
+ },
463
+ html.button(
464
+ {
465
+ "style": {
466
+ "position": "flex",
467
+ "background": "none",
468
+ "color": "#757575",
469
+ "border": "none",
470
+ "cursor": "pointer",
471
+ "fontFamily": "Arial",
472
+ "fontSize": "14px",
473
+ },
474
+ "type": "button",
475
+ "onClick": lambda _: set_show_logs(True)
476
+ },
477
+ "Show Logs"
478
+ )
479
+ )
480
+ else:
481
+ return html.div(
482
+ {
483
+ "style": {
484
+ "display": "flex",
485
+ "transform": "translateX(20%)",
486
+ "border": "2px solid #e2dfdf",
487
+ "borderRadius": "10px",
488
+ "width": "75%",
489
+ }
490
+ },
491
+ html.div(
492
+ [
493
+ html.p(
494
+ {
495
+ "style": {
496
+ "fontSize": "14px",
497
+ "marginLeft": "10px",
498
+ }
499
+ },
500
+ entry
501
+ ) for entry in log_entries
502
+ ],
503
+ html.button(
504
+ {
505
+ "style": {
506
+ "position": "flex",
507
+ "background": "none",
508
+ "color": "#757575",
509
+ "border": "none",
510
+ "cursor": "pointer",
511
+ "fontFamily": "Arial",
512
+ "fontSize": "14px",
513
+ "marginBottom": "10px",
514
+ "marginLeft": "5px",
515
+ },
516
+ "type": "button",
517
+ "onClick": lambda _: set_show_logs(False)
518
+ },
519
+ "Hide Logs"
520
+ )
521
+ )
522
+ )
523
+
524
+ return None
525
+
526
+ @component
527
+ def ChatUI():
528
+
529
+ def render_chat_content():
530
+ if first_turn:
531
+ return ExamplePrompts()
532
+ else:
533
+ return [ChatMessage(msg["user"], msg["message"]) for msg in messages]
534
+
535
+ return html.div(
536
+ {
537
+ "style": {
538
+ "display": "flex",
539
+ "flexDirection": "column",
540
+ "padding": "10px",
541
+ "backgroundColor": "#F4F1F4",
542
+ "overflowY": "auto",
543
+ "height": f"calc(100vh - {265 if collapsed else 335}px)",
544
+ "marginBottom": "15px",
545
+ "paddingBottom": "15px",
546
+ },
547
+ },
548
+ render_chat_content(),
549
+ Logs()
550
+ )
551
+
552
+
553
+ @component
554
+ def Footer():
555
+
556
+ if collapsed:
557
+ return html.footer(
558
+ {
559
+ "style": {
560
+ "backgroundColor": "#FFFFFF",
561
+ "position": "fixed",
562
+ "bottom": 0,
563
+ "width": "100%",
564
+ "height": "50px",
565
+ }
566
+ },
567
+ html.button(
568
+ {
569
+ "style": {
570
+ "background": "none",
571
+ "border": "none",
572
+ "color": "#757575",
573
+ "cursor": "pointer",
574
+ "position": "absolute",
575
+ "bottom": "5px",
576
+ "right": "10px",
577
+ },
578
+ "type": "button",
579
+ "on_click": toggle_footer,
580
+ },
581
+ svg.svg(
582
+ {
583
+ "xmlns": "http://www.w3.org/2000/svg",
584
+ "width": "24",
585
+ "height": "24",
586
+ "fill": "none",
587
+ "stroke": "currentColor",
588
+ "stroke-linecap": "round",
589
+ "stroke-linejoin": "round",
590
+ "stroke-width": "3",
591
+ "viewBox": "0 0 24 24",
592
+ },
593
+ svg.path({"d": "M19 14l-7-7-7 7"})
594
+ )
595
+ )
596
+ )
597
+
598
+ else:
599
+ return html.footer(
600
+ {
601
+ "style": {
602
+ "backgroundColor": "#FFFFFF",
603
+ "position": "fixed",
604
+ "bottom": 0,
605
+ "width": "100%",
606
+ }
607
+ },
608
+ html.div(
609
+ {
610
+ "style": {
611
+ "backgroundColor": "#FFFFFF",
612
+ "padding": "0px 20px 0px 50px",
613
+ "position": "relative",
614
+ "display": "block",
615
+ }
616
+ },
617
+ html.button(
618
+ {
619
+ "style": {
620
+ "position": "absolute",
621
+ "right": "10px",
622
+ "background": "none",
623
+ "border": "none",
624
+ "color": "#757575",
625
+ "cursor": "pointer",
626
+ },
627
+ "type": "button",
628
+ "on_click": toggle_footer,
629
+ },
630
+ svg.svg(
631
+ {
632
+ "xmlns": "http://www.w3.org/2000/svg",
633
+ "width": "24",
634
+ "height": "24",
635
+ "fill": "none",
636
+ "stroke": "currentColor",
637
+ "stroke-linecap": "round",
638
+ "stroke-linejoin": "round",
639
+ "stroke-width": "3",
640
+ "viewBox": "0 0 24 24",
641
+ },
642
+ svg.path({"d": "M18 6L6 18"}),
643
+ svg.path({"d": "M6 6l12 12"})
644
+ )
645
+ ),
646
+ html.p(
647
+ {
648
+ "style": {
649
+ "fontSize": "20px",
650
+ "color": "#4b4851"
651
+ }
652
+ },
653
+ "How this works?",
654
+ ),
655
+ html.p(
656
+ {
657
+ "style": {
658
+ "color": "#757575",
659
+ }
660
+ },
661
+ "This app was built with ",
662
+ html.a(
663
+ {
664
+ "href": "https://vectara.com/",
665
+ "target": "_blank",
666
+ },
667
+ "Vectara",
668
+ ),
669
+ html.br(),
670
+ "It demonstrates the use of Agentic-RAG functionality with Vectara",
671
+ )
672
+ )
673
+ )
674
+
675
+ return html.div(
676
+ {
677
+ "style": {
678
+ "backgroundColor": "#F4F1F4",
679
+ "margin": "0",
680
+ "padding": "0",
681
+ "minHeight": "100vh",
682
+ "display": "flex",
683
+ "boxSizing": "border-box",
684
+ "flexDirection": "column",
685
+ "overflowX": "hidden",
686
+ "position": "relative",
687
+ }
688
+ },
689
+ html.head(
690
+ html.style("""
691
+ body {
692
+ margin: 0;
693
+ padding: 0;
694
+ }
695
+ """)
696
+ ),
697
+ Header(
698
+ demo_name=cfg['demo_name'],
699
+ short_description=cfg['short_description'],
700
+ extra_info=cfg['extra_info']
701
+ ),
702
+ ChatUI(),
703
+ ChatBox(),
704
+ Footer(),
705
+ )
706
+
707
+ # app = Starlette()
708
+
709
+ # configure(app, App)
710
+
711
+ # Should only need for local testing
712
+
713
+ if __name__ == "__main__":
714
+ run(App)
chatbox.py ADDED
@@ -0,0 +1,71 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from reactpy import component, html, svg
2
+
3
+ @component
4
+ def ChatBox(message, handle_input_change, handle_key_down, send_message, collapsed):
5
+ return html.div(
6
+ {
7
+ "style": {
8
+ "position": "fixed",
9
+ "bottom": "70px" if collapsed else "140px",
10
+ "left": "50%",
11
+ "transform": "translateX(-50%)",
12
+ "width": "80%",
13
+ "display": "flex",
14
+ "alignItems": "center",
15
+ "backgroundColor": "#FFFFFF",
16
+ "borderRadius": "50px",
17
+ "zIndex": "1000",
18
+ }
19
+ },
20
+ html.input(
21
+ {
22
+ "type": "text",
23
+ "value": message,
24
+ "placeholder": "Your Message",
25
+ "onChange": handle_input_change,
26
+ "onKeyDown": handle_key_down,
27
+ "style": {
28
+ "width": "100%",
29
+ "padding": "10px 50px 10px 20px",
30
+ "border": "none",
31
+ "borderRadius": "50px",
32
+ "color": "#65558F",
33
+ }
34
+ }
35
+ ),
36
+ html.button(
37
+ {
38
+ "type": "button",
39
+ "onClick": send_message,
40
+ "style": {
41
+ "position": "absolute",
42
+ "right": "8px",
43
+ "top": "50%",
44
+ "transform":"translateY(-50%)",
45
+ "background": "none",
46
+ "border": "none",
47
+ "cursor": "pointer",
48
+ "padding": "8px",
49
+ "display": "flex",
50
+ "alignItems": "center",
51
+ "justifyContent": "center",
52
+ }
53
+ },
54
+ svg.svg(
55
+ {
56
+ "xmlns": "http://www.w3.org/2000/svg",
57
+ "width": "20",
58
+ "height": "20",
59
+ "fill": "none",
60
+ "stroke": "currentColor",
61
+ "stroke-linecap": "round",
62
+ "stroke-linejoin": "round",
63
+ "stroke-width": "2",
64
+ "viewBox": "0 0 24 24",
65
+ "class": "feather feather-send",
66
+ },
67
+ svg.path({"d": "M22 2 11 13"}),
68
+ svg.path({"d": "M22 2 15 22 11 13 2 9z"})
69
+ )
70
+ )
71
+ )
chatui.py ADDED
@@ -0,0 +1,219 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from markdown_it import MarkdownIt
2
+ from mdit_py_plugins import front_matter
3
+
4
+ from reactpy import component, html
5
+
6
+ from utils import wait_message
7
+
8
+
9
+ def markdown_to_html(markdown_text):
10
+ md = (
11
+ MarkdownIt("commonmark", {"breaks": True, "html": True})
12
+ .use(front_matter.front_matter_plugin)
13
+ )
14
+ return md.render(markdown_text)
15
+
16
+ @component
17
+ def MarkdownRenderer(content):
18
+ html_content = markdown_to_html(content)
19
+ return html.div(
20
+ {
21
+ "style": {
22
+ "fontFamily": "Arial",
23
+ "color": "#49454F",
24
+ "fontSize": "14px",
25
+ },
26
+ "dangerouslySetInnerHTML": {"__html": html_content}
27
+ }
28
+ )
29
+
30
+ @component
31
+ def ExamplePrompts(examples, send_example, first_turn):
32
+ example_questions = [example.strip() for example in examples.split(";")] if examples else []
33
+
34
+ def create_prompt_button(question):
35
+ return html.button(
36
+ {
37
+ "style": {
38
+ "backgroundColor": "#FFFFFF",
39
+ "borderWidth": "1px",
40
+ "borderColor": "#65558F",
41
+ "borderRadius": "20px",
42
+ "padding": "5px 7px",
43
+ "margin": "5px",
44
+ "cursor": "pointer",
45
+ "color": "#21005D",
46
+ "fontSize": "13px",
47
+ },
48
+ "type": "button",
49
+ "onClick": lambda _: send_example(question),
50
+ },
51
+ question
52
+ )
53
+
54
+ if first_turn:
55
+ return html.div(
56
+ {
57
+ "style": {
58
+ "display": "flex",
59
+ "transform": "translate(4%, 100%)",
60
+ "flexDirection": "column",
61
+ "justifyContent": "center",
62
+ "width": "90%",
63
+ }
64
+ },
65
+ html.p(
66
+ {
67
+ "style": {
68
+ "fontSize": "16px",
69
+ "fontFamily": "Arial",
70
+ "color": "#49454F",
71
+ "marginBottom": "5px",
72
+ "transform": "translateX(3%)",
73
+ "textAlign": "left",
74
+ }
75
+ },
76
+ "Queries to try:"
77
+ ),
78
+ html.div(
79
+ {
80
+ "style": {
81
+ "display": "flex",
82
+ "flexWrap": "wrap",
83
+ "justifyContent": "center",
84
+ }
85
+ },
86
+ *[create_prompt_button(q) for q in example_questions]
87
+ )
88
+ )
89
+
90
+ return None
91
+
92
+ @component
93
+ def ChatMessage(user, message):
94
+
95
+ return html.div(
96
+ {
97
+ "style": {
98
+ "display": "flex",
99
+ "width": "75%",
100
+ "transform": "translateX(20%)",
101
+ "justifyContent": "flex-end" if user == "human" else "flex-start",
102
+ "margin": "20px 0px 10px 0px"
103
+ }
104
+ },
105
+ html.div(
106
+ {
107
+ "style": {
108
+ "maxWidth": "50%",
109
+ "padding": "0px 15px 0px 10px",
110
+ "borderRadius": "15px",
111
+ "backgroundColor": "#E8DEF8" if user == "human" else "#ECE6F0",
112
+ "color": "#000000",
113
+ }
114
+ },
115
+ MarkdownRenderer(message)
116
+ )
117
+ )
118
+
119
+ @component
120
+ def Logs(messages, log_entries, show_logs, set_show_logs, show_logs_button):
121
+ if (len(messages) > 0) and (len(log_entries) > 0) and (messages[-1]["message"] != wait_message) and (show_logs_button):
122
+ if not show_logs:
123
+ return html.div(
124
+ {
125
+ "style": {
126
+ "display": "flex",
127
+ "transform": "translateX(75%)",
128
+ "width": "20%",
129
+ "margin": "0px",
130
+ }
131
+ },
132
+ html.button(
133
+ {
134
+ "style": {
135
+ "position": "flex",
136
+ "background": "none",
137
+ "color": "#757575",
138
+ "border": "none",
139
+ "cursor": "pointer",
140
+ "fontFamily": "Arial",
141
+ "fontSize": "14px",
142
+ },
143
+ "type": "button",
144
+ "onClick": lambda _: set_show_logs(True)
145
+ },
146
+ "Show Logs"
147
+ )
148
+ )
149
+ else:
150
+ return html.div(
151
+ {
152
+ "style": {
153
+ "display": "flex",
154
+ "transform": "translateX(20%)",
155
+ "border": "2px solid #e2dfdf",
156
+ "borderRadius": "10px",
157
+ "width": "75%",
158
+ }
159
+ },
160
+ html.div(
161
+ [
162
+ html.p(
163
+ {
164
+ "style": {
165
+ "fontSize": "14px",
166
+ "marginLeft": "10px",
167
+ }
168
+ },
169
+ entry
170
+ ) for entry in log_entries
171
+ ],
172
+ html.button(
173
+ {
174
+ "style": {
175
+ "position": "flex",
176
+ "background": "none",
177
+ "color": "#757575",
178
+ "border": "none",
179
+ "cursor": "pointer",
180
+ "fontFamily": "Arial",
181
+ "fontSize": "14px",
182
+ "marginBottom": "10px",
183
+ "marginLeft": "5px",
184
+ },
185
+ "type": "button",
186
+ "onClick": lambda _: set_show_logs(False)
187
+ },
188
+ "Hide Logs"
189
+ )
190
+ )
191
+ )
192
+
193
+ return None
194
+
195
+ @component
196
+ def ChatUI(messages, first_turn, examples, send_example, log_entries, show_logs, set_show_logs, show_logs_button, collapsed):
197
+
198
+ def render_chat_content():
199
+ if first_turn:
200
+ return ExamplePrompts(examples, send_example, first_turn)
201
+ else:
202
+ return [ChatMessage(msg["user"], msg["message"]) for msg in messages]
203
+
204
+ return html.div(
205
+ {
206
+ "style": {
207
+ "display": "flex",
208
+ "flexDirection": "column",
209
+ "padding": "10px",
210
+ "backgroundColor": "#F4F1F4",
211
+ "overflowY": "auto",
212
+ "height": f"calc(100vh - {265 if collapsed else 335}px)",
213
+ "marginBottom": "15px",
214
+ "paddingBottom": "15px",
215
+ },
216
+ },
217
+ render_chat_content(),
218
+ Logs(messages, log_entries, show_logs, set_show_logs, show_logs_button)
219
+ )
footer.py ADDED
@@ -0,0 +1,127 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from reactpy import component, html, svg, hooks
2
+
3
+ @component
4
+ def Footer(collapsed, set_collapsed):
5
+
6
+ # def toggle_footer(event=None):
7
+ # set_collapse(not collapsed)
8
+
9
+ if collapsed:
10
+ return html.footer(
11
+ {
12
+ "style": {
13
+ "backgroundColor": "#FFFFFF",
14
+ "position": "fixed",
15
+ "bottom": 0,
16
+ "width": "100%",
17
+ "height": "50px",
18
+ }
19
+ },
20
+ html.button(
21
+ {
22
+ "style": {
23
+ "background": "none",
24
+ "border": "none",
25
+ "color": "#757575",
26
+ "cursor": "pointer",
27
+ "position": "absolute",
28
+ "bottom": "5px",
29
+ "right": "10px",
30
+ },
31
+ "type": "button",
32
+ "on_click": lambda _: set_collapsed(False)
33
+ # "on_click": toggle_footer,
34
+ },
35
+ svg.svg(
36
+ {
37
+ "xmlns": "http://www.w3.org/2000/svg",
38
+ "width": "24",
39
+ "height": "24",
40
+ "fill": "none",
41
+ "stroke": "currentColor",
42
+ "stroke-linecap": "round",
43
+ "stroke-linejoin": "round",
44
+ "stroke-width": "3",
45
+ "viewBox": "0 0 24 24",
46
+ },
47
+ svg.path({"d": "M19 14l-7-7-7 7"})
48
+ )
49
+ )
50
+ )
51
+ else:
52
+ return html.footer(
53
+ {
54
+ "style": {
55
+ "backgroundColor": "#FFFFFF",
56
+ "position": "fixed",
57
+ "bottom": 0,
58
+ "width": "100%",
59
+ }
60
+ },
61
+ html.div(
62
+ {
63
+ "style": {
64
+ "backgroundColor": "#FFFFFF",
65
+ "padding": "0px 20px 0px 50px",
66
+ "position": "relative",
67
+ "display": "block",
68
+ }
69
+ },
70
+ html.button(
71
+ {
72
+ "style": {
73
+ "position": "absolute",
74
+ "right": "10px",
75
+ "background": "none",
76
+ "border": "none",
77
+ "color": "#757575",
78
+ "cursor": "pointer",
79
+ },
80
+ "type": "button",
81
+ "on_click": lambda _: set_collapsed(True)
82
+ # "on_click": toggle_footer,
83
+ },
84
+ svg.svg(
85
+ {
86
+ "xmlns": "http://www.w3.org/2000/svg",
87
+ "width": "24",
88
+ "height": "24",
89
+ "fill": "none",
90
+ "stroke": "currentColor",
91
+ "stroke-linecap": "round",
92
+ "stroke-linejoin": "round",
93
+ "stroke-width": "3",
94
+ "viewBox": "0 0 24 24",
95
+ },
96
+ svg.path({"d": "M18 6L6 18"}),
97
+ svg.path({"d": "M6 6l12 12"})
98
+ )
99
+ ),
100
+ html.p(
101
+ {
102
+ "style": {
103
+ "fontSize": "20px",
104
+ "color": "#4b4851"
105
+ }
106
+ },
107
+ "How this works?",
108
+ ),
109
+ html.p(
110
+ {
111
+ "style": {
112
+ "color": "#757575",
113
+ }
114
+ },
115
+ "This app was built with ",
116
+ html.a(
117
+ {
118
+ "href": "https://vectara.com/",
119
+ "target": "_blank",
120
+ },
121
+ "Vectara",
122
+ ),
123
+ html.br(),
124
+ "It demonstrates the use of Agentic-RAG functionality with Vectara",
125
+ )
126
+ )
127
+ )
header.py ADDED
@@ -0,0 +1,152 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from reactpy import component, html, svg, hooks
2
+
3
+ @component
4
+ def Header(demo_name: str, short_description: str, extra_info: str, start_over):
5
+ more_info, set_more_info = hooks.use_state(False)
6
+
7
+ def toggle_header(event=None):
8
+ set_more_info(not more_info)
9
+
10
+ return html.header(
11
+ {
12
+ "style": {
13
+ "backgroundColor": "#FFFFFF",
14
+ "display": "flex",
15
+ "justifyContent": "space-between",
16
+ "alignItems": "center",
17
+ }
18
+ },
19
+ html.div(
20
+ {
21
+ "style": {
22
+ "display": "flex",
23
+ "alignItems": "center",
24
+ "flex": 2,
25
+ "textAlign": "left",
26
+ "padding": "10px",
27
+ }
28
+ },
29
+ html.img(
30
+ {
31
+ "src": "https://avatars.githubusercontent.com/u/108304503?s=200&v=4",
32
+ "style": {
33
+ "height": "30px",
34
+ "marginRight": "15px",
35
+ "marginLeft": "5px",
36
+ "transform": "translateY(-2px)",
37
+ }
38
+ }
39
+ ),
40
+ html.p(
41
+ {
42
+ "style": {
43
+ "fontSize": "25px",
44
+ "fontFamily": "Georgia, 'Times New Roman', Times, serif",
45
+ }
46
+ },
47
+ f"{demo_name}"
48
+ ),
49
+ ),
50
+ html.div(
51
+ {
52
+ "style": {
53
+ "flex": 5,
54
+ "textAlign": "center",
55
+ "padding": "10px 0px 15px 0px",
56
+ "fontFamily": "Lato",
57
+ "position": "relative",
58
+ }
59
+ },
60
+ html.h3(f"Welcome to the {demo_name} Demo"),
61
+ html.p(
62
+ short_description,
63
+ html.button(
64
+ {
65
+ "style": {
66
+ "display": "inline" if not more_info else "none",
67
+ "backgroundColor": "#FFFFFF",
68
+ "color": "#757575",
69
+ "fontSize": "13px",
70
+ "cursor": "pointer",
71
+ "border": "none",
72
+ "padding": "0px 0px 0px 5px",
73
+ },
74
+ "type": "button",
75
+ "onClick": toggle_header,
76
+ },
77
+ html.u(
78
+ {
79
+ "style": {
80
+ "flex": 2,
81
+ "textAlign": "right",
82
+ "padding": "10px",
83
+ }
84
+ },
85
+ "Learn More"
86
+ )
87
+ ),
88
+ f" {extra_info}" if more_info else ""
89
+ ),
90
+ html.button(
91
+ {
92
+ "style": {
93
+ "display": "block" if more_info else "none",
94
+ "background": "none",
95
+ "border": "none",
96
+ "color": "#757575",
97
+ "cursor": "pointer",
98
+ "position": "absolute",
99
+ "left": "50%",
100
+ "transform": "translateX(-50%)",
101
+ "bottom": "1px",
102
+ },
103
+ "type": "button",
104
+ "on_click": toggle_header,
105
+ },
106
+ svg.svg(
107
+ {
108
+ "width": "20",
109
+ "height": "20",
110
+ "viewBox": "0 0 20 20",
111
+ "fill": "none",
112
+ "stroke": "black",
113
+ "strokeWidth": "2",
114
+ "strokeLinecap": "round",
115
+ "strokeLinejoin": "round",
116
+ },
117
+ svg.path(
118
+ {
119
+ "d": "M12 19V5M5 12l7-7 7 7",
120
+ "stroke": "currentColor",
121
+ }
122
+ )
123
+ )
124
+ )
125
+ ),
126
+ html.div(
127
+ {
128
+ "style": {
129
+ "flex": 2,
130
+ "textAlign": "right",
131
+ "padding": "10px",
132
+ }
133
+ },
134
+ html.button(
135
+ {
136
+ "style": {
137
+ "backgroundColor": "#FFFFFF",
138
+ "color": "#757575",
139
+ "fontSize": "14px",
140
+ "cursor": "pointer",
141
+ "border": "1px solid #e2dfdf",
142
+ "borderRadius": "5px",
143
+ "padding": "6px 20px",
144
+ "marginRight": "15px",
145
+ },
146
+ "type": "button",
147
+ "onClick": start_over,
148
+ },
149
+ "Start Over?"
150
+ )
151
+ )
152
+ )
requirements.txt CHANGED
@@ -5,6 +5,6 @@ python-dotenv==1.0.1
5
  uuid==1.30
6
  langdetect==1.0.9
7
  langcodes==3.4.0
8
- vectara-agentic==0.1.6
9
  markdown-it-py==3.0.0
10
  mdit-py-plugins==0.4.1
 
5
  uuid==1.30
6
  langdetect==1.0.9
7
  langcodes==3.4.0
8
+ vectara-agentic==0.1.7
9
  markdown-it-py==3.0.0
10
  mdit-py-plugins==0.4.1
utils.py ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+
2
+
3
+ wait_message = "Please wait. Assistant at work..."