Skip to content

Commit 16f96ad

Browse files
committed
feat: provider-specific rate limiting (Ollama has no limits)
1 parent e80cb3d commit 16f96ad

File tree

3 files changed

+32
-12
lines changed

3 files changed

+32
-12
lines changed

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "opencode-codebase-index",
3-
"version": "0.2.4",
3+
"version": "0.2.5",
44
"description": "Semantic codebase indexing and search for OpenCode - find code by meaning, not just keywords",
55
"type": "module",
66
"main": "dist/index.js",

src/indexer/index.ts

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,26 @@ export class Indexer {
177177
this.saveFailedBatches(existing);
178178
}
179179

180+
private getProviderRateLimits(provider: string): {
181+
concurrency: number;
182+
intervalMs: number;
183+
minRetryMs: number;
184+
maxRetryMs: number;
185+
} {
186+
switch (provider) {
187+
case "github-copilot":
188+
return { concurrency: 1, intervalMs: 4000, minRetryMs: 5000, maxRetryMs: 60000 };
189+
case "openai":
190+
return { concurrency: 3, intervalMs: 500, minRetryMs: 1000, maxRetryMs: 30000 };
191+
case "google":
192+
return { concurrency: 5, intervalMs: 200, minRetryMs: 1000, maxRetryMs: 30000 };
193+
case "ollama":
194+
return { concurrency: 5, intervalMs: 0, minRetryMs: 500, maxRetryMs: 5000 };
195+
default:
196+
return { concurrency: 3, intervalMs: 1000, minRetryMs: 1000, maxRetryMs: 30000 };
197+
}
198+
}
199+
180200
async initialize(): Promise<void> {
181201
this.detectedProvider = await detectEmbeddingProvider(this.config.embeddingProvider);
182202
if (!this.detectedProvider) {
@@ -498,15 +518,17 @@ export class Indexer {
498518
}
499519
}
500520

501-
// GitHub Models API rate limit: 15 requests/minute for embeddings
502-
// Use concurrency 1 with 4-second delay to stay under limit (15 req/min = 1 req per 4 sec)
503-
const queue = new PQueue({ concurrency: 1, interval: 4000, intervalCap: 1 });
521+
const providerRateLimits = this.getProviderRateLimits(detectedProvider.provider);
522+
const queue = new PQueue({
523+
concurrency: providerRateLimits.concurrency,
524+
interval: providerRateLimits.intervalMs,
525+
intervalCap: providerRateLimits.concurrency
526+
});
504527
const dynamicBatches = createDynamicBatches(chunksNeedingEmbedding);
505528
let rateLimitBackoffMs = 0;
506529

507530
for (const batch of dynamicBatches) {
508531
queue.add(async () => {
509-
// Additional backoff if we've been rate limited
510532
if (rateLimitBackoffMs > 0) {
511533
await new Promise(resolve => setTimeout(resolve, rateLimitBackoffMs));
512534
}
@@ -519,14 +541,13 @@ export class Indexer {
519541
},
520542
{
521543
retries: this.config.indexing.retries,
522-
minTimeout: Math.max(this.config.indexing.retryDelayMs, 5000), // Minimum 5s between retries
523-
maxTimeout: 60000, // Max 60s backoff
544+
minTimeout: Math.max(this.config.indexing.retryDelayMs, providerRateLimits.minRetryMs),
545+
maxTimeout: providerRateLimits.maxRetryMs,
524546
factor: 2,
525547
onFailedAttempt: (error) => {
526548
const message = getErrorMessage(error);
527549
if (isRateLimitError(error)) {
528-
// Exponential backoff: 10s, 20s, 40s...
529-
rateLimitBackoffMs = Math.min(60000, (rateLimitBackoffMs || 5000) * 2);
550+
rateLimitBackoffMs = Math.min(providerRateLimits.maxRetryMs, (rateLimitBackoffMs || providerRateLimits.minRetryMs) * 2);
530551
console.error(
531552
`Rate limited (attempt ${error.attemptNumber}/${error.retriesLeft + error.attemptNumber}): waiting ${rateLimitBackoffMs / 1000}s before retry...`
532553
);
@@ -539,7 +560,6 @@ export class Indexer {
539560
}
540561
);
541562

542-
// Success - gradually reduce backoff
543563
if (rateLimitBackoffMs > 0) {
544564
rateLimitBackoffMs = Math.max(0, rateLimitBackoffMs - 2000);
545565
}

0 commit comments

Comments
 (0)