-
Notifications
You must be signed in to change notification settings - Fork 51
Daily branch 2025 10 26 #63
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
- Add real-time rate limit warning banner above chat input - Show warning when users have 10 (paid) or 5 (free) requests remaining - Warning displays remaining count and reset time in user-friendly format - Dismissible banner that persists across chat switches (global state) - Live count updates as user continues sending messages - Different message for zero remaining: "You've reached your [mode] limit" - Upgrade button for free users with primary styling - Clean UI matching chat input design (bg-input-chat, border styling) - Backend sends rate limit info via SSE stream (transient data) - Production-ready: no debug logs, fully typed, proper error handling
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
WalkthroughClient UI displays server-emitted rate-limit warnings in chat input/messages and lets users dismiss them; server checkRateLimit now returns RateLimitInfo and chat-handler emits a data-rate-limit-warning SSE event; global state persists dismissal; provider mapping adjusted. Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant ChatUI as Chat UI
participant Handler as Chat Handler
participant RateLimit as Rate Limit
participant Redis as Redis (opt)
participant Writer as Stream Writer
participant GlobalState as Global State
User->>ChatUI: Submit message
ChatUI->>Handler: Send request
Handler->>RateLimit: checkRateLimit(userId, mode, subscription)
alt Redis configured
RateLimit->>Redis: query counters
Redis-->>RateLimit: remaining, resetTime, limit
else Redis not configured
RateLimit-->>Handler: default RateLimitInfo
end
RateLimit-->>Handler: RateLimitInfo
alt remaining <= threshold
Handler->>Writer: writeRateLimitWarning({remaining, resetTime, mode, subscription})
Writer-->>ChatUI: SSE event data-rate-limit-warning
end
Handler->>Writer: stream chat SSE
Writer-->>ChatUI: SSE chunks
ChatUI->>GlobalState: read hasUserDismissedRateLimitWarning
alt not dismissed
ChatUI->>ChatUI: render RateLimitWarning
User->>ChatUI: click dismiss
ChatUI->>GlobalState: setHasUserDismissedRateLimitWarning(true)
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes
Possibly related PRs
Poem
Pre-merge checks and finishing touches❌ Failed checks (2 warnings)
✅ Passed checks (1 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
📜 Recent review detailsConfiguration used: CodeRabbit UI Review profile: CHILL Plan: Pro 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
🧹 Nitpick comments (5)
types/chat.ts (1)
91-95: Unify rate‑limit types across server and SSE.RateLimitInfo.resetTime is Date, but SSE payload uses a string. Define a dedicated payload type and use it in stream-writer-utils and emitters.
Apply this addition near these lines:
export type RateLimitInfo = { remaining: number; resetTime: Date; limit: number; }; + +// For SSE/UI payloads (wire format) +export type RateLimitWarningPayload = { + remaining: number; + resetTime: string; // ISO string + limit: number; + mode: ChatMode; + subscription: SubscriptionTier; +};lib/utils/stream-writer-utils.ts (1)
68-83: Type the payload and mark event type as const.Use a shared RateLimitWarningPayload to avoid drift and include limit.
-import type { ChatMode, SubscriptionTier } from "@/types"; +import type { RateLimitWarningPayload } from "@/types"; ... -export const writeRateLimitWarning = ( - writer: StreamWriter, - data: { - remaining: number; - resetTime: string; // ISO date string - mode: ChatMode; - subscription: SubscriptionTier; - }, -): void => { +export const writeRateLimitWarning = ( + writer: StreamWriter, + data: RateLimitWarningPayload, +): void => { writer.write({ - type: "data-rate-limit-warning", + type: "data-rate-limit-warning" as const, data, transient: true, }); };app/contexts/GlobalState.tsx (1)
180-185: Persist dismissal across reloads.Store in localStorage to avoid re-showing after refresh.
-] = useState(false); +] = useState<boolean>(() => { + if (typeof window === "undefined") return false; + return window.localStorage.getItem("rlw-dismissed") === "1"; +}); + +useEffect(() => { + if (typeof window !== "undefined") { + if (hasUserDismissedRateLimitWarning) { + window.localStorage.setItem("rlw-dismissed", "1"); + } else { + window.localStorage.removeItem("rlw-dismissed"); + } + } +}, [hasUserDismissedRateLimitWarning]);lib/api/chat-handler.ts (1)
185-197: Include limit in the warning payload.UI can render “remaining/limit”; aligns with shared payload type.
if (rateLimitInfo.remaining <= warningThreshold) { writeRateLimitWarning(writer, { remaining: rateLimitInfo.remaining, resetTime: rateLimitInfo.resetTime.toISOString(), + limit: rateLimitInfo.limit, mode, subscription, }); }app/components/chat.tsx (1)
581-586: Consider simplifying the ternary conversion.The ternary
rateLimitWarning ? rateLimitWarning : undefinedis explicit but could be simplified torateLimitWarning ?? undefinedfor brevity, since the prop type expectsundefinedrather thannull.Apply this diff if you prefer a more concise expression:
- rateLimitWarning={ - rateLimitWarning ? rateLimitWarning : undefined - } + rateLimitWarning={rateLimitWarning ?? undefined}
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (9)
app/components/ChatInput.tsx(4 hunks)app/components/RateLimitWarning.tsx(1 hunks)app/components/chat.tsx(8 hunks)app/contexts/GlobalState.tsx(3 hunks)lib/ai/providers.ts(1 hunks)lib/api/chat-handler.ts(4 hunks)lib/rate-limit.ts(3 hunks)lib/utils/stream-writer-utils.ts(2 hunks)types/chat.ts(1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/convex_rules.mdc)
**/*.{ts,tsx}: Use Id from ./_generated/dataModel for document IDs (e.g., Id<'users'>) instead of string
Ensure Record key/value types align with validators (e.g., v.record(v.id('users'), v.string()) -> Record<Id<'users'>, string>)
Use as const on string literals in discriminated unions
When using Array type, explicitly declare arrays with const array: Array = [...]
When using Record type, explicitly declare with const record: Record<KeyType, ValueType> = {...}
Files:
lib/utils/stream-writer-utils.tslib/api/chat-handler.tstypes/chat.tslib/rate-limit.tslib/ai/providers.tsapp/contexts/GlobalState.tsxapp/components/chat.tsxapp/components/RateLimitWarning.tsxapp/components/ChatInput.tsx
🧬 Code graph analysis (6)
lib/utils/stream-writer-utils.ts (1)
types/chat.ts (2)
ChatMode(5-5)SubscriptionTier(7-7)
lib/api/chat-handler.ts (2)
lib/rate-limit.ts (1)
checkRateLimit(7-126)lib/utils/stream-writer-utils.ts (1)
writeRateLimitWarning(69-83)
lib/rate-limit.ts (1)
types/chat.ts (3)
ChatMode(5-5)SubscriptionTier(7-7)RateLimitInfo(91-95)
app/components/chat.tsx (2)
types/chat.ts (2)
ChatMode(5-5)SubscriptionTier(7-7)app/hooks/useLatestRef.ts (1)
useLatestRef(3-9)
app/components/RateLimitWarning.tsx (2)
types/chat.ts (2)
ChatMode(5-5)SubscriptionTier(7-7)app/hooks/usePricingDialog.ts (1)
redirectToPricing(68-70)
app/components/ChatInput.tsx (2)
types/chat.ts (2)
ChatMode(5-5)SubscriptionTier(7-7)app/components/RateLimitWarning.tsx (1)
RateLimitWarning(71-111)
🔇 Additional comments (12)
lib/utils/stream-writer-utils.ts (1)
4-4: LGTM. Importing types from "@/types" is consistent with the project’s barrel.app/contexts/GlobalState.tsx (2)
91-94: LGTM. Interface surface for dismissal state is clear.
588-590: LGTM. Exposed via provider value.lib/api/chat-handler.ts (1)
339-346: Both "price" and "latency" are valid values for providerOptions.openrouter.provider.sort according to OpenRouter documentation.lib/ai/providers.ts (1)
10-10: Verify OpenRouter availability in your setup; consider env-configurable slug.The model slug "qwen/qwen3-coder:exacto" is valid and documented by OpenRouter. However, Exacto endpoints route only to a curated subset of providers, so availability can vary by provider and region. Verify this model is available in your account/region before deployment. Additionally, making the slug env-configurable would enable fast rollback if needed.
app/components/RateLimitWarning.tsx (1)
71-111: LGTM!The component implementation is clean and well-structured. The conditional messaging for zero vs. non-zero remaining interactions is appropriate, and the accessibility attributes are properly included.
app/components/chat.tsx (3)
89-92: Excellent use of ref to prevent stale closures in streaming callbacks.Using
useLatestReffor the dismissal flag ensures theonDatacallback always reads the current value, avoiding race conditions during streaming.
198-215: LGTM!The rate limit warning integration correctly checks the dismissal flag before updating state and properly converts the ISO timestamp to a Date object.
423-427: LGTM!The dismiss handler correctly clears the local warning state and persists the dismissal preference globally.
lib/rate-limit.ts (2)
7-23: LGTM!The function signature update to return
RateLimitInfois appropriate for the warning system. The default values when Redis is not configured (999 remaining with a 5-hour window) effectively disable rate limiting while maintaining consistent return types.
107-113: LGTM!The return value correctly constructs a
RateLimitInfoobject with all required fields:remainingfrom the rate limiter response,resetTimeas a Date object, andlimitfrom the calculated request limit.app/components/ChatInput.tsx (1)
222-231: LGTM!The
RateLimitWarningintegration is clean and correctly positioned before the Todo Panel. The conditional rendering appropriately checks for both the warning data and the dismiss handler before displaying the component.
- Replace broken day comparison that failed across month/year boundaries - Add relative time display for durations < 6 hours (e.g., 'in 2h 30m') - Use proper Date arithmetic for today/tomorrow checks with setDate() - Handles all edge cases including month-end and year-end rollovers
…ours - Remove unnecessary date/time logic for longer durations - Only show relative time format (e.g., 'in 2h 30m') - Simplifies code since max window is 5 hours
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
app/components/RateLimitWarning.tsx(1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/convex_rules.mdc)
**/*.{ts,tsx}: Use Id from ./_generated/dataModel for document IDs (e.g., Id<'users'>) instead of string
Ensure Record key/value types align with validators (e.g., v.record(v.id('users'), v.string()) -> Record<Id<'users'>, string>)
Use as const on string literals in discriminated unions
When using Array type, explicitly declare arrays with const array: Array = [...]
When using Record type, explicitly declare with const record: Record<KeyType, ValueType> = {...}
Files:
app/components/RateLimitWarning.tsx
🧬 Code graph analysis (1)
app/components/RateLimitWarning.tsx (2)
types/chat.ts (2)
ChatMode(5-5)SubscriptionTier(7-7)app/hooks/usePricingDialog.ts (1)
redirectToPricing(68-70)
🔇 Additional comments (2)
app/components/RateLimitWarning.tsx (2)
1-12: LGTM! Clean imports and well-typed interface.The imports are appropriate and the
RateLimitWarningPropsinterface is well-structured with proper type references.
50-73: LGTM! Well-structured and accessible UI.The JSX rendering is clean with proper conditional logic, good accessibility (aria-label on dismiss button), and appropriate use of UI components.
Summary by CodeRabbit
New Features
Improvements