Plugins
Створення плагінів провайдерів
Цей посібник покроково показує, як створити provider plugin, що додає model provider (LLM) до OpenClaw. Наприкінці у вас буде provider з каталогом моделей, автентифікацією за API-ключем і динамічним визначенням моделей.
Покроковий посібник
Пакет і маніфест
Крок 1: Пакет і маніфест
{
"name": "@myorg/openclaw-acme-ai",
"version": "1.0.0",
"type": "module",
"openclaw": {
"extensions": ["./index.ts"],
"providers": ["acme-ai"],
"compat": {
"pluginApi": ">=2026.3.24-beta.2",
"minGatewayVersion": "2026.3.24-beta.2"
},
"build": {
"openclawVersion": "2026.3.24-beta.2",
"pluginSdkVersion": "2026.3.24-beta.2"
}
}
}
{
"id": "acme-ai",
"name": "Acme AI",
"description": "Acme AI model provider",
"providers": ["acme-ai"],
"modelSupport": {
"modelPrefixes": ["acme-"]
},
"providerAuthEnvVars": {
"acme-ai": ["ACME_AI_API_KEY"]
},
"providerAuthAliases": {
"acme-ai-coding": "acme-ai"
},
"providerAuthChoices": [
{
"provider": "acme-ai",
"method": "api-key",
"choiceId": "acme-ai-api-key",
"choiceLabel": "Acme AI API key",
"groupId": "acme-ai",
"groupLabel": "Acme AI",
"cliFlag": "--acme-ai-api-key",
"cliOption": "--acme-ai-api-key <key>",
"cliDescription": "Acme AI API key"
}
],
"configSchema": {
"type": "object",
"additionalProperties": false
}
}
Маніфест оголошує providerAuthEnvVars, щоб OpenClaw міг виявляти
облікові дані без завантаження runtime вашого plugin. Додайте providerAuthAliases,
коли варіант provider має повторно використовувати автентифікацію іншого provider id. modelSupport
є необов’язковим і дає OpenClaw змогу автоматично завантажувати ваш provider plugin зі скорочених
model ids на кшталт acme-large ще до появи runtime hooks. Якщо ви публікуєте
provider у ClawHub, ці поля openclaw.compat і openclaw.build
обов’язкові в package.json.
Зареєструйте provider
Мінімальному provider потрібні id, label, auth і catalog:
import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";
import { createProviderApiKeyAuthMethod } from "openclaw/plugin-sdk/provider-auth";
export default definePluginEntry({
id: "acme-ai",
name: "Acme AI",
description: "Acme AI model provider",
register(api) {
api.registerProvider({
id: "acme-ai",
label: "Acme AI",
docsPath: "/providers/acme-ai",
envVars: ["ACME_AI_API_KEY"],
auth: [
createProviderApiKeyAuthMethod({
providerId: "acme-ai",
methodId: "api-key",
label: "Acme AI API key",
hint: "API key from your Acme AI dashboard",
optionKey: "acmeAiApiKey",
flagName: "--acme-ai-api-key",
envVar: "ACME_AI_API_KEY",
promptMessage: "Enter your Acme AI API key",
defaultModel: "acme-ai/acme-large",
}),
],
catalog: {
order: "simple",
run: async (ctx) => {
const apiKey =
ctx.resolveProviderApiKey("acme-ai").apiKey;
if (!apiKey) return null;
return {
provider: {
baseUrl: "https://api.acme-ai.com/v1",
apiKey,
api: "openai-completions",
models: [
{
id: "acme-large",
name: "Acme Large",
reasoning: true,
input: ["text", "image"],
cost: { input: 3, output: 15, cacheRead: 0.3, cacheWrite: 3.75 },
contextWindow: 200000,
maxTokens: 32768,
},
{
id: "acme-small",
name: "Acme Small",
reasoning: false,
input: ["text"],
cost: { input: 1, output: 5, cacheRead: 0.1, cacheWrite: 1.25 },
contextWindow: 128000,
maxTokens: 8192,
},
],
},
};
},
},
});
},
});
Це робочий provider. Тепер користувачі можуть
openclaw onboard --acme-ai-api-key <key> і вибрати
acme-ai/acme-large як свою модель.
Якщо upstream provider використовує інші контрольні токени, ніж OpenClaw, додайте невелике двонапрямне перетворення тексту замість заміни stream path:
api.registerTextTransforms({
input: [
{ from: /red basket/g, to: "blue basket" },
{ from: /paper ticket/g, to: "digital ticket" },
{ from: /left shelf/g, to: "right shelf" },
],
output: [
{ from: /blue basket/g, to: "red basket" },
{ from: /digital ticket/g, to: "paper ticket" },
{ from: /right shelf/g, to: "left shelf" },
],
});
input переписує фінальний системний промпт і текстовий вміст повідомлень перед
transport. output переписує assistant text deltas і фінальний текст перед тим,
як OpenClaw розбирає власні контрольні маркери або channel delivery.
Для bundled providers, які реєструють лише один text provider з API-key
auth і єдиним catalog-backed runtime, віддавайте перевагу вужчому
helper defineSingleProviderPluginEntry(...):
import { defineSingleProviderPluginEntry } from "openclaw/plugin-sdk/provider-entry";
export default defineSingleProviderPluginEntry({
id: "acme-ai",
name: "Acme AI",
description: "Acme AI model provider",
provider: {
label: "Acme AI",
docsPath: "/providers/acme-ai",
auth: [
{
methodId: "api-key",
label: "Acme AI API key",
hint: "API key from your Acme AI dashboard",
optionKey: "acmeAiApiKey",
flagName: "--acme-ai-api-key",
envVar: "ACME_AI_API_KEY",
promptMessage: "Enter your Acme AI API key",
defaultModel: "acme-ai/acme-large",
},
],
catalog: {
buildProvider: () => ({
api: "openai-completions",
baseUrl: "https://api.acme-ai.com/v1",
models: [{ id: "acme-large", name: "Acme Large" }],
}),
buildStaticProvider: () => ({
api: "openai-completions",
baseUrl: "https://api.acme-ai.com/v1",
models: [{ id: "acme-large", name: "Acme Large" }],
}),
},
},
});
buildProvider — це live catalog path, який використовується, коли OpenClaw може визначити реальну
автентифікацію provider. Він може виконувати provider-specific discovery. Використовуйте
buildStaticProvider лише для offline rows, які безпечно показувати до налаштування auth;
він не повинен вимагати облікових даних або виконувати мережеві запити.
Відображення models list --all в OpenClaw наразі виконує static catalogs
лише для bundled provider plugins, з порожньою конфігурацією, порожнім env і без
agent/workspace paths.
Якщо ваш auth flow також має змінювати models.providers.*, aliases і
agent default model під час onboarding, використовуйте preset helpers з
openclaw/plugin-sdk/provider-onboard. Найвужчі helpers:
createDefaultModelPresetAppliers(...),
createDefaultModelsPresetAppliers(...) і
createModelCatalogPresetAppliers(...).
Коли нативний endpoint provider підтримує streamed usage blocks на
звичайному transport openai-completions, віддавайте перевагу shared catalog helpers у
openclaw/plugin-sdk/provider-catalog-shared замість жорстко заданих
перевірок provider-id. supportsNativeStreamingUsageCompat(...) і
applyProviderNativeStreamingUsageCompat(...) визначають підтримку з
endpoint capability map, тому нативні endpoint-и у стилі Moonshot/DashScope все одно
вмикаються, навіть коли plugin використовує кастомний provider id.
Додайте динамічне визначення моделей
Якщо ваш provider приймає довільні model IDs (наприклад proxy або router),
додайте resolveDynamicModel:
api.registerProvider({
// ... id, label, auth, catalog from above
resolveDynamicModel: (ctx) => ({
id: ctx.modelId,
name: ctx.modelId,
provider: "acme-ai",
api: "openai-completions",
baseUrl: "https://api.acme-ai.com/v1",
reasoning: false,
input: ["text"],
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
contextWindow: 128000,
maxTokens: 8192,
}),
});
Якщо визначення потребує мережевого виклику, використовуйте prepareDynamicModel для async
warm-up - resolveDynamicModel запускається знову після його завершення.
Додайте runtime hooks (за потреби)
Більшості providers потрібні лише catalog + resolveDynamicModel. Додавайте hooks
поступово, коли вони потрібні вашому provider.
Shared helper builders тепер покривають найпоширеніші сімейства replay/tool-compat, тож plugins зазвичай не потрібно вручну під’єднувати кожен hook окремо:
import { buildProviderReplayFamilyHooks } from "openclaw/plugin-sdk/provider-model-shared";
import { buildProviderStreamFamilyHooks } from "openclaw/plugin-sdk/provider-stream";
import { buildProviderToolCompatFamilyHooks } from "openclaw/plugin-sdk/provider-tools";
const GOOGLE_FAMILY_HOOKS = {
...buildProviderReplayFamilyHooks({ family: "google-gemini" }),
...buildProviderStreamFamilyHooks("google-thinking"),
...buildProviderToolCompatFamilyHooks("gemini"),
};
api.registerProvider({
id: "acme-gemini-compatible",
// ...
...GOOGLE_FAMILY_HOOKS,
});
Доступні replay families сьогодні:
| Family | Що під’єднується | Bundled examples |
|---|---|---|
openai-compatible |
Shared OpenAI-style replay policy для OpenAI-compatible transports, зокрема tool-call-id sanitation, виправлення assistant-first ordering і generic Gemini-turn validation там, де це потрібно transport | moonshot, ollama, xai, zai |
anthropic-by-model |
Claude-aware replay policy, вибрана за modelId, тож Anthropic-message transports отримують Claude-specific thinking-block cleanup лише тоді, коли визначена модель справді є Claude id |
amazon-bedrock, anthropic-vertex |
google-gemini |
Native Gemini replay policy плюс bootstrap replay sanitation і tagged reasoning-output mode | google, google-gemini-cli |
passthrough-gemini |
Gemini thought-signature sanitation для Gemini models, що працюють через OpenAI-compatible proxy transports; не вмикає native Gemini replay validation або bootstrap rewrites | openrouter, kilocode, opencode, opencode-go |
hybrid-anthropic-openai |
Hybrid policy для providers, які змішують Anthropic-message і OpenAI-compatible model surfaces в одному plugin; необов’язкове Claude-only thinking-block dropping залишається обмеженим Anthropic side | minimax |
Доступні сімейства потоків сьогодні:
| Сімейство | Що воно підключає | Вбудовані приклади |
|---|---|---|
google-thinking |
Нормалізація thinking-навантаження Gemini у спільному шляху потоку | google, google-gemini-cli |
kilocode-thinking |
Обгортка reasoning Kilo у спільному шляху proxy-потоку, де kilo/auto і непідтримувані proxy reasoning ids пропускають інʼєктоване thinking |
kilocode |
moonshot-thinking |
Зіставлення бінарного native-thinking-навантаження Moonshot із конфігурації + рівня /think |
moonshot |
minimax-fast-mode |
Переписування моделі fast-mode MiniMax у спільному шляху потоку | minimax, minimax-portal |
openai-responses-defaults |
Спільні нативні обгортки OpenAI/Codex Responses: заголовки attribution, /fast/serviceTier, докладність тексту, нативний вебпошук Codex, формування reasoning-compat навантаження та керування контекстом Responses |
openai, openai-codex |
openrouter-thinking |
Обгортка reasoning OpenRouter для proxy-маршрутів, із централізованою обробкою пропусків для непідтримуваних моделей/auto |
openrouter |
tool-stream-default-on |
Обгортка tool_stream, увімкнена за замовчуванням, для постачальників на кшталт Z.AI, яким потрібен streaming інструментів, якщо його явно не вимкнено |
zai |
SDK seams, що живлять побудовники сімейств
Кожен побудовник сімейства складено з нижчорівневих публічних помічників, експортованих із того самого пакета; до них можна звернутися, коли постачальнику потрібно відійти від спільного шаблону:
openclaw/plugin-sdk/provider-model-shared-ProviderReplayFamily,buildProviderReplayFamilyHooks(...)і сирі побудовники replay (buildOpenAICompatibleReplayPolicy,buildAnthropicReplayPolicyForModel,buildGoogleGeminiReplayPolicy,buildHybridAnthropicOrOpenAIReplayPolicy). Також експортує помічники replay для Gemini (sanitizeGoogleGeminiReplayHistory,resolveTaggedReasoningOutputMode) і помічники endpoint/model (resolveProviderEndpoint,normalizeProviderId,normalizeGooglePreviewModelId,normalizeNativeXaiModelId).openclaw/plugin-sdk/provider-stream-ProviderStreamFamily,buildProviderStreamFamilyHooks(...),composeProviderStreamWrappers(...), а також спільні обгортки OpenAI/Codex (createOpenAIAttributionHeadersWrapper,createOpenAIFastModeWrapper,createOpenAIServiceTierWrapper,createOpenAIResponsesContextManagementWrapper,createCodexNativeWebSearchWrapper), OpenAI-сумісну обгортку DeepSeek V4 (createDeepSeekV4OpenAICompatibleThinkingWrapper), очищення thinking prefill для Anthropic Messages (createAnthropicThinkingPrefillPayloadWrapper) і спільні proxy/provider обгортки (createOpenRouterWrapper,createToolStreamWrapper,createMinimaxFastModeWrapper).openclaw/plugin-sdk/provider-tools-ProviderToolCompatFamily,buildProviderToolCompatFamilyHooks("gemini"), базові помічники схем Gemini (normalizeGeminiToolSchemas,inspectGeminiToolSchemas) і помічники сумісності xAI (resolveXaiModelCompatPatch(),applyXaiModelCompat(model)). Вбудований xAI Plugin використовуєnormalizeResolvedModel+contributeResolvedModelCompatразом із ними, щоб правила xAI залишалися у власності постачальника.
Деякі потокові помічники навмисно залишаються локальними для постачальника. @openclaw/anthropic-provider тримає wrapAnthropicProviderStream, resolveAnthropicBetas, resolveAnthropicFastMode, resolveAnthropicServiceTier і нижчорівневі побудовники обгорток Anthropic у власному публічному seam api.ts / contract-api.ts, бо вони кодують обробку Claude OAuth beta та gating context1m. xAI Plugin так само тримає формування нативних xAI Responses у власному wrapStreamFn (псевдоніми /fast, типовий tool_stream, очищення непідтримуваних strict-tool, видалення reasoning-навантаження, специфічного для xAI).
Той самий шаблон package-root також підтримує @openclaw/openai-provider (побудовники постачальника, помічники моделей за замовчуванням, побудовники realtime-постачальника) і @openclaw/openrouter-provider (побудовник постачальника плюс помічники onboarding/config).
Обмін токена
Для постачальників, яким потрібен обмін токена перед кожним викликом inference:
prepareRuntimeAuth: async (ctx) => {
const exchanged = await exchangeToken(ctx.apiKey);
return {
apiKey: exchanged.token,
baseUrl: exchanged.baseUrl,
expiresAt: exchanged.expiresAt,
};
},
Користувацькі заголовки
Для постачальників, яким потрібні користувацькі заголовки запиту або зміни тіла:
// wrapStreamFn returns a StreamFn derived from ctx.streamFn
wrapStreamFn: (ctx) => {
if (!ctx.streamFn) return undefined;
const inner = ctx.streamFn;
return async (params) => {
params.headers = {
...params.headers,
"X-Acme-Version": "2",
};
return inner(params);
};
},
Нативна ідентичність транспорту
Для постачальників, яким потрібні нативні заголовки або метадані запиту/сеансу на загальних HTTP- або WebSocket-транспортах:
resolveTransportTurnState: (ctx) => ({
headers: {
"x-request-id": ctx.turnId,
},
metadata: {
session_id: ctx.sessionId ?? "",
turn_id: ctx.turnId,
},
}),
resolveWebSocketSessionPolicy: (ctx) => ({
headers: {
"x-session-id": ctx.sessionId ?? "",
},
degradeCooldownMs: 60_000,
}),
Використання та білінг
Для постачальників, які надають дані використання/білінгу:
resolveUsageAuth: async (ctx) => {
const auth = await ctx.resolveOAuthToken();
return auth ? { token: auth.token } : null;
},
fetchUsageSnapshot: async (ctx) => {
return await fetchAcmeUsage(ctx.token, ctx.timeoutMs);
},
Усі доступні hooks постачальника
OpenClaw викликає hooks у такому порядку. Більшість постачальників використовує лише 2-3:
Поля постачальника лише для сумісності, які OpenClaw більше не викликає, як-от
ProviderPlugin.capabilities і suppressBuiltInModel, тут не наведені.
| # | Hook | Коли використовувати |
|---|---|---|
| 1 | catalog |
Каталог моделей або значення base URL за замовчуванням |
| 2 | applyConfigDefaults |
Глобальні значення за замовчуванням, що належать постачальнику, під час матеріалізації конфігурації |
| 3 | normalizeModelId |
Очищення legacy/preview псевдонімів model-id перед lookup |
| 4 | normalizeTransport |
Очищення api / baseUrl сімейства постачальника перед загальним складанням моделі |
| 5 | normalizeConfig |
Нормалізація конфігурації models.providers.<id> |
| 6 | applyNativeStreamingUsageCompat |
Compat-переписування нативного streaming-usage для config-постачальників |
| 7 | resolveConfigApiKey |
Розвʼязання auth через env-marker, що належить постачальнику |
| 8 | resolveSyntheticAuth |
Локальна/самохостингова або config-backed synthetic auth |
| 9 | shouldDeferSyntheticProfileAuth |
Пониження synthetic stored-profile placeholders за env/config auth |
| 10 | resolveDynamicModel |
Прийняття довільних upstream model IDs |
| 11 | prepareDynamicModel |
Асинхронне отримання metadata перед розвʼязанням |
| 12 | normalizeResolvedModel |
Переписування транспорту перед runner |
| 13 | contributeResolvedModelCompat |
Compat-прапорці для моделей постачальника за іншим сумісним транспортом |
| 14 | normalizeToolSchemas |
Очищення tool-schema, що належить постачальнику, перед реєстрацією |
| 15 | inspectToolSchemas |
Діагностика tool-schema, що належить постачальнику |
| 16 | resolveReasoningOutputMode |
Контракт tagged vs native reasoning-output |
| 17 | prepareExtraParams |
Параметри запиту за замовчуванням |
| 18 | createStreamFn |
Повністю користувацький транспорт StreamFn |
| 19 | wrapStreamFn |
Користувацькі обгортки заголовків/тіла у звичайному шляху потоку |
| 20 | resolveTransportTurnState |
Нативні per-turn заголовки/метадані |
| 21 | resolveWebSocketSessionPolicy |
Нативні заголовки/cool-down WS-сеансу |
| 22 | formatApiKey |
Користувацька форма runtime-токена |
| 23 | refreshOAuth |
Користувацьке оновлення OAuth |
| 24 | buildAuthDoctorHint |
Настанови з відновлення auth |
| 25 | matchesContextOverflowError |
Виявлення overflow, що належить постачальнику |
| 26 | classifyFailoverReason |
Класифікація rate-limit/overload, що належить постачальнику |
| 27 | isCacheTtlEligible |
Gating TTL prompt cache |
| 28 | buildMissingAuthMessage |
Користувацька підказка про відсутню auth |
| 29 | augmentModelCatalog |
Synthetic forward-compat рядки |
| 30 | resolveThinkingProfile |
Набір опцій /think, специфічний для моделі |
| 31 | isBinaryThinking |
Сумісність binary thinking on/off |
| 32 | supportsXHighThinking |
Сумісність підтримки reasoning xhigh |
| 33 | resolveDefaultThinkingLevel |
Сумісність політики /think за замовчуванням |
| 34 | isModernModelRef |
Зіставлення live/smoke моделей |
| 35 | prepareRuntimeAuth |
Обмін токена перед inference |
| 36 | resolveUsageAuth |
Користувацький parsing облікових даних використання |
| 37 | fetchUsageSnapshot |
Користувацький endpoint використання |
| 38 | createEmbeddingProvider |
Адаптер embedding, що належить постачальнику, для memory/search |
| 39 | buildReplayPolicy |
Користувацька політика replay/Compaction transcript |
| 40 | sanitizeReplayHistory |
Переписування replay, специфічне для постачальника, після загального очищення |
| 41 | validateReplayTurns |
Сувора перевірка replay-turn перед embedded runner |
| 42 | onModelSelected |
Callback після вибору (наприклад, telemetry) |
Примітки щодо runtime fallback:
normalizeConfigспершу перевіряє відповідного постачальника, а потім інші hook-capable Plugins постачальників, доки один із них справді не змінить конфігурацію. Якщо жоден hook постачальника не переписує підтримуваний запис конфігурації сімейства Google, вбудований нормалізатор конфігурації Google усе одно застосовується.resolveConfigApiKeyвикористовує hook постачальника, коли його надано. Вбудований шляхamazon-bedrockтакож має тут вбудований AWS env-marker resolver, хоча сама runtime auth Bedrock і далі використовує типовий ланцюг AWS SDK.resolveSystemPromptContributionдає постачальнику змогу інʼєктувати cache-aware настанови system-prompt для сімейства моделей. Надавайте йому перевагу надbefore_prompt_build, коли поведінка належить одному постачальнику/сімейству моделей і має зберігати розділення stable/dynamic cache.
Докладні описи й реальні приклади див. у Внутрішня архітектура: Provider Runtime Hooks.
Додайте додаткові можливості (необовʼязково)
Крок 5: Додайте додаткові можливості
Provider Plugin може реєструвати speech, realtime transcription, realtime voice, media understanding, image generation, video generation, web fetch і web search поряд із text inference. OpenClaw класифікує це як hybrid-capability Plugin - рекомендований шаблон для Plugins компаній (один Plugin на постачальника). Див. Внутрішня архітектура: Capability Ownership.
Зареєструйте кожну можливість усередині register(api) поряд із вашим наявним
викликом api.registerProvider(...). Виберіть лише потрібні вкладки:
Мовлення (TTS)
import {
assertOkOrThrowProviderError,
postJsonRequest,
} from "openclaw/plugin-sdk/provider-http";
api.registerSpeechProvider({
id: "acme-ai",
label: "Acme Speech",
isConfigured: ({ config }) => Boolean(config.messages?.tts),
synthesize: async (req) => {
const { response, release } = await postJsonRequest({
url: "https://api.example.com/v1/speech",
headers: new Headers({ "Content-Type": "application/json" }),
body: { text: req.text },
timeoutMs: req.timeoutMs,
fetchFn: fetch,
auditContext: "acme speech",
});
try {
await assertOkOrThrowProviderError(response, "Acme Speech API error");
return {
audioBuffer: Buffer.from(await response.arrayBuffer()),
outputFormat: "mp3",
fileExtension: ".mp3",
voiceCompatible: false,
};
} finally {
await release();
}
},
});
Використовуйте assertOkOrThrowProviderError(...) для HTTP-збоїв провайдера, щоб
plugins спільно використовували обмежене читання тіла помилки, розбір помилок JSON і
суфікси ідентифікаторів запитів.
Транскрипція в реальному часі
Надавайте перевагу createRealtimeTranscriptionWebSocketSession(...) - спільний
допоміжний засіб обробляє захоплення проксі, відступи повторного підключення, скидання під час закриття, готові
рукостискання, постановку аудіо в чергу та діагностику подій закриття. Ваш plugin
лише зіставляє події upstream.
api.registerRealtimeTranscriptionProvider({
id: "acme-ai",
label: "Acme Realtime Transcription",
isConfigured: () => true,
createSession: (req) => {
const apiKey = String(req.providerConfig.apiKey ?? "");
return createRealtimeTranscriptionWebSocketSession({
providerId: "acme-ai",
callbacks: req,
url: "wss://api.example.com/v1/realtime-transcription",
headers: { Authorization: `Bearer ${apiKey}` },
onMessage: (event, transport) => {
if (event.type === "session.created") {
transport.sendJson({ type: "session.update" });
transport.markReady();
return;
}
if (event.type === "transcript.final") {
req.onTranscript?.(event.text);
}
},
sendAudio: (audio, transport) => {
transport.sendJson({
type: "audio.append",
audio: audio.toString("base64"),
});
},
onClose: (transport) => {
transport.sendJson({ type: "audio.end" });
},
});
},
});
Пакетні STT-провайдери, які надсилають multipart-аудіо через POST, мають використовувати
buildAudioTranscriptionFormData(...) з
openclaw/plugin-sdk/provider-http. Допоміжний засіб нормалізує назви файлів для завантаження,
зокрема AAC-завантаження, яким потрібна назва файлу в стилі M4A для
сумісних API транскрипції.
Голос у реальному часі
api.registerRealtimeVoiceProvider({
id: "acme-ai",
label: "Acme Realtime Voice",
capabilities: {
transports: ["gateway-relay"],
inputAudioFormats: [{ encoding: "pcm16", sampleRateHz: 24000, channels: 1 }],
outputAudioFormats: [{ encoding: "pcm16", sampleRateHz: 24000, channels: 1 }],
supportsBargeIn: true,
supportsToolCalls: true,
},
isConfigured: ({ providerConfig }) => Boolean(providerConfig.apiKey),
createBridge: (req) => ({
// Set this only if the provider accepts multiple tool responses for
// one call, for example an immediate "working" response followed by
// the final result.
supportsToolResultContinuation: false,
connect: async () => {},
sendAudio: () => {},
setMediaTimestamp: () => {},
handleBargeIn: () => {},
submitToolResult: () => {},
acknowledgeMark: () => {},
close: () => {},
isConnected: () => true,
}),
});
Оголосіть capabilities, щоб talk.catalog міг надавати допустимі режими,
транспорти, аудіоформати та прапорці функцій браузерним і нативним клієнтам Talk.
Реалізуйте handleBargeIn, коли транспорт може визначити, що
людина перериває відтворення асистента, а провайдер підтримує
обрізання або очищення активної аудіовідповіді.
Розуміння медіа
api.registerMediaUnderstandingProvider({
id: "acme-ai",
capabilities: ["image", "audio"],
describeImage: async (req) => ({ text: "A photo of..." }),
transcribeAudio: async (req) => ({ text: "Transcript..." }),
});
Генерація зображень і відео
Можливості відео використовують форму, орієнтовану на режим: generate,
imageToVideo і videoToVideo. Плоских агрегованих полів на кшталт
maxInputImages / maxInputVideos / maxDurationSeconds недостатньо,
щоб коректно оголошувати підтримку режимів трансформації або вимкнені режими.
Генерація музики дотримується тієї самої схеми з явними блоками generate /
edit.
api.registerImageGenerationProvider({
id: "acme-ai",
label: "Acme Images",
generate: async (req) => ({ /* image result */ }),
});
api.registerVideoGenerationProvider({
id: "acme-ai",
label: "Acme Video",
capabilities: {
generate: { maxVideos: 1, maxDurationSeconds: 10, supportsResolution: true },
imageToVideo: {
enabled: true,
maxVideos: 1,
maxInputImages: 1,
maxInputImagesByModel: { "acme/reference-to-video": 9 },
maxDurationSeconds: 5,
},
videoToVideo: { enabled: false },
},
generateVideo: async (req) => ({ videos: [] }),
});
Веботримання та пошук
api.registerWebFetchProvider({
id: "acme-ai-fetch",
label: "Acme Fetch",
hint: "Fetch pages through Acme's rendering backend.",
envVars: ["ACME_FETCH_API_KEY"],
placeholder: "acme-...",
signupUrl: "https://acme.example.com/fetch",
credentialPath: "plugins.entries.acme.config.webFetch.apiKey",
getCredentialValue: (fetchConfig) => fetchConfig?.acme?.apiKey,
setCredentialValue: (fetchConfigTarget, value) => {
const acme = (fetchConfigTarget.acme ??= {});
acme.apiKey = value;
},
createTool: () => ({
description: "Fetch a page through Acme Fetch.",
parameters: {},
execute: async (args) => ({ content: [] }),
}),
});
api.registerWebSearchProvider({
id: "acme-ai-search",
label: "Acme Search",
search: async (req) => ({ content: [] }),
});
Тестування
Крок 6: Тестування
import { describe, it, expect } from "vitest";
// Export your provider config object from index.ts or a dedicated file
import { acmeProvider } from "./provider.js";
describe("acme-ai provider", () => {
it("resolves dynamic models", () => {
const model = acmeProvider.resolveDynamicModel!({
modelId: "acme-beta-v3",
} as any);
expect(model.id).toBe("acme-beta-v3");
expect(model.provider).toBe("acme-ai");
});
it("returns catalog when key is available", async () => {
const result = await acmeProvider.catalog!.run({
resolveProviderApiKey: () => ({ apiKey: "test-key" }),
} as any);
expect(result?.provider?.models).toHaveLength(2);
});
it("returns null catalog when no key", async () => {
const result = await acmeProvider.catalog!.run({
resolveProviderApiKey: () => ({ apiKey: undefined }),
} as any);
expect(result).toBeNull();
});
});
Публікація в ClawHub
Plugins провайдерів публікуються так само, як і будь-який інший зовнішній кодовий plugin:
clawhub package publish your-org/your-plugin --dry-run
clawhub package publish your-org/your-plugin
Не використовуйте тут застарілий псевдонім публікації лише для skill; пакети plugin мають використовувати
clawhub package publish.
Структура файлів
<bundled-plugin-root>/acme-ai/
├── package.json # openclaw.providers metadata
├── openclaw.plugin.json # Manifest with provider auth metadata
├── index.ts # definePluginEntry + registerProvider
└── src/
├── provider.test.ts # Tests
└── usage.ts # Usage endpoint (optional)
Довідник порядку каталогу
catalog.order керує тим, коли ваш каталог об'єднується відносно вбудованих
провайдерів:
| Порядок | Коли | Сценарій використання |
|---|---|---|
simple |
Перший прохід | Прості провайдери API-ключів |
profile |
Після simple | Провайдери, обмежені auth profiles |
paired |
Після profile | Синтез кількох пов'язаних записів |
late |
Останній прохід | Перевизначення наявних провайдерів (перемагає при колізії) |
Наступні кроки
- Channel Plugins - якщо ваш plugin також надає канал
- SDK Runtime - допоміжні засоби
api.runtime(TTS, пошук, субагент) - Огляд SDK - повний довідник імпортів підшляхів
- Внутрішня архітектура Plugin - подробиці хуків і приклади вбудованих пакетів