Files
personal-ai-assistant/backend/app/workers/health_review.py
dolgolyov.alexei b0790d719c Generalize from health-specific to universal personal assistant
The app manages multiple life areas (health, finance, personal, work),
not just health. Updated all health-specific language throughout:

Backend:
- Default system prompt: general personal assistant (not health-only)
- AI tool descriptions: generic (not health records/medications)
- Memory categories: health, finance, personal, work, document_summary, other
  (replaces condition, medication, allergy, vital)
- PDF template: "Prepared for" (not "Patient"), "Key Information" (not "Health Profile")
- Renamed generate_health_pdf -> generate_pdf_report, health_report.html -> report.html
- Renamed run_daily_health_review -> run_daily_review
- Context assembly: "User Profile" (not "Health Profile")
- OpenAPI: generic descriptions

Frontend:
- Dashboard subtitle: "Your personal AI assistant"
- Memory categories: Health, Finance, Personal, Work
- Document types: Report, Contract, Receipt, Certificate (not lab_result, etc.)
- Updated en + ru translations throughout

Documentation:
- README: general personal assistant description
- Removed health-only feature descriptions

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 15:15:39 +03:00

78 lines
2.8 KiB
Python

"""Daily job: proactive review for all users with stored data."""
import asyncio
import json
import logging
from anthropic import AsyncAnthropic
from sqlalchemy import select
from app.config import settings
from app.database import async_session_factory
from app.models.user import User
from app.services.memory_service import get_critical_memories
from app.services.notification_service import create_notification
from app.services.ws_manager import manager
logger = logging.getLogger(__name__)
client = AsyncAnthropic(api_key=settings.ANTHROPIC_API_KEY)
async def run_daily_review():
"""Review each user's profile and generate reminder notifications."""
if not settings.ANTHROPIC_API_KEY:
return
async with async_session_factory() as db:
result = await db.execute(select(User).where(User.is_active == True)) # noqa: E712
users = result.scalars().all()
for user in users:
try:
await _review_user(user)
await asyncio.sleep(1) # Rate limit
except Exception:
logger.exception(f"Daily review failed for user {user.id}")
async def _review_user(user: User):
async with async_session_factory() as db:
memories = await get_critical_memories(db, user.id)
if not memories:
return
memory_text = "\n".join(f"- [{m.category}] {m.title}: {m.content}" for m in memories)
response = await client.messages.create(
model=settings.CLAUDE_MODEL,
max_tokens=500,
system="You are a personal assistant. Based on the user's stored information, suggest any upcoming actions, deadlines, follow-ups, or reminders that would be helpful. Respond with a JSON array of objects with 'title' and 'body' fields. If no reminders are needed, return an empty array [].",
messages=[{"role": "user", "content": f"User profile data:\n{memory_text}"}],
)
try:
text = response.content[0].text.strip()
if "[" in text:
json_str = text[text.index("["):text.rindex("]") + 1]
reminders = json.loads(json_str)
else:
return
except (json.JSONDecodeError, ValueError):
return
for reminder in reminders[:5]:
if "title" in reminder and "body" in reminder:
notif = await create_notification(
db,
user.id,
title=reminder["title"],
body=reminder["body"],
type="ai_generated",
)
await db.commit()
from app.workers.notification_sender import _serialize_notification
await manager.send_to_user(user.id, {
"type": "new_notification",
"notification": _serialize_notification(notif),
})