pikam00 commited on
Commit
3198ff8
·
verified ·
1 Parent(s): 7832a06

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +161 -92
app.py CHANGED
@@ -1,117 +1,186 @@
1
  import gradio as gr
2
- import random
3
  from huggingface_hub import InferenceClient
4
- # import lines go at the top: any libraries I need to import go up here ^^
5
  from sentence_transformers import SentenceTransformer
6
  import torch
 
7
 
 
8
  client = InferenceClient("HuggingFaceH4/zephyr-7b-beta")
 
9
 
10
- # Step 1
11
- with open("Untitled document.txt", "r", encoding="utf-8") as f:
12
- skincare_text = f.read()
13
 
14
- # Step 2: Preprocess text into sentence chunks
15
  def preprocess_text(text):
16
- cleaned_text = text.strip()
17
- sentences = [s.strip() for s in cleaned_text.split('.') if s.strip()]
18
- sentence_chunks = [s.strip() for s in sentences if len(s.strip()) > 10]
19
-
20
  combined_chunks = []
21
  for i in range(0, len(sentences), 2):
22
  chunk = '. '.join(sentences[i:i+3]).strip()
23
  if len(chunk) > 20:
24
  combined_chunks.append(chunk)
25
-
26
- paragraphs = [p.strip() for p in cleaned_text.split('\n\n') if p.strip()]
27
  paragraph_chunks = [p for p in paragraphs if len(p) > 30]
28
-
29
  all_chunks = sentence_chunks + combined_chunks + paragraph_chunks
30
-
31
- seen = set()
32
- final_chunks = []
33
- for chunk in all_chunks:
34
- if chunk not in seen and len(chunk) > 15:
35
- seen.add(chunk)
36
- final_chunks.append(chunk)
37
-
38
- print(f"Created {len(final_chunks)} chunks using advanced strategy")
39
- print(f"Sample chunks: {final_chunks[:3]}")
40
  return final_chunks
41
 
42
- cleaned_chunks = preprocess_text(skincare_text)
43
-
44
- # Step 3: Convert chunks into embeddings
45
- model = SentenceTransformer('all-MiniLM-L6-v2')
46
-
47
- def create_embeddings(text_chunks):
48
- chunk_embeddings = model.encode(text_chunks, convert_to_tensor=True)
49
- print(f"Embeddings shape: {chunk_embeddings.shape}")
50
- return chunk_embeddings
51
-
52
- chunk_embeddings = create_embeddings(cleaned_chunks)
53
-
54
- # Step 4: Retrieve top matching chunks
55
- def get_top_chunks(query, chunk_embeddings, text_chunks, top_k=3):
56
- query_embedding = model.encode(query, convert_to_tensor=True)
57
- query_norm = query_embedding / query_embedding.norm()
58
- chunks_norm = chunk_embeddings / chunk_embeddings.norm(dim=1, keepdim=True)
59
- similarities = torch.matmul(chunks_norm, query_norm)
60
- top_scores, top_indices = torch.topk(similarities, k=min(top_k, len(text_chunks)))
61
-
62
- results = []
63
- for i, idx in enumerate(top_indices):
64
- score = top_scores[i].item()
65
- if score > 0.3: # Only include reasonably relevant chunks
66
- results.append(text_chunks[idx])
67
-
68
- return results, top_scores[:len(results)]
69
-
70
- # Step 5: Relevance checker
71
- def is_skincare_related(query):
72
- skincare_keywords = [
73
- 'skin', 'skincare', 'acne', 'wrinkles', 'moisturizer', 'cleanser',
74
- 'sunscreen', 'serum', 'retinol', 'vitamin', 'dry', 'oily', 'sensitive',
75
- 'aging', 'pores', 'blackheads', 'routine', 'face', 'facial', 'beauty',
76
- 'dermatology', 'cosmetic', 'cream', 'lotion', 'toner', 'exfoliate',
77
- 'hydration', 'anti-aging', 'blemish', 'spot', 'dark circles'
78
- ]
79
-
80
- query_lower = query.lower()
81
- return any(keyword in query_lower for keyword in skincare_keywords)
82
-
83
- queries = [
84
- "Consistent skincare routine",
85
- "Applying sunscreen daily",
86
- "Choosing products that match your skin type"
87
- ]
88
-
89
- for q in queries:
90
- print(f"\nQuery: {q}")
91
- results = get_top_chunks(q, chunk_embeddings, cleaned_chunks)
92
- for idx, res in enumerate(results, 1):
93
- print(f"Result {idx}: {res}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
94
 
95
  def respond(message, history):
96
- top_results = get_top_chunks(message, chunk_embeddings, cleaned_chunks)
97
- print(top_results)
98
-
99
- messages = [{"role": "system", "content": f"You are a friendly chatbot. You give people advice about skincare. Base your response on the following information: {top_results}"}]
100
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
101
  if history:
102
- messages.extend(history)
103
- messages.append({"role": "user", "content": message})
104
-
105
- response = client.chat_completion(messages, max_tokens=100)
106
- return response['choices'][0]['message']['content'].strip()
107
 
108
- def echo(message, history):
109
- return message
110
 
111
- def yes_or_no(message, history):
112
- return random.choice(['Yes', 'No', 'Maybe', 'Ask Again'])
113
 
114
- chatbot = gr.ChatInterface(respond)
115
- # defining my chatbot so that the user can interact and see their conversation history and send new messages
 
 
 
 
116
 
117
- chatbot.launch()
 
 
1
  import gradio as gr
 
2
  from huggingface_hub import InferenceClient
 
3
  from sentence_transformers import SentenceTransformer
4
  import torch
5
+ import random
6
 
7
+ # Models
8
  client = InferenceClient("HuggingFaceH4/zephyr-7b-beta")
9
+ embedder = SentenceTransformer("all-MiniLM-L6-v2")
10
 
11
+ # Load corpus
12
+ with open("journal.txt", "r", encoding="utf-8") as f:
13
+ journal_text = f.read()
14
 
15
+ # Preprocess into chunks
16
  def preprocess_text(text):
17
+ cleaned = text.strip()
18
+ sentences = [s.strip() for s in cleaned.split('.') if s.strip()]
19
+ sentence_chunks = [s for s in sentences if len(s) > 10]
 
20
  combined_chunks = []
21
  for i in range(0, len(sentences), 2):
22
  chunk = '. '.join(sentences[i:i+3]).strip()
23
  if len(chunk) > 20:
24
  combined_chunks.append(chunk)
25
+ paragraphs = [p.strip() for p in cleaned.split('\n\n') if p.strip()]
 
26
  paragraph_chunks = [p for p in paragraphs if len(p) > 30]
 
27
  all_chunks = sentence_chunks + combined_chunks + paragraph_chunks
28
+ seen, final_chunks = set(), []
29
+ for ch in all_chunks:
30
+ if ch not in seen and len(ch) > 15:
31
+ seen.add(ch)
32
+ final_chunks.append(ch)
33
+ print(f"Prepared {len(final_chunks)} chunks")
 
 
 
 
34
  return final_chunks
35
 
36
+ chunks = preprocess_text(journal_text)
37
+
38
+ # Embeddings
39
+ embeddings = embedder.encode(chunks, convert_to_tensor=True)
40
+ print(f"Embeddings ready: {embeddings.shape}")
41
+
42
+ # Retrieval
43
+ def get_top_chunks(query, embeddings, text_chunks, top_k=5):
44
+ if not query:
45
+ return [], []
46
+ q = embedder.encode(query, convert_to_tensor=True)
47
+ q = q / q.norm()
48
+ M = embeddings / embeddings.norm(dim=1, keepdim=True)
49
+ sims = torch.matmul(M, q)
50
+ k = min(top_k, len(text_chunks))
51
+ scores, idxs = torch.topk(sims, k=k)
52
+ results, kept_scores = [], []
53
+ for i, idx in enumerate(idxs):
54
+ sc = scores[i].item()
55
+ if sc > 0.25:
56
+ results.append(text_chunks[int(idx)])
57
+ kept_scores.append(sc)
58
+ return results, kept_scores
59
+
60
+ def join_context(chunks_list, max_chars=1000):
61
+ out = ""
62
+ for c in chunks_list:
63
+ c = c.strip()
64
+ if len(out) + len(c) + 2 > max_chars:
65
+ break
66
+ out += (("\n\n" if out else "") + c)
67
+ return out
68
+
69
+ # Safety net
70
+ CRISIS_TERMS = ["suicide","kill myself","end my life","self-harm","hurt myself","overdose","harm others","kill someone"]
71
+ def is_crisis(message: str) -> bool:
72
+ m = (message or "").lower()
73
+ return any(term in m for term in CRISIS_TERMS)
74
+
75
+ # Personas + break ideas
76
+ BREAK_IDEAS = {
77
+ "Sage": [
78
+ "Look out the window for 1 minute and notice what moves.",
79
+ "Breathe in 4, out 6, slowly.",
80
+ "Think of 3 natural places you enjoy."
81
+ ],
82
+ "Buddy": [
83
+ "Stand and stretch for 20 seconds.",
84
+ "Send a kind message to a friend.",
85
+ "Play a short upbeat song."
86
+ ],
87
+ "Monk": [
88
+ "Close your eyes, breathe 4 in, hold 4, breathe 4 out.",
89
+ "Choose one small task to finish after this.",
90
+ "Turn your phone face down for a minute."
91
+ ],
92
+ "Librarian": [
93
+ "Write one sentence starting with: 'Today I noticed...'.",
94
+ "Put 3 things neatly in place.",
95
+ "Organize a small space for 1 minute."
96
+ ],
97
+ "Cozy": [
98
+ "Make a warm drink or sip water slowly.",
99
+ "Wrap yourself in a blanket for 1 minute.",
100
+ "Notice 3 soft textures nearby."
101
+ ],
102
+ }
103
+
104
+ PERSONALITY_TONES = {
105
+ "Sage": "calm, thoughtful, nature imagery",
106
+ "Buddy": "upbeat, encouraging, simple language",
107
+ "Monk": "minimalist, focused, mindful",
108
+ "Librarian": "gentle, organized, caring",
109
+ "Cozy": "warm, comforting, home-like"
110
+ }
111
+
112
+ CURRENT_PERSONA = {"name": "Cozy"}
113
+
114
+ def set_persona(name: str) -> str:
115
+ names = list(PERSONALITY_TONES.keys())
116
+ lookup = {n.lower(): n for n in names}
117
+ key = (name or "").strip().lower()
118
+ if key in lookup:
119
+ CURRENT_PERSONA["name"] = lookup[key]
120
+ return f"Persona set to {CURRENT_PERSONA['name']}."
121
+ else:
122
+ return "Unknown persona. Options: Sage, Buddy, Monk, Librarian, Cozy."
123
+
124
+ def pick_break() -> str:
125
+ persona = CURRENT_PERSONA["name"]
126
+ return random.choice(BREAK_IDEAS.get(persona, BREAK_IDEAS["Cozy"]))
127
+
128
+ def build_system_message(context_block: str) -> str:
129
+ persona = CURRENT_PERSONA["name"]
130
+ tone = PERSONALITY_TONES.get(persona, PERSONALITY_TONES["Cozy"])
131
+ return (
132
+ "You are Otium, a warm journaling buddy. Not medical advice. "
133
+ f"Adopt the persona {persona}. Style: {tone}. "
134
+ "Use plain, kind language. Reflect feelings from the user’s message, "
135
+ "ask 1–2 gentle follow-up questions, keep replies short (3–6 sentences). "
136
+ "Offer exactly one tiny break idea. Avoid clinical diagnoses or medical guidance.\n\n"
137
+ f"Helpful snippets from the user's content:\n{context_block}"
138
+ )
139
+
140
+ # Chat handler
141
+ HELP_TEXT = (
142
+ "Type `/personas` to see options, or `/persona NAME` to switch.\n"
143
+ "Choices: Sage, Buddy, Monk, Librarian, Cozy."
144
+ )
145
 
146
  def respond(message, history):
147
+ if (message or "").strip().lower() == "/personas":
148
+ return HELP_TEXT
149
+ if (message or "").strip().lower().startswith("/persona"):
150
+ parts = message.split(maxsplit=1)
151
+ if len(parts) == 1:
152
+ return "Usage: `/persona NAME` — " + HELP_TEXT
153
+ return set_persona(parts[1])
154
+
155
+ if is_crisis(message):
156
+ return (
157
+ "I’m glad you reached out. I’m not a crisis service, but help is available:\n"
158
+ "• U.S.: call or text 988 (988lifeline.org)\n"
159
+ "• Elsewhere: contact local emergency services."
160
+ )
161
+
162
+ top_chunks, _ = get_top_chunks(message, embeddings, chunks, top_k=5)
163
+ context_block = join_context(top_chunks)
164
+ system_msg = build_system_message(context_block)
165
+
166
+ messages = [{"role": "system", "content": system_msg}]
167
  if history:
168
+ for u, a in history:
169
+ if u: messages.append({"role": "user", "content": u})
170
+ if a: messages.append({"role": "assistant", "content": a})
171
+ messages.append({"role": "user", "content": message or ""})
 
172
 
173
+ resp = client.chat_completion(messages=messages, max_tokens=220, temperature=0.7)
174
+ text = resp["choices"][0]["message"]["content"].strip()
175
 
176
+ return f"{text}\n\n**Tiny break idea:** {pick_break()}"
 
177
 
178
+ # Minimal UI
179
+ chatbot = gr.ChatInterface(
180
+ respond,
181
+ title="Otium — A Friendly Check-In",
182
+ description="Talk about your day. Otium reflects feelings and suggests tiny breaks. Type `/personas` for options."
183
+ )
184
 
185
+ if __name__ == "__main__":
186
+ chatbot.launch()