Saltar al contenido principal
Crea un Plugin proveedor para agregar un proveedor de modelos (LLM) a OpenClaw: un catálogo de modelos, autenticación con clave de API y resolución dinámica de modelos.
¿Nuevo en los plugins de OpenClaw? Lee primero Primeros pasos para conocer la estructura del paquete y la configuración del manifiesto.
Los plugins proveedores agregan modelos al bucle normal de inferencia de OpenClaw. Si el modelo debe ejecutarse mediante un demonio de agente nativo que posee hilos, Compaction o eventos de herramientas, combina el proveedor con un arnés de agente en lugar de poner detalles del protocolo del demonio en el núcleo.

Guía paso a paso

1

Paquete y manifiesto

Paso 1: Paquete y manifiesto

{
  "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
  }
}
setup.providers[].envVars permite que OpenClaw detecte credenciales sin cargar el runtime de tu Plugin. Agrega providerAuthAliases cuando una variante de proveedor deba reutilizar la autenticación de otro id de proveedor. modelSupport es opcional y permite que OpenClaw cargue automáticamente tu Plugin proveedor a partir de ids de modelo abreviados como acme-large antes de que existan los hooks de runtime. openclaw.compat y openclaw.build en package.json son obligatorios para la publicación en ClawHub (openclaw.compat.pluginApi y openclaw.build.openclawVersion son los dos campos obligatorios; minGatewayVersion recurre a openclaw.install.minHostVersion cuando se omite).
2

Registrar el proveedor

Un proveedor de texto mínimo necesita un id, label, auth y catalog. catalog es el hook de runtime/configuración propiedad del proveedor; puede llamar a APIs del proveedor en vivo y devuelve entradas 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 es la superficie de catálogo del plano de control más reciente para la IU de listas/ayuda/selector, y cubre filas text, voice, image_generation, video_generation y music_generation. Mantén las llamadas a endpoints del proveedor y el mapeo de respuestas en el Plugin; OpenClaw posee la forma compartida de las filas, las etiquetas de origen y el renderizado de ayuda.Ese es un proveedor funcional. Los usuarios ahora pueden ejecutar openclaw onboard --acme-ai-api-key <key> y seleccionar acme-ai/acme-large como su modelo.

Descubrimiento de modelos en vivo

Si tu proveedor expone una API de estilo /models, mantén el endpoint específico del proveedor y la proyección de filas en tu Plugin, y usa openclaw/plugin-sdk/provider-catalog-live-runtime para el ciclo de vida compartido de fetch. El helper te da fetches HTTP protegidos, encabezados de autenticación del proveedor, errores HTTP estructurados, caché con TTL y comportamiento de fallback estático sin poner política de proveedor en el núcleo de OpenClaw.Usa buildLiveModelProviderConfig cuando la API en vivo solo te indica qué filas del catálogo estático propiedad del proveedor están disponibles actualmente:
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 cuando la API del proveedor devuelva metadatos más completos y el Plugin necesite proyectar las filas a definiciones de modelos de OpenClaw por sí mismo:
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 debe permanecer condicionado por autenticación y devolver null cuando no haya ninguna credencial utilizable disponible. Mantén un staticRun sin conexión o un fallback estático para que la configuración, la documentación, las pruebas y las superficies de selector no dependan del acceso a la red en vivo. Usa un TTL apropiado para la frescura de la lista de modelos, evita el sondeo del sistema de archivos en tiempo de solicitud y pasa un readRows / readModelId específico del proveedor solo cuando la respuesta upstream no tenga una forma compatible con OpenAI { data: [{ id, object }] }.Si el proveedor upstream usa tokens de control distintos de OpenClaw, agrega una pequeña transformación de texto bidireccional en lugar de reemplazar la ruta de streaming:
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 reescribe el prompt final del sistema y el contenido de los mensajes de texto antes del transporte. output reescribe los deltas de texto del asistente y el texto final antes de que OpenClaw analice sus propios marcadores de control o la entrega por canal.Para proveedores incluidos que solo registran un proveedor de texto con autenticación por clave de API más un único runtime respaldado por catálogo, prefiere el helper más acotado 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 es la ruta del catálogo en vivo que se usa cuando OpenClaw puede resolver la autenticación real del proveedor. Puede realizar descubrimiento específico del proveedor. Usa buildStaticProvider solo para filas sin conexión que sean seguras de mostrar antes de configurar la autenticación; no debe requerir credenciales ni realizar solicitudes de red. La visualización actual de models list --all de OpenClaw ejecuta catálogos estáticos solo para Plugins de proveedor incluidos, con una configuración vacía, un entorno vacío y sin rutas de agente/espacio de trabajo.Si tu flujo de autenticación también necesita aplicar parches a models.providers.*, alias y el modelo predeterminado del agente durante la incorporación, usa los helpers predefinidos de openclaw/plugin-sdk/provider-onboard. Los helpers más específicos son createDefaultModelPresetAppliers(...), createDefaultModelsPresetAppliers(...) y createModelCatalogPresetAppliers(...).Cuando el endpoint nativo de un proveedor admite bloques de uso transmitidos en el transporte normal openai-completions, prefiere los helpers de catálogo compartidos en openclaw/plugin-sdk/provider-catalog-shared en lugar de codificar comprobaciones de id de proveedor. supportsNativeStreamingUsageCompat(...) y applyProviderNativeStreamingUsageCompat(...) detectan la compatibilidad a partir del mapa de capacidades del endpoint, de modo que los endpoints nativos de estilo Moonshot/DashScope sigan participando incluso cuando un Plugin use un id de proveedor personalizado.Los ejemplos de descubrimiento en vivo anteriores cubren APIs de proveedor de estilo /models. Mantén ese descubrimiento dentro de catalog.run, protegido por autenticación utilizable, y mantén staticRun sin red para la generación de catálogos sin conexión.
3

Add dynamic model resolution

Si tu proveedor acepta IDs de modelo arbitrarios (como un proxy o enrutador), agrega 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,
  }),
});
Si resolverlo requiere una llamada de red, usa prepareDynamicModel para el calentamiento asíncrono: resolveDynamicModel se ejecuta de nuevo cuando termina.
4

Add runtime hooks (as needed)

La mayoría de los proveedores solo necesitan catalog + resolveDynamicModel. Agrega hooks de forma incremental a medida que tu proveedor los requiera.Los constructores de helpers compartidos ahora cubren las familias de reproducción/compatibilidad con herramientas más comunes, por lo que normalmente los Plugins no necesitan cablear cada hook uno por 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,
});
Familias de reproducción disponibles hoy:
FamiliaQué cableaEjemplos incluidos
openai-compatiblePolítica de reproducción compartida de estilo OpenAI para transportes compatibles con OpenAI, incluida la limpieza de ids de llamadas a herramientas, correcciones de orden con el asistente primero y validación genérica de turnos de Gemini cuando el transporte la necesitamoonshot, ollama, xai, zai
anthropic-by-modelPolítica de reproducción consciente de Claude elegida por modelId, de modo que los transportes de mensajes de Anthropic solo reciban limpieza de bloques de pensamiento específica de Claude cuando el modelo resuelto sea realmente un id de Claudeamazon-bedrock
native-anthropic-by-modelLa misma política Claude por modelo que anthropic-by-model, además de limpieza de ids de llamadas a herramientas y preservación nativa de ids de uso de herramientas de Anthropic para transportes que deben conservar ids nativos del proveedoranthropic-vertex, clawrouter
google-geminiPolítica de reproducción nativa de Gemini más limpieza de reproducción de arranque. La familia compartida mantiene la CLI de Gemini con salida de texto en razonamiento etiquetado; el proveedor directo google sobrescribe resolveReasoningOutputMode a native porque el pensamiento de la API de Gemini llega como partes de pensamiento nativas.google, google-gemini-cli
passthrough-geminiLimpieza de firmas de pensamiento de Gemini para modelos Gemini que se ejecutan a través de transportes proxy compatibles con OpenAI; no habilita validación de reproducción nativa de Gemini ni reescrituras de arranqueopenrouter, kilocode, opencode, opencode-go
hybrid-anthropic-openaiPolítica híbrida para proveedores que mezclan superficies de modelos de mensajes de Anthropic y compatibles con OpenAI en un Plugin; la eliminación opcional de bloques de pensamiento solo de Claude permanece limitada al lado de Anthropicminimax
Familias de transmisión disponibles hoy:
FamiliaQué cableaEjemplos incluidos
google-thinkingNormalización de la carga útil de pensamiento de Gemini en la ruta de transmisión compartidagoogle, google-gemini-cli
kilocode-thinkingEnvoltura de razonamiento de Kilo en la ruta de transmisión proxy compartida, con kilo/auto e ids de razonamiento proxy no compatibles omitiendo el pensamiento inyectadokilocode
moonshot-thinkingMapeo de carga útil binaria de pensamiento nativo de Moonshot desde configuración + nivel /thinkmoonshot
minimax-fast-modeReescritura del modelo de modo rápido de MiniMax en la ruta de transmisión compartidaminimax, minimax-portal
openai-responses-defaultsEnvolturas compartidas nativas de OpenAI/Codex Responses: encabezados de atribución, /fast/serviceTier, verbosidad de texto, búsqueda web nativa de Codex, conformación de carga útil de compatibilidad de razonamiento y gestión de contexto de Responsesopenai
openrouter-thinkingEnvoltura de razonamiento de OpenRouter para rutas proxy, con omisiones de modelos no compatibles/auto manejadas de forma centralizadaopenrouter
tool-stream-default-onEnvoltura tool_stream activada de forma predeterminada para proveedores como Z.AI que quieren transmisión de herramientas salvo que se deshabilite explícitamentezai
Cada constructor de familia se compone a partir de helpers públicos de nivel inferior exportados desde el mismo paquete, a los que puedes recurrir cuando un proveedor necesita salirse del patrón común:
  • openclaw/plugin-sdk/provider-model-shared - ProviderReplayFamily, buildProviderReplayFamilyHooks(...) y los constructores de reproducción sin procesar (buildOpenAICompatibleReplayPolicy, buildAnthropicReplayPolicyForModel, buildGoogleGeminiReplayPolicy, buildHybridAnthropicOrOpenAIReplayPolicy). También exporta helpers de reproducción de Gemini (sanitizeGoogleGeminiReplayHistory, resolveTaggedReasoningOutputMode) y helpers de endpoint/modelo (resolveProviderEndpoint, normalizeProviderId, normalizeGooglePreviewModelId).
  • openclaw/plugin-sdk/provider-stream - ProviderStreamFamily, buildProviderStreamFamilyHooks(...), composeProviderStreamWrappers(...), además de las envolturas compartidas de OpenAI/Codex (createOpenAIAttributionHeadersWrapper, createOpenAIFastModeWrapper, createOpenAIServiceTierWrapper, createOpenAIResponsesContextManagementWrapper, createCodexNativeWebSearchWrapper), la envoltura DeepSeek V4 compatible con OpenAI (createDeepSeekV4OpenAICompatibleThinkingWrapper), la limpieza de precarga de pensamiento de Anthropic Messages (createAnthropicThinkingPrefillPayloadWrapper), la compatibilidad de llamadas a herramientas en texto plano (createPlainTextToolCallCompatWrapper) y las envolturas compartidas de proxy/proveedor (createOpenRouterWrapper, createToolStreamWrapper, createMinimaxFastModeWrapper).
  • openclaw/plugin-sdk/provider-stream-shared - envolturas ligeras de carga útil y eventos para rutas calientes de proveedor, incluidas createOpenAICompatibleCompletionsThinkingOffWrapper, createPayloadPatchStreamWrapper, createPlainTextToolCallCompatWrapper, normalizeOpenAICompatibleReasoningPayload(...) y setQwenChatTemplateThinking(...).
  • openclaw/plugin-sdk/provider-tools - ProviderToolCompatFamily, buildProviderToolCompatFamilyHooks("deepseek" | "gemini" | "openai") y helpers subyacentes de esquema de proveedor.
Para proveedores de la familia Gemini, mantén el modo de salida de razonamiento alineado con el transporte. Los proveedores directos de la API Google Gemini deben usar salida de razonamiento native para que OpenClaw consuma partes de pensamiento nativas sin agregar directivas de prompt <think> / <final>. Los backends de estilo CLI de Gemini solo de texto que analizan una respuesta final JSON/texto pueden mantener el contrato etiquetado compartido google-gemini.Algunos helpers de transmisión permanecen locales al proveedor a propósito. @openclaw/anthropic-provider mantiene wrapAnthropicProviderStream, resolveAnthropicBetas, resolveAnthropicFastMode, resolveAnthropicServiceTier y los constructores de envolturas de Anthropic de nivel inferior en su propia unión pública api.ts / contract-api.ts porque codifican el manejo de beta de OAuth de Claude y la protección de context1m. El Plugin xAI conserva de forma similar la conformación nativa de xAI Responses en su propio wrapStreamFn (alias /fast, tool_stream predeterminado, limpieza de herramientas estrictas no compatibles, eliminación de carga útil de razonamiento específica de xAI).El mismo patrón de raíz de paquete también respalda @openclaw/openai-provider (constructores de proveedor, helpers de modelo predeterminado, constructores de proveedor en tiempo real) y @openclaw/openrouter-provider (constructor de proveedor más helpers de incorporación/configuración).
Para proveedores que necesitan un intercambio de token antes de cada llamada de inferencia:
prepareRuntimeAuth: async (ctx) => {
  const exchanged = await exchangeToken(ctx.apiKey);
  return {
    apiKey: exchanged.token,
    baseUrl: exchanged.baseUrl,
    expiresAt: exchanged.expiresAt,
  };
},
OpenClaw llama a los hooks aproximadamente en este orden para plugins de modelo/proveedor. La mayoría de los proveedores solo usan 2 o 3. Este no es el contrato completo de ProviderPlugin; consulta Internos: Hooks de runtime de proveedor para ver la lista de hooks completa y actualmente precisa, así como las notas de alternativa. Los campos de proveedor solo de compatibilidad que OpenClaw ya no llama, como ProviderPlugin.capabilities y suppressBuiltInModel, no se enumeran aquí.
HookCuándo usarlo
catalogCatálogo de modelos o valores predeterminados de URL base
applyConfigDefaultsValores predeterminados globales propiedad del proveedor durante la materialización de configuración
normalizeModelIdLimpieza de alias de id de modelo heredado/vista previa antes de la búsqueda
normalizeTransportLimpieza de api / baseUrl de familia de proveedor antes del ensamblaje genérico del modelo
normalizeConfigNormalizar la configuración de models.providers.<id>
applyNativeStreamingUsageCompatReescrituras de compatibilidad de uso de streaming nativo para proveedores de configuración
resolveConfigApiKeyResolución de autenticación de marcador env propiedad del proveedor
resolveSyntheticAuthAutenticación sintética local/autohospedada o respaldada por configuración
resolveExternalAuthProfilesSuperponer perfiles de autenticación externos propiedad del proveedor para credenciales gestionadas por CLI/app
shouldDeferSyntheticProfileAuthRebajar marcadores de posición de perfiles almacenados sintéticos detrás de autenticación env/config
resolveDynamicModelAceptar IDs arbitrarios de modelos upstream
prepareDynamicModelObtención asíncrona de metadatos antes de resolver
normalizeResolvedModelReescrituras de transporte antes del runner
normalizeToolSchemasLimpieza de esquemas de herramientas propiedad del proveedor antes del registro
inspectToolSchemasDiagnósticos de esquemas de herramientas propiedad del proveedor
resolveReasoningOutputModeContrato de salida de razonamiento etiquetada frente a nativa
prepareExtraParamsParámetros de solicitud predeterminados
createStreamFnTransporte StreamFn completamente personalizado
wrapStreamFnEnvoltorios personalizados de encabezados/cuerpo en la ruta normal de stream
resolveTransportTurnStateEncabezados/metadatos nativos por turno
resolveWebSocketSessionPolicyEncabezados/cool-down de sesión WS nativos
formatApiKeyForma personalizada de token de runtime
refreshOAuthActualización OAuth personalizada
buildAuthDoctorHintGuía de reparación de autenticación
matchesContextOverflowErrorDetección de desbordamiento propiedad del proveedor
classifyFailoverReasonClasificación de límite de tasa/sobrecarga propiedad del proveedor
isCacheTtlEligibleControl de elegibilidad de TTL de caché de prompts
buildMissingAuthMessageIndicación personalizada de autenticación faltante
augmentModelCatalogFilas sintéticas de compatibilidad futura (obsoleto; prefiere registerModelCatalogProvider)
resolveThinkingProfileConjunto de opciones /think específico del modelo
isBinaryThinkingCompatibilidad de razonamiento binario activado/desactivado (obsoleto; prefiere resolveThinkingProfile)
supportsXHighThinkingCompatibilidad de razonamiento xhigh (obsoleto; prefiere resolveThinkingProfile)
resolveDefaultThinkingLevelCompatibilidad de política /think predeterminada (obsoleto; prefiere resolveThinkingProfile)
isModernModelRefCoincidencia de modelo en vivo/smoke
prepareRuntimeAuthIntercambio de token antes de la inferencia
resolveUsageAuthAnálisis personalizado de credenciales de uso
fetchUsageSnapshotEndpoint de uso personalizado
createEmbeddingProviderAdaptador de embeddings propiedad del proveedor para memoria/búsqueda
buildReplayPolicyPolítica personalizada de repetición/Compaction de transcripción
sanitizeReplayHistoryReescrituras de repetición específicas del proveedor después de la limpieza genérica
validateReplayTurnsValidación estricta de turnos de repetición antes del runner incrustado
onModelSelectedCallback posterior a la selección (por ejemplo, telemetría)
Notas de alternativa de runtime:
  • normalizeConfig resuelve un plugin propietario por id de proveedor (primero proveedores incluidos, luego el plugin de runtime coincidente) y llama solo a ese hook; no hay escaneo entre otros proveedores. El propio hook normalizeConfig de Google es el que normaliza las entradas de configuración de google / google-vertex / google-antigravity; no es una alternativa separada del núcleo.
  • resolveConfigApiKey usa el hook del proveedor cuando está expuesto. Amazon Bedrock mantiene la resolución de marcadores env de AWS en su plugin de proveedor; la autenticación de runtime en sí sigue usando la cadena predeterminada del SDK de AWS cuando se configura con auth: "aws-sdk".
  • resolveThinkingProfile(ctx) recibe el provider seleccionado, modelId, la pista opcional fusionada del catálogo reasoning y los datos opcionales fusionados de compat del modelo. Usa compat solo para seleccionar la UI/el perfil de pensamiento del proveedor.
  • resolveSystemPromptContribution permite que un proveedor inyecte guía de prompt del sistema consciente de caché para una familia de modelos. Prefiérelo al hook heredado before_prompt_build de todo el plugin cuando el comportamiento pertenece a una familia de proveedor/modelo y debe preservar la división estable/dinámica de la caché.
5

Agregar capacidades adicionales (opcional)

Paso 5: Agregar capacidades adicionales

Un plugin de proveedor puede registrar embeddings, voz, transcripción en tiempo real, voz en tiempo real, comprensión de medios, generación de imágenes, generación de video, obtención web y búsqueda web junto con la inferencia de texto. OpenClaw clasifica esto como un plugin de capacidad híbrida: el patrón recomendado para plugins de empresa (un plugin por proveedor). Consulta Internos: Propiedad de capacidades.Registra cada capacidad dentro de register(api) junto con tu llamada existente a api.registerProvider(...). Elige solo las pestañas que necesites:
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(...) para fallos HTTP del proveedor, de modo que los plugins compartan lecturas acotadas del cuerpo de error, análisis de errores JSON y sufijos de request-id.
6

Test

Paso 6: Probar

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

Publicar en ClawHub

Los plugins de proveedor se publican igual que cualquier otro plugin de código externo:
clawhub package publish your-org/your-plugin --dry-run
clawhub package publish your-org/your-plugin
clawhub skill publish <path> es un comando distinto para publicar una carpeta de skill, no un paquete de plugin; no lo uses aquí.

Estructura de archivos

<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 controla cuándo se fusiona tu catálogo en relación con los proveedores integrados:
OrdenCuándoCaso de uso
simplePrimera pasadaProveedores simples con clave de API
profileDespués de simpleProveedores restringidos por perfiles de autenticación
pairedDespués de profileSintetizar varias entradas relacionadas
lateÚltima pasadaSobrescribir proveedores existentes (gana en caso de colisión)

Próximos pasos

Relacionado