Vai al contenuto principale
Questa guida illustra come creare un Plugin provider che aggiunge un provider di modelli (LLM) a OpenClaw. Alla fine avrai un provider con un catalogo di modelli, autenticazione tramite chiave API e risoluzione dinamica dei modelli.
Se non hai mai creato un Plugin OpenClaw prima, leggi prima Introduzione per la struttura di base del pacchetto e la configurazione del manifest.
I Plugin provider aggiungono modelli al normale ciclo di inferenza di OpenClaw. Se il modello deve essere eseguito tramite un daemon agente nativo che possiede thread, Compaction o eventi degli strumenti, abbina il provider a un harness agente invece di inserire i dettagli del protocollo del daemon nel core.

Procedura guidata

1

Package and manifest

Passaggio 1: pacchetto e 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
  }
}
Il manifest dichiara setup.providers[].envVars così OpenClaw può rilevare le credenziali senza caricare il runtime del tuo Plugin. Aggiungi providerAuthAliases quando una variante del provider deve riutilizzare l’autenticazione dell’id di un altro provider. modelSupport è opzionale e consente a OpenClaw di caricare automaticamente il tuo Plugin provider da id di modelli abbreviati come acme-large prima che esistano gli hook runtime. Se pubblichi il provider su ClawHub, quei campi openclaw.compat e openclaw.build sono obbligatori in package.json.
2

Register the provider

Un provider testuale minimale richiede un id, una label, auth e catalog. catalog è l’hook runtime/config di proprietà del provider; può chiamare API live del vendor e restituisce voci 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 è la superficie di catalogo control-plane più recente per interfacce di elenco/aiuto/selettore. Usala per righe di testo, generazione di immagini, generazione di video e generazione musicale. Mantieni nel Plugin le chiamate agli endpoint del vendor e la mappatura delle risposte; OpenClaw possiede la forma condivisa delle righe, le etichette di origine e il rendering dell’aiuto.Questo è un provider funzionante. Gli utenti ora possono eseguire openclaw onboard --acme-ai-api-key <key> e selezionare acme-ai/acme-large come modello.

Rilevamento live dei modelli

Se il tuo provider espone un’API in stile /models, mantieni nel tuo Plugin l’endpoint specifico del provider e la proiezione delle righe e usa openclaw/plugin-sdk/provider-catalog-live-runtime per il ciclo di vita condiviso del fetch. L’helper fornisce fetch HTTP protetti, header di autenticazione del provider, errori HTTP strutturati, caching TTL e comportamento di fallback statico senza inserire policy del provider nel core di OpenClaw.Usa buildLiveModelProviderConfig quando l’API live indica solo quali righe del catalogo statico di proprietà del provider sono attualmente disponibili:
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],
          },
        }),
      },
    });
  },
});
Usa getCachedLiveProviderModelRows quando l’API del provider restituisce metadati più ricchi e il Plugin deve proiettare autonomamente le righe nelle definizioni dei modelli 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 deve restare vincolato all’autenticazione e restituire null quando non è disponibile alcuna credenziale utilizzabile. Mantieni un staticRun offline o un fallback statico in modo che configurazione, documentazione, test e superfici di selezione non dipendano dall’accesso alla rete live. Usa un TTL appropriato per la freschezza dell’elenco dei modelli, evita il polling del filesystem al momento della richiesta e passa un readRows / readModelId specifico del provider solo quando la risposta upstream non ha una forma compatibile con OpenAI { data: [{ id, object }] }.Se il provider upstream usa token di controllo diversi da OpenClaw, aggiungi una piccola trasformazione testuale bidirezionale invece di sostituire il percorso di stream:
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 riscrive il prompt di sistema finale e il contenuto dei messaggi di testo prima del trasporto. output riscrive i delta testuali dell’assistente e il testo finale prima che OpenClaw analizzi i propri marker di controllo o la consegna al canale.Per provider in bundle che registrano solo un provider testuale con autenticazione tramite chiave API più un singolo runtime basato su catalogo, preferisci l’helper più ristretto 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 è il percorso del catalogo live usato quando OpenClaw può risolvere l’autenticazione reale del provider. Può eseguire discovery specifica del provider. Usa buildStaticProvider solo per righe offline che è sicuro mostrare prima che l’autenticazione sia configurata; non deve richiedere credenziali né effettuare richieste di rete. La visualizzazione models list --all di OpenClaw attualmente esegue i cataloghi statici solo per i Plugin provider inclusi, con configurazione vuota, env vuoto e nessun percorso agente/workspace.Se il tuo flusso di autenticazione deve anche applicare patch a models.providers.*, alias e al modello predefinito dell’agente durante l’onboarding, usa gli helper preset da openclaw/plugin-sdk/provider-onboard. Gli helper più specifici sono createDefaultModelPresetAppliers(...), createDefaultModelsPresetAppliers(...) e createModelCatalogPresetAppliers(...).Quando l’endpoint nativo di un provider supporta blocchi di utilizzo in streaming sul normale trasporto openai-completions, preferisci gli helper di catalogo condivisi in openclaw/plugin-sdk/provider-catalog-shared invece di codificare controlli provider-id. supportsNativeStreamingUsageCompat(...) e applyProviderNativeStreamingUsageCompat(...) rilevano il supporto dalla mappa delle capability dell’endpoint, quindi gli endpoint nativi in stile Moonshot/DashScope si attivano comunque anche quando un Plugin usa un id provider personalizzato.Gli esempi di discovery live sopra coprono API provider in stile /models. Mantieni tale discovery dentro catalog.run, vincolata ad autenticazione utilizzabile, e mantieni staticRun senza rete per la generazione del catalogo offline.
3

Aggiungi la risoluzione dinamica del modello

Se il tuo provider accetta ID modello arbitrari (come un proxy o un router), aggiungi 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,
  }),
});
Se la risoluzione richiede una chiamata di rete, usa prepareDynamicModel per il warm-up asincrono: resolveDynamicModel viene eseguito di nuovo al completamento.
4

Aggiungi hook runtime (secondo necessità)

La maggior parte dei provider necessita solo di catalog + resolveDynamicModel. Aggiungi hook in modo incrementale man mano che il tuo provider li richiede.I builder helper condivisi ora coprono le famiglie più comuni di compatibilità replay/tool, quindi i Plugin di solito non devono collegare manualmente ogni hook uno per uno:
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,
});
Famiglie di replay disponibili oggi:
FamigliaCosa collegaEsempi inclusi
openai-compatiblePolicy di replay condivisa in stile OpenAI per trasporti compatibili con OpenAI, inclusa la sanitizzazione dei tool-call-id, correzioni dell’ordinamento con assistente per primo e validazione generica dei turni Gemini dove il trasporto ne ha bisognomoonshot, ollama, xai, zai
anthropic-by-modelPolicy di replay consapevole di Claude scelta da modelId, così i trasporti con messaggi Anthropic ricevono la pulizia dei blocchi di pensiero specifica di Claude solo quando il modello risolto è effettivamente un id Claudeamazon-bedrock, anthropic-vertex
google-geminiPolicy di replay Gemini nativa più sanitizzazione del replay di bootstrap. La famiglia condivisa mantiene la CLI Gemini con output testuale sul ragionamento con tag; il provider diretto google sovrascrive resolveReasoningOutputMode su native perché il pensiero dell’API Gemini arriva come parti di pensiero native.google, google-gemini-cli
passthrough-geminiSanitizzazione della firma di pensiero Gemini per modelli Gemini eseguiti tramite trasporti proxy compatibili con OpenAI; non abilita la validazione del replay Gemini nativa né le riscritture di bootstrapopenrouter, kilocode, opencode, opencode-go
hybrid-anthropic-openaiPolicy ibrida per provider che combinano superfici modello con messaggi Anthropic e compatibili con OpenAI in un unico Plugin; l’eliminazione opzionale dei blocchi di pensiero solo Claude resta limitata al lato Anthropicminimax
Famiglie di stream disponibili oggi:
FamigliaCosa collegaEsempi inclusi
google-thinkingNormalizzazione del payload di pensiero Gemini sul percorso stream condivisogoogle, google-gemini-cli
kilocode-thinkingWrapper di ragionamento Kilo sul percorso stream proxy condiviso, con kilo/auto e id di ragionamento proxy non supportati che saltano il pensiero iniettatokilocode
moonshot-thinkingMapping del payload binario di pensiero nativo Moonshot da config + livello /thinkmoonshot
minimax-fast-modeRiscrittura del modello fast-mode MiniMax sul percorso stream condivisominimax, minimax-portal
openai-responses-defaultsWrapper Responses nativi OpenAI/Codex condivisi: header di attribuzione, /fast/serviceTier, verbosità del testo, ricerca web nativa Codex, modellazione del payload di compatibilità del ragionamento e gestione del contesto Responsesopenai
openrouter-thinkingWrapper di ragionamento OpenRouter per route proxy, con salti per modelli non supportati/auto gestiti centralmenteopenrouter
tool-stream-default-onWrapper tool_stream attivo per impostazione predefinita per provider come Z.AI che vogliono lo streaming degli strumenti salvo disabilitazione esplicitazai
Ogni builder di famiglia è composto da helper pubblici di livello inferiore esportati dallo stesso pacchetto, che puoi usare quando un provider deve discostarsi dal pattern comune:
  • openclaw/plugin-sdk/provider-model-shared - ProviderReplayFamily, buildProviderReplayFamilyHooks(...) e i builder di replay grezzi (buildOpenAICompatibleReplayPolicy, buildAnthropicReplayPolicyForModel, buildGoogleGeminiReplayPolicy, buildHybridAnthropicOrOpenAIReplayPolicy). Esporta anche helper di replay Gemini (sanitizeGoogleGeminiReplayHistory, resolveTaggedReasoningOutputMode) e helper per endpoint/modello (resolveProviderEndpoint, normalizeProviderId, normalizeGooglePreviewModelId).
  • openclaw/plugin-sdk/provider-stream - ProviderStreamFamily, buildProviderStreamFamilyHooks(...), composeProviderStreamWrappers(...), più i wrapper OpenAI/Codex condivisi (createOpenAIAttributionHeadersWrapper, createOpenAIFastModeWrapper, createOpenAIServiceTierWrapper, createOpenAIResponsesContextManagementWrapper, createCodexNativeWebSearchWrapper), wrapper compatibile con OpenAI DeepSeek V4 (createDeepSeekV4OpenAICompatibleThinkingWrapper), pulizia del prefill di pensiero Anthropic Messages (createAnthropicThinkingPrefillPayloadWrapper), compatibilità delle chiamate agli strumenti in testo semplice (createPlainTextToolCallCompatWrapper) e wrapper proxy/provider condivisi (createOpenRouterWrapper, createToolStreamWrapper, createMinimaxFastModeWrapper).
  • openclaw/plugin-sdk/provider-stream-shared - wrapper leggeri per payload ed eventi per percorsi provider caldi, inclusi createOpenAICompatibleCompletionsThinkingOffWrapper, createPayloadPatchStreamWrapper, createPlainTextToolCallCompatWrapper, normalizeOpenAICompatibleReasoningPayload(...) e setQwenChatTemplateThinking(...).
  • openclaw/plugin-sdk/provider-tools - ProviderToolCompatFamily, buildProviderToolCompatFamilyHooks("deepseek" | "gemini" | "openai") e helper sottostanti per gli schemi provider.
Per i provider della famiglia Gemini, mantieni la modalità di output del ragionamento allineata al trasporto. I provider diretti dell’API Google Gemini dovrebbero usare l’output di ragionamento native così OpenClaw consuma parti di pensiero native senza aggiungere direttive di prompt <think> / <final>. I backend Gemini in stile CLI solo testo che analizzano una risposta finale JSON/testo possono mantenere il contratto condiviso google-gemini con tag.Alcuni helper di stream restano locali al provider di proposito. @openclaw/anthropic-provider mantiene wrapAnthropicProviderStream, resolveAnthropicBetas, resolveAnthropicFastMode, resolveAnthropicServiceTier e i builder di wrapper Anthropic di livello inferiore nel proprio punto di integrazione pubblico api.ts / contract-api.ts perché codificano la gestione beta OAuth di Claude e il gating context1m. Il Plugin xAI mantiene analogamente la modellazione nativa xAI Responses nel proprio wrapStreamFn (alias /fast, tool_stream predefinito, pulizia strict-tool non supportata, rimozione del payload di ragionamento specifica di xAI).Lo stesso pattern a radice di pacchetto supporta anche @openclaw/openai-provider (builder provider, helper per modello predefinito, builder provider realtime) e @openclaw/openrouter-provider (builder provider più helper di onboarding/configurazione).
Per i provider che richiedono uno scambio token prima di ogni chiamata di inferenza:
prepareRuntimeAuth: async (ctx) => {
  const exchanged = await exchangeToken(ctx.apiKey);
  return {
    apiKey: exchanged.token,
    baseUrl: exchanged.baseUrl,
    expiresAt: exchanged.expiresAt,
  };
},
OpenClaw chiama gli hook in questo ordine. La maggior parte dei provider ne usa solo 2-3: i campi provider solo per compatibilità che OpenClaw non chiama più, come ProviderPlugin.capabilities e suppressBuiltInModel, non sono elencati qui.
#HookQuando usarlo
1catalogCatalogo dei modelli o valori predefiniti dell’URL di base
2applyConfigDefaultsValori predefiniti globali di proprietà del provider durante la materializzazione della configurazione
3normalizeModelIdPulizia degli alias legacy/preview degli ID modello prima della ricerca
4normalizeTransportPulizia di api / baseUrl della famiglia di provider prima dell’assemblaggio generico del modello
5normalizeConfigNormalizza la configurazione models.providers.<id>
6applyNativeStreamingUsageCompatRiscritture di compatibilità dell’utilizzo dello streaming nativo per i provider di configurazione
7resolveConfigApiKeyRisoluzione dell’autenticazione con marker env di proprietà del provider
8resolveSyntheticAuthAutenticazione sintetica locale/self-hosted o basata su configurazione
9shouldDeferSyntheticProfileAuthAbbassa i segnaposto sintetici del profilo memorizzato dietro l’autenticazione env/config
10resolveDynamicModelAccetta ID modello upstream arbitrari
11prepareDynamicModelRecupero asincrono dei metadati prima della risoluzione
12normalizeResolvedModelRiscritture del trasporto prima del runner
13normalizeToolSchemasPulizia degli schemi degli strumenti di proprietà del provider prima della registrazione
14inspectToolSchemasDiagnostica degli schemi degli strumenti di proprietà del provider
15resolveReasoningOutputModeContratto di output di reasoning con tag rispetto a nativo
16prepareExtraParamsParametri di richiesta predefiniti
17createStreamFnTrasporto StreamFn completamente personalizzato
19wrapStreamFnWrapper personalizzati di header/body sul percorso di stream normale
20resolveTransportTurnStateHeader/metadati nativi per turno
21resolveWebSocketSessionPolicyHeader sessione WS/cool-down nativi
22formatApiKeyForma personalizzata del token di runtime
23refreshOAuthRefresh OAuth personalizzato
24buildAuthDoctorHintGuida alla riparazione dell’autenticazione
25matchesContextOverflowErrorRilevamento dell’overflow di proprietà del provider
26classifyFailoverReasonClassificazione rate-limit/sovraccarico di proprietà del provider
27isCacheTtlEligibleGate TTL della cache dei prompt
28buildMissingAuthMessageSuggerimento personalizzato per autenticazione mancante
29augmentModelCatalogRighe sintetiche di compatibilità futura
30resolveThinkingProfileSet di opzioni /think specifico del modello
31isBinaryThinkingCompatibilità thinking binario on/off
32supportsXHighThinkingCompatibilità del supporto al reasoning xhigh
33resolveDefaultThinkingLevelCompatibilità della policy /think predefinita
34isModernModelRefCorrispondenza modello live/smoke
35prepareRuntimeAuthScambio del token prima dell’inferenza
36resolveUsageAuthParsing personalizzato delle credenziali di utilizzo
37fetchUsageSnapshotEndpoint di utilizzo personalizzato
38createEmbeddingProviderAdapter embedding di proprietà del provider per memoria/ricerca
39buildReplayPolicyPolicy personalizzata di replay/compaction della trascrizione
40sanitizeReplayHistoryRiscritture replay specifiche del provider dopo la pulizia generica
41validateReplayTurnsValidazione rigorosa dei turni replay prima del runner incorporato
42onModelSelectedCallback post-selezione (ad es. telemetria)
Note sul fallback di runtime:
  • normalizeConfig controlla prima il provider corrispondente, poi altri Plugin provider con hook finché uno non modifica effettivamente la configurazione. Se nessun hook provider riscrive una voce di configurazione supportata della famiglia Google, si applica comunque il normalizzatore di configurazione Google incluso.
  • resolveConfigApiKey usa l’hook provider quando esposto. Amazon Bedrock mantiene la risoluzione del marker env AWS nel proprio Plugin provider; l’autenticazione di runtime in sé usa comunque la catena predefinita dell’AWS SDK quando configurata con auth: "aws-sdk".
  • resolveThinkingProfile(ctx) riceve il provider selezionato, modelId, l’hint del catalogo reasoning unito facoltativo e i fatti compat del modello uniti facoltativi. Usa compat solo per selezionare l’interfaccia/profilo thinking del provider.
  • resolveSystemPromptContribution consente a un provider di iniettare una guida al prompt di sistema consapevole della cache per una famiglia di modelli. Preferiscilo a before_prompt_build quando il comportamento appartiene a una sola famiglia provider/modello e deve preservare la separazione stabile/dinamica della cache.
Per descrizioni dettagliate ed esempi reali, consulta Internals: Hook di runtime del provider.
5

Aggiungi capacità extra (facoltativo)

Passaggio 5: Aggiungi capacità extra

Un Plugin provider può registrare embedding, sintesi vocale, trascrizione realtime, voce realtime, comprensione dei media, generazione di immagini, generazione di video, recupero web e ricerca web insieme all’inferenza testuale. OpenClaw classifica questo come un Plugin hybrid-capability, il pattern consigliato per i Plugin aziendali (un Plugin per vendor). Consulta Internals: Proprietà delle capacità.Registra ogni capacità dentro register(api) insieme alla tua chiamata api.registerProvider(...) esistente. Scegli solo le schede che ti servono:
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();
    }
  },
});
Usa assertOkOrThrowProviderError(...) per gli errori HTTP del provider, così i Plugin condividono letture limitate del corpo d’errore, parsing degli errori JSON e suffissi request-id.
6

Test

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

Pubblicare su ClawHub

I Plugin provider vengono pubblicati nello stesso modo di qualsiasi altro Plugin di codice esterno:
clawhub package publish your-org/your-plugin --dry-run
clawhub package publish your-org/your-plugin
Non usare qui l’alias di pubblicazione legacy solo per Skills; i pacchetti Plugin dovrebbero usare clawhub package publish.

Struttura dei file

<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 controlla quando il tuo catalogo viene unito rispetto ai provider integrati:
OrdineQuandoCaso d’uso
simplePrimo passaggioProvider con semplice chiave API
profileDopo simpleProvider vincolati ai profili di autenticazione
pairedDopo profileSintetizzare più voci correlate
lateUltimo passaggioSovrascrivere provider esistenti (vince in caso di collisione)

Passaggi successivi

Correlati