-
Notifications
You must be signed in to change notification settings - Fork 11
🤖 Track usage in stream abort events #110
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
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.
💡 Codex Review
Lines 1026 to 1036 in 6af4919
| // Handle stream abort events | |
| this.aiService.on( | |
| "stream-abort", | |
| (data: { type: string; workspaceId: string; messageId?: string }) => { | |
| if (this.mainWindow) { | |
| // Send the stream-abort event to frontend | |
| this.mainWindow.webContents.send(getChatChannel(data.workspaceId), { | |
| type: "stream-abort", | |
| workspaceId: data.workspaceId, | |
| messageId: data.messageId, | |
| }); |
The backend now emits stream-abort events with metadata containing usage and duration, and the UI expects that payload in handleStreamAbort to populate message.usage for aborted messages. However this IPC forwarding layer still strips the metadata and only forwards type, workspaceId, and messageId, so the renderer never receives the usage data and the new UsageFooter never renders for interrupted streams. Any aborted message will therefore show no token counts or cost despite the backend calculating them. Please include metadata in the object sent through webContents.send.
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Final Code Review: PR #110SummaryThe PR successfully implements usage tracking for interrupted streams and displays token counts + costs in the chat UI. The core functionality works correctly, but there are a few polish issues. Issues Found1. 🟡 MINOR: StreamAbortEvent not in AIServiceEvent unionLocation:
Impact: LOW - Type is not used anywhere in the codebase, so this is purely for completeness. Recommendation: Add it for type consistency, but not blocking. 2. 🟠 MODERATE: UsageFooter shows "0 in / 0 out" when data is missingLocation: Current behavior: const showUsageFooter = !isStreaming && message.usage;Shows footer if const inputTokens = usage.inputTokens ?? 0;
const outputTokens = usage.outputTokens ?? 0;This displays "0 in / 0 out (0 total) cost: $0.00000" when usage data is unavailable. Issue: Misleading - looks like the message was free, but we just don't have data. Fix: // In AssistantMessage.tsx
const showUsageFooter = !isStreaming && message.usage &&
(message.usage.inputTokens !== undefined || message.usage.outputTokens !== undefined);Priority: Should fix - impacts UX clarity. 3. 🟡 MINOR: Test doesn't strongly assert usage is capturedLocation: Test allows usage to be undefined: if (abortEvent.metadata.usage) {
expect(abortEvent.metadata.usage.inputTokens).toBeGreaterThan(0);
expect(abortEvent.metadata.usage.outputTokens).toBeGreaterThan(0);
}Issue: Test passes even if feature doesn't work. After generating tokens and aborting, usage SHOULD be present. Recommendation: Assert usage is defined: expect(abortEvent.metadata.usage).toBeDefined();
expect(abortEvent.metadata.usage!.inputTokens).toBeGreaterThan(0);
expect(abortEvent.metadata.usage!.outputTokens).toBeGreaterThan(0);Priority: Nice to have - test still validates the flow works. What Works Well ✅
RecommendationOverall assessment: Good implementation with minor polish needed. Suggested fixes before merge:
Can be addressed in follow-up:
|
PR #110 In-Depth ReviewSummaryThe PR adds usage tracking and cost display for assistant messages, including interrupted streams. The implementation is generally solid but has a few issues to address. Critical Issues1. ❌ Test doesn't verify usage is actually presentLocation: // Usage should be present (AI SDK provides this even on abort)
if (abortEvent.metadata.usage) {
expect(abortEvent.metadata.usage.inputTokens).toBeGreaterThan(0);
expect(abortEvent.metadata.usage.outputTokens).toBeGreaterThan(0);
}Problem: The test has a nested Impact: The test doesn't actually prove that usage data is available on abort - it just checks that IF usage is present, it has the right properties. Fix: Make the usage assertion mandatory: // Usage should be present (AI SDK provides this even on abort)
expect(abortEvent.metadata.usage).toBeDefined();
expect(abortEvent.metadata.usage!.inputTokens).toBeGreaterThan(0);
expect(abortEvent.metadata.usage!.outputTokens).toBeGreaterThan(0);Minor Issues2.
|
0cb63c2 to
681a890
Compare
345f28c to
69f6edc
Compare
Captures usage data when streams are interrupted, consistent with normal stream completion. Provides better visibility into API costs for interrupted requests. Changes: - StreamAbortEvent includes optional metadata (usage, duration) - StreamManager extracts usage with timeout to handle early aborts - IPC forwards complete abort event (not just type/workspaceId) - StreamingMessageAggregator merges abort metadata into message - Integration test verifies abort events include duration Usage availability on abort depends on timing: - Early abort (before API completes): usage is undefined - Late abort (after API finishes): usage is available The timeout prevents hanging when usage promise doesn't resolve. _Generated with `cmux`_
69f6edc to
e090d22
Compare
Overview
Capture token usage and duration when streams are interrupted. The AI SDK provides usage data even when streams are aborted, so we should track it for accurate cost accounting.
Changes
Backend Changes
metadatawithusageanddurationfieldsgetStreamMetadata()helper to eliminate duplicationcancelStreamSafely()(abort case) and stream completionTesting
Why This Matters
No UI Changes
This PR focuses solely on backend tracking. Usage data is now available in abort events for future UI consumption.
Generated with
cmux