Skip to content

Commit a57e89f

Browse files
annawiewerCopilot
andcommitted
Harden status/date chat behavior and snapshot fallback
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 59fb483 commit a57e89f

4 files changed

Lines changed: 894 additions & 54 deletions

File tree

β€Žsrc/wiesn_agent/api.pyβ€Ž

Lines changed: 41 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,8 @@ def _save_chat_log() -> None:
6363
CHAT_LOG_FILE.parent.mkdir(parents=True, exist_ok=True)
6464
persistent = [m for m in _chat_log if m.get("role") != "thinking"]
6565
CHAT_LOG_FILE.write_text(
66-
json.dumps(persistent, ensure_ascii=False, indent=2)
66+
json.dumps(persistent, ensure_ascii=False, indent=2),
67+
encoding="utf-8",
6768
)
6869
except Exception as e:
6970
logger.warning("Failed to save chat log: %s", e)
@@ -74,7 +75,7 @@ def _load_chat_log() -> None:
7475
if not CHAT_LOG_FILE.exists():
7576
return
7677
try:
77-
data = json.loads(CHAT_LOG_FILE.read_text())
78+
data = json.loads(CHAT_LOG_FILE.read_text(encoding="utf-8"))
7879
if isinstance(data, list):
7980
_chat_log.extend(data[-200:])
8081
logger.info("Restored %d chat messages from disk", len(_chat_log))
@@ -87,7 +88,8 @@ def _save_activity_log() -> None:
8788
try:
8889
ACTIVITY_LOG_FILE.parent.mkdir(parents=True, exist_ok=True)
8990
ACTIVITY_LOG_FILE.write_text(
90-
json.dumps(list(_activity_log), ensure_ascii=False, indent=2)
91+
json.dumps(list(_activity_log), ensure_ascii=False, indent=2),
92+
encoding="utf-8",
9193
)
9294
except Exception as e:
9395
logger.warning("Failed to save activity log: %s", e)
@@ -98,7 +100,7 @@ def _load_activity_log() -> None:
98100
if not ACTIVITY_LOG_FILE.exists():
99101
return
100102
try:
101-
data = json.loads(ACTIVITY_LOG_FILE.read_text())
103+
data = json.loads(ACTIVITY_LOG_FILE.read_text(encoding="utf-8"))
102104
if isinstance(data, list):
103105
_activity_log.extend(data[-200:])
104106
logger.info("Restored %d activity events from disk", len(_activity_log))
@@ -134,6 +136,32 @@ def _chat_reply(message: str, **extra: Any) -> dict:
134136
return entry
135137

136138

139+
def _build_status_summary(snapshots: dict[str, PortalSnapshot]) -> str:
140+
"""Build a deterministic status summary from persisted snapshots."""
141+
if not snapshots:
142+
return "Es liegen noch keine Scan-Daten vor. Starte einen Scan mit **scan all**."
143+
144+
portal_names = sorted(snapshots.keys())
145+
with_dates = [name for name in portal_names if snapshots[name].datum_options]
146+
without_dates = [name for name in portal_names if not snapshots[name].datum_options]
147+
with_errors = [name for name in portal_names if snapshots[name].error]
148+
149+
lines = [
150+
f"**{len(with_dates)} von {len(portal_names)} Zelten** haben aktuell Termine verfΓΌgbar: "
151+
f"{', '.join(with_dates)}."
152+
]
153+
if without_dates:
154+
lines.append(
155+
f"**{len(without_dates)} Zelte** sind geschlossen oder haben keine Termine: "
156+
f"{', '.join(without_dates)}."
157+
)
158+
if with_errors:
159+
lines.append(
160+
f"Hinweis: Bei folgenden Zelten gab es zuletzt Scan-Fehler: {', '.join(with_errors)}."
161+
)
162+
return "\n\n".join(lines)
163+
164+
137165
async def _scan_portals(portals: list, config: WiesnConfig) -> list[dict]:
138166
"""Shared scan logic used by background scanner, chat scan, and API trigger.
139167
@@ -568,6 +596,7 @@ def _classify_intent(text: str) -> str:
568596
@app.post("/api/chat")
569597
async def post_chat(body: ChatMessage):
570598
"""Handle a user chat message β€” LLM agent with keyword fallback."""
599+
global _thinking_status
571600
text = body.message.strip()
572601
if not text:
573602
raise HTTPException(400, "Empty message")
@@ -581,6 +610,14 @@ async def post_chat(body: ChatMessage):
581610
_chat_log.append(user_entry)
582611
_save_chat_log()
583612

613+
intent = _classify_intent(text)
614+
615+
# Status answers are deterministic and history-independent by design.
616+
if intent == "status":
617+
snapshots = load_snapshots()
618+
reply = _chat_reply(_build_status_summary(snapshots))
619+
return {"user": user_entry, "reply": reply}
620+
584621
# ── Try LLM agent first ───────────────────────
585622
try:
586623
from wiesn_agent.chat_agent import chat as llm_chat
@@ -694,9 +731,6 @@ def _on_tool_progress(tool_name: str, tool_args: dict) -> None:
694731
reply = _chat_reply(f"**{mentioned_portal}** wurde noch nicht gescannt.")
695732
return {"user": user_entry, "reply": reply}
696733

697-
# Fall back to keyword classification for generic intents
698-
intent = _classify_intent(text)
699-
700734
# ── Intent: Scan ──────────────────────────────
701735
if intent == "scan":
702736
config = _load_config()
@@ -712,27 +746,6 @@ def _on_tool_progress(tool_name: str, tool_args: dict) -> None:
712746

713747
return {"user": user_entry, "reply": reply}
714748

715-
# ── Intent: Status / Summary ──────────────────
716-
if intent == "status":
717-
snapshots = load_snapshots()
718-
config = _load_config()
719-
with_dates = [n for n, s in snapshots.items() if s.datum_options]
720-
matching = [
721-
n for n, s in snapshots.items()
722-
if any(matches_wunsch(d.get("text", d.get("value", "")), config) for d in s.datum_options)
723-
]
724-
725-
lines = [
726-
f"**{len(snapshots)}** portals scanned, **{len(with_dates)}** have open dates.",
727-
]
728-
if matching:
729-
lines.append(f"**{len(matching)}** match your preferred days: {', '.join(matching)}")
730-
else:
731-
lines.append("No matches on your preferred days yet.")
732-
733-
reply = _chat_reply("\n".join(lines))
734-
return {"user": user_entry, "reply": reply}
735-
736749
# ── Intent: Show matches / evening ────────────
737750
if intent == "matches":
738751
snapshots = load_snapshots()

0 commit comments

Comments
Β (0)