Spaces:
Sleeping
Sleeping
| """Tool functions exposed to Gemini. Uses real Google APIs when authenticated, mock data otherwise.""" | |
| import json | |
| from pathlib import Path | |
| from google_auth import is_authenticated | |
| from google_services import fetch_emails, fetch_events, fetch_doc_content, list_recent_docs, search_project_signals | |
| from mock_data import MOCK_EMAILS, MOCK_EVENTS, MOCK_SEARCH | |
| def _load_project_keywords(project_id: str) -> list[str]: | |
| """Load keywords for a project from config.json.""" | |
| config_path = Path(__file__).parent / "config.json" | |
| with open(config_path) as f: | |
| config = json.load(f) | |
| for p in config.get("projects", []): | |
| if p["id"] == project_id: | |
| return p.get("keywords", []) | |
| return [] | |
| def read_emails(project_id: str) -> str: | |
| """Read recent emails for a project. Uses Gmail API if authenticated, mock data otherwise.""" | |
| if is_authenticated(): | |
| keywords = _load_project_keywords(project_id) | |
| emails = fetch_emails(project_id, keywords=keywords) | |
| if not emails: | |
| return f"No emails found for project {project_id}." | |
| lines = [] | |
| for e in emails: | |
| days = e.get("days_since_reply") | |
| silence = f"{days} days ago" if days is not None else "unknown" | |
| lines.append( | |
| f"From: {e['from']}\n" | |
| f"Subject: {e['subject']}\n" | |
| f"Body: {e['body']}\n" | |
| f"Last reply: {silence}" | |
| ) | |
| return "\n---\n".join(lines) | |
| # Fallback to mock data | |
| emails = MOCK_EMAILS.get(project_id, []) | |
| if not emails: | |
| return f"No emails found for project {project_id}." | |
| lines = [] | |
| for e in emails: | |
| silence = f"{e['days_since_reply']} days ago" if e["days_since_reply"] is not None else "N/A (newsletter)" | |
| lines.append( | |
| f"From: {e['from']}\n" | |
| f"Subject: {e['subject']}\n" | |
| f"Body: {e['body']}\n" | |
| f"Last reply: {silence}" | |
| ) | |
| return "\n---\n".join(lines) | |
| def get_events(project_id: str) -> str: | |
| """Get upcoming calendar events for a project. Uses Calendar API if authenticated, mock data otherwise.""" | |
| if is_authenticated(): | |
| keywords = _load_project_keywords(project_id) | |
| events = fetch_events(project_id, keywords=keywords) | |
| if not events: | |
| return f"No upcoming events for project {project_id}." | |
| lines = [] | |
| for ev in events: | |
| prep = "YES" if ev["prep_block"] else "NO" | |
| lines.append(f"{ev['title']} at {ev['time']} (prep block: {prep})") | |
| return "\n".join(lines) | |
| # Fallback to mock data | |
| events = MOCK_EVENTS.get(project_id, []) | |
| if not events: | |
| return f"No upcoming events for project {project_id}." | |
| lines = [] | |
| for ev in events: | |
| prep = "YES" if ev["prep_block"] else "NO" | |
| lines.append(f"{ev['title']} at {ev['time']} (prep block: {prep})") | |
| return "\n".join(lines) | |
| def search_web(project_id: str) -> str: | |
| """Search for recent external signals relevant to a project. Uses Gemini + Google Search grounding when possible.""" | |
| keywords = _load_project_keywords(project_id) | |
| # Load project name from config | |
| config_path = Path(__file__).parent / "config.json" | |
| with open(config_path) as f: | |
| config = json.load(f) | |
| project_name = project_id | |
| for p in config.get("projects", []): | |
| if p["id"] == project_id: | |
| project_name = p.get("name", project_id) | |
| break | |
| result = search_project_signals(project_name, keywords=keywords) | |
| if result: | |
| return result | |
| # Fallback to mock data | |
| return MOCK_SEARCH.get(project_id, "No relevant external signals found.") | |
| def read_doc(doc_id: str) -> str: | |
| """Read a Google Doc by its document ID. Returns title and content.""" | |
| if not is_authenticated(): | |
| return "Not connected to Google. Please authenticate first." | |
| result = fetch_doc_content(doc_id) | |
| if not result["content"]: | |
| return f"Could not read document {doc_id}." | |
| return f"Title: {result['title']}\n\nContent:\n{result['content']}" | |
| def list_docs() -> str: | |
| """List recently modified Google Docs.""" | |
| if not is_authenticated(): | |
| return "Not connected to Google. Please authenticate first." | |
| docs = list_recent_docs(max_results=10) | |
| if not docs: | |
| return "No recent Google Docs found." | |
| lines = [] | |
| for d in docs: | |
| lines.append(f"- {d['title']} (ID: {d['id']}, modified: {d['modified_time']})") | |
| return "\n".join(lines) | |