agent: look at current info, its aUEC, feat: pull up notifcations.

This commit is contained in:
2026-05-05 20:05:33 -04:00
parent f7ac45ddd8
commit 761eda6155
11 changed files with 198 additions and 8 deletions
+67
View File
@@ -7,23 +7,34 @@ from uuid import uuid4
from apscheduler.schedulers.asyncio import AsyncIOScheduler
from apscheduler.triggers.cron import CronTrigger
from apscheduler.triggers.date import DateTrigger
from apscheduler.triggers.interval import IntervalTrigger
from tzlocal import get_localzone
from traderai.memory import MemoryStore, iso_now, time_since
UEX_NOTIFICATION_JOB_ID = "uex-notification-poll"
class WakeScheduler:
def __init__(self, memory: MemoryStore) -> None:
self.memory = memory
self.scheduler = AsyncIOScheduler(timezone=get_localzone())
self.agent = None
self.uex = None
self.notification_poll_seconds = 60
def bind_agent(self, agent: Any) -> None:
self.agent = agent
def bind_uex_notifications(self, uex: Any, poll_seconds: int = 60) -> None:
self.uex = uex
self.notification_poll_seconds = max(15, poll_seconds)
def start(self) -> None:
if not self.scheduler.running:
self.scheduler.start()
self._schedule_notification_poll()
for job in self.memory.list_jobs():
self._schedule_existing(job)
@@ -77,3 +88,59 @@ class WakeScheduler:
text = await self.agent.generate_wake_response(wake_message)
self.memory.add_outbox(text)
self.memory.mark_job_run(job_id)
def _schedule_notification_poll(self) -> None:
if self.uex is None:
return
self.scheduler.add_job(
self.poll_uex_notifications,
trigger=IntervalTrigger(seconds=self.notification_poll_seconds),
id=UEX_NOTIFICATION_JOB_ID,
replace_existing=True,
next_run_time=datetime.now(),
)
async def poll_uex_notifications(self) -> list[dict[str, Any]]:
if self.uex is None:
return []
response = await self.uex.get_user_notifications()
notifications = response.get("notifications") or []
pending = [item for item in notifications if not item.get("date_read")]
profile = self.memory.get_profile()
seen = set(profile.get("uex_seen_notification_keys") or [])
new_pending = [item for item in pending if self._notification_key(item) not in seen]
if new_pending:
for item in new_pending:
self.memory.add_outbox(self._notification_text(item))
seen.update(self._notification_key(item) for item in new_pending)
self.memory.set_profile("uex_seen_notification_keys", sorted(seen))
self.memory.set_profile("uex_last_notification_check", iso_now())
elif notifications:
seen.update(self._notification_key(item) for item in pending)
self.memory.set_profile("uex_seen_notification_keys", sorted(seen))
self.memory.set_profile("uex_last_notification_check", iso_now())
return new_pending
@staticmethod
def _notification_key(item: dict[str, Any]) -> str:
for key in ("code", "id"):
value = item.get(key)
if value not in (None, ""):
return f"{key}:{value}"
return f"notification:{item.get('date_added')}:{item.get('message')}"
@staticmethod
def _notification_text(item: dict[str, Any]) -> str:
message = item.get("message") or "You have a pending UEX notification."
redir = item.get("redir")
code = item.get("code")
details = []
if code:
details.append(f"code `{code}`")
if redir:
details.append(f"path `{redir}`")
suffix = f" ({', '.join(details)})" if details else ""
return f"UEX notification: {message}{suffix}"