Правила хоста
Хост — общая половина системы плагинов: обнаружение и загрузка файлов, передача аргументов, семантика возврата, изоляция сбоев, порядок, перезагрузка, эпохи, интроспекция, версии и dry-run. Это общий контракт семейства; реализации встроены в HelpSearchServer, SSLSearchServer, SyntaxCheckServer, TemplatesSearchServer, Graph Metadata Search и 1CCodeChecker. Различается только набор хуков — он в справочнике по серверам.
Загрузка файла
Плагин — один файл, без церемоний. Загрузка не требует базового класса, декоратора, регистрации, записи в манифесте и импорта. Файл, не объявивший ни хука, ни таблицы, загружается успешно и ничего не меняет.
Имена хуков — закрытый набор. Разрешены ровно те имена, которые объявил продукт. Имя верхнего уровня, похожее на хук, но отсутствующее в наборе, отклоняет загрузку всего файла, называя нарушителя и перечисляя допустимые имена. Сервер при этом стартует с остальными плагинами. Функция, чьё имя не похоже на имя хука, — ваш собственный помощник, её не трогают.
Файлы читаются как UTF-8, с BOM или без. Другая кодировка отклоняет файл с указанием имени и правила, а не угадывается.
Порядок детерминирован и объявлен. Файлы выполняются в отсортированном порядке имён, каждый хук применяется конвейером: выход одного файла — вход следующего. Действующий порядок показывает интроспекция.
Аргументы и возврат
Аргументы передаются по объявленным именам. Хук получает ровно те аргументы, которые названы в его сигнатуре, выбранные по имени из предложенных для этого хука. def on_result(result): и def on_result(result, request): — оба корректны. Имя параметра, которое хост не может предоставить, отклоняет файл при загрузке, а не при первом вызове.
Возврат — единственный канал. Хук получает копию payload, применяется только возвращённое значение. Возврат None (или выход из функции без return) оставляет payload неизменным — это нормальный способ сказать «не мой случай». Изменение аргумента без возврата не даёт никакого эффекта.
В хуки передаются данные, а не живые объекты. Каждое значение — примитив, список или словарь. Из аргумента хука недостижимы ни хранилище, ни модель, ни соединение, ни поколение индекса, ни объект сервера.
Изоляция сбоев
Упавший плагин никогда не роняет сервер. Исключение перехватывается, пишется в журнал с файлом, именем хука и трассировкой, и не распространяется дальше. Сервер продолжает так, как если бы хук вернул None.
Упавший call-scoped хук отключается до следующей перезагрузки и показывается как отключённый со своей ошибкой и счётчиком сбоев.
Упавший derived-state хук не оставляет отдельную единицу наполовину преобразованной: по умолчанию она берётся неизменённой, сбой считается и попадает в сводку. В строгом режиме (
PLUGIN_STRICT_DERIVED_STATE, у Graph —GRAPH_PLUGIN_STRICT_BUILD) сборка падает целиком. Сохраняется ли прежнее поколение в обслуживании, зависит от механизма перестроения конкретного сервера — см. предупреждение ниже.Файл, который не загрузился вообще, не мешает старту: сервер поднимается и называет файл и причину.
Строгий режим — инструмент отладки. TemplatesSearchServer строит новое поколение рядом с обслуживающим, проверяет его и только затем атомарно переключает указатель; прежнее поколение остаётся rollback target. У SSLSearchServer это нельзя обобщать на любую инвалидизацию: обычное изменение plugin/normalization/chunking/extraction проходит через recreate_collection, а соседние поколения гарантируются отдельной операторской миграцией MIGRATE_VECTOR_STORE. Перед изменением derived-state хука конкретного продукта проверьте его раздел индексации.
Границы производного состояния
Derived-state хуки участвуют в отпечатке годности. Там, где сервер решает, актуально ли сохранённое производное состояние, сравнивая отпечаток породивших его правил, исходник каждого derived-state хука входит в этот отпечаток. Правка такого файла делает сохранённое состояние устаревшим: следующий старт пересобирает его. Серверы с поколенческой схемой продолжают обслуживать прежнее поколение до проверки и promotion; для SSL обычная инвалидизация и операторская миграция имеют разную семантику, описанную выше. Манифест состояния перечисляет плагины, которые его сформировали, с отпечатком исходника каждого.
Call-scoped хуки не инвалидируют производное состояние. Их правка не входит ни в один отпечаток и не вызывает пересборки.
Токены продолжения несут эпоху плагинов. Там, где сервер выдаёт курсор для продолжения ответа, курсор привязан к действовавшему набору плагинов так же, как к поколению индекса и запросу. Курсор, предъявленный в другой эпохе, отклоняется как принадлежащий устаревшему состоянию (например, кодом stale_cursor), и это означает «искать заново», а не «продолжать». Пока эпоха держится, страницы не дублируются и ничего не теряется.
Контракт ответа
Опубликованный контракт ответа применяется после хуков:
объявленные пределы (
max_items,max_chars,top_k, лимиты бюджета) измеряются после последнего хука;счётчики в ответе пересчитываются из фактически возвращённого — счёт никогда не принимается на веру;
payload проверяется по схеме ответа продукта.
Результат плагина, нарушающий контракт, отбрасывается в пользу неизменённого, а нарушение пишется в журнал с именем файла и проблемной записью. Практическое следствие: хук может переупорядочивать и отбрасывать, но не может добавить запись, которой сервер не производил, продублировать её или расширить ответ за опубликованные границы.
Изменённый запрос виден вызывающей стороне. Если хук поменял аргументы вызова, ответ сообщает и то, что прислал клиент, и то, что было использовано, и файл плагина, который это сделал.
Классификация приватности распространяется на плагины. Поля, которые продукт считает чувствительными, остаются такими и внутри плагина, включая значения, произведённые плагином. Секреты не приходят в on_startup в открытом виде: часть серверов подменяет их на <redacted> / ***redacted***, Graph Metadata Search передаёт булево «задано или нет».
Версии и совместимость
Константа в plugin_api.py
Что версионирует
HOST_CONTRACT_VERSION
Общий контракт хоста — правила этой страницы. Одинаков во всех серверах семейства
PRODUCT
Идентификатор продукта: 1c-help, ssl-search, bsl-syntax-check, template-search, graph-metadata-search, onec-code-checker
HOOKS_VERSION
Набор хуков и таблиц конкретного продукта; поднимается, когда payload меняет форму так, что плагин этого не переживёт
Плагин может закрепить любую из трёх через REQUIRES. Это необязательно, и единственная причина так делать — быть отклонённым рано и внятно:
Плагин, написанный для другого продукта семейства, отклоняется с указанием обоих продуктов, а не сообщением о пяти неизвестных именах функций. Плагин, требующий версию набора хуков, которую продукт больше не предлагает, отклоняется с указанием обеих версий.
Интроспекция и перезагрузка
Перезагрузка без перезапуска. Каталог перечитывается целиком, действующие хуки заменяются атомарно, начинается новая эпоха плагинов. Неудачная перезагрузка не меняет ничего: прежний набор остаётся в силе, ответ называет файл и причину. Derived-state хуки вступают в силу со следующего построения, а не задним числом.
Загруженные плагины интроспектируемы. Состояние подсистемы показывает: каждый файл, объявленные им хуки и таблицы, классификацию каждого хука, включён он или отключён, ошибку, которая его отключила, счётчик сбоев, накопленное в каждом хуке время, порядок файлов и текущую эпоху.
Бюджет времени хука — продуктовое дополнение: Graph Metadata Search ограничивает один hook значением GRAPH_PLUGIN_HOOK_TIMEOUT_SECONDS (0 отключает контроль), CodeMetadataSearchServer — значением PLUGIN_HOOK_TIMEOUT_SECONDS (по умолчанию 5.0; превысивший бюджет хук считается упавшим и call-scoped отключается до следующей перезагрузки каталога). Остальные серверы бюджета не навязывают, но накопленное время каждого хука показывают.
Упаковка и dry-run
plugin_api.py поставляется читаемым исходником в runtime-образе и является полным справочником: форма payload и рабочий пример для каждого хука и таблицы продукта. Он не импортирует ничего вне стандартной библиотеки. Скомпилированные модули собраны без встроенных docstring — описания реализации в бинарниках нет.
Каталог плагинов самоописывающийся. В нём лежат инструкция для агента (AGENTS.md) и закомментированный пример (example.py), объявляющий все хуки и таблицы продукта.
Плагин можно попробовать без данных и без сборки. Каждый образ содержит команду, которая прогоняет хуки файла на зашитых в образ фикстурах и печатает payload до и после — без смонтированных данных, без построенного состояния и без запущенного сервера. Ошибку загрузки dry-run сообщает теми же словами, что и сервер. Команды по серверам — в Как написать плагин.
Чего система не обещает
Хук — это Python внутри процесса сервера, поэтому плагин может прочитать всё, что может процесс. Компиляция модулей повышает стоимость чтения реализации, но не останавливает плагин. Система не защищает реализацию от автора плагина — она гарантирует, что плагин не испортит производное состояние, не нарушит опубликованный контракт ответа и не уронит сервер.
Last updated