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

Контракт адаптера

Контракт адаптера — это контракт, который реализует каждый провайдер LLM, TTS, STT и изображений. Если вы напишете адаптер, удовлетворяющий ему, весь конвейер заработает с вашим провайдером.

Интерфейс

Интерфейс ProviderAdapter имеет стабильный kind, необязательные объявления модальностей и обязательные методы. Текстовая генерация — базовая возможность; методы речи, изображений и транскрипции необязательны, поэтому адаптер только для LLM всё равно остаётся валидным провайдером.

interface ProviderAdapter {
readonly kind: string;
readonly modalities?: readonly ProviderModality[];
readonly capabilities?: {
assistantPrefill?: boolean;
textCompletion?: boolean;
};
validateConfig(): Promise<ValidationResult>;
listModels(signal: AbortSignal): Promise<ModelInfo[]>;
generate(request: GenerationRequest, signal: AbortSignal): AsyncIterable<GenerationEvent>;
speech?(request: SpeechRequest, signal: AbortSignal): AsyncIterable<SpeechEvent>;
image?(request: ImageRequest, signal: AbortSignal): AsyncIterable<ImageEvent>;
transcribe?(request: TranscriptionRequest, signal: AbortSignal): Promise<TranscriptionResult>;
countTokens?(request: TokenCountRequest): Promise<TokenCount>;
}

Обязательное поведение

Контракт требует восемь поведений:

  • Проверка конфигурацииvalidateConfig() проверяет собственную конфигурацию адаптера без сетевых вызовов и возвращает список проблем.
  • Перечисление моделейlistModels(signal) возвращает доступные модели и должен уважать сигнал отмены.
  • Отмена — каждый длительный метод получает AbortSignal и должен прерываться незамедлительно при его срабатывании.
  • Единый поток событийgenerate() выдаёт поток типизированных GenerationEvent и должен завершаться ровно одним терминальным событием, done или error. Генерация речи и изображений использует ту же форму стриминга.
  • Нормализация ошибок — сбои провайдера сопоставляются со стабильными кодами AppError с машинночитаемыми кодами и параметрами. HTTP-статусы вышестоящей системы различаются (auth, rate limit, плохая модель, ошибка сервера), а сырые тела ответов вышестоящей системы никогда не пересылаются клиентам.
  • Таймауты — адаптер не должен полагаться только на сигнал вызывающей стороны. Ему нужны собственные дедлайны для соединения, тишины стриминга и чтения всего ответа. SDK поставляет ProviderTimeouts (по умолчанию: 30 с соединение, 60 с простой, 30 с чтение) и DeadlineController, который комбинирует сигнал вызывающей стороны с переустанавливаемыми дедлайнами и прерывается ошибкой TIMEOUT.
  • Безопасное логирование — API-ключ предоставляется из защищённого хранилища и никогда не должен логироваться или попадать в диагностику и вывод ошибок.
  • Регистрация — адаптеры регистрируются по виду, либо в основном реестре, либо через backend-API Plugin SDK.

Нейтральность к вендорам

Ядро не привязано ни к одному SDK вендора. От новых адаптеров ожидается использование глобального fetch и парсера SSE из SDK (parseSseStream) для стриминговых ответов.

Есть ровно одно документированное исключение: адаптер Anthropic использует @anthropic-ai/sdk, потому что API Anthropic — расширенное мышление и поддержку бета-заголовков — официальный SDK обрабатывает точнее, чем написанный вручную fetch-клиент. Это единственный адаптер, подключённый к библиотеке вендора; всё остальное говорит по HTTP напрямую.

Интеграция с хостом

ProviderRegistry сопоставляет виды провайдеров с фабриками адаптеров. register возвращает функцию отмены регистрации, create создаёт экземпляр адаптера и бросает PROVIDER_NOT_FOUND для неизвестных видов, а реестр также размещает локальный реестр токенизаторов. Объявленные wire-возможности вроде assistantPrefill используются для проверки профилей подключения — хост никогда не отбрасывает молча сохранённый оверрайд профиля, который адаптер не поддерживает.

О реальных поставляемых адаптерах и их назначении см. Адаптеры. О регистрации адаптера из плагина см. backend-API Plugin SDK.