Spaces:
Runtime error
Runtime error
Update app.py
Browse files
app.py
CHANGED
|
@@ -420,22 +420,23 @@ def _parse_ids(text, valid_ids):
|
|
| 420 |
import re as _re
|
| 421 |
return [int(n) for n in _re.findall(r'\b(\d+)\b', text) if int(n) in valid_ids]
|
| 422 |
|
| 423 |
-
def
|
| 424 |
-
"""Return
|
| 425 |
-
|
| 426 |
-
|
| 427 |
-
|
| 428 |
-
|
|
|
|
| 429 |
|
| 430 |
def handle_start_journal(user_id):
|
| 431 |
blank_outputs = ([], [], False,
|
| 432 |
gr.update(interactive=False), gr.update(interactive=False), gr.update(interactive=False),
|
| 433 |
[], "marking", "")
|
| 434 |
if not user_id:
|
| 435 |
-
return ([(
|
| 436 |
tasks = get_tasks(user_id, only_today=True)
|
| 437 |
if not tasks:
|
| 438 |
-
return ([(
|
| 439 |
|
| 440 |
tasks_state = [{
|
| 441 |
"task_id": t["id"], "title": t["title"], "life_area": t.get("life_area",""),
|
|
@@ -460,7 +461,7 @@ def handle_start_journal(user_id):
|
|
| 460 |
f"Done: **{completed}/{total}**. Still open: {inc_str}. "
|
| 461 |
f"Tell me if you finished any more, or say **skip** to reflect.")
|
| 462 |
|
| 463 |
-
chat = [(
|
| 464 |
hist = [{"role": "assistant", "content": opener, "focus": "task_review"}]
|
| 465 |
return (
|
| 466 |
chat, hist, True,
|
|
@@ -478,14 +479,14 @@ def handle_journal_message(user_id, user_text, audio_path, chat, hist, tasks_sta
|
|
| 478 |
user_text = transcribed
|
| 479 |
voice_used = True
|
| 480 |
except Exception as e:
|
| 481 |
-
chat = list(chat) + [(
|
| 482 |
return chat, hist, "", None, tasks_state, active, phase, ""
|
| 483 |
|
| 484 |
if not active or not user_text.strip():
|
| 485 |
return list(chat), hist, "", None, tasks_state, active, phase, ""
|
| 486 |
|
| 487 |
display_text = f"\U0001f3a4 *{user_text}*" if voice_used else user_text
|
| 488 |
-
chat = list(chat) + [(display_text
|
| 489 |
hist = list(hist) + [{"role": "user", "content": user_text}]
|
| 490 |
|
| 491 |
# Phase: marking tasks
|
|
@@ -509,14 +510,14 @@ def handle_journal_message(user_id, user_text, audio_path, chat, hist, tasks_sta
|
|
| 509 |
ctx = _ensure_context(user_id)
|
| 510 |
opening = build_opening_question(ctx, tasks_state)
|
| 511 |
bridge = confirm + opening["question"]
|
| 512 |
-
chat = list(chat) + [(
|
| 513 |
hist.append({"role": "assistant", "content": bridge, "focus": opening["question_focus"]})
|
| 514 |
return chat, hist, "", None, tasks_state, active, "reflecting", ""
|
| 515 |
else:
|
| 516 |
remaining = [t for t in tasks_state if not t["completed"]]
|
| 517 |
rem_str = " | ".join(f"#{t['task_id']} {t['title']}" for t in remaining[:5])
|
| 518 |
follow = confirm + f"Still pending: {rem_str}\n\nAnything else done? Or say **skip** to start reflecting."
|
| 519 |
-
chat = list(chat) + [(
|
| 520 |
hist.append({"role": "assistant", "content": follow, "focus": "task_marking"})
|
| 521 |
return chat, hist, "", None, tasks_state, active, "marking", ""
|
| 522 |
|
|
@@ -526,34 +527,34 @@ def handle_journal_message(user_id, user_text, audio_path, chat, hist, tasks_sta
|
|
| 526 |
ctx = _ensure_context(user_id)
|
| 527 |
result = get_next_journal_question(ctx, tasks_state, hist)
|
| 528 |
except Exception as e:
|
| 529 |
-
chat = list(chat) + [(
|
| 530 |
return chat, hist, "", None, tasks_state, active, phase, ""
|
| 531 |
|
| 532 |
if result.get("session_complete"):
|
| 533 |
closing = "That's a solid reflection \u2014 I have plenty to work with. \U0001f9e0 Hit **Finish & Save** to lock in your insights!"
|
| 534 |
-
chat = list(chat) + [(
|
| 535 |
hist.append({"role": "assistant", "content": closing, "focus": "complete"})
|
| 536 |
return chat, hist, "", None, tasks_state, active, "done", _ok("Session complete \u2014 click Finish & Save")
|
| 537 |
|
| 538 |
next_q = result.get("question") or "Anything else you'd like to reflect on?"
|
| 539 |
-
chat = list(chat) + [(
|
| 540 |
hist.append({"role": "assistant", "content": next_q, "focus": result.get("question_focus","")})
|
| 541 |
return chat, hist, "", None, tasks_state, active, "reflecting", ""
|
| 542 |
|
| 543 |
# Phase: done
|
| 544 |
-
chat = list(chat) + [(
|
| 545 |
return chat, hist, "", None, tasks_state, active, "done", ""
|
| 546 |
|
| 547 |
|
| 548 |
def handle_finish_journal(user_id, chat, hist, tasks_state):
|
| 549 |
if not user_id or not hist:
|
| 550 |
-
return list(chat) + [(
|
| 551 |
try:
|
| 552 |
ctx = _ensure_context(user_id)
|
| 553 |
updated = synthesize_journal(ctx, tasks_state, hist)
|
| 554 |
save_user_context(user_id, updated)
|
| 555 |
except Exception as e:
|
| 556 |
-
return list(chat) + [(
|
| 557 |
total = len(tasks_state)
|
| 558 |
done = sum(1 for t in tasks_state if t.get("completed"))
|
| 559 |
rate = round(done / total * 100) if total else 0
|
|
@@ -562,7 +563,7 @@ def handle_finish_journal(user_id, chat, hist, tasks_state):
|
|
| 562 |
summary = (f"\u2705 **Insights saved!** {done}/{total} tasks ({rate}%) complete today.\n\n"
|
| 563 |
f"**What I learned:**\n{notes_md}\n\n"
|
| 564 |
f"I'll use this to make tomorrow's scheduling smarter. Great work! \U0001f319")
|
| 565 |
-
return list(chat) + [(
|
| 566 |
|
| 567 |
def handle_restart_journal():
|
| 568 |
return [], [], False, gr.update(interactive=False), gr.update(interactive=False), gr.update(interactive=False), [], "marking", ""
|
|
@@ -864,7 +865,7 @@ with gr.Blocks(title="\U0001f9e0 Second Brain") as demo:
|
|
| 864 |
gr.HTML('<p style="color:#475569;font-size:12px;margin:0 0 10px">Deadline is auto-extracted from your text. Date will be assigned by the AI scheduler.</p>')
|
| 865 |
with gr.Row():
|
| 866 |
f_title = gr.Textbox(label="Title", scale=3)
|
| 867 |
-
f_area = gr.Dropdown(label="Life Area", choices=["Work","Health","Finance","Learning","Personal","Family","Other"], scale=1)
|
| 868 |
with gr.Row():
|
| 869 |
f_urgency = gr.Dropdown(label="Urgency", choices=["Urgent","Not Urgent","Habit"], value="Not Urgent")
|
| 870 |
f_importance = gr.Dropdown(label="Importance", choices=["Move the Needle","Important","Not Important"], value="Important")
|
|
@@ -900,7 +901,7 @@ with gr.Blocks(title="\U0001f9e0 Second Brain") as demo:
|
|
| 900 |
gr.HTML('<div class="sec-label" style="margin-top:12px">Review & Confirm</div>')
|
| 901 |
with gr.Row():
|
| 902 |
vf_title = gr.Textbox(label="Title", scale=3)
|
| 903 |
-
vf_area = gr.Dropdown(label="Life Area", choices=["Work","Health","Finance","Learning","Personal","Family","Other"], scale=1)
|
| 904 |
with gr.Row():
|
| 905 |
vf_urgency = gr.Dropdown(label="Urgency", choices=["Urgent","Not Urgent","Habit"], value="Not Urgent")
|
| 906 |
vf_importance = gr.Dropdown(label="Importance", choices=["Move the Needle","Important","Not Important"], value="Important")
|
|
@@ -969,7 +970,7 @@ with gr.Blocks(title="\U0001f9e0 Second Brain") as demo:
|
|
| 969 |
# ββ TAB 3: JOURNAL ββββββββββββββββββββββββββββββββββββββββββββ
|
| 970 |
with gr.Tab("\U0001f4d3 Journal"):
|
| 971 |
gr.HTML('<p style="color:#475569;font-size:13px;margin-bottom:12px">End-of-day reflection. Chat naturally \u2014 AI marks tasks, asks questions, updates your profile.</p>')
|
| 972 |
-
journal_chatbot = gr.Chatbot(label="", height=440)
|
| 973 |
with gr.Row():
|
| 974 |
j_answer = gr.Textbox(label="", placeholder="Type or use the mic\u2026", lines=1, scale=5, interactive=False, show_label=False)
|
| 975 |
j_audio_in = gr.Audio(sources=["microphone"], type="filepath", label="\U0001f3a4", scale=1, interactive=False)
|
|
|
|
| 420 |
import re as _re
|
| 421 |
return [int(n) for n in _re.findall(r'\b(\d+)\b', text) if int(n) in valid_ids]
|
| 422 |
|
| 423 |
+
def _bot(text):
|
| 424 |
+
"""Return assistant message dict for Gradio Chatbot type='messages'."""
|
| 425 |
+
return {"role": "assistant", "content": text}
|
| 426 |
+
|
| 427 |
+
def _usr(text):
|
| 428 |
+
"""Return user message dict for Gradio Chatbot type='messages'."""
|
| 429 |
+
return {"role": "user", "content": text}
|
| 430 |
|
| 431 |
def handle_start_journal(user_id):
|
| 432 |
blank_outputs = ([], [], False,
|
| 433 |
gr.update(interactive=False), gr.update(interactive=False), gr.update(interactive=False),
|
| 434 |
[], "marking", "")
|
| 435 |
if not user_id:
|
| 436 |
+
return ([_bot("\u26a0 Please sign in first.")],) + blank_outputs[1:]
|
| 437 |
tasks = get_tasks(user_id, only_today=True)
|
| 438 |
if not tasks:
|
| 439 |
+
return ([_bot("No tasks scheduled for today. Add and schedule tasks first, then come back to reflect!")],) + blank_outputs[1:]
|
| 440 |
|
| 441 |
tasks_state = [{
|
| 442 |
"task_id": t["id"], "title": t["title"], "life_area": t.get("life_area",""),
|
|
|
|
| 461 |
f"Done: **{completed}/{total}**. Still open: {inc_str}. "
|
| 462 |
f"Tell me if you finished any more, or say **skip** to reflect.")
|
| 463 |
|
| 464 |
+
chat = [_bot(opener)]
|
| 465 |
hist = [{"role": "assistant", "content": opener, "focus": "task_review"}]
|
| 466 |
return (
|
| 467 |
chat, hist, True,
|
|
|
|
| 479 |
user_text = transcribed
|
| 480 |
voice_used = True
|
| 481 |
except Exception as e:
|
| 482 |
+
chat = list(chat) + [_bot(f"\u26a0 Transcription error: {e}. Please type instead.")]
|
| 483 |
return chat, hist, "", None, tasks_state, active, phase, ""
|
| 484 |
|
| 485 |
if not active or not user_text.strip():
|
| 486 |
return list(chat), hist, "", None, tasks_state, active, phase, ""
|
| 487 |
|
| 488 |
display_text = f"\U0001f3a4 *{user_text}*" if voice_used else user_text
|
| 489 |
+
chat = list(chat) + [_usr(display_text)]
|
| 490 |
hist = list(hist) + [{"role": "user", "content": user_text}]
|
| 491 |
|
| 492 |
# Phase: marking tasks
|
|
|
|
| 510 |
ctx = _ensure_context(user_id)
|
| 511 |
opening = build_opening_question(ctx, tasks_state)
|
| 512 |
bridge = confirm + opening["question"]
|
| 513 |
+
chat = list(chat) + [_bot(bridge)]
|
| 514 |
hist.append({"role": "assistant", "content": bridge, "focus": opening["question_focus"]})
|
| 515 |
return chat, hist, "", None, tasks_state, active, "reflecting", ""
|
| 516 |
else:
|
| 517 |
remaining = [t for t in tasks_state if not t["completed"]]
|
| 518 |
rem_str = " | ".join(f"#{t['task_id']} {t['title']}" for t in remaining[:5])
|
| 519 |
follow = confirm + f"Still pending: {rem_str}\n\nAnything else done? Or say **skip** to start reflecting."
|
| 520 |
+
chat = list(chat) + [_bot(follow)]
|
| 521 |
hist.append({"role": "assistant", "content": follow, "focus": "task_marking"})
|
| 522 |
return chat, hist, "", None, tasks_state, active, "marking", ""
|
| 523 |
|
|
|
|
| 527 |
ctx = _ensure_context(user_id)
|
| 528 |
result = get_next_journal_question(ctx, tasks_state, hist)
|
| 529 |
except Exception as e:
|
| 530 |
+
chat = list(chat) + [_bot(f"\u26a0 AI error: {e}. Try again.")]
|
| 531 |
return chat, hist, "", None, tasks_state, active, phase, ""
|
| 532 |
|
| 533 |
if result.get("session_complete"):
|
| 534 |
closing = "That's a solid reflection \u2014 I have plenty to work with. \U0001f9e0 Hit **Finish & Save** to lock in your insights!"
|
| 535 |
+
chat = list(chat) + [_bot(closing)]
|
| 536 |
hist.append({"role": "assistant", "content": closing, "focus": "complete"})
|
| 537 |
return chat, hist, "", None, tasks_state, active, "done", _ok("Session complete \u2014 click Finish & Save")
|
| 538 |
|
| 539 |
next_q = result.get("question") or "Anything else you'd like to reflect on?"
|
| 540 |
+
chat = list(chat) + [_bot(next_q)]
|
| 541 |
hist.append({"role": "assistant", "content": next_q, "focus": result.get("question_focus","")})
|
| 542 |
return chat, hist, "", None, tasks_state, active, "reflecting", ""
|
| 543 |
|
| 544 |
# Phase: done
|
| 545 |
+
chat = list(chat) + [_bot("Session complete! Click **Finish & Save** to save your insights.")]
|
| 546 |
return chat, hist, "", None, tasks_state, active, "done", ""
|
| 547 |
|
| 548 |
|
| 549 |
def handle_finish_journal(user_id, chat, hist, tasks_state):
|
| 550 |
if not user_id or not hist:
|
| 551 |
+
return list(chat) + [_bot("\u26a0 Nothing to save yet.")], ""
|
| 552 |
try:
|
| 553 |
ctx = _ensure_context(user_id)
|
| 554 |
updated = synthesize_journal(ctx, tasks_state, hist)
|
| 555 |
save_user_context(user_id, updated)
|
| 556 |
except Exception as e:
|
| 557 |
+
return list(chat) + [_bot(f"\u26a0 Save failed: {e}")], _err("Save failed")
|
| 558 |
total = len(tasks_state)
|
| 559 |
done = sum(1 for t in tasks_state if t.get("completed"))
|
| 560 |
rate = round(done / total * 100) if total else 0
|
|
|
|
| 563 |
summary = (f"\u2705 **Insights saved!** {done}/{total} tasks ({rate}%) complete today.\n\n"
|
| 564 |
f"**What I learned:**\n{notes_md}\n\n"
|
| 565 |
f"I'll use this to make tomorrow's scheduling smarter. Great work! \U0001f319")
|
| 566 |
+
return list(chat) + [_bot(summary)], _ok(f"Saved! {done}/{total} tasks ({rate}%)")
|
| 567 |
|
| 568 |
def handle_restart_journal():
|
| 569 |
return [], [], False, gr.update(interactive=False), gr.update(interactive=False), gr.update(interactive=False), [], "marking", ""
|
|
|
|
| 865 |
gr.HTML('<p style="color:#475569;font-size:12px;margin:0 0 10px">Deadline is auto-extracted from your text. Date will be assigned by the AI scheduler.</p>')
|
| 866 |
with gr.Row():
|
| 867 |
f_title = gr.Textbox(label="Title", scale=3)
|
| 868 |
+
f_area = gr.Dropdown(label="Life Area", choices=["Work","Health","Finance","Learning","Personal","Family","Other"], scale=1, allow_custom_value=True)
|
| 869 |
with gr.Row():
|
| 870 |
f_urgency = gr.Dropdown(label="Urgency", choices=["Urgent","Not Urgent","Habit"], value="Not Urgent")
|
| 871 |
f_importance = gr.Dropdown(label="Importance", choices=["Move the Needle","Important","Not Important"], value="Important")
|
|
|
|
| 901 |
gr.HTML('<div class="sec-label" style="margin-top:12px">Review & Confirm</div>')
|
| 902 |
with gr.Row():
|
| 903 |
vf_title = gr.Textbox(label="Title", scale=3)
|
| 904 |
+
vf_area = gr.Dropdown(label="Life Area", choices=["Work","Health","Finance","Learning","Personal","Family","Other"], scale=1, allow_custom_value=True)
|
| 905 |
with gr.Row():
|
| 906 |
vf_urgency = gr.Dropdown(label="Urgency", choices=["Urgent","Not Urgent","Habit"], value="Not Urgent")
|
| 907 |
vf_importance = gr.Dropdown(label="Importance", choices=["Move the Needle","Important","Not Important"], value="Important")
|
|
|
|
| 970 |
# ββ TAB 3: JOURNAL ββββββββββββββββββββββββββββββββββββββββββββ
|
| 971 |
with gr.Tab("\U0001f4d3 Journal"):
|
| 972 |
gr.HTML('<p style="color:#475569;font-size:13px;margin-bottom:12px">End-of-day reflection. Chat naturally \u2014 AI marks tasks, asks questions, updates your profile.</p>')
|
| 973 |
+
journal_chatbot = gr.Chatbot(label="", height=440, type="messages")
|
| 974 |
with gr.Row():
|
| 975 |
j_answer = gr.Textbox(label="", placeholder="Type or use the mic\u2026", lines=1, scale=5, interactive=False, show_label=False)
|
| 976 |
j_audio_in = gr.Audio(sources=["microphone"], type="filepath", label="\U0001f3a4", scale=1, interactive=False)
|