somratpro commited on
Commit
304ec00
·
1 Parent(s): 03cf867

feat: add support for registering custom OpenAI-compatible providers via environment variables

Browse files
Files changed (3) hide show
  1. .env.example +24 -0
  2. README.md +44 -0
  3. start.sh +67 -0
.env.example CHANGED
@@ -124,6 +124,30 @@ LLM_API_KEY=your_api_key_here
124
  # Or any other OpenClaw-supported provider (format: provider/model-name)
125
  LLM_MODEL=anthropic/claude-sonnet-4-5
126
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
127
  # [REQUIRED] Gateway authentication token
128
  # Generate: openssl rand -hex 32
129
  GATEWAY_TOKEN=your_gateway_token_here
 
124
  # Or any other OpenClaw-supported provider (format: provider/model-name)
125
  LLM_MODEL=anthropic/claude-sonnet-4-5
126
 
127
+ # Optional: custom OpenAI-compatible provider
128
+ # Only use this if you want to register your own endpoint at startup.
129
+ # Leave all of these empty unless you need a custom provider.
130
+ #
131
+ # Example:
132
+ # CUSTOM_PROVIDER_NAME=modal
133
+ # CUSTOM_BASE_URL=https://api.us-west-2.modal.direct/v1
134
+ # CUSTOM_MODEL_ID=zai-org/GLM-5.1-FP8
135
+ # LLM_MODEL=modal/zai-org/GLM-5.1-FP8
136
+ #
137
+ # Notes:
138
+ # - CUSTOM_BASE_URL should be the API base URL, not /chat/completions
139
+ # - CUSTOM_PROVIDER_NAME must not reuse a built-in provider name
140
+ # - Uses LLM_API_KEY by default; set CUSTOM_API_KEY only if different
141
+ #
142
+ # CUSTOM_PROVIDER_NAME=modal
143
+ # CUSTOM_BASE_URL=https://your-openai-compatible-endpoint/v1
144
+ # CUSTOM_MODEL_ID=your-model-id
145
+ # CUSTOM_MODEL_NAME=Friendly Model Name
146
+ # CUSTOM_API_KEY=your_custom_api_key_here
147
+ # CUSTOM_API_TYPE=openai-completions
148
+ # CUSTOM_CONTEXT_WINDOW=128000
149
+ # CUSTOM_MAX_TOKENS=500
150
+
151
  # [REQUIRED] Gateway authentication token
152
  # Generate: openssl rand -hex 32
153
  GATEWAY_TOKEN=your_gateway_token_here
README.md CHANGED
@@ -219,6 +219,50 @@ LLM_MODEL=provider/model-name
219
 
220
  The provider prefix in `LLM_MODEL` tells HuggingClaw how to call it. See [OpenClaw Model Providers](https://docs.openclaw.ai/concepts/model-providers) for the full list.
221
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
222
  ## 💻 Local Development
223
 
224
  ```bash
 
219
 
220
  The provider prefix in `LLM_MODEL` tells HuggingClaw how to call it. See [OpenClaw Model Providers](https://docs.openclaw.ai/concepts/model-providers) for the full list.
221
 
222
+ ### Custom OpenAI-Compatible Provider
223
+
224
+ You can register your own OpenAI-compatible endpoint at startup without touching OpenClaw CLI.
225
+
226
+ Required env vars:
227
+
228
+ ```bash
229
+ LLM_API_KEY=your_api_key
230
+ LLM_MODEL=modal/zai-org/GLM-5.1-FP8
231
+ CUSTOM_PROVIDER_NAME=modal
232
+ CUSTOM_BASE_URL=https://api.us-west-2.modal.direct/v1
233
+ CUSTOM_MODEL_ID=zai-org/GLM-5.1-FP8
234
+ ```
235
+
236
+ Optional env vars:
237
+
238
+ ```bash
239
+ CUSTOM_MODEL_NAME=GLM-5.1-FP8
240
+ CUSTOM_API_KEY=your_custom_api_key
241
+ CUSTOM_API_TYPE=openai-completions
242
+ CUSTOM_CONTEXT_WINDOW=128000
243
+ CUSTOM_MAX_TOKENS=500
244
+ ```
245
+
246
+ Notes:
247
+
248
+ - This is for **OpenAI-compatible** endpoints only.
249
+ - `CUSTOM_BASE_URL` should be the API base URL, not `/chat/completions`.
250
+ - `CUSTOM_PROVIDER_NAME` must be a new name and cannot override built-in providers like `openai`, `openrouter`, `google`, or `anthropic`.
251
+ - If `CUSTOM_API_KEY` is not set, HuggingClaw uses `LLM_API_KEY`.
252
+
253
+ Examples:
254
+
255
+ - Modal-hosted model:
256
+ - `CUSTOM_PROVIDER_NAME=modal`
257
+ - `CUSTOM_BASE_URL=https://api.us-west-2.modal.direct/v1`
258
+ - `CUSTOM_MODEL_ID=zai-org/GLM-5.1-FP8`
259
+ - `LLM_MODEL=modal/zai-org/GLM-5.1-FP8`
260
+ - Self-hosted vLLM:
261
+ - `CUSTOM_PROVIDER_NAME=vllm`
262
+ - `CUSTOM_BASE_URL=https://your-vllm.example.com/v1`
263
+ - `CUSTOM_MODEL_ID=Qwen/Qwen2.5-72B-Instruct`
264
+ - `LLM_MODEL=vllm/Qwen/Qwen2.5-72B-Instruct`
265
+
266
  ## 💻 Local Development
267
 
268
  ```bash
start.sh CHANGED
@@ -231,6 +231,73 @@ CONFIG_JSON=$(echo "$CONFIG_JSON" | jq ".gateway.auth.token = \"$GATEWAY_TOKEN\"
231
  # Model configuration at top level
232
  CONFIG_JSON=$(echo "$CONFIG_JSON" | jq ".agents.defaults.model = \"$LLM_MODEL\"")
233
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
234
  # Browser configuration (managed local Chromium in HF/Docker)
235
  BROWSER_EXECUTABLE_PATH=""
236
  for candidate in /usr/bin/chromium /usr/bin/chromium-browser /snap/bin/chromium; do
 
231
  # Model configuration at top level
232
  CONFIG_JSON=$(echo "$CONFIG_JSON" | jq ".agents.defaults.model = \"$LLM_MODEL\"")
233
 
234
+ # Optional: dynamic custom OpenAI-compatible provider registration
235
+ CUSTOM_PROVIDER_NAME="${CUSTOM_PROVIDER_NAME:-}"
236
+ CUSTOM_BASE_URL="${CUSTOM_BASE_URL:-}"
237
+ CUSTOM_MODEL_ID="${CUSTOM_MODEL_ID:-}"
238
+ CUSTOM_MODEL_NAME="${CUSTOM_MODEL_NAME:-$CUSTOM_MODEL_ID}"
239
+ CUSTOM_API_KEY="${CUSTOM_API_KEY:-$LLM_API_KEY}"
240
+ CUSTOM_API_TYPE="${CUSTOM_API_TYPE:-openai-completions}"
241
+ CUSTOM_CONTEXT_WINDOW="${CUSTOM_CONTEXT_WINDOW:-128000}"
242
+ CUSTOM_MAX_TOKENS="${CUSTOM_MAX_TOKENS:-500}"
243
+
244
+ if [ -n "$CUSTOM_PROVIDER_NAME" ] || [ -n "$CUSTOM_BASE_URL" ] || [ -n "$CUSTOM_MODEL_ID" ]; then
245
+ CUSTOM_PROVIDER_NORMALIZED=$(printf '%s' "$CUSTOM_PROVIDER_NAME" | tr '[:upper:]' '[:lower:]')
246
+ CUSTOM_BASE_URL_NORMALIZED="${CUSTOM_BASE_URL%/}"
247
+ CUSTOM_PROVIDER_OK=true
248
+
249
+ if [ -z "$CUSTOM_PROVIDER_NAME" ] || [ -z "$CUSTOM_BASE_URL" ] || [ -z "$CUSTOM_MODEL_ID" ]; then
250
+ echo "⚠️ Custom provider skipped: set CUSTOM_PROVIDER_NAME, CUSTOM_BASE_URL, and CUSTOM_MODEL_ID together."
251
+ CUSTOM_PROVIDER_OK=false
252
+ fi
253
+
254
+ case "$CUSTOM_PROVIDER_NORMALIZED" in
255
+ anthropic|openai|openai-codex|google|google-vertex|deepseek|opencode|opencode-go|openrouter|kilocode|vercel-ai-gateway|zai|z-ai|z.ai|zhipu|moonshot|kimi-coding|minimax|qwen|modelstudio|xiaomi|volcengine|volcengine-plan|byteplus|byteplus-plan|qianfan|mistral|mistralai|xai|x-ai|nvidia|cohere|groq|together|huggingface|cerebras|venice|synthetic|github-copilot)
256
+ echo "⚠️ Custom provider skipped: CUSTOM_PROVIDER_NAME='$CUSTOM_PROVIDER_NAME' conflicts with a built-in provider."
257
+ CUSTOM_PROVIDER_OK=false
258
+ ;;
259
+ esac
260
+
261
+ if [[ "$CUSTOM_BASE_URL_NORMALIZED" == */chat/completions ]] || [[ "$CUSTOM_BASE_URL_NORMALIZED" == */completions ]]; then
262
+ echo "⚠️ Custom provider skipped: CUSTOM_BASE_URL should be the API base URL, not a completions endpoint."
263
+ CUSTOM_PROVIDER_OK=false
264
+ fi
265
+
266
+ if ! [[ "$CUSTOM_CONTEXT_WINDOW" =~ ^[0-9]+$ ]] || ! [[ "$CUSTOM_MAX_TOKENS" =~ ^[0-9]+$ ]]; then
267
+ echo "⚠️ Custom provider skipped: CUSTOM_CONTEXT_WINDOW and CUSTOM_MAX_TOKENS must be whole numbers."
268
+ CUSTOM_PROVIDER_OK=false
269
+ fi
270
+
271
+ if [ "$CUSTOM_PROVIDER_OK" = "true" ]; then
272
+ echo "🔧 Registering custom provider: $CUSTOM_PROVIDER_NAME → $CUSTOM_BASE_URL_NORMALIZED"
273
+ CONFIG_JSON=$(jq \
274
+ --arg provider "$CUSTOM_PROVIDER_NAME" \
275
+ --arg baseUrl "$CUSTOM_BASE_URL_NORMALIZED" \
276
+ --arg apiKey "$CUSTOM_API_KEY" \
277
+ --arg apiType "$CUSTOM_API_TYPE" \
278
+ --arg modelId "$CUSTOM_MODEL_ID" \
279
+ --arg modelName "$CUSTOM_MODEL_NAME" \
280
+ --argjson contextWindow "$CUSTOM_CONTEXT_WINDOW" \
281
+ --argjson maxTokens "$CUSTOM_MAX_TOKENS" \
282
+ '.models.mode = "merge" |
283
+ .models.providers[$provider] = {
284
+ "baseUrl": $baseUrl,
285
+ "apiKey": $apiKey,
286
+ "api": $apiType,
287
+ "models": [{
288
+ "id": $modelId,
289
+ "name": $modelName,
290
+ "contextWindow": $contextWindow,
291
+ "maxTokens": $maxTokens
292
+ }]
293
+ }' <<<"$CONFIG_JSON")
294
+
295
+ if [[ "$LLM_MODEL" != "$CUSTOM_PROVIDER_NAME/"* ]]; then
296
+ echo "⚠️ Custom provider registered, but LLM_MODEL='$LLM_MODEL' does not start with '$CUSTOM_PROVIDER_NAME/'."
297
+ fi
298
+ fi
299
+ fi
300
+
301
  # Browser configuration (managed local Chromium in HF/Docker)
302
  BROWSER_EXECUTABLE_PATH=""
303
  for candidate in /usr/bin/chromium /usr/bin/chromium-browser /snap/bin/chromium; do