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.
Tutorial
Sección titulada «Tutorial»Paquete y manifiesto
Paso 1: Paquete y manifiesto
Sección titulada «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-"]},"providerAuthEnvVars": {"acme-ai": ["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
providerAuthEnvVarspara que OpenClaw pueda detectar las credenciales sin cargar el tiempo de ejecución de tu plugin. AñadeproviderAuthAliasescuando una variante de proveedor deba reutilizar la autenticación de otro ID de proveedor.modelSupportes opcional y permite a OpenClaw cargar automáticamente tu plugin de proveedor desde IDs de modelo abreviados comoacme-largeantes de que existan los ganchos de tiempo de ejecución. Si publicas el proveedor en ClawHub, esos camposopenclaw.compatyopenclaw.buildson obligatorios enpackage.json.Registrar el proveedor
Un proveedor de texto mínimo necesita un
id, unlabel, unauthy uncatalog. Elcataloges el enlace de tiempo de ejecución/configuración propiedad del proveedor; puede llamar a APIs de proveedores en vivo y devuelve entradasmodels.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",},];},});},});El
registerModelCatalogProvideres 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 endpoints de proveedores 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. Los usuarios ahora pueden `openclaw onboard —acme-ai-api-key
y seleccionaracme-ai/acme-large` como su modelo.Si el proveedor upstream utiliza diferentes tokens de control que OpenClaw, agregue unapequeña transformación de texto bidireccional en lugar de reemplazar la ruta de flujo (stream):```typescriptapi.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" },],});```El `input` reescribe el contenido final del mensaje del sistema y de texto antesdel transporte. El `output` reescribe los deltas de texto del asistente y el texto final antesde que OpenClaw analice sus propios marcadores de control o la entrega del canal.Para proveedores integrados que solo registran un proveedor de texto con autenticaciónde clave de API más un tiempo de ejecución respaldado por un solo catálogo, prefiera el auxiliarmás estrecho `defineSingleProviderPluginEntry(...)`:```typescriptimport { 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" }],}),},},});```El `buildProvider` es la ruta del catálogo en vivo utilizada cuando OpenClaw puede resolver laautenticación real del proveedor. Puede realizar descubrimientos específicos del proveedor. Use`buildStaticProvider` solo para filas sin conexión que sean seguras de mostrar antes de que laautenticación esté configurada; no debe requerir credenciales ni realizar solicitudes de red.La visualización del `models list --all` de OpenClaw actualmente ejecuta catálogos estáticossolo para complementos de proveedor integrados, con una configuración vacía, un entorno vacío y sinrutas de agente/espacio de trabajo.Si su flujo de autenticación también necesita parchear `models.providers.*`, alias yel 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 en flujo (streamed) en eltransporte normal `openai-completions`, prefiera los auxiliares de catálogo compartidos en`openclaw/plugin-sdk/provider-catalog-shared` en lugar de codificarcomprobaciones de ID de proveedor. El `supportsNativeStreamingUsageCompat(...)` yel `applyProviderNativeStreamingUsageCompat(...)` detectan compatibilidad desde elmapa de capacidades del endpoint, por lo que los endpoints nativos de estilo Moonshot/DashScope aúnse activan incluso cuando un complemento utiliza un ID de proveedor personalizado.Añadir resolución dinámica de modelos
Si su proveedor acepta IDs de modelo arbitrarios (como un proxy o enrutador), añada
resolveDynamicModel:api.registerProvider({// ... id, label, auth, catalog from aboveresolveDynamicModel: (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
prepareDynamicModelpara el calentamiento asíncrono:resolveDynamicModelse ejecuta de nuevo después de que se complete.Añadir ganchos de ejecución (según sea necesario)
La mayoría de los proveedores solo necesitan
catalog+resolveDynamicModel. Añada ganchos de manera incremental a medida que su proveedor los requiera.Los constructores de ayuda compartidos ahora cubren las familias de repetición/compatibilidad de herramientas más comunes, por lo que los complementos generalmente no necesitan conectar cada gancho uno por uno manualmente:
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:
Familia Lo que conecta Ejemplos incluidos openai-compatiblePolítica de repetición estilo OpenAI compartida para transportes compatibles con OpenAI, que incluye saneamiento de ID de llamada de herramienta, correcciones de ordenamiento asistente-primero y validación genérica de turnos Gemini donde el transporte lo necesita moonshot,ollama,xai,zaianthropic-by-modelPolítica de repetición con conocimiento de Claude elegida por modelId, por lo que los transportes de mensajes Anthropic solo obtienen la limpieza de bloques de pensamiento específicos de Claude cuando el modelo resuelto es realmente una ID de Claudeamazon-bedrock,anthropic-vertexgoogle-geminiPolítica de repetición nativa de Gemini más saneamiento de repetición de arranque y modo de salida de razonamiento etiquetado google,google-gemini-clipassthrough-geminiSaneamiento de firma de pensamiento de Gemini para modelos de 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 arranque openrouter,kilocode,opencode,opencode-gohybrid-anthropic-openaiPolítica híbrida para proveedores que mezclan superficies de modelo de mensajes Anthropic y compatibles con OpenAI en un solo complemento; la eliminación opcional de bloques de pensamiento solo para Claude permanece limitada al lado Anthropic minimaxFamilias de transmisión disponibles hoy:
Familia Lo que conecta Ejemplos incluidos google-thinkingNormalización de carga útil de pensamiento de Gemini en la ruta de transmisión compartida google,google-gemini-clikilocode-thinkingEnvoltorio de razonamiento Kilo en la ruta de transmisión de proxy compartida, con kilo/autoe IDs de razonamiento de proxy no compatibles que omiten el pensamiento inyectadokilocodemoonshot-thinkingMapeo de carga útil de pensamiento nativo binario de Moonshot desde la configuración + nivel /thinkmoonshotminimax-fast-modeReescritura de modelo en modo rápido MiniMax en la ruta de transmisión compartida minimax,minimax-portalopenai-responses-defaultsEnvoltorios nativos compartidos de Respuestas OpenAI/Codex: encabezados de atribución, /fast/serviceTier, verbosidad de texto, búsqueda web nativa de Codex, formación de carga útil de compatibilidad de razonamiento y gestión de contexto de Respuestasopenai,openai-codexopenrouter-thinkingEnvoltorio de razonamiento de OpenRouter para rutas de proxy, con omisiones de modelo no compatible/ automanejadas centralmenteopenroutertool-stream-default-onEnvoltorio tool_streamhabilitado por defecto para proveedores como Z.AI que desean transmisión de herramientas a menos que se deshabilite explícitamentezaiCosturas del SDK que alimentan a los constructores de familias
Cada constructor de familia 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 salirse 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 punto de final/modelo (resolveProviderEndpoint,normalizeProviderId,normalizeGooglePreviewModelId).openclaw/plugin-sdk/provider-stream-ProviderStreamFamily,buildProviderStreamFamilyHooks(...),composeProviderStreamWrappers(...), además de los envoltorios compartidos OpenAI/Codex (createOpenAIAttributionHeadersWrapper,createOpenAIFastModeWrapper,createOpenAIServiceTierWrapper,createOpenAIResponsesContextManagementWrapper,createCodexNativeWebSearchWrapper), el envoltorio compatible con OpenAI de DeepSeek V4 (createDeepSeekV4OpenAICompatibleThinkingWrapper), la limpieza de relleno previo de pensamiento de Mensajes de Anthropic (createAnthropicThinkingPrefillPayloadWrapper) y los envoltorios de proxy/proveedor compartidos (createOpenRouterWrapper,createToolStreamWrapper,createMinimaxFastModeWrapper).openclaw/plugin-sdk/provider-tools-ProviderToolCompatFamily,buildProviderToolCompatFamilyHooks("deepseek" | "gemini" | "openai")y los ayudantes del esquema de proveedor subyacente.
Algunos ayudantes de transmisión se mantienen locales al proveedor a propósito.
@openclaw/anthropic-providermantienewrapAnthropicProviderStream,resolveAnthropicBetas,resolveAnthropicFastMode,resolveAnthropicServiceTiery los constructores de envoltorios de Anthropic de nivel inferior en su propia costura públicaapi.ts/contract-api.tsporque codifican el manejo beta de OAuth de Claude y la limitacióncontext1m. El complemento xAI mantiene de manera similar la formación nativa de Respuestas xAI en su propiowrapStreamFn(alias/fast,tool_streampredeterminado, 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 proveedor, ayudantes de modelo predeterminado, constructores de proveedor en tiempo real) y@openclaw/openrouter-provider(constructor de proveedor más ayudantes de integració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,};},Para proveedores que necesitan encabezados de solicitud personalizados o modificaciones del cuerpo:
// wrapStreamFn returns a StreamFn derived from ctx.streamFnwrapStreamFn: (ctx) => {if (!ctx.streamFn) return undefined;const inner = ctx.streamFn;return async (params) => {params.headers = {...params.headers,"X-Acme-Version": "2",};return inner(params);};},Para proveedores que necesitan encabezados de solicitud/sesión nativos o metadatos en transportes HTTP genéricos o WebSocket:
resolveTransportTurnState: (ctx) => ({headers: {"x-request-id": ctx.turnId,},metadata: {session_id: ctx.sessionId ?? "",turn_id: ctx.turnId,},}),resolveWebSocketSessionPolicy: (ctx) => ({headers: {"x-session-id": ctx.sessionId ?? "",},degradeCooldownMs: 60_000,}),Para proveedores que exponen datos de uso/facturación:
resolveUsageAuth: async (ctx) => {const auth = await ctx.resolveOAuthToken();return auth ? { token: auth.token } : null;},fetchUsageSnapshot: async (ctx) => {return await fetchAcmeUsage(ctx.token, ctx.timeoutMs);},Todos los ganchos de proveedor disponibles
OpenClaw llama a los ganchos en este orden. La mayoría de los proveedores solo usan 2-3: Los campos de proveedor solo de compatibilidad que OpenClaw ya no llama, como
ProviderPlugin.capabilitiesysuppressBuiltInModel, no están enumerados aquí.# Gancho Cuándo usar 1 catalogCatálogo de modelos o URL base predeterminadas 2 applyConfigDefaultsValores globales predeterminados propiedad del proveedor durante la materialización de la configuración 3 normalizeModelIdLimpieza de alias de ID de modelo heredados/vista previa antes de la búsqueda 4 normalizeTransportLimpieza de api/baseUrlde familia de proveedores antes del ensamblaje de modelo genérico5 normalizeConfigNormalizar configuración `models.providers. | | 6 |applyNativeStreamingUsageCompat| Reescrituras de compatibilidad de uso de transmisión nativa 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/autohospedada o respaldada por configuración | | 9 |shouldDeferSyntheticProfileAuth| Bajar marcadores de posición de perfil almacenado sintéticos detrás de la autenticación de entorno/configuración | | 10 |resolveDynamicModel| Aceptar ID de modelos ascendentes arbitrarios | | 11 |prepareDynamicModel| Obtención asincrónica de metadatos antes de resolver | | 12 |normalizeResolvedModel| Reescrituras de transporte antes del ejecutor | | 13 |contributeResolvedModelCompat| Indicadores de compatibilidad para modelos de proveedores detrás de otro transporte compatible | | 14 |normalizeToolSchemas| Limpieza de esquema de herramienta propiedad del proveedor antes del registro | | 15 |inspectToolSchemas| Diagnósticos de esquema de herramienta propiedad del proveedor | | 16 |resolveReasoningOutputMode| Contrato de salida de razonamiento etiquetado frente a nativo | | 17 |prepareExtraParams| Parámetros de solicitud predeterminados | | 18 |createStreamFn| Transporte StreamFn completamente personalizado | | 19 |wrapStreamFn| Envoltorios 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/período de 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| Limitación de TTL de caché de prompt | | 28 |buildMissingAuthMessage| Sugerencia personalizada de falta de autenticación | | 29 |augmentModelCatalog| Filas sintéticas de compatibilidad futura | | 30 |resolveThinkingProfile| Conjunto de opciones/thinkespecíficas del modelo | | 31 |isBinaryThinking| Compatibilidad de activación/desactivación de pensamiento binario | | 32 |supportsXHighThinking| Compatibilidad de soporte de razonamientoxhigh| | 33 |resolveDefaultThinkingLevel| Compatibilidad de política/thinkpredeterminada | | 34 |isModernModelRef| Coincidencia de modelos en vivo/prueba | | 35 |prepareRuntimeAuth| Intercambio de tokens antes de la inferencia | | 36 |resolveUsageAuth| Análisis personalizado de credenciales de uso | | 37 |fetchUsageSnapshot| Punto final 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 transcripciones | | 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 reserva en tiempo de ejecución:- `normalizeConfig` verifica el proveedor coincidente primero, luego otros complementos de proveedor con capacidad de gancho hasta que uno realmente cambie la configuración. Si ningún gancho de proveedor reescribe una entrada de configuración de familia Google compatible, el normalizador de configuración de Google incluido aún se aplica.- `resolveConfigApiKey` utiliza el gancho del proveedor cuando se expone. La ruta `amazon-bedrock` incluida también tiene un resolutor de marcador de entorno de AWS integrado aquí, aunque la autenticación en tiempo de ejecución de Bedrock aún utiliza la cadena predeterminada del SDK de AWS.- `resolveSystemPromptContribution` permite que un proveedor inyecte orientación de prompt del sistema consciente de la caché para una familia de modelos. Prefiérala 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 [Internalidades: Ganchos de tiempo de ejecución del proveedor](/es/plugins/architecture-internals#provider-runtime-hooks).Añadir capacidades adicionales (opcional)
Paso 5: Añadir capacidades adicionales
Sección titulada «Paso 5: Añadir capacidades adicionales»Un proveedor de complementos puede registrar 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 Internalidades: Propiedad de capacidades.
Registre cada capacidad dentro de
register(api)junto con su llamada existenteapi.registerProvider(...). Elija solo las pestañas que necesite:```typescriptimport {assertOkOrThrowProviderError,postJsonRequest,} from "openclaw/plugin-sdk/provider-http";api.registerSpeechProvider({id: "acme-ai",label: "Acme Speech",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 quelos complementos compartan lecturas limitadas del cuerpo del error, análisis de errores JSON y sufijos de ID de solicitud.
Prefiera `createRealtimeTranscriptionWebSocketSession(...)`: el asistente compartidomaneja la captura de proxy, el retroceso de reconexión, el vaciado de cierre, los apretones de mano listos, la puesta en cola de audio y el diagnóstico de eventos de cierre. Su complemento solo mapea los eventos de flujo ascendente.
```typescriptapi.registerRealtimeTranscriptionProvider({id: "acme-ai",label: "Acme Realtime Transcription",isConfigured: () => true,createSession: (req) => {const apiKey = String(req.providerConfig.apiKey ?? "");return createRealtimeTranscriptionWebSocketSession({providerId: "acme-ai",callbacks: req,url: "wss://api.example.com/v1/realtime-transcription",headers: { Authorization: `Bearer ${apiKey}` },onMessage: (event, transport) => {if (event.type === "session.created") {transport.sendJson({ type: "session.update" });transport.markReady();return;}if (event.type === "transcript.final") {req.onTranscript?.(event.text);}},sendAudio: (audio, transport) => {transport.sendJson({type: "audio.append",audio: audio.toString("base64"),});},onClose: (transport) => {transport.sendJson({ type: "audio.end" });},});},});```Los proveedores STT por lotes que publican audio multiparte deben usar`buildAudioTranscriptionFormData(...)` de`openclaw/plugin-sdk/provider-http`. El asistente normaliza los nombres de archivo de carga,incluyendo las cargas AAC que necesitan un nombre de archivo estilo M4A paraAPIs de transcripción compatibles.```typescriptapi.registerRealtimeVoiceProvider({id: "acme-ai",label: "Acme Realtime Voice",capabilities: {transports: ["gateway-relay"],inputAudioFormats: [{ encoding: "pcm16", sampleRateHz: 24000, channels: 1 }],outputAudioFormats: [{ encoding: "pcm16", sampleRateHz: 24000, channels: 1 }],supportsBargeIn: true,supportsToolCalls: true,},isConfigured: ({ providerConfig }) => Boolean(providerConfig.apiKey),createBridge: (req) => ({// Set this only if the provider accepts multiple tool responses for// one call, for example an immediate "working" response followed by// the final result.supportsToolResultContinuation: false,connect: async () => {},sendAudio: () => {},setMediaTimestamp: () => {},handleBargeIn: () => {},submitToolResult: () => {},acknowledgeMark: () => {},close: () => {},isConnected: () => true,}),});```Declare `capabilities` para que `talk.catalog` pueda exponer modos válidos,transportes, formatos de audio y marcas de características a los clientes de Talkdel navegador y nativos. Implemente
handleBargeIncuando un transporte pueda detectar que un humano está interrumpiendo la reproducción del asistente y el proveedor admite truncar o borrar la respuesta de audio activa.api.registerMediaUnderstandingProvider({id: "acme-ai",capabilities: ["image", "audio"],describeImage: async (req) => ({ text: "A photo of..." }),transcribeAudio: async (req) => ({ text: "Transcript..." }),});Las capacidades de video usan una forma **consciente del modo**: `generate`,`imageToVideo` y `videoToVideo`. Los campos agregados planos como`maxInputImages` / `maxInputVideos` / `maxDurationSeconds` no sonsuficientes para anunciar el soporte del modo de transformación o los modos deshabilitados de manera limpia. La generación de música sigue el mismo patrón con bloques explícitos
generate/edit.```typescriptapi.registerImageGenerationProvider({id: "acme-ai",label: "Acme Images",generate: async (req) => ({ /* image result */ }),});api.registerVideoGenerationProvider({id: "acme-ai",label: "Acme Video",capabilities: {generate: { maxVideos: 1, maxDurationSeconds: 10, supportsResolution: true },imageToVideo: {enabled: true,maxVideos: 1,maxInputImages: 1,maxInputImagesByModel: { "acme/reference-to-video": 9 },maxDurationSeconds: 5,},videoToVideo: { enabled: false },},generateVideo: async (req) => ({ videos: [] }),});```api.registerWebFetchProvider({id: "acme-ai-fetch",label: "Acme Fetch",hint: "Fetch pages through Acme's rendering backend.",envVars: ["ACME_FETCH_API_KEY"],placeholder: "acme-...",signupUrl: "https://acme.example.com/fetch",credentialPath: "plugins.entries.acme.config.webFetch.apiKey",getCredentialValue: (fetchConfig) => fetchConfig?.acme?.apiKey,setCredentialValue: (fetchConfigTarget, value) => {const acme = (fetchConfigTarget.acme ??= {});acme.apiKey = value;},createTool: () => ({description: "Fetch a page through Acme Fetch.",parameters: {},execute: async (args) => ({ content: [] }),}),});api.registerWebSearchProvider({id: "acme-ai-search",label: "Acme Search",search: async (req) => ({ content: [] }),});Test
Paso 6: Probar
Sección titulada «Paso 6: Probar»import { describe, it, expect } from "vitest";// Export your provider config object from index.ts or a dedicated fileimport { 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
Sección titulada «Publicar en ClawHub»Los plugins de proveedores se publican de la misma manera que cualquier otro plugin de código externo:
clawhub package publish your-org/your-plugin --dry-runclawhub package publish your-org/your-pluginNo use el alias de publicación heredado solo de habilidades aquí; los paquetes de complementos deben usar
clawhub package publish.
Estructura de archivos
Sección titulada «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)Referencia del orden del catálogo
Sección titulada «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
Sección titulada «Siguientes pasos»- Complementos de canal: si su complemento también proporciona un canal
- Tiempo de ejecución del SDK: asistentes
api.runtime(TTS, búsqueda, subagente) - Descripción general del SDK: referencia completa de importación de subrutas
- Aspectos internos del complemento: detalles de los enlaces y ejemplos incluidos