Skip to content

Commit

Permalink
fix(deep-cody) Rate Limiter Reset Logic (#6366)
Browse files Browse the repository at this point in the history
FIX https://linear.app/sourcegraph/issue/CODY-4529

# Fix Deep Cody Rate Limiter Reset Logic

This PR fixes an issue where users were unable to use Deep Cody even
after the 24-hour cooldown period had elapsed.

## Changes

- Added explicit 24-hour reset check in
`DeepCodyRateLimiter.isAtLimit()`
- Simplified quota replenishment logic
- Improved handling of initial quota state

## Technical Details

Previously, the rate limiter used a continuous time-based quota
replenishment system that calculated partial quotas based on elapsed
time. This led to unexpected behavior where users wouldn't get a fresh
quota even after not using Deep Cody for over 24 hours.

The updated implementation:
1. First checks if a full day (24 hours) has passed since last usage
2. If yes, immediately resets to full quota
3. If no, calculates remaining quota using time-based replenishment


## Test plan

<!-- Required. See
https://docs-legacy.sourcegraph.com/dev/background-information/testing_principles.
-->

Added new test case resets quota after 24 hours of non-use that
specifically tests the 24-hour reset functionality
Test verifies that:

- When last use was 25 hours ago (>24h)
- With an empty quota (0)
- The limiter resets to full quota (50)
- Confirms the storage is updated with new quota and timestamp

Maintained all existing test cases to ensure no regressions
Uses the same test infrastructure (mocks, beforeEach setup)

## Changelog

<!-- OPTIONAL; info at
https://www.notion.so/sourcegraph/Writing-a-changelog-entry-dd997f411d524caabf0d8d38a24a878c
-->
  • Loading branch information
abeatrix authored Dec 16, 2024
1 parent 7d60340 commit 34d5e74
Show file tree
Hide file tree
Showing 2 changed files with 25 additions and 13 deletions.
12 changes: 12 additions & 0 deletions vscode/src/chat/agentic/DeepCodyRateLimiter.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,18 @@ describe('DeepCodyRateLimiter', () => {

expect(rateLimiter.isAtLimit()).toBeUndefined()
})

it('resets quota after 24 hours of non-use', () => {
rateLimiter = new DeepCodyRateLimiter(50, 1)
const mockUsage = {
quota: 0, // Empty quota
lastUsed: new Date(NOW.getTime() - 25 * 60 * 60 * 1000), // 25 hours ago
}
vi.spyOn(localStorage, 'getDeepCodyUsage').mockImplementation(() => mockUsage)

expect(rateLimiter.isAtLimit()).toBeUndefined()
expect(localStorage.setDeepCodyUsage).toHaveBeenCalledWith(50, NOW.toISOString())
})
})

describe('getRateLimitError', () => {
Expand Down
26 changes: 13 additions & 13 deletions vscode/src/chat/agentic/DeepCodyRateLimiter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,38 +15,38 @@ export class DeepCodyRateLimiter {
private readonly multiplier: number = 1
) {}

/**
* Returns the number of seconds the user needs to wait before they can use DeepCody again.
*/
public isAtLimit(): string | undefined {
const DAILY_QUOTA = this.baseQuota * this.multiplier

// If there is no quota set, there is no limit.
// If there is no quota set, there is no limit
if (!DAILY_QUOTA) {
return undefined
}

// Get current quota and last used time, with defaults
const now = new Date().getTime()
const { quota, lastUsed } = localStorage.getDeepCodyUsage()
const currentQuota = quota ?? DAILY_QUOTA
const lastUsedTime = lastUsed.getTime()

const now = new Date().getTime()
const timeDiff = now - lastUsedTime

// Calculate quota replenishment based on time passed
// Reset quota if more than 24 hours have passed
if (timeDiff >= this.ONE_DAY_MS) {
// Reset to full quota and update last used time
localStorage.setDeepCodyUsage(DAILY_QUOTA, new Date().toISOString())
return undefined
}

// Calculate remaining quota with time-based replenishment
const quotaToAdd = DAILY_QUOTA * (timeDiff / this.ONE_DAY_MS)
const currentQuota = quota ?? DAILY_QUOTA
const newQuota = Math.min(DAILY_QUOTA, currentQuota + quotaToAdd)

// If we have at least 1 quota available
if (newQuota >= 1) {
// Update quota and timestamp
localStorage?.setDeepCodyUsage(newQuota - 1, new Date().toISOString())
localStorage.setDeepCodyUsage(newQuota - 1, new Date().toISOString())
return undefined
}

// No quota available.
// Calculate how much time after the lastUsedTime we need to wait.
// Calculate wait time if no quota available
const timeToWait = this.ONE_DAY_MS - timeDiff
return Math.floor(timeToWait / 1000).toString()
}
Expand Down

0 comments on commit 34d5e74

Please sign in to comment.