Zum Hauptinhalt springen
Dieser Leitfaden führt Sie durch den Aufbau eines Provider-Plugins, das OpenClaw einen Modell-Provider (LLM) hinzufügt. Am Ende haben Sie einen Provider mit einem Modellkatalog, API-Schlüssel-Authentifizierung und dynamischer Modellauflösung.
Wenn Sie noch kein OpenClaw-Plugin erstellt haben, lesen Sie zuerst Erste Schritte für die grundlegende Paketstruktur und die Manifest-Einrichtung.
Provider-Plugins fügen Modelle zur normalen Inferenzschleife von OpenClaw hinzu. Wenn das Modell über einen nativen Agent-Daemon laufen muss, der Threads, Compaction oder Tool- Events besitzt, kombinieren Sie den Provider mit einem Agent-Harness, statt Daemon-Protokolldetails im Core abzulegen.

Schritt-für-Schritt-Anleitung

1

Paket und Manifest

Schritt 1: Paket und 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
  }
}
Das Manifest deklariert setup.providers[].envVars, damit OpenClaw Anmeldedaten erkennen kann, ohne die Runtime Ihres Plugins zu laden. Fügen Sie providerAuthAliases hinzu, wenn eine Provider-Variante die Authentifizierung einer anderen Provider-ID wiederverwenden soll. modelSupport ist optional und ermöglicht OpenClaw, Ihr Provider-Plugin automatisch anhand von Kurzform- Modell-IDs wie acme-large zu laden, bevor Runtime-Hooks existieren. Wenn Sie den Provider auf ClawHub veröffentlichen, sind diese Felder openclaw.compat und openclaw.build in package.json erforderlich.
2

Provider registrieren

Ein minimaler Text-Provider benötigt id, label, auth und catalog. catalog ist der vom Provider besessene Runtime-/Config-Hook; er kann Live- Vendor-APIs aufrufen und gibt models.providers-Einträge zurück.
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 ist die neuere Control-Plane-Katalogoberfläche für Listen-/Hilfe-/Picker-UI. Verwenden Sie sie für Text-, Bilderzeugungs-, Videoerzeugungs- und Musikerzeugungszeilen. Belassen Sie Vendor-Endpunktaufrufe und Response-Mapping im Plugin; OpenClaw besitzt die gemeinsame Zeilenform, Source- Labels und Hilfe-Rendering.Das ist ein funktionsfähiger Provider. Benutzer können jetzt openclaw onboard --acme-ai-api-key <key> ausführen und acme-ai/acme-large als ihr Modell auswählen.

Live-Modellerkennung

Wenn Ihr Provider eine API im Stil von /models bereitstellt, behalten Sie den Provider-spezifischen Endpunkt und die Zeilenprojektion in Ihrem Plugin und verwenden Sie openclaw/plugin-sdk/provider-catalog-live-runtime für den gemeinsamen Fetch- Lebenszyklus. Der Helper bietet Ihnen abgesicherte HTTP-Fetches, Provider-Auth-Header, strukturierte HTTP-Fehler, TTL-Caching und statisches Fallback-Verhalten, ohne Provider-Policy in den OpenClaw-Core zu legen.Verwenden Sie buildLiveModelProviderConfig, wenn die Live-API Ihnen nur mitteilt, welche vom Provider besessenen statischen Katalogzeilen derzeit verfügbar sind:
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],
          },
        }),
      },
    });
  },
});
Verwenden Sie getCachedLiveProviderModelRows, wenn die Provider-API umfangreichere Metadaten zurückgibt und das Plugin selbst Zeilen in OpenClaw-Modell- Definitionen projizieren muss:
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 sollte auth-gated bleiben und null zurückgeben, wenn keine nutzbaren Anmeldedaten verfügbar sind. Behalten Sie ein Offline-staticRun oder einen statischen Fallback bei, damit Setup, Docs, Tests und Picker-Oberflächen nicht von Live-Netzwerkzugriff abhängen. Verwenden Sie eine TTL, die zur Aktualität der Modellliste passt, vermeiden Sie Filesystem-Polling zur Request-Zeit und übergeben Sie provider-spezifisches readRows / readModelId nur dann, wenn die Upstream-Antwort nicht der OpenAI-kompatiblen Form { data: [{ id, object }] } entspricht.Wenn der Upstream-Provider andere Steuertokens als OpenClaw verwendet, fügen Sie eine kleine bidirektionale Texttransformation hinzu, statt den Stream-Pfad zu ersetzen:
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 schreibt den finalen System-Prompt und den Inhalt von Textnachrichten vor dem Transport um. output schreibt Assistant-Text-Deltas und finalen Text um, bevor OpenClaw seine eigenen Steuermarker parst oder die Channel-Zustellung erfolgt.Für gebündelte Provider, die nur einen Text-Provider mit API-Schlüssel- Auth plus einer einzelnen kataloggestützten Runtime registrieren, bevorzugen Sie den enger gefassten 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 ist der Live-Katalogpfad, der verwendet wird, wenn OpenClaw echte Provider-Authentifizierung auflösen kann. Er kann Provider-spezifische Erkennung ausführen. Verwenden Sie buildStaticProvider nur für Offline-Zeilen, die vor der Konfiguration der Authentifizierung sicher angezeigt werden können; es darf keine Zugangsdaten erfordern oder Netzwerkanfragen stellen. Die Anzeige models list --all von OpenClaw führt statische Kataloge derzeit nur für gebündelte Provider-Plugins aus, mit leerer Konfiguration, leerer Umgebung und ohne Agent-/Arbeitsbereichspfade.Wenn Ihr Authentifizierungsablauf während des Onboardings auch models.providers.*, Aliasse und das Standardmodell des Agents patchen muss, verwenden Sie die Preset-Helfer aus openclaw/plugin-sdk/provider-onboard. Die engsten Helfer sind createDefaultModelPresetAppliers(...), createDefaultModelsPresetAppliers(...) und createModelCatalogPresetAppliers(...).Wenn ein nativer Endpunkt eines Providers gestreamte Nutzungsblöcke über den normalen openai-completions-Transport unterstützt, bevorzugen Sie die gemeinsamen Kataloghelfer in openclaw/plugin-sdk/provider-catalog-shared statt fest codierter Provider-ID-Prüfungen. supportsNativeStreamingUsageCompat(...) und applyProviderNativeStreamingUsageCompat(...) erkennen Unterstützung aus der Endpunkt-Capability-Map, sodass native Endpunkte im Moonshot-/DashScope-Stil weiterhin opt-in verwenden, auch wenn ein Plugin eine benutzerdefinierte Provider-ID nutzt.Die obigen Beispiele für Live-Erkennung decken Provider-APIs im Stil von /models ab. Behalten Sie diese Erkennung in catalog.run, abgesichert durch nutzbare Authentifizierung, und halten Sie staticRun für die Offline-Kataloggenerierung netzwerkfrei.
3

Add dynamic model resolution

Wenn Ihr Provider beliebige Modell-IDs akzeptiert (wie ein Proxy oder Router), fügen Sie resolveDynamicModel hinzu:
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,
  }),
});
Wenn die Auflösung einen Netzwerkaufruf erfordert, verwenden Sie prepareDynamicModel für das asynchrone Aufwärmen - resolveDynamicModel wird nach Abschluss erneut ausgeführt.
4

Add runtime hooks (as needed)

Die meisten Provider benötigen nur catalog + resolveDynamicModel. Fügen Sie Hooks schrittweise hinzu, wenn Ihr Provider sie benötigt.Gemeinsame Helfer-Builder decken inzwischen die häufigsten Replay-/Tool-Kompatibilitäts- Familien ab, sodass Plugins normalerweise nicht jeden Hook einzeln von Hand verdrahten müssen:
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,
});
Heute verfügbare Replay-Familien:
FamilieWas eingebunden wirdGebündelte Beispiele
openai-compatibleGemeinsame Replay-Richtlinie im OpenAI-Stil für OpenAI-kompatible Transporte, einschließlich Bereinigung von Tool-Call-IDs, Korrekturen für Assistant-First-Reihenfolge und generischer Gemini-Turn-Validierung, wo der Transport sie benötigtmoonshot, ollama, xai, zai
anthropic-by-modelClaude-bewusste Replay-Richtlinie, ausgewählt nach modelId, sodass Anthropic-Message-Transporte Claude-spezifische Thinking-Block-Bereinigung nur erhalten, wenn das aufgelöste Modell tatsächlich eine Claude-ID istamazon-bedrock, anthropic-vertex
google-geminiNative Gemini-Replay-Richtlinie plus Bootstrap-Replay-Bereinigung. Die gemeinsame Familie hält die Gemini CLI mit Textausgabe bei markiertem Reasoning; der direkte google-Provider überschreibt resolveReasoningOutputMode zu native, weil Gemini API Thinking als native Thought-Parts ankommt.google, google-gemini-cli
passthrough-geminiBereinigung von Gemini-Thought-Signaturen für Gemini-Modelle, die über OpenAI-kompatible Proxy-Transporte laufen; aktiviert keine native Gemini-Replay-Validierung oder Bootstrap-Umschreibungenopenrouter, kilocode, opencode, opencode-go
hybrid-anthropic-openaiHybride Richtlinie für Provider, die Anthropic-Message- und OpenAI-kompatible Modelloberflächen in einem Plugin mischen; optionales Entfernen von Claude-only-Thinking-Blocks bleibt auf die Anthropic-Seite beschränktminimax
Heute verfügbare Stream-Familien:
FamilieWas eingebunden wirdGebündelte Beispiele
google-thinkingGemini-Thinking-Payload-Normalisierung auf dem gemeinsamen Stream-Pfadgoogle, google-gemini-cli
kilocode-thinkingKilo-Reasoning-Wrapper auf dem gemeinsamen Proxy-Stream-Pfad, wobei kilo/auto und nicht unterstützte Proxy-Reasoning-IDs eingefügtes Thinking überspringenkilocode
moonshot-thinkingMoonshot-Binärmapping nativer Thinking-Payloads aus Konfiguration + /think-Levelmoonshot
minimax-fast-modeMiniMax-Fast-Mode-Modellumschreibung auf dem gemeinsamen Stream-Pfadminimax, minimax-portal
openai-responses-defaultsGemeinsame native OpenAI/Codex-Responses-Wrapper: Attribution-Header, /fast/serviceTier, Textausführlichkeit, native Codex-Websuche, Reasoning-Kompatibilitäts-Payload-Formung und Responses-Kontextverwaltungopenai
openrouter-thinkingOpenRouter-Reasoning-Wrapper für Proxy-Routen, wobei nicht unterstützte Modelle/auto zentral übersprungen werdenopenrouter
tool-stream-default-onStandardmäßig aktivierter tool_stream-Wrapper für Provider wie Z.AI, die Tool-Streaming wünschen, sofern es nicht explizit deaktiviert wurdezai
Jeder Familien-Builder setzt sich aus öffentlichen Helfern niedrigerer Ebene zusammen, die aus demselben Paket exportiert werden und die Sie verwenden können, wenn ein Provider vom gemeinsamen Muster abweichen muss:
  • openclaw/plugin-sdk/provider-model-shared - ProviderReplayFamily, buildProviderReplayFamilyHooks(...) und die rohen Replay-Builder (buildOpenAICompatibleReplayPolicy, buildAnthropicReplayPolicyForModel, buildGoogleGeminiReplayPolicy, buildHybridAnthropicOrOpenAIReplayPolicy). Exportiert außerdem Gemini-Replay-Helfer (sanitizeGoogleGeminiReplayHistory, resolveTaggedReasoningOutputMode) und Endpunkt-/Modellhelfer (resolveProviderEndpoint, normalizeProviderId, normalizeGooglePreviewModelId).
  • openclaw/plugin-sdk/provider-stream - ProviderStreamFamily, buildProviderStreamFamilyHooks(...), composeProviderStreamWrappers(...) sowie die gemeinsamen OpenAI/Codex-Wrapper (createOpenAIAttributionHeadersWrapper, createOpenAIFastModeWrapper, createOpenAIServiceTierWrapper, createOpenAIResponsesContextManagementWrapper, createCodexNativeWebSearchWrapper), DeepSeek-V4-OpenAI-kompatibler Wrapper (createDeepSeekV4OpenAICompatibleThinkingWrapper), Anthropic-Messages-Thinking-Prefill-Bereinigung (createAnthropicThinkingPrefillPayloadWrapper), Plain-Text-Tool-Call-Kompatibilität (createPlainTextToolCallCompatWrapper) und gemeinsame Proxy-/Provider-Wrapper (createOpenRouterWrapper, createToolStreamWrapper, createMinimaxFastModeWrapper).
  • openclaw/plugin-sdk/provider-stream-shared - leichtgewichtige Payload- und Event-Wrapper für heiße Provider-Pfade, einschließlich createOpenAICompatibleCompletionsThinkingOffWrapper, createPayloadPatchStreamWrapper, createPlainTextToolCallCompatWrapper, normalizeOpenAICompatibleReasoningPayload(...) und setQwenChatTemplateThinking(...).
  • openclaw/plugin-sdk/provider-tools - ProviderToolCompatFamily, buildProviderToolCompatFamilyHooks("deepseek" | "gemini" | "openai") und die zugrunde liegenden Provider-Schema-Helfer.
Halten Sie bei Providern der Gemini-Familie den Reasoning-Ausgabemodus am Transport ausgerichtet. Direkte Google-Gemini-API-Provider sollten native Reasoning-Ausgabe verwenden, damit OpenClaw native Thought-Parts konsumiert, ohne <think>- / <final>-Prompt-Direktiven hinzuzufügen. Text-only-Backends im Stil der Gemini CLI, die eine finale JSON-/Textantwort parsen, können den gemeinsamen markierten google-gemini-Vertrag beibehalten.Einige Stream-Helfer bleiben absichtlich Provider-lokal. @openclaw/anthropic-provider behält wrapAnthropicProviderStream, resolveAnthropicBetas, resolveAnthropicFastMode, resolveAnthropicServiceTier und die Anthropic-Wrapper-Builder niedrigerer Ebene in seiner eigenen öffentlichen api.ts- / contract-api.ts-Naht, weil sie Claude-OAuth-Beta-Behandlung und context1m-Gating codieren. Das xAI-Plugin behält native xAI-Responses-Formung ähnlich in seinem eigenen wrapStreamFn (/fast-Aliasse, Standard-tool_stream, Bereinigung nicht unterstützter Strict-Tools, xAI-spezifische Entfernung von Reasoning-Payloads).Dasselbe Paket-Root-Muster unterstützt auch @openclaw/openai-provider (Provider-Builder, Standardmodell-Helfer, Realtime-Provider-Builder) und @openclaw/openrouter-provider (Provider-Builder plus Onboarding-/Konfigurationshelfer).
Für Provider, die vor jedem Inferenzaufruf einen Token-Austausch benötigen:
prepareRuntimeAuth: async (ctx) => {
  const exchanged = await exchangeToken(ctx.apiKey);
  return {
    apiKey: exchanged.token,
    baseUrl: exchanged.baseUrl,
    expiresAt: exchanged.expiresAt,
  };
},
OpenClaw ruft Hooks in dieser Reihenfolge auf. Die meisten Provider verwenden nur 2-3: Provider-Felder, die nur noch der Kompatibilität dienen und von OpenClaw nicht mehr aufgerufen werden, wie ProviderPlugin.capabilities und suppressBuiltInModel, sind hier nicht aufgeführt.
#HookWann verwenden
1catalogModellkatalog oder Standardwerte für die Basis-URL
2applyConfigDefaultsProvider-eigene globale Standardwerte während der Konfigurationsmaterialisierung
3normalizeModelIdBereinigung von Legacy-/Preview-Modell-ID-Aliasen vor dem Lookup
4normalizeTransportBereinigung von api / baseUrl der Provider-Familie vor der generischen Modellzusammenstellung
5normalizeConfigmodels.providers.<id>-Konfiguration normalisieren
6applyNativeStreamingUsageCompatNative Streaming-Nutzungs-Kompatibilitätsumschreibungen für Konfigurations-Provider
7resolveConfigApiKeyProvider-eigene Authentifizierungsauflösung für Env-Marker
8resolveSyntheticAuthLokale/selbst gehostete oder konfigurationsgestützte synthetische Authentifizierung
9shouldDeferSyntheticProfileAuthSynthetische gespeicherte Profil-Platzhalter hinter Env-/Config-Authentifizierung zurückstellen
10resolveDynamicModelBeliebige Upstream-Modell-IDs akzeptieren
11prepareDynamicModelAsynchroner Metadatenabruf vor der Auflösung
12normalizeResolvedModelTransport-Umschreibungen vor dem Runner
13normalizeToolSchemasProvider-eigene Tool-Schema-Bereinigung vor der Registrierung
14inspectToolSchemasProvider-eigene Tool-Schema-Diagnose
15resolveReasoningOutputModeVertrag für getaggte vs. native Reasoning-Ausgabe
16prepareExtraParamsStandard-Anfrageparameter
17createStreamFnVollständig benutzerdefinierter StreamFn-Transport
19wrapStreamFnBenutzerdefinierte Header-/Body-Wrapper auf dem normalen Stream-Pfad
20resolveTransportTurnStateNative Header/Metadaten pro Turn
21resolveWebSocketSessionPolicyNative WS-Session-Header/Cool-down
22formatApiKeyBenutzerdefinierte Runtime-Token-Form
23refreshOAuthBenutzerdefinierte OAuth-Aktualisierung
24buildAuthDoctorHintAnleitung zur Authentifizierungsreparatur
25matchesContextOverflowErrorProvider-eigene Overflow-Erkennung
26classifyFailoverReasonProvider-eigene Klassifizierung von Rate-Limit/Überlastung
27isCacheTtlEligibleTTL-Gating für Prompt-Cache
28buildMissingAuthMessageBenutzerdefinierter Hinweis bei fehlender Authentifizierung
29augmentModelCatalogSynthetische Forward-Compat-Zeilen
30resolveThinkingProfileModellspezifischer /think-Optionssatz
31isBinaryThinkingKompatibilität für binäres Thinking ein/aus
32supportsXHighThinkingKompatibilität für xhigh-Reasoning-Unterstützung
33resolveDefaultThinkingLevelKompatibilität der Standard-/think-Policy
34isModernModelRefLive-/Smoke-Modellabgleich
35prepareRuntimeAuthToken-Austausch vor der Inferenz
36resolveUsageAuthBenutzerdefiniertes Parsen von Nutzungs-Anmeldedaten
37fetchUsageSnapshotBenutzerdefinierter Nutzungs-Endpunkt
38createEmbeddingProviderProvider-eigener Embedding-Adapter für Memory/Suche
39buildReplayPolicyBenutzerdefinierte Richtlinie für Transcript-Replay/Compaction
40sanitizeReplayHistoryProvider-spezifische Replay-Umschreibungen nach generischer Bereinigung
41validateReplayTurnsStrikte Replay-Turn-Validierung vor dem eingebetteten Runner
42onModelSelectedCallback nach der Auswahl (z. B. Telemetrie)
Hinweise zum Runtime-Fallback:
  • normalizeConfig prüft zuerst den passenden Provider und danach andere hook-fähige Provider-Plugins, bis eines die Konfiguration tatsächlich ändert. Wenn kein Provider-Hook einen unterstützten Google-Familien-Konfigurationseintrag umschreibt, wird weiterhin der gebündelte Google-Konfigurationsnormalisierer angewendet.
  • resolveConfigApiKey verwendet den Provider-Hook, wenn er bereitgestellt wird. Amazon Bedrock behält die AWS-Env-Marker-Auflösung in seinem Provider-Plugin; die Runtime-Authentifizierung selbst verwendet weiterhin die Standardkette des AWS SDK, wenn sie mit auth: "aws-sdk" konfiguriert ist.
  • resolveThinkingProfile(ctx) erhält den ausgewählten provider, modelId, einen optional zusammengeführten reasoning-Kataloghinweis und optional zusammengeführte compat-Fakten des Modells. Verwenden Sie compat nur, um die Thinking-UI/das Thinking-Profil des Providers auszuwählen.
  • resolveSystemPromptContribution ermöglicht es einem Provider, cache-bewusste System-Prompt-Hinweise für eine Modellfamilie einzufügen. Bevorzugen Sie dies gegenüber before_prompt_build, wenn das Verhalten zu einer Provider-/Modellfamilie gehört und die stabile/dynamische Cache-Trennung beibehalten soll.
Ausführliche Beschreibungen und Praxisbeispiele finden Sie unter Interna: Provider-Runtime-Hooks.
5

Zusätzliche Fähigkeiten hinzufügen (optional)

Schritt 5: Zusätzliche Fähigkeiten hinzufügen

Ein Provider-Plugin kann Embeddings, Sprache, Echtzeit-Transkription, Echtzeit-Sprache, Medienverständnis, Bildgenerierung, Videogenerierung, Web Fetch und Websuche neben Textinferenz registrieren. OpenClaw klassifiziert dies als Hybrid-Capability-Plugin - das empfohlene Muster für Unternehmens-Plugins (ein Plugin pro Anbieter). Siehe Interna: Capability Ownership.Registrieren Sie jede Fähigkeit innerhalb von register(api) neben Ihrem vorhandenen api.registerProvider(...)-Aufruf. Wählen Sie nur die Tabs aus, die Sie benötigen:
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();
    }
  },
});
Verwenden Sie assertOkOrThrowProviderError(...) für Provider-HTTP-Fehler, damit Plugins begrenzte Error-Body-Lesevorgänge, JSON-Fehlerparsing und Request-ID-Suffixe gemeinsam nutzen.
6

Testen

Schritt 6: Testen

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();
  });
});

In ClawHub veröffentlichen

Provider-Plugins werden genauso veröffentlicht wie jedes andere externe Code- Plugin:
clawhub package publish your-org/your-plugin --dry-run
clawhub package publish your-org/your-plugin
Verwenden Sie hier nicht den veralteten Alias nur für Skills; Plugin-Pakete sollten clawhub package publish verwenden.

Dateistruktur

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

Referenz zur Katalogreihenfolge

catalog.order steuert, wann Ihr Katalog im Verhältnis zu integrierten Providern zusammengeführt wird:
ReihenfolgeWannAnwendungsfall
simpleErster DurchlaufEinfache API-Key-Provider
profileNach simpleProvider, die von Auth-Profilen abhängen
pairedNach profileMehrere zusammengehörige Einträge erzeugen
lateLetzter DurchlaufBestehende Provider überschreiben (gewinnt bei Kollision)

Nächste Schritte

Verwandt