Przejdź do głównej treści
Ten przewodnik prowadzi przez budowanie providera Plugin, który dodaje dostawcę modelu (LLM) do OpenClaw. Na końcu będziesz mieć providera z katalogiem modeli, uwierzytelnianiem kluczem API i dynamicznym rozpoznawaniem modeli.
Jeśli wcześniej nie budowałeś żadnego Plugin OpenClaw, najpierw przeczytaj Pierwsze kroki, aby poznać podstawową strukturę pakietu i konfigurację manifestu.
Plugin providera dodaje modele do standardowej pętli inferencji OpenClaw. Jeśli model musi działać przez natywnego demona agenta, który zarządza wątkami, compaction lub zdarzeniami narzędzi, sparuj providera z uprzężą agenta zamiast umieszczać szczegóły protokołu demona w rdzeniu.

Przewodnik

1

Package and manifest

Krok 1: Pakiet i manifest

{
  "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-"]
  },
  "setup": {
    "providers": [
      {
        "id": "acme-ai",
        "envVars": ["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
  }
}
Manifest deklaruje setup.providers[].envVars, dzięki czemu OpenClaw może wykrywać poświadczenia bez ładowania runtime Twojego Plugin. Dodaj providerAuthAliases, gdy wariant providera powinien ponownie używać uwierzytelniania identyfikatora innego providera. modelSupport jest opcjonalne i pozwala OpenClaw automatycznie wczytać Plugin providera ze skróconych identyfikatorów modeli, takich jak acme-large, zanim będą istnieć haki runtime. Jeśli publikujesz providera w ClawHub, te pola openclaw.compat i openclaw.build są wymagane w package.json.
2

Register the provider

Minimalny provider tekstowy wymaga id, label, auth i catalog. catalog jest należącym do providera hakiem runtime/konfiguracji; może wywoływać live interfejsy API dostawcy i zwraca wpisy models.providers.
index.ts
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,
                },
              ],
            },
          };
        },
      },
    });

    api.registerModelCatalogProvider({
      provider: "acme-ai",
      kinds: ["text"],
      liveCatalog: async (ctx) => {
        const apiKey = ctx.resolveProviderApiKey("acme-ai").apiKey;
        if (!apiKey) return null;
        return [
          {
            kind: "text",
            provider: "acme-ai",
            model: "acme-large",
            label: "Acme Large",
            source: "live",
          },
        ];
      },
    });
  },
});
registerModelCatalogProvider to nowsza powierzchnia katalogu płaszczyzny sterowania dla interfejsu list/pomocy/selektora. Używaj jej dla wierszy tekstu, generowania obrazów, generowania wideo i generowania muzyki. Zachowaj wywołania punktów końcowych dostawcy i mapowanie odpowiedzi w Plugin; OpenClaw odpowiada za wspólny kształt wiersza, etykiety źródła i renderowanie pomocy.To działający provider. Użytkownicy mogą teraz uruchomić openclaw onboard --acme-ai-api-key <key> i wybrać acme-ai/acme-large jako swój model.

Wykrywanie modeli live

Jeśli Twój provider udostępnia interfejs API w stylu /models, zachowaj specyficzny dla providera punkt końcowy i projekcję wierszy w Plugin oraz użyj openclaw/plugin-sdk/provider-catalog-live-runtime dla wspólnego cyklu życia pobierania. Helper zapewnia chronione pobieranie HTTP, nagłówki uwierzytelniania providera, ustrukturyzowane błędy HTTP, buforowanie TTL i statyczne zachowanie fallback bez umieszczania polityki providera w rdzeniu OpenClaw.Użyj buildLiveModelProviderConfig, gdy live API informuje tylko, które należące do providera statyczne wiersze katalogu są obecnie dostępne:
index.ts
import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";
import {
  buildLiveModelProviderConfig,
  type LiveModelCatalogFetchGuard,
} from "openclaw/plugin-sdk/provider-catalog-live-runtime";

const STATIC_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,
  },
] as const;

async function buildAcmeLiveProvider(params: {
  apiKey: string;
  discoveryApiKey?: string;
  fetchGuard?: LiveModelCatalogFetchGuard;
}) {
  return await buildLiveModelProviderConfig({
    providerId: "acme-ai",
    endpoint: "https://api.acme-ai.com/v1/models",
    providerConfig: {
      baseUrl: "https://api.acme-ai.com/v1",
      api: "openai-completions",
    },
    models: STATIC_MODELS,
    apiKey: params.apiKey,
    discoveryApiKey: params.discoveryApiKey,
    fetchGuard: params.fetchGuard,
    ttlMs: 60_000,
    auditContext: "acme-ai-model-discovery",
  });
}

export default definePluginEntry({
  id: "acme-ai",
  name: "Acme AI",
  register(api) {
    api.registerProvider({
      id: "acme-ai",
      label: "Acme AI",
      catalog: {
        order: "simple",
        run: async (ctx) => {
          const auth = ctx.resolveProviderAuth("acme-ai");
          const apiKey =
            auth.apiKey ?? ctx.resolveProviderApiKey("acme-ai").apiKey;
          if (!apiKey) return null;
          return {
            provider: await buildAcmeLiveProvider({
              apiKey,
              discoveryApiKey: auth.discoveryApiKey,
            }),
          };
        },
      },
      staticCatalog: {
        order: "simple",
        run: async () => ({
          provider: {
            baseUrl: "https://api.acme-ai.com/v1",
            api: "openai-completions",
            models: [...STATIC_MODELS],
          },
        }),
      },
    });
  },
});
Użyj getCachedLiveProviderModelRows, gdy API providera zwraca bogatsze metadane, a Plugin musi samodzielnie projektować wiersze do definicji modeli OpenClaw:
index.ts
import {
  getCachedLiveProviderModelRows,
  LiveModelCatalogHttpError,
} from "openclaw/plugin-sdk/provider-catalog-live-runtime";

async function discoverAcmeModels(apiKey: string) {
  try {
    const rows = await getCachedLiveProviderModelRows({
      providerId: "acme-ai",
      endpoint: "https://api.acme-ai.com/v1/models",
      apiKey,
      ttlMs: 60_000,
      auditContext: "acme-ai-model-discovery",
    });
    return rows
      .map((row) => projectAcmeModel(row))
      .filter((model) => model !== null);
  } catch (error) {
    if (error instanceof LiveModelCatalogHttpError) {
      return STATIC_MODELS;
    }
    throw error;
  }
}
run powinno pozostać bramkowane uwierzytelnianiem i zwracać null, gdy nie ma dostępnych użytecznych poświadczeń. Zachowaj offline staticRun lub statyczny fallback, aby konfiguracja, dokumentacja, testy i powierzchnie selektora nie zależały od dostępu do sieci live. Użyj TTL odpowiedniego do świeżości listy modeli, unikaj odpytywania systemu plików w czasie żądania i przekaż specyficzne dla providera readRows / readModelId tylko wtedy, gdy odpowiedź upstream nie ma zgodnego z OpenAI kształtu { data: [{ id, object }] }.Jeśli upstream provider używa innych tokenów kontrolnych niż OpenClaw, dodaj małą dwukierunkową transformację tekstu zamiast zastępować ścieżkę strumienia:
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 przepisuje końcowy prompt systemowy i treść wiadomości tekstowej przed transportem. output przepisuje delty tekstu asystenta i końcowy tekst, zanim OpenClaw sparsuje własne znaczniki kontrolne lub dostarczenie kanałem.W przypadku bundled providerów, które rejestrują tylko jednego providera tekstowego z uwierzytelnianiem kluczem API oraz pojedynczym runtime opartym na katalogu, preferuj węższy 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 to ścieżka katalogu na żywo używana, gdy OpenClaw może rozpoznać rzeczywiste uwierzytelnianie dostawcy. Może wykonywać wykrywanie specyficzne dla dostawcy. Używaj buildStaticProvider tylko dla wierszy offline, które można bezpiecznie pokazać przed skonfigurowaniem uwierzytelniania; nie może wymagać poświadczeń ani wykonywać żądań sieciowych. Widok models list --all w OpenClaw obecnie wykonuje katalogi statyczne tylko dla wbudowanych pluginów dostawców, z pustą konfiguracją, pustym środowiskiem i bez ścieżek agenta/przestrzeni roboczej.Jeśli Twój przepływ uwierzytelniania musi także poprawiać models.providers.*, aliasy oraz domyślny model agenta podczas wdrażania, użyj pomocników presetów z openclaw/plugin-sdk/provider-onboard. Najwęższe pomocniki to createDefaultModelPresetAppliers(...), createDefaultModelsPresetAppliers(...) oraz createModelCatalogPresetAppliers(...).Gdy natywny endpoint dostawcy obsługuje przesyłane strumieniowo bloki użycia na standardowym transporcie openai-completions, preferuj współdzielone pomocniki katalogu w openclaw/plugin-sdk/provider-catalog-shared zamiast wpisywać na stałe sprawdzenia identyfikatorów dostawcy. supportsNativeStreamingUsageCompat(...) i applyProviderNativeStreamingUsageCompat(...) wykrywają obsługę na podstawie mapy możliwości endpointu, więc natywne endpointy w stylu Moonshot/DashScope nadal zgłaszają obsługę, nawet gdy plugin używa niestandardowego identyfikatora dostawcy.Powyższe przykłady wykrywania na żywo obejmują API dostawców w stylu /models. Zachowaj to wykrywanie wewnątrz catalog.run, bramkowane użytecznym uwierzytelnianiem, i pozostaw staticRun bez sieci na potrzeby generowania katalogu offline.
3

Add dynamic model resolution

Jeśli Twój dostawca akceptuje dowolne identyfikatory modeli (jak proxy lub router), dodaj 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,
  }),
});
Jeśli rozpoznanie wymaga wywołania sieciowego, użyj prepareDynamicModel do asynchronicznego rozgrzania - resolveDynamicModel uruchomi się ponownie po jego zakończeniu.
4

Add runtime hooks (as needed)

Większość dostawców potrzebuje tylko catalog + resolveDynamicModel. Dodawaj hooki stopniowo, gdy dostawca ich wymaga.Współdzielone konstruktory pomocników obejmują teraz najczęstsze rodziny zgodności replay/narzędzi, więc pluginy zwykle nie muszą ręcznie podłączać każdego hooka osobno:
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,
});
Obecnie dostępne rodziny replay:
RodzinaCo podłączaWbudowane przykłady
openai-compatibleWspółdzielona polityka replay w stylu OpenAI dla transportów zgodnych z OpenAI, w tym oczyszczanie identyfikatorów wywołań narzędzi, poprawki kolejności zaczynającej od asystenta oraz ogólna walidacja tur Gemini tam, gdzie wymaga tego transportmoonshot, ollama, xai, zai
anthropic-by-modelPolityka replay świadoma Claude wybierana według modelId, dzięki czemu transporty wiadomości Anthropic otrzymują oczyszczanie bloków myślenia specyficzne dla Claude tylko wtedy, gdy rozpoznany model faktycznie jest identyfikatorem Claudeamazon-bedrock, anthropic-vertex
google-geminiNatywna polityka replay Gemini oraz oczyszczanie replay podczas bootstrapu. Współdzielona rodzina utrzymuje tekstowe wyjście Gemini CLI przy tagowanym rozumowaniu; bezpośredni dostawca google nadpisuje resolveReasoningOutputMode na native, ponieważ myślenie Gemini API przychodzi jako natywne części myśli.google, google-gemini-cli
passthrough-geminiOczyszczanie sygnatur myśli Gemini dla modeli Gemini uruchamianych przez transporty proxy zgodne z OpenAI; nie włącza natywnej walidacji replay Gemini ani przepisywania bootstrapuopenrouter, kilocode, opencode, opencode-go
hybrid-anthropic-openaiPolityka hybrydowa dla dostawców, którzy mieszają powierzchnie modeli wiadomości Anthropic i zgodne z OpenAI w jednym pluginie; opcjonalne usuwanie bloków myślenia tylko dla Claude pozostaje ograniczone do strony Anthropicminimax
Obecnie dostępne rodziny strumieni:
RodzinaCo podłączaWbudowane przykłady
google-thinkingNormalizacja ładunku myślenia Gemini na współdzielonej ścieżce strumieniagoogle, google-gemini-cli
kilocode-thinkingWrapper rozumowania Kilo na współdzielonej ścieżce strumienia proxy, z kilo/auto i nieobsługiwanymi identyfikatorami rozumowania proxy pomijającymi wstrzyknięte myśleniekilocode
moonshot-thinkingMapowanie binarnego natywnego ładunku myślenia Moonshot z konfiguracji + poziomu /thinkmoonshot
minimax-fast-modePrzepisanie modelu trybu szybkiego MiniMax na współdzielonej ścieżce strumieniaminimax, minimax-portal
openai-responses-defaultsWspółdzielone natywne wrappery OpenAI/Codex Responses: nagłówki atrybucji, /fast/serviceTier, szczegółowość tekstu, natywne wyszukiwanie internetowe Codex, kształtowanie ładunku zgodności rozumowania oraz zarządzanie kontekstem Responsesopenai
openrouter-thinkingWrapper rozumowania OpenRouter dla tras proxy, z pominięciami nieobsługiwanych modeli/auto obsługiwanymi centralnieopenrouter
tool-stream-default-onDomyślnie włączony wrapper tool_stream dla dostawców takich jak Z.AI, którzy chcą strumieniowania narzędzi, chyba że zostanie jawnie wyłączonezai
Każdy konstruktor rodziny składa się z publicznych pomocników niższego poziomu eksportowanych z tego samego pakietu, po które możesz sięgnąć, gdy dostawca musi odejść od wspólnego wzorca:
  • openclaw/plugin-sdk/provider-model-shared - ProviderReplayFamily, buildProviderReplayFamilyHooks(...) oraz surowe konstruktory replay (buildOpenAICompatibleReplayPolicy, buildAnthropicReplayPolicyForModel, buildGoogleGeminiReplayPolicy, buildHybridAnthropicOrOpenAIReplayPolicy). Eksportuje także pomocniki replay Gemini (sanitizeGoogleGeminiReplayHistory, resolveTaggedReasoningOutputMode) oraz pomocniki endpointów/modeli (resolveProviderEndpoint, normalizeProviderId, normalizeGooglePreviewModelId).
  • openclaw/plugin-sdk/provider-stream - ProviderStreamFamily, buildProviderStreamFamilyHooks(...), composeProviderStreamWrappers(...), a także współdzielone wrappery OpenAI/Codex (createOpenAIAttributionHeadersWrapper, createOpenAIFastModeWrapper, createOpenAIServiceTierWrapper, createOpenAIResponsesContextManagementWrapper, createCodexNativeWebSearchWrapper), wrapper zgodny z OpenAI dla DeepSeek V4 (createDeepSeekV4OpenAICompatibleThinkingWrapper), czyszczenie prefill myślenia Anthropic Messages (createAnthropicThinkingPrefillPayloadWrapper), zgodność wywołań narzędzi w zwykłym tekście (createPlainTextToolCallCompatWrapper) oraz współdzielone wrappery proxy/dostawców (createOpenRouterWrapper, createToolStreamWrapper, createMinimaxFastModeWrapper).
  • openclaw/plugin-sdk/provider-stream-shared - lekkie wrappery ładunku i zdarzeń dla gorących ścieżek dostawców, w tym createOpenAICompatibleCompletionsThinkingOffWrapper, createPayloadPatchStreamWrapper, createPlainTextToolCallCompatWrapper, normalizeOpenAICompatibleReasoningPayload(...) oraz setQwenChatTemplateThinking(...).
  • openclaw/plugin-sdk/provider-tools - ProviderToolCompatFamily, buildProviderToolCompatFamilyHooks("deepseek" | "gemini" | "openai") oraz bazowe pomocniki schematów dostawców.
W przypadku dostawców z rodziny Gemini utrzymuj tryb wyjścia rozumowania zgodny z transportem. Bezpośredni dostawcy Google Gemini API powinni używać wyjścia rozumowania native, aby OpenClaw zużywał natywne części myśli bez dodawania dyrektyw promptu <think> / <final>. Tekstowe backendy w stylu Gemini CLI, które parsują końcową odpowiedź JSON/tekst, mogą zachować współdzielony tagowany kontrakt google-gemini.Niektóre pomocniki strumienia celowo pozostają lokalne dla dostawcy. @openclaw/anthropic-provider utrzymuje wrapAnthropicProviderStream, resolveAnthropicBetas, resolveAnthropicFastMode, resolveAnthropicServiceTier oraz niższego poziomu konstruktory wrapperów Anthropic we własnym publicznym szwie api.ts / contract-api.ts, ponieważ kodują obsługę beta Claude OAuth oraz bramkowanie context1m. Plugin xAI podobnie utrzymuje natywne kształtowanie xAI Responses we własnym wrapStreamFn (aliasy /fast, domyślne tool_stream, czyszczenie nieobsługiwanych narzędzi ścisłych, usuwanie ładunku rozumowania specyficzne dla xAI).Ten sam wzorzec korzenia pakietu obsługuje także @openclaw/openai-provider (konstruktory dostawców, pomocniki modeli domyślnych, konstruktory dostawcy realtime) oraz @openclaw/openrouter-provider (konstruktor dostawcy oraz pomocniki onboardingu/konfiguracji).
Dla dostawców, którzy potrzebują wymiany tokenu przed każdym wywołaniem inferencji:
prepareRuntimeAuth: async (ctx) => {
  const exchanged = await exchangeToken(ctx.apiKey);
  return {
    apiKey: exchanged.token,
    baseUrl: exchanged.baseUrl,
    expiresAt: exchanged.expiresAt,
  };
},
OpenClaw wywołuje hooki w tej kolejności. Większość dostawców używa tylko 2-3: Pola dostawcy przeznaczone wyłącznie do zgodności, których OpenClaw już nie wywołuje, takie jak ProviderPlugin.capabilities i suppressBuiltInModel, nie są tutaj wymienione.
#HookKiedy używać
1catalogKatalog modeli lub domyślne wartości bazowego URL
2applyConfigDefaultsGlobalne wartości domyślne należące do dostawcy podczas materializacji konfiguracji
3normalizeModelIdCzyszczenie aliasów identyfikatorów modeli legacy/podglądowych przed wyszukiwaniem
4normalizeTransportCzyszczenie api / baseUrl rodziny dostawcy przed ogólnym składaniem modelu
5normalizeConfigNormalizacja konfiguracji models.providers.<id>
6applyNativeStreamingUsageCompatPrzepisywanie zgodności natywnego użycia strumieniowania dla dostawców konfiguracji
7resolveConfigApiKeyRozwiązywanie uwierzytelniania markerów env należące do dostawcy
8resolveSyntheticAuthSyntetyczne uwierzytelnianie lokalne/self-hosted lub oparte na konfiguracji
9shouldDeferSyntheticProfileAuthObniżenie syntetycznych symboli zastępczych zapisanych profili za uwierzytelnianie env/config
10resolveDynamicModelAkceptowanie dowolnych identyfikatorów modeli upstream
11prepareDynamicModelAsynchroniczne pobranie metadanych przed rozwiązaniem
12normalizeResolvedModelPrzepisywanie transportu przed runnerem
13normalizeToolSchemasCzyszczenie schematów narzędzi należące do dostawcy przed rejestracją
14inspectToolSchemasDiagnostyka schematów narzędzi należąca do dostawcy
15resolveReasoningOutputModeKontrakt wyniku rozumowania tagowanego kontra natywnego
16prepareExtraParamsDomyślne parametry żądania
17createStreamFnW pełni niestandardowy transport StreamFn
19wrapStreamFnNiestandardowe opakowania nagłówków/treści na normalnej ścieżce strumienia
20resolveTransportTurnStateNatywne nagłówki/metadane dla każdej tury
21resolveWebSocketSessionPolicyNatywne nagłówki sesji WS/okres wyciszenia
22formatApiKeyNiestandardowy kształt tokenu runtime
23refreshOAuthNiestandardowe odświeżanie OAuth
24buildAuthDoctorHintWskazówki naprawy uwierzytelniania
25matchesContextOverflowErrorWykrywanie przepełnienia należące do dostawcy
26classifyFailoverReasonKlasyfikacja limitu szybkości/przeciążenia należąca do dostawcy
27isCacheTtlEligibleBramkowanie TTL pamięci podręcznej promptów
28buildMissingAuthMessageNiestandardowa wskazówka brakującego uwierzytelniania
29augmentModelCatalogSyntetyczne wiersze zgodności w przód
30resolveThinkingProfileZestaw opcji /think specyficzny dla modelu
31isBinaryThinkingZgodność włączania/wyłączania binarnego myślenia
32supportsXHighThinkingZgodność obsługi rozumowania xhigh
33resolveDefaultThinkingLevelZgodność domyślnej polityki /think
34isModernModelRefDopasowywanie modelu live/smoke
35prepareRuntimeAuthWymiana tokenu przed inferencją
36resolveUsageAuthNiestandardowe parsowanie poświadczeń użycia
37fetchUsageSnapshotNiestandardowy endpoint użycia
38createEmbeddingProviderAdapter embeddingów należący do dostawcy dla pamięci/wyszukiwania
39buildReplayPolicyNiestandardowa polityka odtwarzania/Compaction transkryptu
40sanitizeReplayHistoryPrzepisywanie odtwarzania specyficzne dla dostawcy po ogólnym czyszczeniu
41validateReplayTurnsŚcisła walidacja tur odtwarzania przed osadzonym runnerem
42onModelSelectedCallback po wyborze (np. telemetria)
Uwagi dotyczące mechanizmu awaryjnego runtime:
  • normalizeConfig najpierw sprawdza dopasowanego dostawcę, a następnie inne pluginy dostawców obsługujące hooki, aż któryś faktycznie zmieni konfigurację. Jeśli żaden hook dostawcy nie przepisze obsługiwanego wpisu konfiguracji z rodziny Google, wbudowany normalizator konfiguracji Google nadal zostanie zastosowany.
  • resolveConfigApiKey używa hooka dostawcy, gdy jest udostępniony. Amazon Bedrock utrzymuje rozwiązywanie markerów env AWS w swoim pluginie dostawcy; samo uwierzytelnianie runtime nadal używa domyślnego łańcucha AWS SDK, gdy jest skonfigurowane z auth: "aws-sdk".
  • resolveThinkingProfile(ctx) otrzymuje wybranego provider, modelId, opcjonalną scaloną wskazówkę katalogu reasoning i opcjonalne scalone fakty compat modelu. Używaj compat tylko do wyboru UI/profilu myślenia dostawcy.
  • resolveSystemPromptContribution pozwala dostawcy wstrzyknąć wskazówki promptu systemowego świadome pamięci podręcznej dla rodziny modeli. Preferuj go zamiast before_prompt_build, gdy zachowanie należy do jednej rodziny dostawcy/modeli i powinno zachować stabilny/dynamiczny podział pamięci podręcznej.
Szczegółowe opisy i przykłady z praktyki znajdziesz w Internals: Provider Runtime Hooks.
5

Dodaj dodatkowe możliwości (opcjonalnie)

Krok 5: Dodaj dodatkowe możliwości

Plugin dostawcy może rejestrować embeddingi, mowę, transkrypcję w czasie rzeczywistym, głos w czasie rzeczywistym, rozumienie mediów, generowanie obrazów, generowanie wideo, pobieranie z sieci i wyszukiwanie w sieci obok inferencji tekstu. OpenClaw klasyfikuje to jako plugin hybrid-capability - zalecany wzorzec dla pluginów firmowych (jeden plugin na dostawcę). Zobacz Internals: Capability Ownership.Zarejestruj każdą możliwość w register(api) obok istniejącego wywołania api.registerProvider(...). Wybierz tylko potrzebne karty:
import {
  assertOkOrThrowProviderError,
  postJsonRequest,
} from "openclaw/plugin-sdk/provider-http";

api.registerSpeechProvider({
  id: "acme-ai",
  label: "Acme Speech",
  defaultTimeoutMs: 120_000,
  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();
    }
  },
});
Użyj assertOkOrThrowProviderError(...) dla błędów HTTP dostawcy, aby pluginy współdzieliły limitowane odczyty treści błędów, parsowanie błędów JSON i sufiksy identyfikatorów żądań.
6

Test

Krok 6: Test

src/provider.test.ts
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();
  });
});

Publikowanie w ClawHub

Pluginy dostawców publikuje się tak samo jak każdy inny zewnętrzny Plugin kodu:
clawhub package publish your-org/your-plugin --dry-run
clawhub package publish your-org/your-plugin
Nie używaj tutaj starszego aliasu publikowania przeznaczonego tylko dla Skills; pakiety Plugin powinny używać clawhub package publish.

Struktura plików

<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)

Odniesienie do kolejności katalogu

catalog.order kontroluje, kiedy katalog jest scalany względem wbudowanych dostawców:
KolejnośćKiedyPrzypadek użycia
simplePierwsze przejścieZwykli dostawcy z kluczem API
profilePo simpleDostawcy zależni od profili uwierzytelniania
pairedPo profileSyntezowanie wielu powiązanych wpisów
lateOstatnie przejścieNadpisanie istniejących dostawców (wygrywa przy kolizji)

Następne kroki

Powiązane