somratpro Claude Sonnet 4.6 commited on
Commit
49cc5fe
Β·
1 Parent(s): 818a8a5

fix(key-rotator): suppress LLM_API_KEY fallback noise in startup logs

Browse files

Providers without dedicated keys were logging "1 key" due to
LLM_API_KEY fallback being included unconditionally. Now only
providers with dedicated env vars log individually; all fallback
providers are summarised in a single line.

Also adds API Key Rotation section to README, HF_TOKEN and
WHATSAPP_ENABLED to Space secrets frontmatter, and key rotation
bullet to features list.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

Files changed (2) hide show
  1. README.md +29 -0
  2. multi-provider-key-rotator.cjs +22 -5
README.md CHANGED
@@ -20,6 +20,10 @@ secrets:
20
  description: "Comma-separated Telegram user IDs for access"
21
  - name: TELEGRAM_BOT_TOKEN
22
  description: "Telegram bot token from BotFather"
 
 
 
 
23
  ---
24
 
25
  <!-- Badges -->
@@ -41,6 +45,7 @@ secrets:
41
  - [πŸ’Ύ Workspace Backup *(Optional)*](#-workspace-backup-optional)
42
  - [πŸ”” Webhooks *(Optional)*](#-webhooks-optional)
43
  - [πŸ” Security & Advanced *(Optional)*](#-security--advanced-optional)
 
44
  - [πŸ€– LLM Providers](#-llm-providers)
45
  - [πŸ’» Local Development](#-local-development)
46
  - [πŸ”— CLI Access](#-cli-access)
@@ -54,6 +59,7 @@ secrets:
54
  ## ✨ Features
55
 
56
  - πŸ”Œ **Any LLM:** Use Claude, OpenAI GPT, Google Gemini, Grok, DeepSeek, Qwen, and 40+ providers (set `LLM_API_KEY` and `LLM_MODEL` accordingly).
 
57
  - ⚑ **Zero Config:** Duplicate this Space and set **just three** secrets (LLM_API_KEY, LLM_MODEL, GATEWAY_TOKEN) – no other setup needed.
58
  - 🐳 **Fast Builds:** Uses a pre-built OpenClaw Docker image to deploy in minutes.
59
  - 🌐 **Cloudflare Outbound Proxy:** HuggingClaw can automatically provision a Cloudflare Worker proxy for blocked outbound traffic such as Telegram API requests.
@@ -181,6 +187,29 @@ Configure password access and network restrictions:
181
  | `ALLOWED_ORIGINS` | β€” | Comma-separated allowed origins for Control UI |
182
  | `CLOUDFLARE_KEEPALIVE_ENABLED` | `true` | Set to `false` to disable the automatic Cloudflare KeepAlive worker |
183
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
184
  ## πŸ€– LLM Providers
185
 
186
  HuggingClaw supports **all providers** from OpenClaw. Set `LLM_MODEL=<provider/model>` and the provider is auto-detected.
 
20
  description: "Comma-separated Telegram user IDs for access"
21
  - name: TELEGRAM_BOT_TOKEN
22
  description: "Telegram bot token from BotFather"
23
+ - name: HF_TOKEN
24
+ description: "HuggingFace token with Write access β€” enables automatic workspace backup."
25
+ - name: WHATSAPP_ENABLED
26
+ description: "Set to 'true' to enable WhatsApp pairing support."
27
  ---
28
 
29
  <!-- Badges -->
 
45
  - [πŸ’Ύ Workspace Backup *(Optional)*](#-workspace-backup-optional)
46
  - [πŸ”” Webhooks *(Optional)*](#-webhooks-optional)
47
  - [πŸ” Security & Advanced *(Optional)*](#-security--advanced-optional)
48
+ - [πŸ”‘ API Key Rotation *(Optional)*](#-api-key-rotation-optional)
49
  - [πŸ€– LLM Providers](#-llm-providers)
50
  - [πŸ’» Local Development](#-local-development)
51
  - [πŸ”— CLI Access](#-cli-access)
 
59
  ## ✨ Features
60
 
61
  - πŸ”Œ **Any LLM:** Use Claude, OpenAI GPT, Google Gemini, Grok, DeepSeek, Qwen, and 40+ providers (set `LLM_API_KEY` and `LLM_MODEL` accordingly).
62
+ - πŸ”‘ **Multi-Key Rotation:** Supply comma-separated key pools per provider (e.g. `ANTHROPIC_API_KEYS=key1,key2,key3`) for automatic round-robin rotation across rate limits.
63
  - ⚑ **Zero Config:** Duplicate this Space and set **just three** secrets (LLM_API_KEY, LLM_MODEL, GATEWAY_TOKEN) – no other setup needed.
64
  - 🐳 **Fast Builds:** Uses a pre-built OpenClaw Docker image to deploy in minutes.
65
  - 🌐 **Cloudflare Outbound Proxy:** HuggingClaw can automatically provision a Cloudflare Worker proxy for blocked outbound traffic such as Telegram API requests.
 
187
  | `ALLOWED_ORIGINS` | β€” | Comma-separated allowed origins for Control UI |
188
  | `CLOUDFLARE_KEEPALIVE_ENABLED` | `true` | Set to `false` to disable the automatic Cloudflare KeepAlive worker |
189
 
190
+ ## πŸ”‘ API Key Rotation *(Optional)*
191
+
192
+ Spread requests across multiple API keys to avoid rate limits. Supply a comma-separated pool for any provider β€” keys rotate round-robin per provider independently.
193
+
194
+ ```bash
195
+ # Single provider, multiple keys
196
+ ANTHROPIC_API_KEYS=sk-ant-key1,sk-ant-key2,sk-ant-key3
197
+
198
+ # Multiple providers simultaneously
199
+ OPENAI_API_KEYS=sk-openai-key1,sk-openai-key2
200
+ GEMINI_API_KEYS=AIza-key1,AIza-key2
201
+ ```
202
+
203
+ **Fallback chain** (per provider):
204
+ 1. `{PROVIDER}_API_KEYS` β€” comma-separated pool *(preferred)*
205
+ 2. `{PROVIDER}_API_KEY` β€” single dedicated key
206
+ 3. `LLM_API_KEY` β€” universal fallback *(default for all providers)*
207
+
208
+ > [!TIP]
209
+ > If you only set `LLM_API_KEY`, all providers use it as a fallback automatically β€” no extra config needed. Add per-provider pools only when you need multi-key rotation.
210
+
211
+ Supported per-provider variables: `ANTHROPIC_API_KEYS`, `OPENAI_API_KEYS`, `GEMINI_API_KEYS`, `DEEPSEEK_API_KEYS`, `GROQ_API_KEYS`, `MISTRAL_API_KEYS`, `OPENROUTER_API_KEYS`, `XAI_API_KEYS`, `NVIDIA_API_KEYS`, `COHERE_API_KEYS`, `TOGETHER_API_KEYS`, `CEREBRAS_API_KEYS`, and more β€” see `.env.example` for the full list.
212
+
213
  ## πŸ€– LLM Providers
214
 
215
  HuggingClaw supports **all providers** from OpenClaw. Set `LLM_MODEL=<provider/model>` and the provider is auto-detected.
multi-provider-key-rotator.cjs CHANGED
@@ -175,19 +175,36 @@ function normalizeKeys(...inputs) {
175
 
176
  // Build per-provider key pools + rotation indices
177
  const providerState = PROVIDERS.map(p => {
178
- const keys = normalizeKeys(
179
  process.env[p.envPlural] || '',
180
  process.env[p.envSingular] || '',
181
- process.env.LLM_API_KEY || '',
182
  );
183
- if (!keys.length) {
184
- console.warn(`[key-rotator] No keys for provider "${p.name}"`);
185
- } else {
 
 
 
186
  console.log(`[key-rotator] ${p.name}: ${keys.length} key${keys.length === 1 ? '' : 's'}`);
 
 
187
  }
 
188
  return { ...p, keys, idx: 0 };
189
  });
190
 
 
 
 
 
 
 
 
 
 
 
 
 
191
  // ─── Runtime helpers ─────────────────────────────────────────────────────────
192
 
193
  function resolveHostname(urlLike) {
 
175
 
176
  // Build per-provider key pools + rotation indices
177
  const providerState = PROVIDERS.map(p => {
178
+ const dedicatedKeys = normalizeKeys(
179
  process.env[p.envPlural] || '',
180
  process.env[p.envSingular] || '',
 
181
  );
182
+ const hasDedicated = dedicatedKeys.length > 0;
183
+ const keys = hasDedicated
184
+ ? dedicatedKeys
185
+ : normalizeKeys(process.env.LLM_API_KEY || '');
186
+
187
+ if (hasDedicated) {
188
  console.log(`[key-rotator] ${p.name}: ${keys.length} key${keys.length === 1 ? '' : 's'}`);
189
+ } else if (!keys.length) {
190
+ console.warn(`[key-rotator] No keys for provider "${p.name}"`);
191
  }
192
+
193
  return { ...p, keys, idx: 0 };
194
  });
195
 
196
+ // Summarise providers that fall back to LLM_API_KEY
197
+ const fallbackCount = providerState.filter(p => {
198
+ const dedicated = normalizeKeys(
199
+ process.env[p.envPlural] || '',
200
+ process.env[p.envSingular] || '',
201
+ );
202
+ return dedicated.length === 0 && p.keys.length > 0;
203
+ }).length;
204
+ if (fallbackCount > 0) {
205
+ console.log(`[key-rotator] ${fallbackCount} provider(s) using LLM_API_KEY fallback`);
206
+ }
207
+
208
  // ─── Runtime helpers ─────────────────────────────────────────────────────────
209
 
210
  function resolveHostname(urlLike) {