chaaim123 commited on
Commit
4798da1
·
verified ·
1 Parent(s): 90339c0

create app.py

Browse files
Files changed (1) hide show
  1. app.py +160 -0
app.py ADDED
@@ -0,0 +1,160 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from shiny import App, ui, reactive, render
2
+ import uuid
3
+ from datetime import datetime
4
+ import os
5
+ import sys
6
+ from pathlib import Path
7
+ from chatbot import ask_question, clear_conversation
8
+ from shinyswatch import theme
9
+
10
+ # Define UI
11
+ app_ui = ui.page_fluid(
12
+ ui.panel_title("American University Academic Advisor Chatbot"),
13
+
14
+ # Use Bootstrap row/column layout
15
+ ui.row(
16
+ # Settings panel (left side)
17
+ ui.column(3,
18
+ ui.div(
19
+ {"class": "card mb-3"},
20
+ ui.div(
21
+ {"class": "card-header"},
22
+ ui.h4("Settings", class_="mb-0")
23
+ ),
24
+ ui.div(
25
+ {"class": "card-body"},
26
+ ui.tags.div(
27
+ {"class": "mb-3"},
28
+ ui.tags.label("Session ID:", class_="form-label"),
29
+ ui.tags.div(str(uuid.uuid4()), class_="form-text")
30
+ ),
31
+ ui.input_action_button("clear_chat", "Clear Chat History",
32
+ class_="btn btn-warning btn-block mb-3 w-100"),
33
+ # Export button
34
+ ui.download_button("download_chat", "Export Chat History",
35
+ class_="btn btn-primary btn-block mb-3 w-100"),
36
+ ui.hr(),
37
+ ui.h5("Model Parameters"),
38
+ ui.input_slider("n_results", "Documents to retrieve",
39
+ min=0, max=16, value=8),
40
+ ui.input_slider("temperature", "Temperature",
41
+ min=0.1, max=1.0, value=0.7, step=0.1),
42
+ ui.hr(),
43
+ ui.h5("About/Warning"),
44
+ ui.p("This AI advisor is an on-going student research project using a RAG architecture with Python, a Chroma database and the Mistral 7B LLM. It provides answers to questions about American University's academic offerings related to the Math/Stat Department. While it draws from authoritative sources, it is known to produce some answers that are incomplete, inaccurate, or irrelevant. All responses should be checked with the references and one's human advisor.")
45
+ )
46
+ )
47
+ ),
48
+
49
+ # Chat area (right side)
50
+ ui.column(9,
51
+ ui.div(
52
+ {"class": "card h-100"},
53
+ ui.div(
54
+ {"class": "card-body"},
55
+ # Chat UI
56
+ ui.chat_ui("academic_chat", width="100%", height="75vh")
57
+ )
58
+ )
59
+ )
60
+ ),
61
+ # Named parameters after all positional arguments
62
+ title="American University Academic Advisor",
63
+ theme=theme.flatly # flatly is good for color-blind users (high contrast, distinguishable colors)
64
+ )
65
+
66
+ # Define server
67
+ def server(input, output, session):
68
+ # Initialize the Shiny Chat UI
69
+ chat = ui.Chat(
70
+ id="academic_chat",
71
+ messages=[{"role": "assistant", "content": "Hello! I'm your American University Academic Advisor. How can I help you today?"}]
72
+ )
73
+
74
+ # Handle clear chat button
75
+ @reactive.Effect
76
+ @reactive.event(input.clear_chat)
77
+ async def _():
78
+ # Clear Shiny chat UI
79
+ await chat.clear()
80
+ await chat.append_message({"role": "assistant",
81
+ "content": "Chat history cleared. How can I help you today?"})
82
+ # Clear internal chatbot conversation
83
+ clear_conversation()
84
+
85
+ # Define download handler for chat history
86
+ @output
87
+ @render.download
88
+ def download_chat():
89
+ # Get current chat messages
90
+ messages = chat.messages()
91
+
92
+ # Create filename with timestamp
93
+ timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
94
+ filename = f"au_advisor_chat_{timestamp}.md"
95
+
96
+ # Format as Markdown
97
+ md_lines = []
98
+ md_lines.append("# American University Academic Advisor Chat")
99
+ md_lines.append(f"*Exported on: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}*\n")
100
+
101
+ for msg in messages:
102
+ role = msg.get("role", "unknown")
103
+ content = msg.get("content", "")
104
+
105
+ if role == "user":
106
+ md_lines.append(f"## User\n\n{content}\n")
107
+ elif role == "assistant":
108
+ md_lines.append(f"## Assistant\n\n{content}\n")
109
+
110
+ content = "\n".join(md_lines)
111
+
112
+ # Return as bytes
113
+ return filename, "text/markdown", content.encode("utf-8")
114
+
115
+ # Define callback for user input
116
+ @chat.on_user_submit
117
+ async def handle_user_input(user_input: str):
118
+ # Process the query with the chatbot
119
+ result = ask_question(
120
+ query=user_input,
121
+ n_results=input.n_results(),
122
+ temperature=input.temperature()
123
+ )
124
+
125
+ # Format the response with better source descriptions
126
+ response = result["response"].strip()
127
+ if result.get("contexts") and result.get("metadata"):
128
+ sources = []
129
+ for i, (context, meta) in enumerate(zip(result.get("contexts", []), result.get("metadata", []))):
130
+ title = meta.get("title", "") if meta else ""
131
+ url = meta.get("url", "")
132
+
133
+ if title in ["Table Row", "Paragraph"] or not title:
134
+ content_preview = context.strip()[:50] + "..." if len(context) > 50 else context.strip()
135
+ description = content_preview
136
+ else:
137
+ description = title
138
+
139
+ if url:
140
+ sources.append(f"[{i+1}] <a href='{url}' target='_blank'>{description}</a>")
141
+ else:
142
+ sources.append(f"[{i+1}] {description}")
143
+
144
+ if sources and "Sources:" not in response:
145
+ response += "<br><br><strong>Sources:</strong><br>" + "<br>".join(sources)
146
+
147
+ # Add the response to the Shiny chat
148
+ await chat.append_message({"role": "assistant", "content": response})
149
+
150
+ # Create and run the app
151
+ app = App(app_ui, server)
152
+
153
+ if __name__ == "__main__":
154
+ # For Shiny apps, make sure to use the correct host/port
155
+ import shiny
156
+ shiny.run_app(
157
+ host="0.0.0.0", # Important to bind to 0.0.0.0, not localhost
158
+ port=7860, # Must match the exposed port
159
+ launch_browser=False
160
+ )