Spaces:
Sleeping
Sleeping
| import json, random | |
| from collections import deque | |
| import numpy as np | |
| import faiss, gradio as gr | |
| from sentence_transformers import SentenceTransformer | |
| from huggingface_hub import hf_hub_download | |
| REPO_ID = "Elevi7/actionmatch-microactions-en" | |
| index_path = hf_hub_download(repo_id=REPO_ID, filename="index/index.faiss", repo_type="dataset") | |
| actions_path = hf_hub_download(repo_id=REPO_ID, filename="actions.jsonl", repo_type="dataset") | |
| with open(actions_path, "r", encoding="utf-8") as f: | |
| actions = [json.loads(l) for l in f] | |
| index = faiss.read_index(index_path) | |
| model = SentenceTransformer("sentence-transformers/all-MiniLM-L6-v2") | |
| recent = deque(maxlen=120) | |
| def render(a): | |
| ctx = ", ".join(a["context"]) if isinstance(a.get("context"), list) else (a.get("context") or "") | |
| return f"**{a['title']}** \n{a['instruction']} \nGoal: {a['goal']} • Duration: {a['duration_min']} min • Energy: {a['energy']} • Context: {ctx}" | |
| def pick_unique(idxs, goal, energy, max_minutes, ignore_goal=False, ignore_energy=False, ignore_minutes=False, need=3, used=None): | |
| if used is None: | |
| used = set() | |
| out = [] | |
| for i in idxs: | |
| a = actions[i] | |
| if a["title"] in used or a["title"] in recent: | |
| continue | |
| if not ignore_goal and goal and a["goal"] != goal: | |
| continue | |
| if not ignore_energy and energy and a["energy"] != energy: | |
| continue | |
| if not ignore_minutes and max_minutes and a["duration_min"] > int(max_minutes): | |
| continue | |
| used.add(a["title"]) | |
| out.append(a) | |
| if len(out) == need: | |
| break | |
| return out, used | |
| def fill_random(need, used, goal, energy, max_minutes): | |
| pool = [a for a in actions if a["title"] not in used and a["title"] not in recent and (not goal or a["goal"]==goal) and (not energy or a["energy"]==energy) and (not max_minutes or a["duration_min"]<=int(max_minutes))] | |
| if len(pool) < need: | |
| pool = [a for a in actions if a["title"] not in used and a["title"] not in recent] | |
| random.shuffle(pool) | |
| return pool[:need] | |
| def search(query, goal, energy, max_minutes): | |
| try: | |
| q = (query or "").strip() | |
| qx = f"{q} Goal:{goal or 'any'} Energy:{energy or 'any'} Max:{int(max_minutes) if max_minutes else ''} minutes" | |
| v = model.encode([qx], normalize_embeddings=True) | |
| D, I = index.search(np.asarray(v, dtype="float32"), 800) | |
| idxs = list(I[0]); random.shuffle(idxs) | |
| res, used = [], set() | |
| step, used = pick_unique(idxs, goal, energy, max_minutes, False, False, False, 3, used); res += step | |
| if len(res) < 3: | |
| step, used = pick_unique(idxs, goal, energy, max_minutes, False, True, False, 3-len(res), used); res += step | |
| if len(res) < 3: | |
| step, used = pick_unique(idxs, goal, energy, max_minutes, False, True, True, 3-len(res), used); res += step | |
| if len(res) < 3: | |
| step, used = pick_unique(idxs, goal, energy, max_minutes, True, True, True, 3-len(res), used); res += step | |
| if len(res) < 3: | |
| res += fill_random(3-len(res), used, goal, energy, max_minutes) | |
| recent.extend([a["title"] for a in res[:3]]) | |
| return "\n\n---\n\n".join(render(a) for a in res[:3]) | |
| except Exception: | |
| pool = [a for a in actions if (not goal or a["goal"]==goal) and (not energy or a["energy"]==energy) and (not max_minutes or a["duration_min"]<=int(max_minutes))] | |
| if len(pool) < 3: | |
| pool = actions[:] | |
| random.shuffle(pool) | |
| return "\n\n---\n\n".join(render(a) for a in pool[:3]) | |
| goals = ["","calm","focus","productivity","wellbeing"] | |
| energies = ["","low","medium","high"] | |
| with gr.Blocks(theme=gr.themes.Soft(primary_hue="indigo", neutral_hue="slate")) as demo: | |
| gr.Markdown("# ActionMatch\nTop-3 micro-actions based on your situation, goal, energy and time.") | |
| with gr.Row(): | |
| with gr.Column(scale=3): | |
| q = gr.Textbox(lines=2, label="Your situation", placeholder="e.g., Stressed before exam") | |
| btn = gr.Button("Recommend") | |
| with gr.Column(scale=2): | |
| g = gr.Dropdown(goals, label="Goal") | |
| e = gr.Radio(energies, label="Energy") | |
| m = gr.Slider(1, 15, step=1, value=5, label="Max minutes") | |
| out = gr.Markdown() | |
| btn.click(search, [q, g, e, m], out) | |
| gr.Examples( | |
| [["Stressed before exam","calm","low",5], | |
| ["No energy but need to start studying","focus","low",7], | |
| ["Keep switching tabs while writing essay","focus","medium",10]], | |
| inputs=[q, g, e, m], | |
| outputs=out, | |
| fn=search, | |
| label="One-click examples", | |
| cache_examples=False | |
| ) | |
| demo.queue() | |
| demo.launch() |