daniellegauthier commited on
Commit
2de35e1
·
verified ·
1 Parent(s): ca33b05

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +249 -115
app.py CHANGED
@@ -1,21 +1,16 @@
1
  import os
2
- import io
3
  from typing import Dict, Tuple, List
4
 
5
  import nltk
6
  import spacy
7
  import torch
8
- import matplotlib.pyplot as plt
9
  import torch.nn.functional as F
 
10
  import pandas as pd
11
  import gradio as gr
12
 
13
- from transformers import (
14
- pipeline,
15
- AutoTokenizer,
16
- AutoModelForSequenceClassification,
17
- )
18
-
19
  from sentence_transformers import SentenceTransformer, util
20
 
21
  # =========================
@@ -42,11 +37,7 @@ nlp = ensure_spacy()
42
  # 1) Models (cached)
43
  # =========================
44
  sbert_model = SentenceTransformer("all-MiniLM-L6-v2")
45
-
46
- bert_sentiment = pipeline(
47
- "sentiment-analysis",
48
- model="distilbert-base-uncased-finetuned-sst-2-english"
49
- )
50
 
51
  emotion_model_name = "j-hartmann/emotion-english-distilroberta-base"
52
  emotion_tokenizer = AutoTokenizer.from_pretrained(emotion_model_name)
@@ -88,13 +79,11 @@ GNH_COLORS: Dict[str, str] = {
88
  }
89
 
90
  # =========================
91
- # 3) Pathway data
92
- # - Reads phrases from bottom of "la matrice plus.csv"
93
- # - Maps sequence keys -> phrase & image path
94
  # =========================
95
  CSV_PATH = "la matrice plus.csv"
96
 
97
- # Aliases so your UI label → CSV row & image file
98
  SEQUENCE_ALIASES = {
99
  "Auto (recommend)": "auto",
100
  "Direct": "direct",
@@ -124,39 +113,57 @@ SEQUENCE_IMAGE_FILES = {
124
  "sad": "sad pathway.png"
125
  }
126
 
127
- def load_pathway_phrases(csv_path: str) -> Dict[str, str]:
128
- """
129
- Build pathway phrase text by concatenating non-null columns
130
- from 'matrice1' onward for each sequence row at the bottom of the sheet.
131
- """
132
  df = pd.read_csv(csv_path)
 
 
 
133
  phrases: Dict[str, str] = {}
134
- # We consider any row whose 'color' is one of our known sequences
135
- valid_keys = set(SEQUENCE_IMAGE_FILES.keys()) | {"spiritual", "sad"}
136
- rows = df[df["color"].astype(str).str.lower().isin(valid_keys)].copy()
 
 
137
 
138
  for _, row in rows.iterrows():
139
  key = str(row["color"]).strip().lower()
140
- # join from column index 4 onward (matrice1 .. last "Unnamed")
141
- text = " ".join(
142
- str(v) for v in row.iloc[4:].tolist() if pd.notna(v)
143
- ).strip()
144
- # clean duplicate/missing spaces
145
- text = " ".join(text.split())
146
- phrases[key] = text
147
-
148
- return phrases
149
 
150
- PATHWAY_PHRASES = load_pathway_phrases(CSV_PATH)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
151
 
152
  def sequence_to_image_path(seq_key: str) -> str | None:
153
  fname = SEQUENCE_IMAGE_FILES.get(seq_key)
154
- if fname and os.path.exists(fname):
155
- return fname
156
- return None # image optional—app will handle gracefully
157
 
158
  # =========================
159
- # 4) Core scoring functions
160
  # =========================
161
  def classify_emotion(text: str) -> Tuple[str, float]:
162
  inputs = emotion_tokenizer(text, return_tensors="pt", truncation=True)
@@ -164,8 +171,8 @@ def classify_emotion(text: str) -> Tuple[str, float]:
164
  logits = emotion_model(**inputs).logits
165
  probs = F.softmax(logits, dim=1).squeeze()
166
  labels = emotion_model.config.id2label
167
- top_idx = int(torch.argmax(probs).item())
168
- return labels[top_idx], float(probs[top_idx].item())
169
 
170
  def score_sentiment(text: str) -> float:
171
  out = bert_sentiment(text[:512])[0]
@@ -176,7 +183,7 @@ def score_sentiment(text: str) -> float:
176
  def score_accomplishment(text: str) -> float:
177
  doc = nlp(text)
178
  score = 5.0
179
- key_phrases = {"finally", "told", "decided", "quit", "refused", "stood", "walked", "walked away"}
180
  for token in doc:
181
  if token.text.lower() in key_phrases:
182
  score += 1.5
@@ -184,48 +191,108 @@ def score_accomplishment(text: str) -> float:
184
  score += 0.5
185
  return round(min(10, max(1, score)), 2)
186
 
187
- def semantic_indicator_mapping(text: str, sentiment_score: float, sentiment_weight: float = 0.3) -> Dict[str, float]:
188
- text_vec = sbert_model.encode(text, convert_to_tensor=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
189
  out: Dict[str, float] = {}
190
  for label, desc in GNH_DOMAINS.items():
191
- desc_vec = sbert_model.encode(desc, convert_to_tensor=True)
192
- sim = float(util.cos_sim(text_vec, desc_vec).item())
193
  sim = max(0.0, min(1.0, sim))
194
  blended = (1 - sentiment_weight) * sim + sentiment_weight * (sentiment_score / 10.0)
195
  out[label] = round(blended, 3)
196
  return dict(sorted(out.items(), key=lambda kv: -kv[1]))
197
 
198
  # =========================
199
- # 5) Pathway selection logic
200
  # =========================
201
- def suggest_sequence(text: str) -> Tuple[str, float]:
 
 
 
202
  """
203
- Choose the best pathway by SBERT similarity between the input text
204
- and each pathway phrase from the CSV.
205
- Returns (sequence_key, similarity_score).
206
  """
207
- if not PATHWAY_PHRASES:
208
- return "direct", 0.0
209
- text_vec = sbert_model.encode(text, convert_to_tensor=True)
210
- best_key, best_sim = None, -1.0
211
- for key, phrase in PATHWAY_PHRASES.items():
212
- if not phrase:
213
- continue
214
- phrase_vec = sbert_model.encode(phrase, convert_to_tensor=True)
215
- sim = float(util.cos_sim(text_vec, phrase_vec).item())
216
- if sim > best_sim:
217
- best_key, best_sim = key, sim
218
- return (best_key or "direct"), best_sim
219
-
220
- def pathway_payload(seq_key: str) -> Tuple[str, str | None]:
221
- """Return (phrase, image_path) for a given sequence key."""
222
- key = seq_key.strip().lower()
223
- phrase = PATHWAY_PHRASES.get(key, "")
224
- img = sequence_to_image_path(key)
225
- return phrase, img
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
226
 
227
  # =========================
228
- # 6) Plot helper (GNH bars)
229
  # =========================
230
  def indicators_plot(indicators: Dict[str, float]):
231
  labels = list(indicators.keys())
@@ -234,64 +301,138 @@ def indicators_plot(indicators: Dict[str, float]):
234
  fig = plt.figure(figsize=(8, 5))
235
  plt.barh(labels, values, color=colors)
236
  plt.gca().invert_yaxis()
237
- plt.title("GNH Indicator Similarity (Sentiment-weighted)")
238
  plt.xlabel("Score")
239
  plt.tight_layout()
240
  return fig
241
 
242
  # =========================
243
- # 7) Gradio app
244
  # =========================
245
  SEQ_CHOICES = list(SEQUENCE_ALIASES.keys())
246
-
247
- def analyze(text: str, seq_choice: str):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
248
  if not text or not text.strip():
249
- return (
250
- 5.0, "neutral (0.0)", 5.0,
251
- "—", None,
252
- "{}", None, "—", 0.0
253
- )
254
 
255
  # 1) scores
256
  sentiment = score_sentiment(text)
257
  emotion, emo_conf = classify_emotion(text)
258
  accomplishment = score_accomplishment(text)
259
- indicators = semantic_indicator_mapping(text, sentiment)
260
- fig = indicators_plot(indicators)
261
 
262
- # 2) pathway
263
- chosen_key = SEQUENCE_ALIASES.get(seq_choice, "auto")
264
- auto_key, auto_sim = suggest_sequence(text) if chosen_key == "auto" else (chosen_key, None)
265
- final_key = auto_key
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
266
 
267
- phrase, img_path = pathway_payload(final_key)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
268
 
269
- # outputs
270
  top5 = list(indicators.items())[:5]
271
  top5_str = "\n".join(f"{k}: {v}" for k, v in top5)
272
 
 
 
 
 
 
 
 
 
273
  return (
274
- sentiment,
275
- f"{emotion} ({emo_conf:.3f})",
276
- accomplishment,
277
- final_key, # selected sequence key
278
- phrase or "—",
279
- top5_str,
280
- fig,
281
- img_path, # pathway image (optional)
282
- auto_key if chosen_key == "auto" else seq_choice,
283
- float(auto_sim or 0.0)
284
  )
285
 
286
  with gr.Blocks(title="RGB Root Matriz Color Plotter") as demo:
287
- gr.Markdown("## La Matriz Consulting, feat. BERT Emotion + GNH + Pathway\n"
288
  "Type a phrase. Choose a **Sequence** or keep **Auto** to recommend a pathway. "
289
  "You’ll get sentiment, emotion, accomplishment, GNH bars, and the pathway phrase + image from the dataset.")
290
 
291
  with gr.Row():
292
- inp = gr.Textbox(lines=4, label="Input text", placeholder="e.g., I finally quit my toxic job and feel lighter.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
293
  with gr.Row():
294
- seq = gr.Dropdown(choices=SEQ_CHOICES, value="Auto (recommend)", label="Sequence choice")
 
295
 
296
  btn = gr.Button("Analyze", variant="primary")
297
 
@@ -301,30 +442,23 @@ with gr.Blocks(title="RGB Root Matriz Color Plotter") as demo:
301
  acc = gr.Number(label="Accomplishment (1–10)")
302
 
303
  with gr.Row():
304
- seq_used = gr.Text(label="Chosen pathway key")
305
  phrase_out = gr.Text(label="Pathway phrase")
306
 
307
  with gr.Row():
308
  gnh_top = gr.Text(label="Top GNH Indicators (Top 5)")
309
- gnh_plot = gr.Plot(label="GNH Similarity")
310
 
311
  with gr.Row():
312
  pathway_img = gr.Image(label="Pathway image", type="filepath")
313
- auto_meta = gr.Text(label="Auto selection (key, similarity)")
314
-
315
- def _wrap_analyze(text, seq_choice):
316
- result = analyze(text, seq_choice)
317
- # build auto meta text
318
- auto_key = result[-2]
319
- auto_sim = result[-1]
320
- meta = f"{auto_key} (similarity={auto_sim:.3f})" if seq_choice == "Auto (recommend)" else "—"
321
- return (*result[:-2], meta)
322
 
323
  btn.click(
324
- fn=_wrap_analyze,
325
- inputs=[inp, seq],
326
- outputs=[sent, emo, acc, seq_used, phrase_out, gnh_top, gnh_plot, pathway_img, auto_meta]
327
  )
328
 
329
  if __name__ == "__main__":
330
  demo.launch()
 
 
 
1
  import os
2
+ import re
3
  from typing import Dict, Tuple, List
4
 
5
  import nltk
6
  import spacy
7
  import torch
 
8
  import torch.nn.functional as F
9
+ import matplotlib.pyplot as plt
10
  import pandas as pd
11
  import gradio as gr
12
 
13
+ from transformers import pipeline, AutoTokenizer, AutoModelForSequenceClassification
 
 
 
 
 
14
  from sentence_transformers import SentenceTransformer, util
15
 
16
  # =========================
 
37
  # 1) Models (cached)
38
  # =========================
39
  sbert_model = SentenceTransformer("all-MiniLM-L6-v2")
40
+ bert_sentiment = pipeline("sentiment-analysis", model="distilbert-base-uncased-finetuned-sst-2-english")
 
 
 
 
41
 
42
  emotion_model_name = "j-hartmann/emotion-english-distilroberta-base"
43
  emotion_tokenizer = AutoTokenizer.from_pretrained(emotion_model_name)
 
79
  }
80
 
81
  # =========================
82
+ # 3) Pathways (CSV + images)
 
 
83
  # =========================
84
  CSV_PATH = "la matrice plus.csv"
85
 
86
+ # UI label → internal key
87
  SEQUENCE_ALIASES = {
88
  "Auto (recommend)": "auto",
89
  "Direct": "direct",
 
113
  "sad": "sad pathway.png"
114
  }
115
 
116
+ # ---- load pathway phrases + colors (many-to-many) ----
117
+ def load_pathway_info(csv_path: str):
 
 
 
118
  df = pd.read_csv(csv_path)
119
+ keys_we_know = set(SEQUENCE_ALIASES.values()) - {"auto"}
120
+ rows = df[df["color"].astype(str).str.lower().isin(keys_we_know)].copy()
121
+
122
  phrases: Dict[str, str] = {}
123
+ seq_to_colors: Dict[str, List[str]] = {}
124
+ color_to_seqs: Dict[str, List[str]] = {}
125
+
126
+ # columns to stitch into a phrase (all except color/r/g/b)
127
+ cols_for_phrase = [c for c in df.columns if c not in ("color", "r", "g", "b")]
128
 
129
  for _, row in rows.iterrows():
130
  key = str(row["color"]).strip().lower()
 
 
 
 
 
 
 
 
 
131
 
132
+ # parse colors list from column 'r' (e.g., "red, orange")
133
+ colors_field = str(row.get("r", "") or "")
134
+ colors = [c.strip().lower() for c in re.split(r"[,\s]+", colors_field) if c.strip()]
135
+ colors = list(dict.fromkeys(colors)) # dedupe, keep order
136
+ seq_to_colors[key] = colors
137
+
138
+ for c in colors:
139
+ color_to_seqs.setdefault(c, [])
140
+ if key not in color_to_seqs[c]:
141
+ color_to_seqs[c].append(key)
142
+
143
+ # phrase: join all non-null from the other columns (keeps "let's ..." fragments etc.)
144
+ vals = []
145
+ for c in cols_for_phrase:
146
+ v = row.get(c)
147
+ if pd.notna(v):
148
+ vs = str(v).strip()
149
+ if vs and vs.lower() != "nan":
150
+ vals.append(vs)
151
+ phrase = " ".join(vals)
152
+ phrase = " ".join(phrase.split())
153
+ phrases[key] = phrase
154
+
155
+ # color vocab for parsing "red-pathway" in text
156
+ color_vocab = sorted(color_to_seqs.keys())
157
+ return phrases, seq_to_colors, color_to_seqs, color_vocab
158
+
159
+ PATHWAY_PHRASES, SEQ_TO_COLORS, COLOR_TO_SEQS, COLOR_VOCAB = load_pathway_info(CSV_PATH)
160
 
161
  def sequence_to_image_path(seq_key: str) -> str | None:
162
  fname = SEQUENCE_IMAGE_FILES.get(seq_key)
163
+ return fname if (fname and os.path.exists(fname)) else None
 
 
164
 
165
  # =========================
166
+ # 4) Scoring
167
  # =========================
168
  def classify_emotion(text: str) -> Tuple[str, float]:
169
  inputs = emotion_tokenizer(text, return_tensors="pt", truncation=True)
 
171
  logits = emotion_model(**inputs).logits
172
  probs = F.softmax(logits, dim=1).squeeze()
173
  labels = emotion_model.config.id2label
174
+ idx = int(torch.argmax(probs).item())
175
+ return labels[idx], float(probs[idx].item())
176
 
177
  def score_sentiment(text: str) -> float:
178
  out = bert_sentiment(text[:512])[0]
 
183
  def score_accomplishment(text: str) -> float:
184
  doc = nlp(text)
185
  score = 5.0
186
+ key_phrases = {"finally", "told", "decided", "quit", "refused", "stood", "walked", "walked away", "returned", "return"}
187
  for token in doc:
188
  if token.text.lower() in key_phrases:
189
  score += 1.5
 
191
  score += 0.5
192
  return round(min(10, max(1, score)), 2)
193
 
194
+ # =========================
195
+ # 5) Pathway-aware vector math
196
+ # =========================
197
+ def encode_text(t: str):
198
+ return sbert_model.encode(t, convert_to_tensor=True)
199
+
200
+ def composite_vector(
201
+ base_text: str,
202
+ boost_terms: List[str],
203
+ boost_seq_keys: List[str],
204
+ limit_seq_keys: List[str],
205
+ boost_w: float = 0.6,
206
+ limit_w: float = 0.6,
207
+ ):
208
+ v = encode_text(base_text)
209
+
210
+ for term in boost_terms:
211
+ t = term.strip()
212
+ if t:
213
+ v = v + boost_w * encode_text(t)
214
+
215
+ for key in boost_seq_keys:
216
+ phrase = PATHWAY_PHRASES.get(key, "")
217
+ if phrase:
218
+ v = v + boost_w * encode_text(phrase)
219
+
220
+ for key in limit_seq_keys:
221
+ phrase = PATHWAY_PHRASES.get(key, "")
222
+ if phrase:
223
+ v = v - limit_w * encode_text(phrase)
224
+
225
+ return v
226
+
227
+ def best_sequence_for_vector(vec) -> Tuple[str, float]:
228
+ best_key, best_sim = None, -1.0
229
+ for key, phrase in PATHWAY_PHRASES.items():
230
+ if not phrase:
231
+ continue
232
+ sim = float(util.cos_sim(vec, encode_text(phrase)).item())
233
+ if sim > best_sim:
234
+ best_key, best_sim = key, sim
235
+ return best_key or "direct", best_sim
236
+
237
+ def semantic_indicator_mapping_from_vec(vec, sentiment_score: float, sentiment_weight: float = 0.3) -> Dict[str, float]:
238
  out: Dict[str, float] = {}
239
  for label, desc in GNH_DOMAINS.items():
240
+ desc_vec = encode_text(desc)
241
+ sim = float(util.cos_sim(vec, desc_vec).item())
242
  sim = max(0.0, min(1.0, sim))
243
  blended = (1 - sentiment_weight) * sim + sentiment_weight * (sentiment_score / 10.0)
244
  out[label] = round(blended, 3)
245
  return dict(sorted(out.items(), key=lambda kv: -kv[1]))
246
 
247
  # =========================
248
+ # 6) Color cues from free text (many-to-many)
249
  # =========================
250
+ _COLOR_RE = re.compile(r"\b(" + "|".join(map(re.escape, COLOR_VOCAB)) + r")\s*(?:\-?\s*pathway)?\b", re.I)
251
+ _LIMIT_CUES = {"limit", "reduce", "lessen", "avoid", "diminish", "lower", "constrain", "suppress"}
252
+
253
+ def infer_color_directives(text: str) -> Tuple[List[str], List[str]]:
254
  """
255
+ Parse '... limit ... red-pathway ...' limit 'red'
256
+ otherwise treat mentioned colors as boost.
257
+ Returns (boost_colors, limit_colors) as lists of color strings.
258
  """
259
+ tokens = re.findall(r"\w+|\S", text.lower())
260
+ idxs = []
261
+ for m in _COLOR_RE.finditer(text):
262
+ start = m.start()
263
+ # find token index closest to this span
264
+ char_count = 0
265
+ tok_index = 0
266
+ for i, tok in enumerate(tokens):
267
+ char_count += len(tok) + 1 # crude but ok
268
+ if char_count > start:
269
+ tok_index = i
270
+ break
271
+ idxs.append((tok_index, m.group(1).lower()))
272
+
273
+ boost_colors, limit_colors = [], []
274
+ for idx, col in idxs:
275
+ # look back a small window for a limit cue
276
+ window = tokens[max(0, idx-4):idx]
277
+ if any(w in _LIMIT_CUES for w in window):
278
+ limit_colors.append(col)
279
+ else:
280
+ boost_colors.append(col)
281
+ # dedupe
282
+ boost_colors = list(dict.fromkeys(boost_colors))
283
+ limit_colors = list(dict.fromkeys(limit_colors))
284
+ return boost_colors, limit_colors
285
+
286
+ def colors_to_seq_keys(colors: List[str]) -> List[str]:
287
+ keys: List[str] = []
288
+ for c in colors:
289
+ for k in COLOR_TO_SEQS.get(c, []):
290
+ if k not in keys:
291
+ keys.append(k)
292
+ return keys
293
 
294
  # =========================
295
+ # 7) Plot helper
296
  # =========================
297
  def indicators_plot(indicators: Dict[str, float]):
298
  labels = list(indicators.keys())
 
301
  fig = plt.figure(figsize=(8, 5))
302
  plt.barh(labels, values, color=colors)
303
  plt.gca().invert_yaxis()
304
+ plt.title("GNH Indicator Similarity (Pathway-weighted)")
305
  plt.xlabel("Score")
306
  plt.tight_layout()
307
  return fig
308
 
309
  # =========================
310
+ # 8) Gradio app
311
  # =========================
312
  SEQ_CHOICES = list(SEQUENCE_ALIASES.keys())
313
+ SEQ_MULTI_CHOICES = [k for k in SEQUENCE_ALIASES.keys() if k != "Auto (recommend)"]
314
+
315
+ def normalize_seq_keys(ui_labels: List[str]) -> List[str]:
316
+ keys = []
317
+ for lab in ui_labels:
318
+ k = SEQUENCE_ALIASES.get(lab, lab).lower()
319
+ keys.append(k)
320
+ return keys
321
+
322
+ def analyze(
323
+ text: str,
324
+ seq_choice: str,
325
+ boost_terms_raw: str,
326
+ boost_seq_labels: List[str],
327
+ limit_seq_labels: List[str],
328
+ boost_w: float,
329
+ limit_w: float,
330
+ ):
331
  if not text or not text.strip():
332
+ return (5.0, "neutral (0.0)", 5.0, "—", "—", "{}", None, None)
 
 
 
 
333
 
334
  # 1) scores
335
  sentiment = score_sentiment(text)
336
  emotion, emo_conf = classify_emotion(text)
337
  accomplishment = score_accomplishment(text)
 
 
338
 
339
+ # 2) UI selections
340
+ boost_seqs_user = normalize_seq_keys(boost_seq_labels)
341
+ limit_seqs_user = normalize_seq_keys(limit_seq_labels)
342
+
343
+ # 3) parse boosts/limits
344
+ boost_terms = [t.strip() for t in boost_terms_raw.split(",")] if boost_terms_raw else []
345
+
346
+ # --- NEW: Color cues from text (many-to-many) ---
347
+ boost_colors, limit_colors = infer_color_directives(text)
348
+ boost_seqs_from_colors = colors_to_seq_keys(boost_colors)
349
+ limit_seqs_from_colors = colors_to_seq_keys(limit_colors)
350
+
351
+ # combine lists (dedupe preserving order)
352
+ def _merge(a: List[str], b: List[str]) -> List[str]:
353
+ out = list(a)
354
+ for x in b:
355
+ if x not in out:
356
+ out.append(x)
357
+ return out
358
+
359
+ boost_seq_keys = _merge(boost_seqs_user, boost_seqs_from_colors)
360
+ limit_seq_keys = _merge(limit_seqs_user, limit_seqs_from_colors)
361
+
362
+ # 4) build context vector
363
+ context_vec = composite_vector(
364
+ base_text=text,
365
+ boost_terms=boost_terms,
366
+ boost_seq_keys=boost_seq_keys,
367
+ limit_seq_keys=limit_seq_keys,
368
+ boost_w=boost_w,
369
+ limit_w=limit_w,
370
+ )
371
 
372
+ # 5) choose pathway (Auto or specific)
373
+ chosen_key = SEQUENCE_ALIASES.get(seq_choice, "auto")
374
+ if chosen_key == "auto":
375
+ final_key, final_sim = best_sequence_for_vector(context_vec)
376
+ else:
377
+ final_key = chosen_key
378
+ phrase_for_final = PATHWAY_PHRASES.get(final_key, "")
379
+ final_sim = float(util.cos_sim(context_vec, encode_text(phrase_for_final)).item()) if phrase_for_final else 0.0
380
+
381
+ # 6) outputs
382
+ phrase = PATHWAY_PHRASES.get(final_key, "—")
383
+ img_path = sequence_to_image_path(final_key)
384
+
385
+ indicators = semantic_indicator_mapping_from_vec(context_vec, sentiment_score=sentiment)
386
+ fig = indicators_plot(indicators)
387
 
 
388
  top5 = list(indicators.items())[:5]
389
  top5_str = "\n".join(f"{k}: {v}" for k, v in top5)
390
 
391
+ # annotated meta
392
+ emo_str = f"{emotion} ({emo_conf:.3f})"
393
+ meta = f"{final_key} (relevance={final_sim:.3f})"
394
+ # show how color cues mapped
395
+ if boost_colors or limit_colors:
396
+ meta += f" | boost colors: {', '.join(boost_colors) or '—'} → {', '.join(boost_seqs_from_colors) or '—'}"
397
+ meta += f" | limit colors: {', '.join(limit_colors) or '—'} → {', '.join(limit_seqs_from_colors) or '—'}"
398
+
399
  return (
400
+ sentiment, # number
401
+ emo_str, # text
402
+ accomplishment, # number
403
+ meta, # chosen pathway + relevance + color cue mapping
404
+ phrase, # pathway phrase
405
+ top5_str, # GNH top5
406
+ fig, # plot
407
+ img_path, # image path (optional)
 
 
408
  )
409
 
410
  with gr.Blocks(title="RGB Root Matriz Color Plotter") as demo:
411
+ gr.Markdown("## RGB Root Matriz Color Plotter\n"
412
  "Type a phrase. Choose a **Sequence** or keep **Auto** to recommend a pathway. "
413
  "You’ll get sentiment, emotion, accomplishment, GNH bars, and the pathway phrase + image from the dataset.")
414
 
415
  with gr.Row():
416
+ inp = gr.Textbox(
417
+ lines=4,
418
+ label="Input text",
419
+ placeholder="e.g., use gratitude from a return and inspiration from clarity to limit from red-pathway the pain from orange-pathway."
420
+ )
421
+
422
+ with gr.Row():
423
+ seq = gr.Dropdown(choices=SEQ_CHOICES, value="Auto (recommend)", label="Primary Pathway")
424
+
425
+ with gr.Row():
426
+ boost_terms = gr.Textbox(label="Boost terms (comma-separated)", placeholder="gratitude, inspiration, clarity")
427
+ with gr.Row():
428
+ boost_seqs = gr.CheckboxGroup(choices=[c for c in SEQ_CHOICES if c != "Auto (recommend)"],
429
+ label="Boost sequences (optional)")
430
+ limit_seqs = gr.CheckboxGroup(choices=[c for c in SEQ_CHOICES if c != "Auto (recommend)"],
431
+ label="Limit sequences (optional)")
432
+
433
  with gr.Row():
434
+ boost_w = gr.Slider(0.0, 1.5, value=0.6, step=0.05, label="Boost weight")
435
+ limit_w = gr.Slider(0.0, 1.5, value=0.6, step=0.05, label="Limit weight")
436
 
437
  btn = gr.Button("Analyze", variant="primary")
438
 
 
442
  acc = gr.Number(label="Accomplishment (1–10)")
443
 
444
  with gr.Row():
445
+ chosen = gr.Text(label="Chosen pathway (relevance + color mapping)")
446
  phrase_out = gr.Text(label="Pathway phrase")
447
 
448
  with gr.Row():
449
  gnh_top = gr.Text(label="Top GNH Indicators (Top 5)")
450
+ gnh_plot = gr.Plot(label="GNH Similarity (Pathway-weighted)")
451
 
452
  with gr.Row():
453
  pathway_img = gr.Image(label="Pathway image", type="filepath")
 
 
 
 
 
 
 
 
 
454
 
455
  btn.click(
456
+ fn=analyze,
457
+ inputs=[inp, seq, boost_terms, boost_seqs, limit_seqs, boost_w, limit_w],
458
+ outputs=[sent, emo, acc, chosen, phrase_out, gnh_top, gnh_plot, pathway_img]
459
  )
460
 
461
  if __name__ == "__main__":
462
  demo.launch()
463
+
464
+