# Contracts — общие схемы Каталог [`contracts/`](../contracts/) — shared truth для формы данных и маршрутизации между ЦА и ЦП. Правило платформы: **сначала contracts**, потом реализация в CA/CP/UI. --- ## Модули | Файл | Назначение | |------|------------| | [`jobs.py`](../contracts/jobs.py) | Контракт задания. Payload задания в Redis | | [`ingest.py`](../contracts/ingest.py) |Контракт результата. Событие и пакет ingest | | [`sources.py`](../contracts/sources.py) | Контракт настроек парсера. Схемы `source_config` по `source_type` | | [`queues.py`](../contracts/queues.py) | Контракт доставки задания нужному воркеру. `source_type` → family → Redis key | ЦА при admin CRUD валидирует конфиг через `parse_source_config`. ЦП адаптеры должны отдавать dict, совместимые с `IngestEventItem`. > На практике CA дублирует часть DTO в `app/schemas.py` для FastAPI; при изменении формы сверяйте оба места и UI. --- ## JobPayload (`jobs.py`) ```python class JobPayload(BaseModel): job_id: int source_type: str source_config: dict = {} ``` Уходит в Redis как JSON. Семейство очереди выбирается по `source_type`, не по содержимому config. --- ## Ingest (`ingest.py`) Ключевые поля `IngestEventItem`: | Поле | Обязательность | Заметка | |------|----------------|---------| | `source_url` | да | Стабильный URL; дедуп в ЦА | | `source_type` | да (часто default) | Должен соответствовать адаптеру | | `raw_text` / `title` / `description` | нет | Текст события | | `latitude` / `longitude` | нет | Без них точка на карту не создаётся | | `event_date`, `locality`, `region`, `topic` | нет | Фильтры карты и ПИ | | `tags`, `metadata` | нет | Расширения | Пакет: `{ job_id?, events: [...] }`. Listener добавляет флаг `listener: true` на стороне CA API (см. internal schemas). --- ## Source config (`sources.py`) | source_type | Модель | Главные поля | |-------------|--------|--------------| | `telegram` | `TelegramSourceConfig` | `channel`, `limit`, `extract_mode` (`heuristic`\|`llm`\|`profile`), `heuristic_profile` | | `crawl4ai` | `Crawl4AISourceConfig` | `urls`, `extract_mode`, `extract_schema`, `domain_profile` | | `viina` | `ViinaSourceConfig` | `urls` / `texts`, `input_mode` | Реестр: `CONFIG_MODELS` + `parse_source_config(source_type, raw)`. Профиль конструктора: `contracts/heuristic_profile.py` (`HeuristicProfile`, `apply_profile`). --- ## Очереди (`queues.py`) ```text SOURCE_FAMILY = { "telegram": "telegram", # cp:jobs:telegram "crawl4ai": "web", # cp:jobs:web "viina": "nlp", # cp:jobs:nlp } ``` - `queue_key_for_source(source_type)` — куда enqueue из ЦА. - Legacy-ключ `cp:jobs` ещё может дренироваться telegram-воркерами (совместимость). При добавлении нового `source_type` обязательно: 1. схема в `sources.py`; 2. запись в `SOURCE_FAMILY`; 3. адаптер + registry + (при необходимости) новый Docker-сервис; 4. поля формы в `ParsersView.vue`. Чеклист: [`.cursor/skills/add-parser-adapter/`](../.cursor/skills/add-parser-adapter/).