igortech commited on
Commit
d89f15e
·
verified ·
1 Parent(s): 7b6d917

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +75 -86
app.py CHANGED
@@ -1,9 +1,8 @@
1
  import json
2
  import random
3
- import re
4
- import string
5
  import os
6
  import datetime
 
7
  import gradio as gr
8
 
9
  # -----------------------------
@@ -29,128 +28,116 @@ QUOTES = load_quotes()
29
  # -----------------------------
30
  # Text helpers
31
  # -----------------------------
32
- STOPWORDS = {
33
- "the","a","an","and","or","but","if","then","so","than","to","of","in","on","at","for",
34
- "is","are","was","were","be","being","been","it","that","this","these","those","with",
35
- "as","by","from","about","into","over","after","before","up","down","out"
36
- }
37
 
38
- POS_HINTS = {"good","great","love","like","enjoy","awesome","amazing","nice","positive","best","fantastic","excellent"}
39
- NEG_HINTS = {"bad","hate","dislike","worst","awful","terrible","negative","poor","meh","gross","unsafe","hard","difficult"}
40
-
41
- punct_re = re.compile(f"[{re.escape(string.punctuation)}]")
42
-
43
- def normalize(text: str) -> str:
44
- return punct_re.sub(" ", (text or "").lower())
45
-
46
- def tokenize(text: str):
47
- return [t for t in normalize(text).split() if t and t not in STOPWORDS]
48
-
49
- def infer_sentiment(user_text: str) -> str:
50
- tl = normalize(user_text)
51
- has_pos = any(w in tl for w in POS_HINTS)
52
- has_neg = any(w in tl for w in NEG_HINTS)
53
- if has_pos and not has_neg:
54
- return "positive"
55
- if has_neg and not has_pos:
56
- return "negative"
57
- return "positive" # default
58
-
59
- # -----------------------------
60
- # Retrieval
61
- # -----------------------------
62
  def best_match_quote(category: str, user_text: str) -> str:
63
- if category not in QUOTES:
64
  return f"No quotes found for category '{category}'."
65
- pool = QUOTES[category]
 
66
  if not pool:
67
- return f"No quotes available in '{category}'."
68
 
69
- q_tokens = set(tokenize(user_text))
70
  best_score = -1
71
  best_quote = None
72
-
73
- for item in pool:
74
- quote = item.get("quote", "")
75
- qtoks = set(tokenize(quote))
76
  score = len(q_tokens & qtoks)
77
  if score > best_score:
78
  best_score = score
79
  best_quote = quote
80
-
81
  if best_quote is None or best_score == 0:
82
- return random.choice([item["quote"] for item in pool])
83
  return best_quote
84
 
 
 
 
 
 
 
 
 
 
 
 
 
85
  # -----------------------------
86
  # Gradio callbacks
87
  # -----------------------------
88
  def respond(message, history, category):
89
- if not QUOTES:
90
- bot = "No dataset loaded. Please upload a JSON file first."
91
- history.append({"role": "user", "content": message})
92
- history.append({"role": "assistant", "content": bot})
93
  return "", history
94
-
95
  if not category:
96
- bot = "Please select a category."
97
- history.append({"role": "user", "content": message})
98
- history.append({"role": "assistant", "content": bot})
99
  return "", history
100
-
101
- bot = best_match_quote(category, message)
102
- history.append({"role": "user", "content": message})
103
- history.append({"role": "assistant", "content": bot})
 
 
 
104
  return "", history
105
 
106
  def clear_chat():
107
  return None
108
 
109
- def stage_conversation(msg_text, history):
110
- if not msg_text:
111
  return "Message is empty."
112
  if "staged_responses" not in QUOTES:
113
  QUOTES["staged_responses"] = []
114
- answer = history[-1]["content"] if history else ""
115
- QUOTES["staged_responses"].append({"question": msg_text, "answer": answer})
116
- return "Staged conversation added!"
117
-
118
- def download_staged_responses():
119
- staged = QUOTES.get("staged_responses", [])
120
- if not staged:
121
- return None
122
- filename = "staged_responses.json"
123
- with open(filename, "w", encoding="utf-8") as f:
124
- json.dump(staged, f, indent=2, ensure_ascii=False)
125
- return filename
126
 
127
  def upload_json(filepath):
128
- global QUOTES, DATA_PATH
129
  try:
130
  with open(filepath, "r", encoding="utf-8") as f:
131
  data = json.load(f)
132
  if not isinstance(data, dict):
133
  return gr.update(value="Upload failed: JSON root must be an object."), gr.update(choices=[])
134
  QUOTES = data
135
- DATA_PATH = os.path.basename(filepath)
136
  cats = sorted(list(QUOTES.keys()))
137
- status = f"Loaded {len(cats)} categories from {DATA_PATH}."
138
  return status, gr.update(choices=cats, value=(cats[0] if cats else None))
139
  except Exception as e:
140
  return f"Error loading file: {e}", gr.update(choices=[])
141
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
142
  # -----------------------------
143
  # UI
144
  # -----------------------------
145
  with gr.Blocks() as demo:
146
- gr.Markdown("## 🎓 College Life Chatbot — Staged Responses with CSV/JSON Export")
147
 
148
  initial_categories = sorted(list(QUOTES.keys()))
149
-
150
  with gr.Row():
151
- category = gr.Dropdown(label="Category", choices=initial_categories,
152
- value=(initial_categories[0] if initial_categories else None))
153
-
 
 
 
154
  chatbot = gr.Chatbot(label="Conversation", height=360, type="messages")
155
  msg = gr.Textbox(label="Your message", placeholder="Ask something like: 'Is food good in college?'", autofocus=True)
156
  send = gr.Button("Send")
@@ -158,30 +145,32 @@ with gr.Blocks() as demo:
158
 
159
  with gr.Row():
160
  stage_btn = gr.Button("Stage conversation to category")
161
- download_staged = gr.File(label="Download staged responses")
162
 
163
- uploader = gr.File(label="Upload dataset (.json)", file_types=[".json"], type="filepath")
164
- upload_status = gr.Textbox(label="Upload status", interactive=False)
165
- download_dataset = gr.File(label="Download current dataset")
 
 
166
 
167
  # Wire events
168
  msg.submit(respond, [msg, chatbot, category], [msg, chatbot])
169
  send.click(respond, [msg, chatbot, category], [msg, chatbot])
170
  clear.click(clear_chat, None, chatbot, queue=False)
171
-
172
- stage_btn.click(stage_conversation, [msg, chatbot], upload_status)
173
- download_staged.download(download_staged_responses)
174
-
175
  uploader.upload(upload_json, uploader, [upload_status, category])
176
- download_dataset.download(lambda: DATA_PATH if os.path.exists(DATA_PATH) else None)
 
177
 
178
  # -----------------------------
179
  # Startup log
180
  # -----------------------------
181
  print(f"===== Application Startup at {datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')} =====")
182
  if QUOTES:
183
- for cat, items in QUOTES.items():
184
- print(f" - {cat}: {len(items)} entries")
185
 
186
  if __name__ == "__main__":
187
  demo.launch(server_name="0.0.0.0", server_port=7860)
 
1
  import json
2
  import random
 
 
3
  import os
4
  import datetime
5
+ import csv
6
  import gradio as gr
7
 
8
  # -----------------------------
 
28
  # -----------------------------
29
  # Text helpers
30
  # -----------------------------
31
+ def simple_tokenize(text: str):
32
+ return set(text.lower().replace(".", "").replace(",", "").split())
 
 
 
33
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
34
  def best_match_quote(category: str, user_text: str) -> str:
35
+ if category not in QUOTES or not QUOTES[category]:
36
  return f"No quotes found for category '{category}'."
37
+
38
+ pool = [q["quote"] for q in QUOTES[category] if "quote" in q]
39
  if not pool:
40
+ return f"No quotes available in category '{category}'."
41
 
42
+ q_tokens = simple_tokenize(user_text)
43
  best_score = -1
44
  best_quote = None
45
+ for quote in pool:
46
+ qtoks = simple_tokenize(quote)
 
 
47
  score = len(q_tokens & qtoks)
48
  if score > best_score:
49
  best_score = score
50
  best_quote = quote
 
51
  if best_quote is None or best_score == 0:
52
+ best_quote = random.choice(pool)
53
  return best_quote
54
 
55
+ def generate_three_fold_response(category: str, user_text: str):
56
+ quote_text = best_match_quote(category, user_text)
57
+ # Simple summary: first sentence
58
+ summary = quote_text.split(".")[0] + "."
59
+ # Fusion: merge similar sentences (naive)
60
+ fusion = " ".join(quote_text.split(".")[1:]).strip()
61
+ if not fusion:
62
+ fusion = summary
63
+ # URL placeholder
64
+ url = "No related URL available"
65
+ return summary, fusion, url
66
+
67
  # -----------------------------
68
  # Gradio callbacks
69
  # -----------------------------
70
  def respond(message, history, category):
71
+ if not message.strip():
 
 
 
72
  return "", history
 
73
  if not category:
 
 
 
74
  return "", history
75
+ summary, fusion, url = generate_three_fold_response(category, message)
76
+ bot_message = [
77
+ ("Summary", summary),
78
+ ("What real people say", fusion),
79
+ ("Related URL", url)
80
+ ]
81
+ history.append((message, bot_message))
82
  return "", history
83
 
84
  def clear_chat():
85
  return None
86
 
87
+ def stage_conversation(message, category):
88
+ if not message.strip() or not category:
89
  return "Message is empty."
90
  if "staged_responses" not in QUOTES:
91
  QUOTES["staged_responses"] = []
92
+ QUOTES["staged_responses"].append({"quote": message})
93
+ return "Message staged for download."
 
 
 
 
 
 
 
 
 
 
94
 
95
  def upload_json(filepath):
96
+ global QUOTES
97
  try:
98
  with open(filepath, "r", encoding="utf-8") as f:
99
  data = json.load(f)
100
  if not isinstance(data, dict):
101
  return gr.update(value="Upload failed: JSON root must be an object."), gr.update(choices=[])
102
  QUOTES = data
 
103
  cats = sorted(list(QUOTES.keys()))
104
+ status = f"Loaded {len(cats)} categories from {filepath}."
105
  return status, gr.update(choices=cats, value=(cats[0] if cats else None))
106
  except Exception as e:
107
  return f"Error loading file: {e}", gr.update(choices=[])
108
 
109
+ def download_dataset():
110
+ tmp = "quotes_export.json"
111
+ with open(tmp, "w", encoding="utf-8") as f:
112
+ json.dump(QUOTES, f, indent=2, ensure_ascii=False)
113
+ return tmp
114
+
115
+ def download_conversation(history):
116
+ tmp = "conversation_export.csv"
117
+ with open(tmp, "w", newline="", encoding="utf-8") as f:
118
+ writer = csv.writer(f)
119
+ writer.writerow(["User", "Summary", "What real people say", "Related URL"])
120
+ for user_msg, bot_msgs in history:
121
+ summary = bot_msgs[0][1] if len(bot_msgs) > 0 else ""
122
+ fusion = bot_msgs[1][1] if len(bot_msgs) > 1 else ""
123
+ url = bot_msgs[2][1] if len(bot_msgs) > 2 else ""
124
+ writer.writerow([user_msg, summary, fusion, url])
125
+ return tmp
126
+
127
  # -----------------------------
128
  # UI
129
  # -----------------------------
130
  with gr.Blocks() as demo:
131
+ gr.Markdown("## 🎓 College Life Chatbot — 3-Fold Responses")
132
 
133
  initial_categories = sorted(list(QUOTES.keys()))
 
134
  with gr.Row():
135
+ category = gr.Dropdown(
136
+ label="Category",
137
+ choices=initial_categories,
138
+ value=(initial_categories[0] if initial_categories else None)
139
+ )
140
+
141
  chatbot = gr.Chatbot(label="Conversation", height=360, type="messages")
142
  msg = gr.Textbox(label="Your message", placeholder="Ask something like: 'Is food good in college?'", autofocus=True)
143
  send = gr.Button("Send")
 
145
 
146
  with gr.Row():
147
  stage_btn = gr.Button("Stage conversation to category")
148
+ stage_status = gr.Textbox(label="Stage status", interactive=False)
149
 
150
+ with gr.Row():
151
+ uploader = gr.File(label="Upload dataset (.json)", file_types=[".json"], type="filepath")
152
+ upload_status = gr.Textbox(label="Upload status", interactive=False)
153
+ dataset_download = gr.File(label="Download current dataset")
154
+ convo_download = gr.File(label="Download conversation CSV")
155
 
156
  # Wire events
157
  msg.submit(respond, [msg, chatbot, category], [msg, chatbot])
158
  send.click(respond, [msg, chatbot, category], [msg, chatbot])
159
  clear.click(clear_chat, None, chatbot, queue=False)
160
+
161
+ stage_btn.click(stage_conversation, [msg, category], stage_status)
162
+
 
163
  uploader.upload(upload_json, uploader, [upload_status, category])
164
+ dataset_download.download(download_dataset)
165
+ convo_download.download(lambda history: download_conversation(history), inputs=chatbot)
166
 
167
  # -----------------------------
168
  # Startup log
169
  # -----------------------------
170
  print(f"===== Application Startup at {datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')} =====")
171
  if QUOTES:
172
+ for cat, quotes in QUOTES.items():
173
+ print(f" - {cat}: {len(quotes)} entries")
174
 
175
  if __name__ == "__main__":
176
  demo.launch(server_name="0.0.0.0", server_port=7860)