Ir al contenido

Construir 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"],
    "providerAuthEnvVars": {
    "acme-ai": ["ACME_AI_API_KEY"]
    },
    "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 providerAuthEnvVars para que OpenClaw pueda detectar las credenciales sin cargar el tiempo de ejecución de tu complemento. Si publicas el proveedor en ClawHub, esos campos openclaw.compat y openclaw.build son obligatorios en package.json.

  2. Registrar el proveedor

    Un proveedor mínimo necesita un id, un label, un auth y un catalog:

    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,
    },
    ],
    },
    };
    },
    },
    });
    },
    });

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

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

    Para proveedores empaquetados que solo registren un proveedor de texto con autenticación
    de clave de API más un tiempo de ejecución con un solo catálogo, prefiere el asistente más
    específico `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" }],
    }),
    },
    },
    });
    ```
    Si tu flujo de autenticación también necesita modificar `models.providers.*`, los alias y
    el modelo predeterminado del agente durante el incorporation, utiliza los asistentes preestablecidos de
    `openclaw/plugin-sdk/provider-onboard`. Los asistentes más específicos son
    `createDefaultModelPresetAppliers(...)`,
    `createDefaultModelsPresetAppliers(...)` y
    `createModelCatalogPresetAppliers(...)`.
  3. Agregar resolución dinámica de modelos

    Si su proveedor acepta ID de modelo arbitrarios (como un proxy o enrutador), agregue 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 la preparación asincrónica — resolveDynamicModel se ejecuta nuevamente después de que se complete.

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

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

    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:

    #HookCuándo usar
    1catalogCatálogo de modelos o URL base predeterminadas
    2resolveDynamicModelAceptar IDs de modelo ascendentes arbitrarios
    3prepareDynamicModelObtención asincrónica de metadatos antes de resolver
    4normalizeResolvedModelReescrituras de transporte antes del ejecutor
    5capabilitiesMetadatos de transcripción/herramientas (datos, no invocables)
    6prepareExtraParamsParámetros de solicitud predeterminados
    7wrapStreamFnEnvoltorios de cabeceras/cuerpo personalizados
    8formatApiKeyForma de token de tiempo de ejecución personalizada
    9refreshOAuthActualización de OAuth personalizada
    10buildAuthDoctorHintGuía de reparación de autenticación
    11isCacheTtlEligibleControl de TTL de caché de prompts
    12buildMissingAuthMessageSugerencia personalizada de autenticación faltante
    13suppressBuiltInModelOcultar filas ascendentes obsoletas
    14augmentModelCatalogFilas sintéticas de compatibilidad futura
    15isBinaryThinkingPensamiento binario activado/desactivado
    16supportsXHighThinkingSoporte de razonamiento xhigh
    17resolveDefaultThinkingLevelPolítica predeterminada de /think
    18isModernModelRefCoincidencia de modelos en vivo/prueba
    19prepareRuntimeAuthIntercambio de tokens antes de la inferencia
    20resolveUsageAuthAnálisis personalizado de credenciales de uso
    21fetchUsageSnapshotPunto final de uso personalizado
    22onModelSelectedDevolución de llamada posterior a la selección (ej. telemetría)

    Para descripciones detalladas y ejemplos del mundo real, consulte Internals: Provider Runtime Hooks.

  5. Añadir capacidades adicionales (opcional)

    Un proveedor de plugins puede registrar voz, comprensión de medios, generación de imágenes y búsqueda web junto con la inferencia de texto:

    register(api) {
    api.registerProvider({ id: "acme-ai", /* ... */ });
    api.registerSpeechProvider({
    id: "acme-ai",
    label: "Acme Speech",
    isConfigured: ({ config }) => Boolean(config.messages?.tts),
    synthesize: async (req) => ({
    audioBuffer: Buffer.from(/* PCM data */),
    outputFormat: "mp3",
    fileExtension: ".mp3",
    voiceCompatible: false,
    }),
    });
    api.registerMediaUnderstandingProvider({
    id: "acme-ai",
    capabilities: ["image", "audio"],
    describeImage: async (req) => ({ text: "A photo of..." }),
    transcribeAudio: async (req) => ({ text: "Transcript..." }),
    });
    api.registerImageGenerationProvider({
    id: "acme-ai",
    label: "Acme Images",
    generate: async (req) => ({ /* image result */ }),
    });
    }

    OpenClaw clasifica esto como un plugin de capacidad híbrida. Este es el patrón recomendado para los plugins de empresas (un plugin por proveedor). Consulte Internals: Capability Ownership.

  6. Probar

    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 heredado solo para habilidades aquí; los paquetes de plugins deben usar clawhub package publish.

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

## Referencia del orden del catálogo
`catalog.order` controla cuándo se fusiona su catálogo en relación con los
proveedores integrados:
| Orden | Cuándo | Caso de uso |
| --------- | ------------------ | ------------------------------------------------------ |
| `simple` | Primera pasada | Proveedores de clave API simple |
| `profile` | Después de simple | Proveedores restringidos por perfiles de autenticación |
| `paired` | Después del perfil | Sintetizar múltiples entradas relacionadas |
| `late` | Última pasada | Anular proveedores existentes (gana en colisión) |
## Siguientes pasos
- [Channel Plugins](/en/plugins/sdk-channel-plugins) — si su plugin también proporciona un canal
- [SDK Runtime](/en/plugins/sdk-runtime) — ayudantes `api.runtime` (TTS, búsqueda, subagente)
- [SDK Overview](/en/plugins/sdk-overview) — referencia completa de importación de subrutas
- [Plugin Internals](/en/plugins/architecture#provider-runtime-hooks) — detalles de los hooks y ejemplos incluidos