Ir al contenido

Crear complementos de proveedor

Esta guía explica cómo crear un complemento de proveedor que añade un proveedor de modelos (LLM) a OpenClaw. Al final tendrás un proveedor con un catálogo de modelos, autenticación de clave de API y resolución dinámica de modelos.

  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

    ”, “cliDescription”: “Acme AI API key” } ], “configSchema”: { “type”: “object”, “additionalProperties”: false } } ```

    El manifiesto declara setup.providers[].envVars para que OpenClaw pueda detectar las credenciales sin cargar el tiempo de ejecución de su complemento. Añada providerAuthAliases cuando una variante de proveedor deba reutilizar la autenticación de otro ID de proveedor. modelSupport es opcional y permite a OpenClaw cargar automáticamente su complemento de proveedor desde ID de modelos abreviados como acme-large antes de que existan los enlaces de tiempo de ejecución. Si publica el proveedor en ClawHub, esos campos openclaw.compat y openclaw.build son obligatorios en package.json.

  2. Registrar el proveedor

    Un proveedor de texto mínimo necesita un id, un label, un auth y un catalog. catalog es el hook de configuración/ejecución propiedad del proveedor; puede llamar a las APIs de proveedores en vivo y devuelve entradas models.providers.

    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 del catálogo del plano de control más reciente para la interfaz de usuario de lista/ayuda/selector. Úselo para filas de texto, generación de imágenes, generación de video y generación de música. Mantenga las llamadas a los endpoints del proveedor y el mapeo de respuestas en el complemento; OpenClaw posee la forma de fila compartida, las etiquetas de origen y el renderizado de ayuda.

    Ese es un proveedor funcional. Ahora los usuarios pueden `openclaw onboard —acme-ai-api-key

    y seleccionar acme-ai/acme-large` como su modelo.

    Si el proveedor upstream utiliza diferentes tokens de control que OpenClaw, añada una
    pequeña transformación de texto bidireccional en lugar de reemplazar la ruta del flujo:
    ```typescript
    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 del sistema final y el contenido del mensaje 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 del canal.
    Para proveedores empaquetados que solo registran un proveedor de texto con autenticación
    de clave de API más un tiempo de ejecución respaldado por un único catálogo, prefiera el auxiliar
    más estrecho `defineSingleProviderPluginEntry(...)`:
    ```typescript
    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 utilizada cuando OpenClaw puede resolver la
    autenticación real del proveedor. Puede realizar un descubrimiento específico del proveedor. Use
    `buildStaticProvider` solo para filas fuera de línea que sean seguras de mostrar antes de que
    se configure la autenticación; no debe requerir credenciales ni hacer solicitudes de red.
    La visualización del `models list --all` de OpenClaw actualmente ejecuta catálicos estáticos
    solo para complementos de proveedor empaquetados, con una configuración vacía, un entorno vacío y sin
    rutas de agente/espacio de trabajo.
    Si su flujo de autenticación también necesita parchear `models.providers.*`, alias y
    el modelo predeterminado del agente durante la incorporación, use los auxiliares preestablecidos de
    `openclaw/plugin-sdk/provider-onboard`. Los auxiliares más estrechos son
    `createDefaultModelPresetAppliers(...)`,
    `createDefaultModelsPresetAppliers(...)` y
    `createModelCatalogPresetAppliers(...)`.
    Cuando el endpoint nativo de un proveedor admite bloques de uso transmitidos en el
    transporte normal `openai-completions`, prefiera los auxiliares de catálogo compartidos en
    `openclaw/plugin-sdk/provider-catalog-shared` en lugar de codificar
    comprobaciones de ID de proveedor. `supportsNativeStreamingUsageCompat(...)` y
    `applyProviderNativeStreamingUsageCompat(...)` detectan el soporte desde el
    mapa de capacidades del endpoint, por lo que los endpoints nativos al estilo Moonshot/DashScope aún
    participan incluso cuando un complemento usa un ID de proveedor personalizado.
  3. Añadir resolución dinámica de modelos

    Si su proveedor acepta ID de modelo arbitrarios (como un proxy o enrutador), añada 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 la resolución requiere una llamada de red, use prepareDynamicModel para el precalentamiento asíncrono - resolveDynamicModel se ejecuta de nuevo después de que se complete.

  4. Añadir hooks de ejecución (según sea necesario)

    La mayoría de los proveedores solo necesitan catalog + resolveDynamicModel. Añada hooks incrementalmente a medida que su proveedor los requiera.

    Los constructores de ayudantes compartidos ahora cubren las familias de repetición (replay)/compatibilidad de herramientas más comunes, por lo que los plugins generalmente no necesitan conectar cada hook manualmente 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 repetición disponibles hoy:

    FamiliaLo que conectaEjemplos incluidos
    openai-compatiblePolítica de repetición estilo OpenAI compartida para transportes compatibles con OpenAI, que incluye saneamiento de tool-call-id, correcciones de ordenamiento assistant-first y validación genérica de turnos Gemini donde el transporte lo necesitamoonshot, ollama, xai, zai
    anthropic-by-modelPolítica de repetición consciente de Claude elegida por modelId, por lo que los transportes de mensajes de Anthropic solo obtienen limpieza de bloques de pensamiento específicos de Claude cuando el modelo resuelto es realmente un id de Claudeamazon-bedrock, anthropic-vertex
    google-geminiPolítica de repetición nativa de Gemini más saneamiento de repetición de arranque. La familia compartida mantiene la CLI de Gemini de salida de texto en razonamiento etiquetado; el proveedor google directo sobrescribe resolveReasoningOutputMode a native porque el pensamiento de la API de Gemini llega como partes de pensamiento nativas.google, google-gemini-cli
    passthrough-geminiSaneamiento de firma de pensamiento de Gemini para modelos Gemini que se ejecutan a través de transportes de proxy compatibles con OpenAI; no habilita la validación de repetició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 solo plugin; la eliminación opcional de bloques de pensamiento solo de Claude permanece limitada al lado de Anthropicminimax

    Familias de transmisión disponibles hoy:

    FamiliaLo que conectaEjemplos incluidos
    google-thinkingNormalización de carga útil de pensamiento de Gemini en la ruta de transmisión compartidagoogle, google-gemini-cli
    kilocode-thinkingContenedor de razonamiento Kilo en la ruta de transmisión de proxy compartido, con kilo/auto y ids de razonamiento de proxy no compatibles omitiendo el pensamiento inyectadokilocode
    moonshot-thinkingMapeo de carga útil de pensamiento nativo binario de Moonshot desde la configuración + nivel /thinkmoonshot
    minimax-fast-modeReescritura de modelo en modo rápido de MiniMax en la ruta de transmisión compartidaminimax, minimax-portal
    openai-responses-defaultsContenedores compartidos nativos de OpenAI/Codex Responses: encabezados de atribución, /fast/serviceTier, verbosidad de texto, búsqueda web nativa de Codex, configuración de carga útil compatible con razonamiento y gestión de contexto de Responsesopenai
    openrouter-thinkingContenedor de razonamiento de OpenRouter para rutas de proxy, con omisiones de modelo no compatible/auto manejadas centralmenteopenrouter
    tool-stream-default-onContenedor tool_stream activado por defecto para proveedores como Z.AI que quieren transmisión de herramientas a menos que se deshabilite explícitamentezai
    Costuras del SDK que alimentan los constructores de familias

    Cada constructor de familias se compone a partir de ayudantes públicos de nivel inferior exportados desde el mismo paquete, a los que puede recurrir cuando un proveedor necesita apartarse del patrón común:

    • openclaw/plugin-sdk/provider-model-shared - ProviderReplayFamily, buildProviderReplayFamilyHooks(...) y los constructores de repetición sin procesar (buildOpenAICompatibleReplayPolicy, buildAnthropicReplayPolicyForModel, buildGoogleGeminiReplayPolicy, buildHybridAnthropicOrOpenAIReplayPolicy). También exporta ayudantes de repetición de Gemini (sanitizeGoogleGeminiReplayHistory, resolveTaggedReasoningOutputMode) y ayudantes de endpoint/modelo (resolveProviderEndpoint, normalizeProviderId, normalizeGooglePreviewModelId).
    • openclaw/plugin-sdk/provider-stream - ProviderStreamFamily, buildProviderStreamFamilyHooks(...), composeProviderStreamWrappers(...), además de los contenedores compartidos de OpenAI/Codex (createOpenAIAttributionHeadersWrapper, createOpenAIFastModeWrapper, createOpenAIServiceTierWrapper, createOpenAIResponsesContextManagementWrapper, createCodexNativeWebSearchWrapper), el contenedor compatible con OpenAI de DeepSeek V4 (createDeepSeekV4OpenAICompatibleThinkingWrapper), la limpieza de relleno previo de pensamiento de Anthropic Messages (createAnthropicThinkingPrefillPayloadWrapper), compatibilidad de llamadas a herramientas en texto sin formato (createPlainTextToolCallCompatWrapper) y contenedores de proxy/proveedor compartidos (createOpenRouterWrapper, createToolStreamWrapper, createMinimaxFastModeWrapper).
    • openclaw/plugin-sdk/provider-tools - ProviderToolCompatFamily, buildProviderToolCompatFamilyHooks("deepseek" | "gemini" | "openai") y ayudantes de esquema de proveedor subyacentes.

    Para los proveedores de la familia Gemini, mantenga el modo de salida de razonamiento alineado con el transporte. Los proveedores directos de la API de Google Gemini deben usar la salida de razonamiento native para que OpenClaw consuma partes de pensamiento nativas sin añadir directivas de prompt `

    /

    . Los backends de estilo CLI de Gemini solo de texto que analizan una respuesta JSON/texto final pueden mantener el contrato etiquetado google-gemini` compartido.

    Algunos ayudantes de transmisión se mantienen locales del proveedor a propósito. `@openclaw/anthropic-provider` mantiene `wrapAnthropicProviderStream`, `resolveAnthropicBetas`, `resolveAnthropicFastMode`, `resolveAnthropicServiceTier` y los constructores de contenedores de Anthropic de nivel inferior en su propia costura pública `api.ts` / `contract-api.ts` porque codifican el manejo beta de OAuth de Claude y el control de acceso `context1m`. El plugin xAI mantiene de manera similar la forma nativa de Responses de xAI en su propio `wrapStreamFn` (alias `/fast`, `tool_stream` predeterminado, limpieza estricta de herramientas 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 proveedores, ayudantes de modelo predeterminado, constructores de proveedores en tiempo real) y `@openclaw/openrouter-provider` (constructor de proveedor más ayudantes de incorporación/configuración).

    Para proveedores que necesitan un intercambio de tokens 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,
    };
    },
    Todos los hooks de proveedor disponibles

    OpenClaw llama a los hooks en este orden. La mayoría de los proveedores solo usan 2-3: Los campos de proveedores solo de compatibilidad que OpenClaw ya no llama, como ProviderPlugin.capabilities y suppressBuiltInModel, no están listados aquí.

    #HookCuándo usar
    1catalogCatálogo de modelos o URL base predeterminada
    2applyConfigDefaultsValores globales propiedad del proveedor durante la materialización de la configuración
    3normalizeModelIdLimpieza de alias de id de modelo heredados/vista previa antes de la búsqueda
    4normalizeTransportLimpieza de api / baseUrl de familia de proveedores antes del ensamblaje de modelo genérico
    5normalizeConfigNormalizar configuración `models.providers.

    | | 6 |applyNativeStreamingUsageCompat| Reescrituras de compatibilidad de uso de transmisión nativas para proveedores de configuración | | 7 |resolveConfigApiKey| Resolución de autenticación de marcador de entorno propiedad del proveedor | | 8 |resolveSyntheticAuth| Autenticación sintética local/autoalojada o respaldada por configuración | | 9 |shouldDeferSyntheticProfileAuth| Colocar marcadores de posición de perfil almacenado sintético de nivel inferior detrás de la autenticación de entorno/configuración | | 10 |resolveDynamicModel| Aceptar IDs de modelo ascendentes arbitrarios | | 11 |prepareDynamicModel| Obtención asincrónica de metadatos antes de resolver | | 12 |normalizeResolvedModel| Reescrituras de transporte antes del ejecutor | | 13 |normalizeToolSchemas| Limpieza de esquema de herramientas propiedad del proveedor antes del registro | | 14 |inspectToolSchemas| Diagnósticos de esquema de herramientas propiedad del proveedor | | 15 |resolveReasoningOutputMode| Contrato de salida de razonamiento etiquetado vs nativo | | 16 |prepareExtraParams| Parámetros de solicitud predeterminados | | 17 |createStreamFn| Transporte StreamFn completamente personalizado | | 19 |wrapStreamFn| Contenedores de encabezados/cuerpo personalizados en la ruta de transmisión normal | | 20 |resolveTransportTurnState| Encabezados/metadatos nativos por turno | | 21 |resolveWebSocketSessionPolicy| Encabezados de sesión WS nativos/enfriamiento | | 22 |formatApiKey| Forma de token de tiempo de ejecución personalizada | | 23 |refreshOAuth| Actualización de OAuth personalizada | | 24 |buildAuthDoctorHint| Guía de reparación de autenticación | | 25 |matchesContextOverflowError| Detección de desbordamiento propiedad del proveedor | | 26 |classifyFailoverReason| Clasificación de límite de tasa/sobrecarga propiedad del proveedor | | 27 |isCacheTtlEligible| Control de acceso TTL de caché de prompt | | 28 |buildMissingAuthMessage| Sugerencia personalizada de autenticación faltante | | 29 |augmentModelCatalog| Filas sintéticas de compatibilidad futura | | 30 |resolveThinkingProfile| Conjunto de opciones/thinkespecífico del modelo | | 31 |isBinaryThinking| Compatibilidad de pensamiento binario activado/desactivado | | 32 |supportsXHighThinking| Compatibilidad de soporte de razonamientoxhigh| | 33 |resolveDefaultThinkingLevel| Compatibilidad de política/thinkpredeterminada | | 34 |isModernModelRef| Coincidencia de modelo en vivo/prueba | | 35 |prepareRuntimeAuth| Intercambio de tokens antes de la inferencia | | 36 |resolveUsageAuth| Análisis personalizado de credenciales de uso | | 37 |fetchUsageSnapshot| Endpoint de uso personalizado | | 38 |createEmbeddingProvider| Adaptador de incrustación propiedad del proveedor para memoria/búsqueda | | 39 |buildReplayPolicy| Política personalizada de repetición/compactación de transcripción | | 40 |sanitizeReplayHistory| Reescrituras de repetición específicas del proveedor después de la limpieza genérica | | 41 |validateReplayTurns| Validación estricta de turno de repetición antes del ejecutor incrustado | | 42 |onModelSelected` | Devolución de llamada posterior a la selección (por ejemplo, telemetría) |

    Notas sobre alternativas de tiempo de ejecución:
    - `normalizeConfig` verifica primero el proveedor coincidente y luego otros plugins de proveedores con capacidad de hooks hasta que uno realmente cambia la configuración. Si ningún hook de proveedor reescribe una entrada de configuración de familia de Google compatible, el normalizador de configuración de Google incluido todavía se aplica.
    - `resolveConfigApiKey` usa el hook del proveedor cuando está expuesto. Amazon Bedrock mantiene la resolución del marcador de entorno de AWS en su plugin de proveedor; la autenticación de tiempo de ejecución en sí todavía usa la cadena predeterminada del SDK de AWS cuando se configura con `auth: "aws-sdk"`.
    - `resolveThinkingProfile(ctx)` recibe el `provider` seleccionado, `modelId`, sugerencia de catálogo `reasoning` fusionada opcional y hechos del modelo `compat` fusionados opcionales. Use `compat` solo para seleccionar la interfaz de usuario/perfil de pensamiento del proveedor.
    - `resolveSystemPromptContribution` permite que un proveedor inyecte orientación de prompt del sistema consciente de la caché para una familia de modelos. Prefiéralo sobre `before_prompt_build` cuando el comportamiento pertenece a una familia de proveedor/modelo y debe preservar la división de caché estable/dinámica.
    Para descripciones detalladas y ejemplos del mundo real, consulte [Internals: Provider Runtime Hooks](/es/plugins/architecture-internals#provider-runtime-hooks).
  5. Añadir capacidades adicionales (opcional)

    Un complemento de proveedor puede registrar incrustaciones, 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 complemento de capacidad híbrida: el patrón recomendado para complementos de empresas (un complemento por proveedor). Consulte Internals: Capability Ownership.

    Registre cada capacidad dentro de register(api) junto con su llamada api.registerProvider(...) existente. Elija solo las pestañas que necesite:

    ```typescript
    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();
    }
    },
    });
    ```
    Use `assertOkOrThrowProviderError(...)` para fallos HTTP del proveedor para que

    los complementos compartan lecturas limitadas del cuerpo del error, análisis de errores JSON y sufijos de ID de solicitud.

  6. Test

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

Los plugins de proveedores se publican de la misma manera que cualquier otro plugin de código externo:

Ventana de terminal
clawhub package publish your-org/your-plugin --dry-run
clawhub package publish your-org/your-plugin

No use el alias de publicación solo de habilidades heredado aquí; los paquetes de complementos deben usar clawhub package publish.

<bundled-plugin-root>/acme-ai/
├── package.json # openclaw.providers metadata
├── openclaw.plugin.json # Manifest with provider auth metadata
├── index.ts # definePluginEntry + registerProvider
└── src/
├── provider.test.ts # Tests
└── usage.ts # Usage endpoint (optional)

catalog.order controla cuándo se fusiona su catálogo en relación con los proveedores integrados:

OrdenCuándoCaso de uso
simplePrimera pasadaProveedores de clave API simple
profileDespués de simpleProveedores restringidos por perfiles de autenticación
pairedDespués del perfilSintetizar múltiples entradas relacionadas
lateÚltima pasadaAnular proveedores existentes (gana en colisión)