Sfoglia il codice sorgente

Use monotonic clock for rate limiting (#8456)

Christiaan Arnoldus 4 mesi fa
parent
commit
c232057e54
2 ha cambiato i file con 10 aggiunte e 10 eliminazioni
  1. 5 5
      src/core/task/Task.ts
  2. 5 5
      src/core/task/__tests__/Task.spec.ts

+ 5 - 5
src/core/task/Task.ts

@@ -2059,7 +2059,7 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
 
 
 					const drainStreamInBackgroundToFindAllUsage = async (apiReqIndex: number) => {
 					const drainStreamInBackgroundToFindAllUsage = async (apiReqIndex: number) => {
 						const timeoutMs = DEFAULT_USAGE_COLLECTION_TIMEOUT_MS
 						const timeoutMs = DEFAULT_USAGE_COLLECTION_TIMEOUT_MS
-						const startTime = Date.now()
+						const startTime = performance.now()
 						const modelId = getModelId(this.apiConfiguration)
 						const modelId = getModelId(this.apiConfiguration)
 
 
 						// Local variables to accumulate usage data without affecting the main flow
 						// Local variables to accumulate usage data without affecting the main flow
@@ -2130,7 +2130,7 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
 							// Use the same iterator that the main loop was using
 							// Use the same iterator that the main loop was using
 							while (!item.done) {
 							while (!item.done) {
 								// Check for timeout
 								// Check for timeout
-								if (Date.now() - startTime > timeoutMs) {
+								if (performance.now() - startTime > timeoutMs) {
 									console.warn(
 									console.warn(
 										`[Background Usage Collection] Timed out after ${timeoutMs}ms for model: ${modelId}, processed ${chunkCount} chunks`,
 										`[Background Usage Collection] Timed out after ${timeoutMs}ms for model: ${modelId}, processed ${chunkCount} chunks`,
 									)
 									)
@@ -2601,10 +2601,10 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
 		// Use the shared timestamp so that subtasks respect the same rate-limit
 		// Use the shared timestamp so that subtasks respect the same rate-limit
 		// window as their parent tasks.
 		// window as their parent tasks.
 		if (Task.lastGlobalApiRequestTime) {
 		if (Task.lastGlobalApiRequestTime) {
-			const now = Date.now()
+			const now = performance.now()
 			const timeSinceLastRequest = now - Task.lastGlobalApiRequestTime
 			const timeSinceLastRequest = now - Task.lastGlobalApiRequestTime
 			const rateLimit = apiConfiguration?.rateLimitSeconds || 0
 			const rateLimit = apiConfiguration?.rateLimitSeconds || 0
-			rateLimitDelay = Math.ceil(Math.max(0, rateLimit * 1000 - timeSinceLastRequest) / 1000)
+			rateLimitDelay = Math.ceil(Math.min(rateLimit, Math.max(0, rateLimit * 1000 - timeSinceLastRequest) / 1000))
 		}
 		}
 
 
 		// Only show rate limiting message if we're not retrying. If retrying, we'll include the delay there.
 		// Only show rate limiting message if we're not retrying. If retrying, we'll include the delay there.
@@ -2619,7 +2619,7 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
 
 
 		// Update last request time before making the request so that subsequent
 		// Update last request time before making the request so that subsequent
 		// requests — even from new subtasks — will honour the provider's rate-limit.
 		// requests — even from new subtasks — will honour the provider's rate-limit.
-		Task.lastGlobalApiRequestTime = Date.now()
+		Task.lastGlobalApiRequestTime = performance.now()
 
 
 		const systemPrompt = await this.getSystemPrompt()
 		const systemPrompt = await this.getSystemPrompt()
 		this.lastUsedInstructions = systemPrompt
 		this.lastUsedInstructions = systemPrompt

+ 5 - 5
src/core/task/__tests__/Task.spec.ts

@@ -1106,9 +1106,9 @@ describe("Cline", () => {
 				await parentIterator.next()
 				await parentIterator.next()
 
 
 				// Simulate time passing (more than rate limit)
 				// Simulate time passing (more than rate limit)
-				const originalDateNow = Date.now
-				const mockTime = Date.now() + (mockApiConfig.rateLimitSeconds + 1) * 1000
-				Date.now = vi.fn(() => mockTime)
+				const originalPerformanceNow = performance.now
+				const mockTime = performance.now() + (mockApiConfig.rateLimitSeconds + 1) * 1000
+				performance.now = vi.fn(() => mockTime)
 
 
 				// Create a subtask after time has passed
 				// Create a subtask after time has passed
 				const child = new Task({
 				const child = new Task({
@@ -1129,8 +1129,8 @@ describe("Cline", () => {
 				// Verify no rate limiting was applied
 				// Verify no rate limiting was applied
 				expect(mockDelay).not.toHaveBeenCalled()
 				expect(mockDelay).not.toHaveBeenCalled()
 
 
-				// Restore Date.now
-				Date.now = originalDateNow
+				// Restore performance.now
+				performance.now = originalPerformanceNow
 			})
 			})
 
 
 			it("should share rate limiting across multiple subtasks", async () => {
 			it("should share rate limiting across multiple subtasks", async () => {