Conversation
…#848) Replace the four mutable dict[str, int] fields in VSCodeLogSummary with Mapping[str, int] backed by types.MappingProxyType. This eliminates the _copy_summary() function and its 4 dict copies on every get_vscode_summary() cache hit. Changes: - VSCodeLogSummary fields typed as Mapping[str, int] - _finalize_summary wraps dicts in MappingProxyType - _copy_summary removed; cache hit returns summary directly - Cache store no longer copies; summary is inherently immutable - Tests updated: cache-hit identity (is), mutation raises TypeError, build_vscode_summary also produces immutable fields Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
This PR improves the steady-state performance of VS Code Copilot Chat log summarization by eliminating repeated deep copies on get_vscode_summary() cache hits, making cached summaries safe to share by ensuring the per-dimension counters are immutable.
Changes:
- Change
VSCodeLogSummarycounter fields fromdict[str, int]toMapping[str, int], and back them withtypes.MappingProxyType. - Remove
_copy_summary()and return cached summaries directly on cache hits (O(1), zero allocation). - Update and add tests to assert cache-hit identity and immutability of summary mapping fields.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
src/copilot_usage/vscode_parser.py |
Wrap summary dict fields in MappingProxyType and remove defensive copying on cache hits. |
tests/copilot_usage/test_vscode_parser.py |
Update tests to validate mapping immutability and cache-hit identity semantics. |
Enforce immutability at the VSCodeLogSummary dataclass boundary:
- Add __post_init__ that wraps any plain dict in MappingProxyType
- Change default_factory to produce MappingProxyType({}) via _EMPTY_MAPPING
- Simplify _finalize_summary to pass plain dicts (wrapping handled by __post_init__)
- Add tests for constructor-level wrapping and default immutability
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
Commit pushed:
|
- Annotate _EMPTY_MAPPING as Final[Mapping[str, int]] to avoid runtime TypeError from subscripting types.MappingProxyType - Always snapshot mapping values in __post_init__ via MappingProxyType(dict(val)) to prevent callers from mutating frozen summaries through retained dict references - Special-case _EMPTY_MAPPING singleton to skip unnecessary allocation - Update docstring to reflect the always-snapshot behavior Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
Commit pushed:
|
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 2 out of 2 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (1)
src/copilot_usage/vscode_parser.py:509
_finalize_summary()currently doesdict(acc.<field>)for each counter, butVSCodeLogSummary.__post_init__then snapshots again viaMappingProxyType(dict(val)). Whenvalis already a plaindict, this results in an extra full copy/allocation of each mapping (4 redundant dict copies) on every summary construction (cache miss / initial build). Consider passing the accumulator mappings directly (e.g., thedefaultdicts) intoVSCodeLogSummary(...)and letting__post_init__perform the single snapshot/copy, which preserves the immutability/aliasing guarantees while reducing allocations.
return VSCodeLogSummary(
total_requests=acc.total_requests,
total_duration_ms=acc.total_duration_ms,
requests_by_model=dict(acc.requests_by_model),
duration_by_model=dict(acc.duration_by_model),
requests_by_category=dict(acc.requests_by_category),
requests_by_date=dict(acc.requests_by_date),
first_timestamp=acc.first_timestamp,
There was a problem hiding this comment.
Quality Gate: APPROVE — Medium-impact performance refactoring with comprehensive test coverage. Auto-approving for merge.
What was evaluated:
VSCodeLogSummarydict fields changed fromdict[str, int]toMapping[str, int]backed byMappingProxyType— enforces immutability at construction time via__post_init___copy_summaryremoved; cache hits now return the same object (O(1), zero allocation)- All downstream consumers (
vscode_report.py) use read-only Mapping operations — no breakage - 8 new/updated tests cover: cache-hit identity, immutability for all 4 fields, constructor wrapping, defaults
- Fully compliant with CODING_GUIDELINES.md (strict typing,
Final, frozen dataclass with slots,default_factory=lambda:)
Closes #848
Problem
get_vscode_summary()called_copy_summary()on every cache hit, constructing 4 newdictobjects (copying ~1,600 key-value pairs for a user with 400 date-bucketed entries). This generated unnecessary GC pressure in the interactive refresh loop.Solution
Replace the four mutable
dict[str, int]fields inVSCodeLogSummarywithMapping[str, int]backed bytypes.MappingProxyType:VSCodeLogSummary: Field types changed fromdict[str, int]toMapping[str, int]_finalize_summary: Wraps accumulator dicts inMappingProxyTypeat construction time_copy_summary: Removed entirely — no longer neededget_vscode_summary: Cache hit returns the summary object directly (O(1), zero allocation); cache store no longer copiesPerformance
Eliminates 4 dict allocations + deep copies per
get_vscode_summary()call in steady state. Each cache-hit call is now O(1) with zero allocation.Tests
get_vscode_summary()calls return the same object (isidentity)summary.requests_by_date["x"] = 1raisesTypeError(all 4 dict fields)build_vscode_summaryalso produces immutableMappingProxyTypefieldstest_cached_return_is_mutation_safeandtest_finalized_summary_uses_mapping_proxy