الانتقال إلى المحتوى الرئيسي

Documentation Index

Fetch the complete documentation index at: https://docs.openclaw.ai/llms.txt

Use this file to discover all available pages before exploring further.

يرشدك هذا الدليل خلال بناء Plugin مزوّد يضيف مزوّد نماذج (LLM) إلى OpenClaw. بحلول النهاية، سيكون لديك مزوّد مع كتالوج نماذج، ومصادقة بمفتاح API، وحل ديناميكي للنماذج.
إذا لم تكن قد أنشأت أي Plugin لـ OpenClaw من قبل، فاقرأ بدء الاستخدام أولًا لمعرفة بنية الحزمة الأساسية وإعداد البيان.
تضيف Plugins المزوّدين نماذج إلى حلقة الاستدلال العادية في OpenClaw. إذا كان النموذج يجب أن يعمل عبر عفريت وكيل أصلي يملك الخيوط، أو Compaction، أو أحداث الأدوات، فاقرن المزوّد مع عدة وكيل بدلًا من وضع تفاصيل بروتوكول العفريت في القلب.

شرح تفصيلي

1

Package and manifest

الخطوة 1: الحزمة والبيان

{
  "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"
    }
  }
}
يعلن البيان عن providerAuthEnvVars حتى يتمكن OpenClaw من اكتشاف بيانات الاعتماد من دون تحميل وقت تشغيل Plugin الخاص بك. أضف providerAuthAliases عندما يجب أن يعيد متغير مزوّد استخدام مصادقة معرّف مزوّد آخر. modelSupport اختياري، ويتيح لـ OpenClaw تحميل Plugin المزوّد تلقائيًا من معرّفات النماذج المختصرة مثل acme-large قبل وجود خطافات وقت التشغيل. إذا نشرت المزوّد على ClawHub، فستكون حقول openclaw.compat وopenclaw.build هذه مطلوبة في package.json.
2

Register the provider

يحتاج مزوّد نصوص بسيط إلى id، وlabel، وauth، وcatalog. catalog هو خطاف وقت التشغيل/الإعداد الذي يملكه المزوّد؛ يمكنه استدعاء واجهات API مباشرة للبائع وإرجاع إدخالات 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 هو سطح كتالوج مستوى التحكم الأحدث لواجهة مستخدم القوائم/المساعدة/الاختيار. استخدمه لصفوف النصوص، وتوليد الصور، وتوليد الفيديو، وتوليد الموسيقى. أبقِ استدعاءات نقاط نهاية البائع وربط الاستجابات داخل Plugin؛ يملك OpenClaw شكل الصف المشترك، وتسميات المصدر، وعرض المساعدة.هذا مزوّد عامل. يمكن للمستخدمين الآن تشغيل openclaw onboard --acme-ai-api-key <key> واختيار acme-ai/acme-large كنموذج لهم.إذا كان المزوّد upstream يستخدم رموز تحكم مختلفة عن OpenClaw، فأضف تحويل نص صغيرًا ثنائي الاتجاه بدلًا من استبدال مسار البث:
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 كتابة موجه النظام النهائي ومحتوى الرسائل النصية قبل النقل. يعيد output كتابة دلتا نص المساعد والنص النهائي قبل أن يحلل OpenClaw علامات التحكم الخاصة به أو تسليم القناة.بالنسبة إلى المزوّدين المضمّنين الذين يسجلون مزوّد نص واحدًا فقط مع مصادقة بمفتاح API إضافة إلى وقت تشغيل واحد مدعوم بكتالوج، فضّل مساعد 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 هو مسار الكتالوج المباشر المستخدم عندما يستطيع OpenClaw حل مصادقة مزوّد حقيقية. يمكنه تنفيذ اكتشاف خاص بالمزوّد. استخدم buildStaticProvider فقط للصفوف غير المتصلة الآمنة للعرض قبل تهيئة المصادقة؛ يجب ألا يتطلب بيانات اعتماد أو يجري طلبات شبكة. ينفّذ عرض models list --all في OpenClaw حاليًا الكتالوجات الثابتة فقط لـ Plugins المزوّدين المضمّنين، مع إعداد فارغ، وبيئة فارغة، ومن دون مسارات وكيل/مساحة عمل.إذا كان تدفق المصادقة لديك يحتاج أيضًا إلى تعديل models.providers.*، والبدائل، والنموذج الافتراضي للوكيل أثناء الإعداد، فاستخدم مساعدات الإعداد المسبق من openclaw/plugin-sdk/provider-onboard. أضيق المساعدات نطاقًا هي createDefaultModelPresetAppliers(...)، وcreateDefaultModelsPresetAppliers(...)، و createModelCatalogPresetAppliers(...).عندما تدعم نقطة النهاية الأصلية لمزوّد كتل استخدام مبثوثة عبر نقل openai-completions العادي، فضّل مساعدات الكتالوج المشتركة في openclaw/plugin-sdk/provider-catalog-shared بدلًا من ترميز فحوصات معرّفات المزوّدين بشكل ثابت. تكتشف supportsNativeStreamingUsageCompat(...) وapplyProviderNativeStreamingUsageCompat(...) الدعم من خريطة قدرات نقطة النهاية، لذلك تظل نقاط النهاية الأصلية بأسلوب Moonshot/DashScope مشتركة حتى عندما يستخدم Plugin معرّف مزوّد مخصصًا.
3

Add dynamic model resolution

إذا كان مزوّدك يقبل معرّفات نماذج عشوائية (مثل وكيل أو موجّه)، فأضف 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,
  }),
});
إذا كان الحل يتطلب استدعاء شبكة، فاستخدم prepareDynamicModel للتهيئة غير المتزامنة - يعمل resolveDynamicModel مرة أخرى بعد اكتمالها.
4

Add runtime hooks (as needed)

يحتاج معظم المزوّدين فقط إلى catalog + resolveDynamicModel. أضف الخطافات تدريجيًا حسب ما يتطلبه مزوّدك.تغطي بُناة المساعدات المشتركة الآن أكثر عائلات إعادة التشغيل/توافق الأدوات شيوعًا، لذلك لا تحتاج Plugins عادةً إلى توصيل كل خطاف يدويًا واحدًا تلو الآخر:
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,
});
عائلات إعادة التشغيل المتاحة اليوم:
العائلةما الذي توصلهأمثلة مضمنة
openai-compatibleسياسة إعادة تشغيل مشتركة بأسلوب OpenAI للنواقل المتوافقة مع OpenAI، بما في ذلك تنظيف معرّفات استدعاءات الأدوات، وإصلاحات ترتيب المساعد أولاً، والتحقق العام من أدوار Gemini حيث يحتاج النقل إلى ذلكmoonshot, ollama, xai, zai
anthropic-by-modelسياسة إعادة تشغيل مدركة لـ Claude تُختار بواسطة modelId، بحيث لا تحصل نواقل رسائل Anthropic إلا على تنظيف كتل التفكير الخاصة بـ Claude عندما يكون النموذج المحسوم معرّف Claude فعلاًamazon-bedrock, anthropic-vertex
google-geminiسياسة إعادة تشغيل Gemini الأصلية، بالإضافة إلى تنظيف إعادة تشغيل التمهيد ووضع إخراج التفكير الموسومgoogle, google-gemini-cli
passthrough-geminiتنظيف توقيع التفكير في Gemini لنماذج Gemini العاملة عبر نواقل وكيل متوافقة مع OpenAI؛ لا يفعّل تحقق إعادة التشغيل الأصلي لـ Gemini أو إعادة كتابة التمهيدopenrouter, kilocode, opencode, opencode-go
hybrid-anthropic-openaiسياسة هجينة لموفري الخدمة الذين يمزجون أسطح نماذج رسائل Anthropic والنماذج المتوافقة مع OpenAI في Plugin واحد؛ يظل إسقاط كتل التفكير الاختياري الخاص بـ Claude محصوراً في جانب Anthropicminimax
عائلات التدفق المتاحة اليوم:
العائلةما الذي توصلهأمثلة مضمنة
google-thinkingتطبيع حمولة تفكير Gemini على مسار التدفق المشتركgoogle, google-gemini-cli
kilocode-thinkingغلاف استدلال Kilo على مسار تدفق الوكيل المشترك، مع تخطي kilo/auto ومعرّفات استدلال الوكيل غير المدعومة للتفكير المحقونkilocode
moonshot-thinkingربط حمولة التفكير الأصلي الثنائية في Moonshot من الإعداد + مستوى /thinkmoonshot
minimax-fast-modeإعادة كتابة نموذج الوضع السريع في MiniMax على مسار التدفق المشتركminimax, minimax-portal
openai-responses-defaultsأغلفة Responses الأصلية المشتركة لـ OpenAI/Codex: ترويسات الإسناد، و/fast/serviceTier، وإسهاب النص، وبحث الويب الأصلي في Codex، وتشكيل حمولة توافق الاستدلال، وإدارة سياق Responsesopenai, openai-codex
openrouter-thinkingغلاف استدلال OpenRouter لمسارات الوكيل، مع معالجة مركزية لتخطي النموذج غير المدعوم/autoopenrouter
tool-stream-default-onغلاف tool_stream المفعّل افتراضياً لموفري الخدمة مثل Z.AI الذين يريدون تدفق الأدوات ما لم يُعطّل صراحةًzai
يتكون كل منشئ عائلة من مساعدين عامين أدنى مستوى مُصدّرين من الحزمة نفسها، ويمكنك استخدامها عندما يحتاج موفر الخدمة إلى الخروج عن النمط الشائع:
  • openclaw/plugin-sdk/provider-model-shared - ProviderReplayFamily وbuildProviderReplayFamilyHooks(...) ومنشئو إعادة التشغيل الخام (buildOpenAICompatibleReplayPolicy وbuildAnthropicReplayPolicyForModel وbuildGoogleGeminiReplayPolicy وbuildHybridAnthropicOrOpenAIReplayPolicy). يصدّر أيضاً مساعدي إعادة تشغيل Gemini (sanitizeGoogleGeminiReplayHistory وresolveTaggedReasoningOutputMode) ومساعدي نقطة النهاية/النموذج (resolveProviderEndpoint وnormalizeProviderId وnormalizeGooglePreviewModelId).
  • openclaw/plugin-sdk/provider-stream - ProviderStreamFamily وbuildProviderStreamFamilyHooks(...) وcomposeProviderStreamWrappers(...)، بالإضافة إلى أغلفة OpenAI/Codex المشتركة (createOpenAIAttributionHeadersWrapper وcreateOpenAIFastModeWrapper وcreateOpenAIServiceTierWrapper وcreateOpenAIResponsesContextManagementWrapper وcreateCodexNativeWebSearchWrapper)، وغلاف DeepSeek V4 المتوافق مع OpenAI (createDeepSeekV4OpenAICompatibleThinkingWrapper)، وتنظيف الملء المسبق لتفكير Anthropic Messages (createAnthropicThinkingPrefillPayloadWrapper)، وأغلفة الوكيل/موفر الخدمة المشتركة (createOpenRouterWrapper وcreateToolStreamWrapper وcreateMinimaxFastModeWrapper).
  • openclaw/plugin-sdk/provider-tools - ProviderToolCompatFamily وbuildProviderToolCompatFamilyHooks("gemini") ومساعدو مخططات Gemini الأساسيون (normalizeGeminiToolSchemas وinspectGeminiToolSchemas).
تبقى بعض مساعدات التدفق محلية لدى موفر الخدمة عمداً. تحتفظ @openclaw/anthropic-provider بكل من wrapAnthropicProviderStream وresolveAnthropicBetas وresolveAnthropicFastMode وresolveAnthropicServiceTier ومنشئي أغلفة Anthropic الأدنى مستوى في seam العامة الخاصة بها api.ts / contract-api.ts لأنها ترمز معالجة Claude OAuth beta وبوابة context1m. وبالمثل، يحتفظ Plugin xAI بتشكيل Responses الأصلي الخاص بـ xAI في wrapStreamFn الخاص به (الأسماء المستعارة لـ /fast، وtool_stream الافتراضي، وتنظيف الأدوات الصارمة غير المدعومة، وإزالة حمولة الاستدلال الخاصة بـ xAI).يدعم نمط جذر الحزمة نفسه أيضاً @openclaw/openai-provider (منشئي موفر الخدمة، ومساعدي النموذج الافتراضي، ومنشئي موفر الخدمة الفوري) و@openclaw/openrouter-provider (منشئ موفر الخدمة بالإضافة إلى مساعدي الإعداد/التهيئة).
لموفري الخدمة الذين يحتاجون إلى تبادل رمز قبل كل استدعاء استدلال:
prepareRuntimeAuth: async (ctx) => {
  const exchanged = await exchangeToken(ctx.apiKey);
  return {
    apiKey: exchanged.token,
    baseUrl: exchanged.baseUrl,
    expiresAt: exchanged.expiresAt,
  };
},
يستدعي OpenClaw الخطاطيف بهذا الترتيب. يستخدم معظم موفري الخدمة 2-3 فقط: حقول موفر الخدمة الخاصة بالتوافق فقط، التي لم يعد OpenClaw يستدعيها، مثل ProviderPlugin.capabilities وsuppressBuiltInModel، غير مدرجة هنا.
#الخطافمتى تستخدمه
1catalogكتالوج النماذج أو افتراضيات عنوان URL الأساسي
2applyConfigDefaultsالافتراضات العامة المملوكة لموفر الخدمة أثناء تجسيد الإعداد
3normalizeModelIdتنظيف الأسماء المستعارة القديمة/المعاينة لمعرّف النموذج قبل البحث
4normalizeTransportتنظيف api / baseUrl لعائلة موفر الخدمة قبل تجميع النموذج العام
5normalizeConfigتطبيع إعداد models.providers.<id>
6applyNativeStreamingUsageCompatإعادة كتابة توافق استخدام التدفق الأصلي لموفري الإعدادات
7resolveConfigApiKeyحل المصادقة بعلامة البيئة المملوك لموفر الخدمة
8resolveSyntheticAuthمصادقة تركيبية محلية/مستضافة ذاتياً أو مدعومة بالإعداد
9shouldDeferSyntheticProfileAuthتخفيض العناصر النائبة للملف الشخصي التركيبي المخزن خلف مصادقة البيئة/الإعداد
10resolveDynamicModelقبول معرّفات نماذج المنبع العشوائية
11prepareDynamicModelجلب بيانات وصفية غير متزامن قبل الحل
12normalizeResolvedModelإعادة كتابة النقل قبل المشغّل
13contributeResolvedModelCompatأعلام التوافق لنماذج المورّد خلف نقل متوافق آخر
14normalizeToolSchemasتنظيف مخطط الأدوات المملوك لموفر الخدمة قبل التسجيل
15inspectToolSchemasتشخيصات مخطط الأدوات المملوكة لموفر الخدمة
16resolveReasoningOutputModeعقد إخراج الاستدلال الموسوم مقابل الأصلي
17prepareExtraParamsمعاملات الطلب الافتراضية
18createStreamFnنقل StreamFn مخصص بالكامل
19wrapStreamFnأغلفة ترويسات/متن مخصصة على مسار التدفق العادي
20resolveTransportTurnStateترويسات/بيانات وصفية أصلية لكل دور
21resolveWebSocketSessionPolicyترويسات/فترة تهدئة جلسة WS أصلية
22formatApiKeyشكل رمز تشغيل مخصص
23refreshOAuthتحديث OAuth مخصص
24buildAuthDoctorHintإرشادات إصلاح المصادقة
25matchesContextOverflowErrorاكتشاف تجاوز السعة المملوك لموفر الخدمة
26classifyFailoverReasonتصنيف حد المعدل/الحمل الزائد المملوك لموفر الخدمة
27isCacheTtlEligibleبوابة TTL لذاكرة التخزين المؤقت للموجه
28buildMissingAuthMessageتلميح مخصص للمصادقة المفقودة
29augmentModelCatalogصفوف توافق أمامية تركيبية
30resolveThinkingProfileمجموعة خيارات /think الخاصة بالنموذج
31isBinaryThinkingتوافق تشغيل/إيقاف التفكير الثنائي
32supportsXHighThinkingتوافق دعم استدلال xhigh
33resolveDefaultThinkingLevelتوافق سياسة /think الافتراضية
34isModernModelRefمطابقة نموذج مباشرة/دخانية
35prepareRuntimeAuthتبادل الرمز قبل الاستدلال
36resolveUsageAuthتحليل بيانات اعتماد الاستخدام المخصصة
37fetchUsageSnapshotنقطة نهاية استخدام مخصصة
38createEmbeddingProviderمحول تضمين مملوك لموفر الخدمة للذاكرة/البحث
39buildReplayPolicyسياسة مخصصة لإعادة تشغيل/Compaction النص
40sanitizeReplayHistoryإعادة كتابة إعادة التشغيل الخاصة بموفر الخدمة بعد التنظيف العام
41validateReplayTurnsتحقق صارم من أدوار إعادة التشغيل قبل المشغّل المضمن
42onModelSelectedاستدعاء لاحق للاختيار (مثل القياسات عن بُعد)
ملاحظات الرجوع الاحتياطي في وقت التشغيل:
  • يتحقق normalizeConfig من موفر الخدمة المطابق أولاً، ثم من إضافات موفري الخدمة الأخرى القادرة على الخطافات إلى أن يغير أحدها الإعداد فعلياً. إذا لم يُعد أي خطاف موفر خدمة كتابة إدخال إعداد مدعوم لعائلة Google، فسيظل مطبّع إعدادات Google المضمن مطبقاً.
  • يستخدم resolveConfigApiKey خطاف موفر الخدمة عندما يكون مكشوفاً. كما يحتوي مسار amazon-bedrock المضمن على حال مدمج لعلامات بيئة AWS هنا، رغم أن مصادقة تشغيل Bedrock نفسها لا تزال تستخدم السلسلة الافتراضية لـ AWS SDK.
  • يتيح resolveSystemPromptContribution لموفر الخدمة حقن إرشادات موجه نظام مدركة لذاكرة التخزين المؤقت لعائلة نموذج. فضّله على before_prompt_build عندما يخص السلوك عائلة موفر/نموذج واحدة وينبغي أن يحافظ على تقسيم ذاكرة التخزين المؤقت المستقر/الديناميكي.
للاطلاع على أوصاف مفصلة وأمثلة من الواقع، راجع العناصر الداخلية: خطاطيف تشغيل موفر الخدمة.
5

Add extra capabilities (optional)

الخطوة 5: أضف قدرات إضافية

يمكن لـ Plugin مزود تسجيل الكلام، والنسخ الفوري، والصوت الفوري، وفهم الوسائط، وتوليد الصور، وتوليد الفيديو، وجلب الويب، والبحث في الويب إلى جانب استدلال النص. يصنّف OpenClaw ذلك باعتباره Plugin ذا قدرات هجينة - وهو النمط الموصى به لـ Plugins الشركات (Plugin واحد لكل مورّد). راجع داخليًا: ملكية القدرات.سجّل كل قدرة داخل register(api) إلى جانب استدعاء api.registerProvider(...) الموجود لديك. اختر علامات التبويب التي تحتاجها فقط:
import {
  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();
    }
  },
});
استخدم assertOkOrThrowProviderError(...) لإخفاقات HTTP الخاصة بالمزود كي تشارك Plugins قراءات جسم الخطأ المحدودة، وتحليل أخطاء JSON، ولاحقات معرّف الطلب.
6

Test

الخطوة 6: الاختبار

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

النشر إلى ClawHub

تنشر Plugins المزود بالطريقة نفسها مثل أي Plugin كود خارجي آخر:
clawhub package publish your-org/your-plugin --dry-run
clawhub package publish your-org/your-plugin
لا تستخدم الاسم المستعار القديم المخصص لنشر Skills فقط هنا؛ يجب أن تستخدم حزم Plugin 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 في وقت دمج الكتالوج الخاص بك بالنسبة إلى المزودين المضمّنين:
الترتيبمتىحالة الاستخدام
simpleالمرور الأولمزودو مفاتيح API البسيطة
profileبعد simpleمزودون مشروطون بملفات تعريف المصادقة
pairedبعد profileتركيب عدة إدخالات مرتبطة
lateالمرور الأخيرتجاوز المزودين الحاليين (يفوز عند التصادم)

الخطوات التالية

ذات صلة