Add usage statistics tracking and display#198
Merged
Conversation
- Created UsageStatistics model to track app usage metrics - Implemented UsageStatsService with debounced persistence - Added tracking for: - Session creation/closing with duration tracking - Messages received from Copilot - Lines of code suggested in code blocks - Longest session duration - Integrated tracking into CopilotService lifecycle - Added Statistics section to Settings page with: - Stats grid showing key metrics - Formatted durations for readability - First used and last updated timestamps - Added comprehensive unit tests (14 tests, all passing) - Persists to ~/.polypilot/usage-stats.json - Uses timer-based debounce (2s) for efficient disk I/O - Thread-safe with lock-based synchronization - Gracefully handles corrupt stats files Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…session key, ActiveSessions serialization - Remove TrackMessage/TrackCodeSuggestion from AssistantMessageEvent handler (kept only in FlushCurrentResponse) to fix double-counting on non-streaming responses - Fix DisposeAsync to call FlushSave() BEFORE setting _disposed=true so final stats flush actually runs - Add _timerLock around timer dispose+create in DebounceSave/FlushSave to prevent concurrent timer leak - Add [JsonIgnore] to ActiveSessions property so runtime state is never persisted to usage-stats.json - Use session GUID (SessionId) as the dictionary key in TrackSessionStart/End instead of display name to avoid collisions when multiple sessions share the same name - Simplify SaveStats() to rely on [JsonIgnore] instead of manually copying all fields Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
7243f7e to
4b2a3b4
Compare
- Make _disposed volatile to ensure timer callback threads see up-to-date value - Move File.WriteAllText inside _statsLock to prevent concurrent stale-snapshot writes - Use pre-compiled static readonly Regex for CodeBlockRegex (not recompiled per message) - Add TrackSessionResume() for restored sessions: records start time for duration tracking without inflating TotalSessionsCreated (sessions counted in prior run) - Call TrackSessionResume() at end of ResumeSessionAsync so closing a restored session correctly records duration and increments TotalSessionsClosed Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
copilotSession.SessionId may be empty at CreateSessionAsync time (see null-check at line 1344). Using it as the TrackSessionStart key stored '' while TrackSessionEnd looked up by GUID, causing every TryGetValue to miss — TotalSessionsClosed never incremented and no session durations ever recorded. Fix: use 'name' (display name) consistently as the ActiveSessions key for all three paths: TrackSessionStart, TrackSessionResume, TrackSessionEnd. The _sessions dictionary is already keyed by name everywhere in CopilotService. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…nters - Move TrackMessage() from FlushCurrentResponse to CompleteResponse so it counts once per completed turn instead of once per tool-round flush segment. A single user prompt with N tool calls was inflating TotalMessagesReceived by N+1x. - Keep TrackCodeSuggestion in both FlushCurrentResponse (intermediate segments) AND CompleteResponse (final segment) so all code blocks are counted. - Broaden CodeBlockRegex from [\w]* to [^\n`]* to match language tags like c++, c#, objective-c that contain non-word characters. - TrackSessionResume now increments TotalSessionsCreated to maintain the invariant Created >= Closed. Without this, restoring 5 sessions from a prior run and closing them produced Closed=5, Created=0. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…turn guard - Add RenameActiveSession() to re-key ActiveSessions entry when a session is renamed, preventing orphaned entries and lost duration/close counts - Revert TrackSessionResume to NOT increment TotalSessionsCreated — the Created >= Closed invariant holds because TrackSessionEnd only increments Closed when the key is found. Previous behavior inflated Created by K×N (K sessions × N restarts) - Promote JsonSerializerOptions to static readonly SaveOptions field to avoid allocating on every save under _statsLock - Remove duplicate _usageStats resolution from public constructor (internal ctor already resolves it via the :this() chain) avoid counting empty-response turns - Add Math.Max(0, seconds) clamp in FormatDuration for NTP clock adjustments Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Add _disposed guard to DebounceSave() to prevent orphaned Timer creation after DisposeAsync (late CopilotService callbacks during shutdown) - Clamp duration to Math.Max(0L, ...) in TrackSessionEnd to prevent NTP clock adjustments from producing negative values that corrupt totals heavy turns flush CurrentResponse before the last tool, leaving response empty, so gating on non-empty text under-counted all tool-using turns Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Implements app usage statistics tracking to monitor session activity, code suggestions, and user engagement.
Changes
Features
~/.polypilot/usage-stats.jsonTesting
Screenshots
Statistics section shows: