Skip to content

Commit 16702f8

Browse files
committed
/usage: Don't add to session credits if credits spent are part of subscription
1 parent 8c65530 commit 16702f8

File tree

4 files changed

+57
-16
lines changed

4 files changed

+57
-16
lines changed

cli/src/chat.tsx

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,11 @@ export const Chat = ({
163163
const { statusMessage } = useClipboard()
164164
const { ad } = useGravityAd()
165165

166+
// Fetch subscription data early - needed for session credits tracking
167+
const { data: subscriptionData } = useSubscriptionQuery({
168+
refetchInterval: 60 * 1000,
169+
})
170+
166171
// Set initial mode from CLI flag on mount
167172
useEffect(() => {
168173
if (initialMode) {
@@ -427,6 +432,7 @@ export const Chat = ({
427432
resumeQueue,
428433
continueChat,
429434
continueChatId,
435+
subscriptionData,
430436
})
431437

432438
sendMessageRef.current = sendMessage
@@ -1280,11 +1286,6 @@ export const Chat = ({
12801286
refetchInterval: 60 * 1000, // Refetch every 60 seconds
12811287
})
12821288

1283-
// Fetch subscription data
1284-
const { data: subscriptionData } = useSubscriptionQuery({
1285-
refetchInterval: 60 * 1000,
1286-
})
1287-
12881289
// Auto-show subscription limit banner when rate limit becomes active
12891290
const subscriptionLimitShownRef = useRef(false)
12901291
const subscriptionRateLimit = subscriptionData?.hasSubscription ? subscriptionData.rateLimit : undefined

cli/src/components/message-footer.tsx

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ import { CopyButton } from './copy-button'
77
import { ElapsedTimer } from './elapsed-timer'
88
import { FeedbackIconButton } from './feedback-icon-button'
99
import { useSubscriptionQuery } from '../hooks/use-subscription-query'
10+
import {
11+
getBlockPercentRemaining,
12+
isCoveredBySubscription,
13+
} from '../utils/subscription'
1014
import { useTheme } from '../hooks/use-theme'
1115
import {
1216
useFeedbackStore,
@@ -221,19 +225,15 @@ const CreditsOrSubscriptionIndicator: React.FC<{ credits: number }> = ({ credits
221225
pauseWhenIdle: false,
222226
})
223227

224-
const activeSubscription = subscriptionData?.hasSubscription ? subscriptionData : null
225-
const rateLimit = activeSubscription?.rateLimit
226-
227-
const blockPercentRemaining = useMemo(() => {
228-
if (!rateLimit?.blockLimit || rateLimit.blockUsed == null) return null
229-
return Math.round(((rateLimit.blockLimit - rateLimit.blockUsed) / rateLimit.blockLimit) * 100)
230-
}, [rateLimit])
228+
const blockPercentRemaining = useMemo(
229+
() => getBlockPercentRemaining(subscriptionData),
230+
[subscriptionData],
231+
)
231232

232-
const showSubscriptionIndicator =
233-
activeSubscription && !rateLimit?.limited && blockPercentRemaining != null && blockPercentRemaining > 0
233+
const showSubscriptionIndicator = isCoveredBySubscription(subscriptionData)
234234

235235
if (showSubscriptionIndicator) {
236-
const label = blockPercentRemaining < 20
236+
const label = (blockPercentRemaining ?? 0) < 20
237237
? `✓ ${SUBSCRIPTION_DISPLAY_NAME} (${blockPercentRemaining}% left)`
238238
: `✓ ${SUBSCRIPTION_DISPLAY_NAME}`
239239
return (

cli/src/hooks/use-send-message.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,9 @@ import type { SendMessageFn } from '../types/contracts/send-message'
3838
import type { AgentMode } from '../utils/constants'
3939
import type { SendMessageTimerEvent } from '../utils/send-message-timer'
4040
import type { AgentDefinition, MessageContent, RunState } from '@codebuff/sdk'
41+
import { isCoveredBySubscription } from '../utils/subscription'
42+
43+
import type { SubscriptionResponse } from './use-subscription-query'
4144

4245
interface UseSendMessageOptions {
4346
inputRef: React.MutableRefObject<any>
@@ -59,6 +62,7 @@ interface UseSendMessageOptions {
5962
resumeQueue?: () => void
6063
continueChat: boolean
6164
continueChatId?: string
65+
subscriptionData?: SubscriptionResponse | null
6266
}
6367

6468
// Choose the agent definition by explicit selection or mode-based fallback.
@@ -109,6 +113,7 @@ export const useSendMessage = ({
109113
resumeQueue,
110114
continueChat,
111115
continueChatId,
116+
subscriptionData,
112117
}: UseSendMessageOptions): {
113118
sendMessage: SendMessageFn
114119
clearMessages: () => void
@@ -431,7 +436,11 @@ export const useSendMessage = ({
431436
setIsRetrying,
432437
onTotalCost: (cost: number) => {
433438
actualCredits = cost
434-
addSessionCredits(cost)
439+
// Only add to session credits if not covered by subscription
440+
// (subscription credits are shown separately in the UI)
441+
if (!isCoveredBySubscription(subscriptionData)) {
442+
addSessionCredits(cost)
443+
}
435444
},
436445
})
437446

cli/src/utils/subscription.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import type { SubscriptionResponse } from '../hooks/use-subscription-query'
2+
3+
/**
4+
* Calculates the percentage of subscription block credits remaining.
5+
* Returns null if the subscription data is incomplete.
6+
*/
7+
export function getBlockPercentRemaining(
8+
subscriptionData: SubscriptionResponse | null | undefined,
9+
): number | null {
10+
if (!subscriptionData?.hasSubscription) return null
11+
const rateLimit = subscriptionData.rateLimit
12+
if (!rateLimit?.blockLimit || rateLimit.blockUsed == null) return null
13+
return Math.round(
14+
((rateLimit.blockLimit - rateLimit.blockUsed) / rateLimit.blockLimit) * 100,
15+
)
16+
}
17+
18+
/**
19+
* Determines if a request is covered by subscription based on subscription data.
20+
* Returns true if the user has an active subscription that's not rate-limited
21+
* and has remaining block credits.
22+
*/
23+
export function isCoveredBySubscription(
24+
subscriptionData: SubscriptionResponse | null | undefined,
25+
): boolean {
26+
if (!subscriptionData?.hasSubscription) return false
27+
const rateLimit = subscriptionData.rateLimit
28+
if (rateLimit?.limited) return false
29+
const blockPercentRemaining = getBlockPercentRemaining(subscriptionData)
30+
return blockPercentRemaining != null && blockPercentRemaining > 0
31+
}

0 commit comments

Comments
 (0)