File size: 5,714 Bytes
a5c1fa0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
# server/memory.py
"""
Context and memory optimization tracker.

Records what the agent has seen, how much context it consumed,
and detects wasteful patterns (re-reading, reading irrelevant content).

This answers: "How efficiently does the agent use its context window?"
"""
from typing import Dict, List, Optional
from dataclasses import dataclass, field


@dataclass
class FileReadRecord:
    """Record of a single file read."""
    path: str
    size_bytes: int
    read_count: int
    was_relevant: bool
    first_read_step: int


@dataclass
class MemoryStats:
    """Comprehensive context usage statistics."""
    total_bytes_read: int = 0
    unique_bytes_read: int = 0
    redundant_bytes_read: int = 0
    total_files_read: int = 0
    unique_files_read: int = 0
    redundant_reads: int = 0
    relevant_files_read: int = 0
    irrelevant_files_read: int = 0
    context_efficiency: float = 0.0     # unique_useful / total
    search_queries: int = 0
    total_content_written: int = 0      # bytes written by agent

    def to_dict(self) -> dict:
        return {
            "total_bytes_read": self.total_bytes_read,
            "unique_bytes_read": self.unique_bytes_read,
            "redundant_bytes_read": self.redundant_bytes_read,
            "total_files_read": self.total_files_read,
            "unique_files_read": self.unique_files_read,
            "redundant_reads": self.redundant_reads,
            "relevant_files_read": self.relevant_files_read,
            "irrelevant_files_read": self.irrelevant_files_read,
            "context_efficiency": round(self.context_efficiency, 3),
            "search_queries": self.search_queries,
            "total_content_written": self.total_content_written,
        }


class MemoryTracker:
    """
    Tracks agent's context consumption and memory patterns.

    Usage:
        tracker = MemoryTracker()
        tracker.start_episode(relevant_files=["src/auth.py", "tests/test_auth.py"])
        tracker.record_read("src/auth.py", 500, step=1)
        tracker.record_read("src/auth.py", 500, step=3)  # redundant!
        stats = tracker.get_stats()
    """

    def __init__(self):
        self._reads: Dict[str, FileReadRecord] = {}
        self._relevant_files: set = set()
        self._search_count: int = 0
        self._bytes_written: int = 0

    def start_episode(self, relevant_files: List[str] = None):
        """Reset tracker for new episode."""
        self._reads.clear()
        self._relevant_files = set(relevant_files or [])
        self._search_count = 0
        self._bytes_written = 0

    def record_read(self, path: str, size_bytes: int, step: int):
        """Record a file read action."""
        if path in self._reads:
            self._reads[path].read_count += 1
        else:
            self._reads[path] = FileReadRecord(
                path=path,
                size_bytes=size_bytes,
                read_count=1,
                was_relevant=path in self._relevant_files,
                first_read_step=step,
            )

    def record_search(self):
        """Record a search query."""
        self._search_count += 1

    def record_write(self, content_bytes: int):
        """Record bytes written by agent."""
        self._bytes_written += content_bytes

    def get_stats(self) -> MemoryStats:
        """Compute comprehensive memory statistics."""
        total_bytes = 0
        unique_bytes = 0
        redundant_bytes = 0
        redundant_reads = 0
        relevant_count = 0
        irrelevant_count = 0

        for record in self._reads.values():
            first_read_bytes = record.size_bytes
            unique_bytes += first_read_bytes
            total_bytes += first_read_bytes * record.read_count

            if record.read_count > 1:
                redundant_reads += record.read_count - 1
                redundant_bytes += first_read_bytes * (record.read_count - 1)

            if record.was_relevant:
                relevant_count += 1
            else:
                irrelevant_count += 1

        # Context efficiency: what fraction of bytes read was useful (relevant + unique)?
        relevant_bytes = sum(
            r.size_bytes for r in self._reads.values() if r.was_relevant
        )
        efficiency = relevant_bytes / max(1, total_bytes)

        return MemoryStats(
            total_bytes_read=total_bytes,
            unique_bytes_read=unique_bytes,
            redundant_bytes_read=redundant_bytes,
            total_files_read=sum(r.read_count for r in self._reads.values()),
            unique_files_read=len(self._reads),
            redundant_reads=redundant_reads,
            relevant_files_read=relevant_count,
            irrelevant_files_read=irrelevant_count,
            context_efficiency=efficiency,
            search_queries=self._search_count,
            total_content_written=self._bytes_written,
        )

    def get_wasteful_patterns(self) -> List[str]:
        """Identify specific wasteful patterns for debugging."""
        patterns = []

        # Files read multiple times
        for record in self._reads.values():
            if record.read_count > 1:
                patterns.append(
                    f"REDUNDANT_READ: '{record.path}' read {record.read_count} times "
                    f"({record.size_bytes * record.read_count} bytes wasted)"
                )

        # Irrelevant files read
        for record in self._reads.values():
            if not record.was_relevant and record.read_count > 0:
                patterns.append(
                    f"IRRELEVANT_READ: '{record.path}' not in relevant files "
                    f"({record.size_bytes} bytes wasted)"
                )

        return patterns