thebird 1.2.74 → 1.2.76

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CLAUDE.md CHANGED
@@ -133,14 +133,41 @@ Run examples against real Gemini API to validate message translation.
133
133
  - Cannot bundle index.js directly for browser — it imports Node-only modules (oauth.js, config.js, cloud-generate.js) at top level. Create a separate browser entry that imports only lib/client.js, lib/errors.js, lib/convert.js. Use ESM wrapper (not CJS module.exports) to preserve named exports in bundle.
134
134
  - Tool parameter types must be lowercase for Gemini API — `object`, `string`, `number` not `OBJECT`, `STRING`, `NUMBER`. Uppercase types fail schema validation.
135
135
  - agentGenerate passes raw Anthropic-format messages to streamGemini internally, which calls convertMessages. Do NOT pre-convert in app.js — double-conversion breaks tool schemas.
136
+ - Anthropic format IS the canonical message format — do NOT add an intermediate transformation layer. Direct translation to provider-native formats is cleaner than another abstraction level.
137
+
138
+ ## Error Architecture
139
+
140
+ **Error hierarchy** (lib/errors.js):
141
+ - `BridgeError(message, { status, code, retryable, provider, headers })` — base class, `GeminiError` alias for backwards compat
142
+ - `AuthError` (401/403), `RateLimitError` (429, retryable), `TimeoutError` (408, retryable), `ContextWindowError` (413), `ContentPolicyError` (451), `ProviderError` (5xx, retryable)
143
+ - `classifyError(status, message, provider)` — factory returns typed error from status code
144
+ - `redactKeys(str)` — masks API keys (AIza, sk-, key- patterns) to `...XXXX`
145
+ - `parseRetryAfterHeader(err)` — standard HTTP Retry-After (seconds + date formats)
146
+
147
+ **Stream guards** (lib/stream-guard.js):
148
+ - `guardStream(iterable, { chunkTimeoutMs, maxRepeats })` — wraps async iterables
149
+ - Chunk timeout default 30s, repeat threshold default 100
150
+
151
+ **Circuit breaker** (lib/circuit-breaker.js):
152
+ - `createCircuitBreaker({ maxFailures, cooldownMs })` — per-provider failure tracking
153
+ - Auto-recovery after cooldown, reset on success
154
+
155
+ **Capabilities** (lib/capabilities.js):
156
+ - `getCapabilities(provider)` — merges provider.capabilities with defaults
157
+ - `stripUnsupported(params, caps)` — removes unsupported features, returns warnings
158
+ - Defaults: streaming, toolUse, vision, systemMessage = true; jsonMode = false
136
159
 
137
160
  ## Files
138
161
 
139
162
  - `lib/convert.js`: Message/tool translation logic
140
163
  - `lib/client.js`: Provider client factory
141
- - `lib/errors.js`: Error handling and retry logic
164
+ - `lib/errors.js`: Typed error hierarchy (BridgeError, AuthError, RateLimitError, etc.), classifyError, redactKeys, withRetry
165
+ - `lib/stream-guard.js`: guardStream — chunk timeout and repeated-chunk detection for async iterables
166
+ - `lib/circuit-breaker.js`: Per-provider failure tracking with auto-recovery
167
+ - `lib/capabilities.js`: Provider capability metadata and unsupported feature stripping
168
+ - `lib/router-stream.js`: Router streaming/generation with circuit breaker and capability integration
142
169
  - `lib/providers/`: Provider-specific streaming implementations
143
- - `index.js`: Main entry point, streaming and generation wrappers
170
+ - `index.js`: Main entry point, Gemini streaming/generation, re-exports
144
171
  - `index.d.ts`: TypeScript type definitions
145
172
  - `examples/`: Working examples using Anthropic SDK format
146
173
  - `wasi/cli.ts`: Deno streaming CLI — `deno run --allow-net --allow-env wasi/cli.ts [--model M] [--system S] <prompt>`
package/README.md CHANGED
@@ -152,6 +152,68 @@ Pass options as a nested array: `["maxtoken", { "max_tokens": 16384 }]`.
152
152
  }
153
153
  ```
154
154
 
155
+ ## Error Handling
156
+
157
+ thebird uses a typed error hierarchy. All errors extend `BridgeError`:
158
+
159
+ | Error | Status | Retryable | When |
160
+ |---|---|---|---|
161
+ | `AuthError` | 401, 403 | No | Invalid API key |
162
+ | `RateLimitError` | 429 | Yes | Quota exceeded |
163
+ | `TimeoutError` | 408 | Yes | Stream chunk timeout |
164
+ | `ContextWindowError` | 413 | No | Input too long |
165
+ | `ContentPolicyError` | 451 | No | Safety filter triggered |
166
+ | `ProviderError` | 5xx | Yes | Upstream server error |
167
+
168
+ `GeminiError` is an alias for `BridgeError` (backwards compatible). API keys are auto-redacted in error messages.
169
+
170
+ ```js
171
+ const { classifyError, BridgeError } = require('thebird');
172
+ try { /* ... */ } catch (err) {
173
+ if (err instanceof BridgeError && err.retryable) { /* retry */ }
174
+ }
175
+ ```
176
+
177
+ ## Streaming Resilience
178
+
179
+ Pass `streamGuard` to protect against stalled or looping streams:
180
+
181
+ ```js
182
+ streamGemini({
183
+ messages,
184
+ streamGuard: { chunkTimeoutMs: 30000, maxRepeats: 100 }
185
+ });
186
+ ```
187
+
188
+ - **Chunk timeout** — throws `TimeoutError` if no chunk received within the timeout (default 30s)
189
+ - **Repeat detection** — throws if the same chunk appears consecutively N times (default 100)
190
+
191
+ ## Circuit Breaker
192
+
193
+ The router tracks per-provider failures. After consecutive failures exceed the threshold, the provider is temporarily skipped:
194
+
195
+ ```js
196
+ createRouter({
197
+ Providers: [/* ... */],
198
+ circuitBreaker: { maxFailures: 5, cooldownMs: 60000 }
199
+ });
200
+ ```
201
+
202
+ ## Provider Capabilities
203
+
204
+ Declare what each provider supports. Unsupported features are stripped automatically with warnings:
205
+
206
+ ```json
207
+ {
208
+ "name": "groq",
209
+ "api_base_url": "...",
210
+ "api_key": "$GROQ_API_KEY",
211
+ "capabilities": { "vision": false, "jsonMode": true }
212
+ }
213
+ ```
214
+
215
+ Defaults: `streaming: true, toolUse: true, vision: true, systemMessage: true, jsonMode: false`.
216
+
155
217
  ## Gemini Direct API
156
218
 
157
219
  `streamGemini` / `generateGemini` bypass routing and call Gemini natively via `@google/genai`. Requires `GEMINI_API_KEY`.
package/index.js CHANGED
@@ -98,6 +98,6 @@ async function generateGemini({ model, system, messages, tools, apiKey, temperat
98
98
  const { streamRouter, generateRouter, createRouter } = require('./lib/router-stream');
99
99
  const { cloudGenerate, streamCloud, cloudStream } = require('./lib/cloud-generate');
100
100
  const { ensureAuth, login: oauthLogin } = require('./lib/oauth');
101
- const { BridgeError, AuthError, RateLimitError, TimeoutError, ContextWindowError, ContentPolicyError, ProviderError, classifyError } = require('./lib/errors');
101
+ const { BridgeError, AuthError, RateLimitError, TimeoutError, ContextWindowError, ContentPolicyError, ProviderError, classifyError, redactKeys } = require('./lib/errors');
102
102
 
103
- module.exports = { streamGemini, createFullStream, generateGemini, streamRouter, generateRouter, createRouter, convertMessages, convertTools, cleanSchema, GeminiError, BridgeError, AuthError, RateLimitError, TimeoutError, ContextWindowError, ContentPolicyError, ProviderError, classifyError, cloudGenerate, streamCloud, cloudStream, ensureAuth, oauthLogin };
103
+ module.exports = { streamGemini, createFullStream, generateGemini, streamRouter, generateRouter, createRouter, convertMessages, convertTools, cleanSchema, GeminiError, BridgeError, AuthError, RateLimitError, TimeoutError, ContextWindowError, ContentPolicyError, ProviderError, classifyError, redactKeys, cloudGenerate, streamCloud, cloudStream, ensureAuth, oauthLogin };
package/lib/errors.js CHANGED
@@ -1,7 +1,7 @@
1
1
  const KEY_PATTERNS = [
2
2
  /\b(AIza[A-Za-z0-9_-]{20,})/g,
3
- /\b(sk-[A-Za-z0-9]{20,})/g,
4
- /\b(key-[A-Za-z0-9]{20,})/g,
3
+ /\b(sk-[A-Za-z0-9_-]{20,})/g,
4
+ /\b(key-[A-Za-z0-9_-]{20,})/g,
5
5
  /((?:api[_-]?key|token|secret|authorization|bearer)[=:\s"']+)([A-Za-z0-9_-]{20,})/gi,
6
6
  ];
7
7
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "thebird",
3
- "version": "1.2.74",
3
+ "version": "1.2.76",
4
4
  "description": "Anthropic SDK to Gemini streaming bridge — drop-in proxy that translates Anthropic message format and tool calls to Google Gemini",
5
5
  "scripts": {
6
6
  "start": "node serve.js"