montana/Русский/Логистика/CLAUDE.md

149 lines
10 KiB
Markdown

# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Project Overview
SeaFare Montana is a maritime logistics AI agent platform. A single-page web app with a Flask API backend, Claude API agent with tool-calling, and PostgreSQL database (SQLite in dev). Deployed on Render.com free tier (Python 3.11) with auto-deploy from `master`.
## Commands
```bash
# Run locally
python seafare_api.py # Starts Flask on port 5050
# Production (Render)
gunicorn seafare_api:app --bind 0.0.0.0:$PORT --workers 1 --threads 2 --timeout 120
# Check Python syntax before committing (all core files)
python -m py_compile seafare_api.py && python -m py_compile seafare_agent.py && python -m py_compile maritime_db.py && python -m py_compile moltbook_bot.py && python -m py_compile telegram_bot.py && python -m py_compile ais_provider.py && python -m py_compile equasis_parser.py && python -m py_compile marinetraffic_parser.py && python -m py_compile maritime_compliance.py
# Run tests
python -m pytest tests/ -v --tb=short
# Deploy: push to master triggers Render auto-deploy
git add <files> && git commit -m "message" && git push
```
Pre-commit hook runs automatically: syntax check → pytest → .env secret guard.
## Architecture
```
index.html ─── Single-page frontend (vanilla JS, Leaflet map, no framework)
↓ fetch /api/v1/*
seafare_api.py ─── Flask REST API (auth, wallet, chat, admin endpoints)
↓ generate_response()
seafare_agent.py ─── Claude API agent with 26 tools (tool-calling loop, max 7 iterations)
↓ _TOOL_DISPATCH dict → handler functions
maritime_db.py ─── Database layer (19 tables, PostgreSQL/SQLite dual-mode)
↑ external data
config.py ─── Centralized constants (version, rate limits, cache TTLs, fees, env var names)
marinetraffic_parser.py ─── Ports DB (16,553 ports) + routing engine + cargo classification + premium tool helpers
ais_provider.py ─── Unified AIS: AISStream WebSocket + AISHub REST + Digitraffic REST + MT fallback
equasis_parser.py ─── IMO vessel registry (authenticated web scraping)
maritime_compliance.py ─── Sanctions screening + dark fleet detection (standalone, no external deps)
moltbook_bot.py ─── Promotional bot for Moltbook platform (background daemon)
telegram_bot.py ─── Telegram bot: standalone service, group chats, vessel watch alerts
```
### AI Agent (`seafare_agent.py`)
Uses Claude `claude-sonnet-4-5-20250929` with tool-calling. The agent loop sends user message → Claude responds with `tool_use` blocks → `_execute_tool_inner()` runs them via `_TOOL_DISPATCH` dict → results sent back → repeats until `stop_reason != "tool_use"` (max 7 iterations via `AGENT_MAX_TOOL_ITERATIONS`). System prompt is action-oriented: agent detects request type and uses tools immediately, no filler text.
**Tools (26):** `search_vessel`, `get_vessel_details`, `get_position`, `search_vessels_near_port`, `calculate_route`, `find_vessels_for_cargo`, `calculate_demurrage`, `search_contacts`, `unlock_contacts`, `get_freight_rate`, `screen_sanctions`, `check_port_congestion`, `get_bunker_prices`, `optimize_bunker`, `generate_charter_party`, `vessel_performance`, `generate_bill_of_lading`, `optimize_crew_change`, `calculate_insurance`, `estimate_port_costs`, `weather_routing`, `generate_fixture_recap`, `detect_ais_anomaly`, `detect_dark_fleet`, `search_web`, `get_revenue`
`user_context` (profile summary + balance) is injected into the system prompt so the agent knows who it's talking to.
### AIS Data Priority Chain (`ais_provider.py`)
For single vessel position (`get_vessel_position`):
1. **DB cache** — fresh position (< 5 min)
2. **AISStream** persistent background thread OR sync fallback (8s WebSocket query in request thread)
3. **AISHub REST** on-demand query (1 min rate limit)
4. **Digitraffic** Finnish/Baltic waters (free, no key)
5. **MarineTraffic** scraping fallback
6. **Stale DB** old cache as last resort
**Important**: Daemon threads die on Render's gunicorn gthread workers. Sync fallback (`query_position_sync`, `query_area_sync`) runs inside Flask request threads and works reliably.
### Vessel Search Features
Both `search_vessels_near_port` and `find_vessels_for_cargo` use **expanding radius** search:
- Radius steps: 50 200 500 NM. First step uses full AIS provider (slow, ~6-8s). Wider steps use DB-only queries (instant).
- Always returns at least 3 vessels (MIN_VESSELS). Each vessel has `distance_nm` and `map_link`.
- `map_link` format: `{{SHOWMAP~lat~lon~zoom~name}}` rendered as clickable buttons in chat by `formatResponse()` step 7 in `index.html`. Uses `~` delimiter (not `|`) to avoid markdown table conflicts.
### Auth System (`seafare_api.py`)
Hybrid stateless + stateful: primary auth uses `itsdangerous` signed tokens (30-day TTL, survives Render DB wipes). Fallback to legacy UUID DB sessions. `auto_promote_admin()` checks `ADMIN_EMAILS` env var on every auth.
### Payment Flow
`search_contacts` free masked preview user confirms `unlock_contacts` `charge_user()` (atomic SQL `WHERE balance >= ?`) `add_service_charge()` `save_purchased_contacts()`. Previously purchased contacts are returned free via `has_purchased_contact()`.
### i18n (Dual)
- **Server-side**: `TEXTS` dict in `seafare_api.py` + `L(lang, key)` helper for API error messages
- **Client-side**: `i18n` JS object in `index.html` + `t(key)` helper for all UI labels. Three languages: EN, RU, ES. Always add keys to all three.
### Background Threads (`seafare_api.py`)
Two daemon threads auto-start at module level:
1. **Keep-alive** (`_start_keep_alive`): pings `/health` every 14 min to prevent sleep
2. **Moltbook bot** (`_start_moltbook_bot`): searches maritime posts on Moltbook every 30 min, generates expert comments via Claude Haiku
### Telegram Bot (`telegram_bot.py` — separate systemd service)
Runs as standalone process (`seafare-telegram.service`), NOT inside gunicorn (daemon threads die on fork with `--preload`).
**Features:** Full AI agent (26 tools), group chat support (@mention/reply/commands), vessel watch alerts (/watch, /unwatch, /watches), auto language detection, per-user rate limiting in groups.
**Vessel Watch:** Background checker thread polls DB positions every 10 min. Arrival watches (one-shot) fire when vessel enters port radius. Status watches (continuous) fire on nav status changes. Max 5 watches per chat, auto-expire 7 days. DB table: `vessel_watches`.
### Adding New Tools
1. Add helper function in `marinetraffic_parser.py` (or `maritime_compliance.py` for compliance tools)
2. Add tool definition to `TOOLS` list in `seafare_agent.py` (with `input_schema`)
3. Add handler function `_tool_<name>()` in `seafare_agent.py`
4. Add entry to `_TOOL_DISPATCH` dict in `seafare_agent.py`
5. Add trigger rule in `SYSTEM_PROMPT` BEHAVIOR section
6. Add UI service card in `index.html` + i18n keys in all 3 languages (EN/RU/ES)
## Key Conventions
- **Versioning**: `APP_VERSION` in `config.py` (semver). Bump with every commit: PATCH for fixes, MINOR for features, MAJOR for breaking changes. Displays in `/health` endpoint and sidebar footer.
- **Config centralization**: All constants live in `config.py` (rate limits, cache TTLs, fees, env var names). Import from there, don't hardcode.
- **Data sources are secret**: System prompt instructs agent to never reveal Equasis/MarineTraffic. Present data as "our maritime intelligence network."
- **Admin**: `is_admin` users get free access to all paid features. Check via `is_admin` flag on user dict.
- **Atomic balance operations**: Always use `charge_user()` (returns bool) never manually subtract balance.
- **DB migrations**: Use `CREATE TABLE IF NOT EXISTS` and `CREATE INDEX IF NOT EXISTS` in `init_db()`. No migration framework backward-compatible additions only.
- **.env contains secrets** never commit. Credentials are set via Render dashboard env vars in production.
- **Single HTML file**: All CSS, HTML, and JS live in `index.html`. No build step, no bundler.
- **Port database**: `world_ports.json` 16,553 ports from UN/LOCODE. Regenerate with `python merge_unlocode.py`.
- **Cargo classification**: `CARGO_TO_VESSEL` dict in `marinetraffic_parser.py` maps cargo types vessel categories (bulk, tanker, container, roro, general).
## Database (PostgreSQL + SQLite)
**Dual-mode**: `maritime_db.py` auto-detects `DATABASE_URL` env var PostgreSQL in production, SQLite locally. Wrapper classes (`_PgCursorWrapper`, `_PgConnectionWrapper`) translate SQLite-style `?` placeholders to `%s` and auto-add `RETURNING id` to INSERTs. **Always write SQL with `?` placeholders** the wrapper handles conversion.
**Tables (22):** `vessels`, `positions`, `contacts`, `port_calls`, `demurrage`, `users`, `sessions`, `chat_history`, `user_profiles`, `wallets`, `deposits`, `withdrawals`, `service_charges`, `purchased_contacts`, `port_vessel_cache`, `equasis_cache`, `equasis_daily_counter`, `query_log`, `moltbook_comments`, `user_memory`, `conversation_summaries`, `vessel_watches`
All user-related tables FK to `users(id)`. Vessel-related tables use `mmsi` as key.
## Environment Variables
| Variable | Purpose |
|----------|---------|
| `ANTHROPIC_API_KEY` | Claude API (required for AI agent + Moltbook bot) |
| `EQUASIS_USER` / `EQUASIS_PASS` | Equasis.org login |
| `GOOGLE_CLIENT_ID` | Google OAuth |
| `ADMIN_EMAILS` | Comma-separated admin emails (auto-promoted on login) |
| `SECRET_KEY` | Token signing (falls back to ANTHROPIC_API_KEY) |
| `MOLTBOOK_API_KEY` | Moltbook platform API key (bot disabled if not set) |
| `AISSTREAM_API_KEY` | AISStream.io WebSocket API key (real-time AIS) |
| `AISHUB_USERNAME` | AISHub.net contributor username (REST AIS queries) |
| `DIGITRAFFIC_ENABLED` | '1' (default) to enable Finnish Digitraffic AIS, '0' to disable |
| `TELEGRAM_BOT_TOKEN` | Telegram Bot API token from @BotFather (bot disabled if not set) |
| `DATABASE_URL` | PostgreSQL connection string (auto-set by Render; if absent SQLite) |