Files
null-bot/AGENTS.md

50 lines
2.6 KiB
Markdown

# AGENTS.md
## Run
```
python main.py
```
Uses `runpy` to exec `bot.py`. No tests, no lint, no CI.
## Architecture
- `bot.py` — Telegram bot (python-telegram-bot), single-worker `asyncio.Queue`, all crawl/parse serialized
- `agent.py` — pydantic-ai with local Ollama (`granite4.1:8b`), three agents (opportunity/event/workshop), `output_type=str`
- `database.py` — PocketBase client, collections `events` / `opportunities` / `workshops`
- `scraper.py` — crawl4ai `AsyncWebCrawler` URL → markdown
- `prompts.py` — system prompts for both agent types
- `schemas.py`**unused at runtime**, per file comment: "File doesn't do anything, its just an outline"
## Windows event-loop quirk
DO NOT remove the dual-event-loop pattern in `bot.py`:
1. PTB runs on `WindowsSelectorEventLoopPolicy` (line 357)
2. Scraper runs in a **separate thread** with `WindowsProactorEventLoopPolicy` (line 25) — required for Playwright subprocesses on Windows
The `get_clean_content()` wrapper in `bot.py` runs `_run_scraper_in_thread` via `ThreadPoolExecutor`. Scraper functions must not be called directly.
## Agent output format
Agents use `output_type=str` (not structured output). JSON is parsed manually from `agent.run()` result:
- LLM returns JSON wrapped in `````json ... ````` markdown backticks
- `agent.py:56` strips the backtick wrappers before `json.loads()`
- The markdown wrapper is specified in `prompts.py` — don't change prompt format without updating the strip logic
## Date formats
- Prompts instruct LLM: `DD-MM-YYYY` (or `DD-MM-YYYY (HH:MM)` for events)
- PocketBase expects: `YYYY-MM-DD HH:MM:SS`
- `database.py` field mapping: event `date_time` → PocketBase `datetime`, `end_date``end_datetime`; workshop same; opportunity `deadline` stays `deadline`
## PocketBase collections
- `events` — fields: `title`, `org`, `datetime`, `end_datetime`, `summary`, `location`, `url`
- `opportunities` — fields: `title`, `org`, `type`, `deadline`, `summary`, `location`, `url`
- `workshops` — fields: `title`, `org`, `datetime`, `end_datetime`, `summary`, `location`, `url`
- Auth uses admin credentials from `.env`
## Env vars
`.env` required: `TG_TOKEN`, `OLLAMA_BASE_URL`, `ALLOWED_USERS`, `POCKETBASE_URL`, `POCKETBASE_ADMIN_EMAIL`, `POCKETBASE_ADMIN_PASSWORD`
## Auth
`ALLOWED_USERS` — comma-separated Telegram user IDs (no brackets). Checked on every command/message handler.
## Save flow
When source is pasted text (not URL), the bot asks for a source URL before saving. `context.user_data` keys: `last_extracted`, `last_source_value`, `last_source_kind`, `last_entry_type`, `awaiting_save_url`, `pending_save_url`, `pending_url_to_process`.