Перейти к основному содержимому

Backend API плагина

Backend API — это то, что серверный плагин получает в своём вызове activate(): ограниченные абстракции для маршрутов, хранилища, событий, логирования, сетевого доступа, провайдеров и файлов — и ничего больше.

Точка входа

Backend-плагин экспортирует определение с функцией activate(api), которая получает объект ServerPluginApi:

import { definePlugin } from '@neotavern/plugin-sdk';

export default definePlugin({
activate(api) {
const off = api.routes.get('/hello', async (request) => ({
status: 200,
body: { hello: 'world' },
}));
},
});

Backend-точка входа выполняется как отдельный процесс Node.js. Плагин никогда не получает корневой экземпляр Fastify, соединение SQLite, внутренние таблицы, абсолютные пути, полное окружение или API-ключи других провайдеров.

Маршруты

api.routes — ограниченный роутер, монтируемый в /api/plugins/{pluginId}/. Каждый метод принимает путь и обработчик и возвращает функцию очистки:

  • api.routes.get(path, handler)
  • api.routes.post(path, handler)
  • api.routes.put(path, handler)
  • api.routes.delete(path, handler)

PluginRequest несёт params, query, headers, разобранное JSON-body и AbortSignal. PluginResponse — это { status, body, headers }. Обработчики могут возвращать значение напрямую или промис; хост обеспечивает таймауты и отменяет работу через сигнал.

Хранилище

api.storage — хранилище ключ/значение с пространством имён, изолированное для каждого плагина:

await api.storage.set('state', { count: 1 });
const state = await api.storage.get('state');
await api.storage.delete('state');
const keys = await api.storage.keys();

Данные ограничены идентификатором вашего плагина, поэтому два плагина не могут пересечься.

События и логирование

api.events — та же типизированная шина событий, что использует фронтенд. Подписка возвращает функцию отписки, а все подписки автоматически удаляются при отключении, сбое или завершении работы. Испускание ограничено вашим собственным пространством имён ({pluginId}.event), нагрузки должны быть безопасными для JSON, а хост ограничивает размер нагрузки и количество имён событий на рантайм.

api.logger предоставляет методы debug, info, warn и error, каждый из которых принимает сообщение и необязательные метаданные. Логи никогда не включают секреты.

Fetch с проверкой разрешений

api.fetch — это fetch, защищённый разрешениями network:<host> плагина:

const response = await api.fetch('https://api.example.com/data', {
method: 'GET',
headers: { Accept: 'application/json' },
signal,
});

Запросы к хостам, не получившим разрешение, отклоняются до любой сетевой активности. Секреты других провайдеров никогда не внедряются в ваши запросы. Объект ответа предоставляет ok, status, text() и json().

Провайдеры и стратегии контекста

api.providers позволяет плагину расширять генерацию:

  • api.providers.register(kind, factory, options) регистрирует новый вид адаптера провайдера (требует providers.register). Регистрация возвращает функцию очистки.
  • api.providers.registerTokenizer(profile) регистрирует локальный модельно-специфичный токенизатор. Профиль объявляет id, approximate, matches(model) и count(text). Точные токенизаторы можно построить из tiktoken, SentencePiece или JSON-токенизатора Hugging Face; пока такой не зарегистрирован для модели, хост переключается на эвристику с учётом письменности и помечает подсчёты как приближённые. Регистрация автоматически удаляется при деактивации.

api.contextStrategies.register(strategy) добавляет стратегию контекст-шифтинга. Хост проверяет, что системные, закреплённые блоки и блок текущего пользователя выживают, и применяет финальный бюджет токенов сам — значение fitsBudget, возвращаемое стратегией, не доверяется.

api.postProcessors.register(processor) добавляет хук пост-генерации. Он выполняется после завершения потока и до сохранения сообщения; возврат новой строки заменяет ответ ассистента. Требуется prompt.modify.

Виртуальная файловая система

api.files — песочная виртуальная файловая система с корнем в собственном каталоге данных плагина:

await api.files.write('notes.txt', 'content');
const content = await api.files.read('notes.txt');
const entries = await api.files.list('.');
await api.files.delete('notes.txt');

Пути не могут выйти за корень плагина, поэтому плагин может касаться только собственных данных.

Что backend-плагин не может делать

Поверхность API сознательно мала. Нет способа добраться до базы данных хоста, хранилища других плагинов, произвольных путей файловой системы или непроверенных сетевых хостов. Если SDK это не предоставляет, значит, это недоступно. Генерируемый справочник Plugin SDK перечисляет полную поверхность ServerPluginApi, а Провайдеры объясняют, как провайдерные плагины вписываются в модель.