feat(billing): single-source usage via main-process relay#2358
Conversation
|
Warning This pull request is not mergeable via GitHub because a downstack PR is open. Once all requirements are satisfied, merge this PR as a stack on Graphite.
This stack of pull requests is managed by Graphite. Learn more about stacking. |
3562601 to
4055770
Compare
923dacd to
08d0f9b
Compare
08d0f9b to
b7de4cf
Compare
4055770 to
49e01ad
Compare
Prompt To Fix All With AIFix the following 2 code review issues. Work through them one at a time, proposing concise fixes.
---
### Issue 1 of 2
apps/code/src/main/services/usage-monitor/schemas.ts:24-25
The `UsageSnapshot` type alias is exported but never consumed — `service.ts` uses `UsageOutput | null` inline everywhere. The Zod schema `usageSnapshotOutput` is legitimately needed for tRPC output validation, but the TypeScript type alongside it is superfluous (simplicity rule #4).
```suggestion
export const usageSnapshotOutput = usageOutput.nullable();
```
### Issue 2 of 2
apps/code/src/renderer/features/billing/hooks/useUsage.ts:37
The entire `refreshMutation` object is listed as a `useCallback` dependency. `useMutation` returns a new object reference on every render (state fields like `isPending` change), so `refetch` is recreated every render. Depending on `refreshMutation.mutateAsync` directly is sufficient — that function reference is stable across renders.
```suggestion
}, [refreshMutation.mutateAsync, queryClient, trpc.usageMonitor.getLatest]);
```
Reviews (1): Last reviewed commit: "feat(billing): single-source usage via m..." | Re-trigger Greptile |
| export const usageSnapshotOutput = usageOutput.nullable(); | ||
| export type UsageSnapshot = UsageOutput | null; |
There was a problem hiding this comment.
The
UsageSnapshot type alias is exported but never consumed — service.ts uses UsageOutput | null inline everywhere. The Zod schema usageSnapshotOutput is legitimately needed for tRPC output validation, but the TypeScript type alongside it is superfluous (simplicity rule #4).
| export const usageSnapshotOutput = usageOutput.nullable(); | |
| export type UsageSnapshot = UsageOutput | null; | |
| export const usageSnapshotOutput = usageOutput.nullable(); |
Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/code/src/main/services/usage-monitor/schemas.ts
Line: 24-25
Comment:
The `UsageSnapshot` type alias is exported but never consumed — `service.ts` uses `UsageOutput | null` inline everywhere. The Zod schema `usageSnapshotOutput` is legitimately needed for tRPC output validation, but the TypeScript type alongside it is superfluous (simplicity rule #4).
```suggestion
export const usageSnapshotOutput = usageOutput.nullable();
```
How can I resolve this? If you propose a fix, please make it concise.Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
| queryClient.setQueryData(trpc.usageMonitor.getLatest.queryKey(), fresh); | ||
| } | ||
| return fresh; | ||
| }, [refreshMutation, queryClient, trpc.usageMonitor.getLatest]); |
There was a problem hiding this comment.
The entire
refreshMutation object is listed as a useCallback dependency. useMutation returns a new object reference on every render (state fields like isPending change), so refetch is recreated every render. Depending on refreshMutation.mutateAsync directly is sufficient — that function reference is stable across renders.
| }, [refreshMutation, queryClient, trpc.usageMonitor.getLatest]); | |
| }, [refreshMutation.mutateAsync, queryClient, trpc.usageMonitor.getLatest]); |
Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/code/src/renderer/features/billing/hooks/useUsage.ts
Line: 37
Comment:
The entire `refreshMutation` object is listed as a `useCallback` dependency. `useMutation` returns a new object reference on every render (state fields like `isPending` change), so `refetch` is recreated every render. Depending on `refreshMutation.mutateAsync` directly is sufficient — that function reference is stable across renders.
```suggestion
}, [refreshMutation.mutateAsync, queryClient, trpc.usageMonitor.getLatest]);
```
How can I resolve this? If you propose a fix, please make it concise.
Problem
The renderer was polling the LLM gateway directly every 30 seconds to fetch usage data, coupling the renderer to the gateway and duplicating polling logic that the
UsageMonitorServicealready handles.Changes
The
UsageMonitorServicenow caches the most recent usage snapshot (latestUsage) and emits aUsageUpdatedevent after every successful poll. Two new methods are exposed:getLatest()to retrieve the cached snapshot synchronously, andrefreshNow()to trigger an immediate poll.The
usageMonitorRouterexposes these via three new tRPC endpoints:onUsageUpdated(a subscription that streams snapshots as they arrive),getLatest(a query to bootstrap the renderer before the first event), andrefresh(a mutation to force an immediate poll). ThellmGateway.usagequery endpoint has been removed since it is no longer needed.The
useUsagehook in the renderer now subscribes toonUsageUpdatedinstead of polling on an interval. It bootstraps fromgetLateston mount and exposes arefetchthat calls therefreshmutation. The window-focus-based refetch interval logic has been removed entirely.How did you test this?
Three new unit tests were added to
service.test.ts:UsageUpdatedis emitted and the snapshot is cached on every successful poll.UsageUpdatedis not emitted and the snapshot remainsnullwhen the gateway throws.refreshNowtriggers a fresh poll and returns the resulting snapshot.Publish to changelog?
No