pyagent-context 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pyagent_context/__init__.py +29 -0
- pyagent_context/compression.py +132 -0
- pyagent_context/item.py +132 -0
- pyagent_context/ledger.py +136 -0
- pyagent_context/lifecycle.py +157 -0
- pyagent_context/memory/__init__.py +12 -0
- pyagent_context/memory/semantic.py +153 -0
- pyagent_context/memory/session.py +151 -0
- pyagent_context/memory/working.py +69 -0
- pyagent_context/py.typed +0 -0
- pyagent_context/redaction.py +71 -0
- pyagent_context/retrieval.py +121 -0
- pyagent_context-0.1.0.dist-info/METADATA +286 -0
- pyagent_context-0.1.0.dist-info/RECORD +15 -0
- pyagent_context-0.1.0.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pyagent-context
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Three-tier memory with trust-aware context ledger for multi-agent LLM systems
|
|
5
|
+
Project-URL: Homepage, https://pyagent.org
|
|
6
|
+
Project-URL: Repository, https://github.com/pyagent-core/pyagent
|
|
7
|
+
Project-URL: Documentation, https://pyagent.org
|
|
8
|
+
Author-email: PyAgent Team <team@pyagent.org>
|
|
9
|
+
License: MIT
|
|
10
|
+
Keywords: LLM,agents,context,memory,retrieval,trust
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
17
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
18
|
+
Classifier: Typing :: Typed
|
|
19
|
+
Requires-Python: >=3.11
|
|
20
|
+
Requires-Dist: pyagent-patterns>=0.1.0
|
|
21
|
+
Provides-Extra: chromadb
|
|
22
|
+
Requires-Dist: chromadb>=0.5; extra == 'chromadb'
|
|
23
|
+
Provides-Extra: compress
|
|
24
|
+
Requires-Dist: pyagent-compress>=0.1.0; extra == 'compress'
|
|
25
|
+
Provides-Extra: dev
|
|
26
|
+
Requires-Dist: mypy>=1.10; extra == 'dev'
|
|
27
|
+
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
|
|
28
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
29
|
+
Requires-Dist: ruff>=0.5; extra == 'dev'
|
|
30
|
+
Description-Content-Type: text/markdown
|
|
31
|
+
|
|
32
|
+
# pyagent-context
|
|
33
|
+
|
|
34
|
+
**Three-tier memory with trust-aware context ledger** for multi-agent LLM systems. Structured context management with trust levels, sensitivity classification, compression policies, and lifecycle management.
|
|
35
|
+
|
|
36
|
+
[](../../LICENSE)
|
|
37
|
+
[](https://www.python.org/downloads/)
|
|
38
|
+
|
|
39
|
+
## Install
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
pip install pyagent-context # Core (working + session + semantic memory)
|
|
43
|
+
pip install pyagent-context[compress] # + ContextCompressor with pyagent-compress
|
|
44
|
+
pip install pyagent-context[chromadb] # + ChromaDB semantic memory backend
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Depends on: `pyagent-patterns`.
|
|
48
|
+
|
|
49
|
+
## Why Structured Context?
|
|
50
|
+
|
|
51
|
+
Without `pyagent-context`, multi-agent workflows pass a flat `list[Message]` that grows unbounded. You lose track of *who* said *what*, *when*, and *how much to trust it*. This package adds trust levels, sensitivity tiers, expiry, compression, and three memory tiers — so your agents always work with the right context.
|
|
52
|
+
|
|
53
|
+
## ContextItem — Everything Has Metadata
|
|
54
|
+
|
|
55
|
+
```python
|
|
56
|
+
import time
|
|
57
|
+
from pyagent_context import ContextItem, TrustLevel, Sensitivity
|
|
58
|
+
|
|
59
|
+
item = ContextItem(
|
|
60
|
+
content="Revenue grew 15% YoY to $25.2B",
|
|
61
|
+
source="analyst",
|
|
62
|
+
trust_level=TrustLevel.VERIFIED, # verified | inferred | user | external
|
|
63
|
+
sensitivity=Sensitivity.INTERNAL, # public | internal | confidential | restricted
|
|
64
|
+
expires_at=time.time() + 3600, # auto-expire in 1 hour
|
|
65
|
+
derived_from="abc123", # parent item ID
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
print(item.id) # unique 12-char hex
|
|
69
|
+
print(item.token_estimate) # auto-calculated: len(content) // 4
|
|
70
|
+
print(item.is_expired) # False (still within TTL)
|
|
71
|
+
print(item.age_seconds) # seconds since creation
|
|
72
|
+
|
|
73
|
+
# Serialize / deserialize
|
|
74
|
+
data = item.to_dict()
|
|
75
|
+
restored = ContextItem.from_dict(data)
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## ContextLedger — Append-Only Context Log
|
|
79
|
+
|
|
80
|
+
```python
|
|
81
|
+
from pyagent_context import ContextLedger, TrustLevel
|
|
82
|
+
|
|
83
|
+
ledger = ContextLedger()
|
|
84
|
+
|
|
85
|
+
# Add items
|
|
86
|
+
ledger.add("User asked about Q3 earnings", "user", TrustLevel.USER_PROVIDED)
|
|
87
|
+
ledger.add("Revenue: $25.2B (+8% YoY)", "analyst", TrustLevel.VERIFIED)
|
|
88
|
+
ledger.add("I think margins will improve", "forecaster", TrustLevel.INFERRED)
|
|
89
|
+
|
|
90
|
+
# Query by trust
|
|
91
|
+
verified = ledger.query(min_trust=TrustLevel.VERIFIED)
|
|
92
|
+
print(len(verified)) # 1
|
|
93
|
+
|
|
94
|
+
# Query by age (last 5 minutes)
|
|
95
|
+
recent = ledger.query(max_age_seconds=300)
|
|
96
|
+
|
|
97
|
+
# Query by source
|
|
98
|
+
from_analyst = ledger.query(source="analyst")
|
|
99
|
+
|
|
100
|
+
# Convert to Messages for pattern consumption
|
|
101
|
+
messages = ledger.to_messages() # all items
|
|
102
|
+
messages = ledger.to_messages(max_tokens=500) # budget-constrained (most recent first)
|
|
103
|
+
|
|
104
|
+
# Snapshot for persistence
|
|
105
|
+
snap = ledger.snapshot() # JSON-serializable dict
|
|
106
|
+
restored = ContextLedger.from_snapshot(snap)
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## Three-Tier Memory
|
|
110
|
+
|
|
111
|
+
### WorkingMemory — Bounded In-Flight Context
|
|
112
|
+
|
|
113
|
+
```python
|
|
114
|
+
from pyagent_context import WorkingMemory, ContextItem
|
|
115
|
+
|
|
116
|
+
wm = WorkingMemory(max_items=50, max_tokens=10_000)
|
|
117
|
+
|
|
118
|
+
item = ContextItem(content="New observation", source="agent_1")
|
|
119
|
+
evicted = wm.add(item) # returns list of evicted items if capacity exceeded
|
|
120
|
+
|
|
121
|
+
print(len(wm)) # current item count
|
|
122
|
+
print(wm.total_tokens) # current token usage
|
|
123
|
+
print(f"{wm.utilization:.0%}") # e.g. "42%"
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### SessionMemory — Persist Across Turns
|
|
127
|
+
|
|
128
|
+
```python
|
|
129
|
+
from pyagent_context import SessionMemory, ContextItem
|
|
130
|
+
|
|
131
|
+
# JSON backend (simple, human-readable)
|
|
132
|
+
session = SessionMemory("user-123-session", backend="json", storage_path=".sessions")
|
|
133
|
+
session.add(ContextItem(content="User prefers concise answers", source="user"))
|
|
134
|
+
session.save()
|
|
135
|
+
|
|
136
|
+
# Later: reload
|
|
137
|
+
session2 = SessionMemory("user-123-session", backend="json", storage_path=".sessions")
|
|
138
|
+
session2.load()
|
|
139
|
+
items = session2.get_all()
|
|
140
|
+
|
|
141
|
+
# SQLite backend (concurrent-safe)
|
|
142
|
+
session = SessionMemory("user-123-session", backend="sqlite", storage_path=".sessions")
|
|
143
|
+
session.add(ContextItem(content="Important context", source="system"))
|
|
144
|
+
session.save()
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
### SemanticMemory — Vector-Indexed Long-Term Store
|
|
148
|
+
|
|
149
|
+
```python
|
|
150
|
+
from pyagent_context import InMemorySemanticStore, ContextItem
|
|
151
|
+
|
|
152
|
+
store = InMemorySemanticStore()
|
|
153
|
+
|
|
154
|
+
# Index items
|
|
155
|
+
store.add(ContextItem(content="Python asyncio event loop concurrency patterns", source="docs"))
|
|
156
|
+
store.add(ContextItem(content="JavaScript React component lifecycle hooks", source="docs"))
|
|
157
|
+
store.add(ContextItem(content="Python FastAPI async web framework REST API design", source="docs"))
|
|
158
|
+
|
|
159
|
+
# Semantic search (TF-IDF cosine similarity)
|
|
160
|
+
results = store.search("Python async web", top_k=3)
|
|
161
|
+
for r in results:
|
|
162
|
+
print(f" [{r.score:.2f}] {r.item.content[:60]}...")
|
|
163
|
+
|
|
164
|
+
# Remove / clear
|
|
165
|
+
store.remove(item_id)
|
|
166
|
+
store.clear()
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
## ContextCompressor — Manage Token Growth
|
|
170
|
+
|
|
171
|
+
Four policies: `NONE`, `FIFO`, `SEMANTIC_LOSSLESS`, `SAWTOOTH`.
|
|
172
|
+
|
|
173
|
+
```python
|
|
174
|
+
from pyagent_context import ContextCompressor, CompressionPolicy, ContextLedger
|
|
175
|
+
|
|
176
|
+
# FIFO: drop oldest items until under floor
|
|
177
|
+
compressor = ContextCompressor(
|
|
178
|
+
policy=CompressionPolicy.FIFO,
|
|
179
|
+
threshold_tokens=10_000, # trigger compression at 10k tokens
|
|
180
|
+
floor_tokens=5_000, # compress down to 5k
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
if compressor.should_compress(ledger):
|
|
184
|
+
compressed = compressor.compress(ledger)
|
|
185
|
+
print(f"Compressed: {ledger.total_tokens} → {compressed.total_tokens} tokens")
|
|
186
|
+
|
|
187
|
+
# SAWTOOTH: compress to floor, then allow growth again
|
|
188
|
+
compressor = ContextCompressor(
|
|
189
|
+
policy=CompressionPolicy.SAWTOOTH,
|
|
190
|
+
threshold_tokens=10_000,
|
|
191
|
+
floor_tokens=3_000,
|
|
192
|
+
)
|
|
193
|
+
|
|
194
|
+
# SEMANTIC_LOSSLESS: compress text but preserve verified items unchanged
|
|
195
|
+
compressor = ContextCompressor(
|
|
196
|
+
policy=CompressionPolicy.SEMANTIC_LOSSLESS,
|
|
197
|
+
threshold_tokens=8_000,
|
|
198
|
+
floor_tokens=4_000,
|
|
199
|
+
)
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
## TrustAwareRetriever — Smart Context Selection
|
|
203
|
+
|
|
204
|
+
Scores items by `trust × recency × relevance`:
|
|
205
|
+
|
|
206
|
+
```python
|
|
207
|
+
from pyagent_context import TrustAwareRetriever
|
|
208
|
+
|
|
209
|
+
retriever = TrustAwareRetriever(
|
|
210
|
+
weight_trust=0.3,
|
|
211
|
+
weight_recency=0.3,
|
|
212
|
+
weight_relevance=0.4,
|
|
213
|
+
recency_half_life=3600.0, # 1 hour half-life
|
|
214
|
+
)
|
|
215
|
+
|
|
216
|
+
results = retriever.retrieve(ledger, "Q3 earnings revenue growth", top_k=5)
|
|
217
|
+
for r in results:
|
|
218
|
+
print(f" [{r.score:.2f}] trust={r.trust_score:.2f} "
|
|
219
|
+
f"recency={r.recency_score:.2f} relevance={r.relevance_score:.2f}")
|
|
220
|
+
print(f" {r.item.content[:80]}...")
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
## ContextLifecycle — Expiry, Decay, Consolidation
|
|
224
|
+
|
|
225
|
+
```python
|
|
226
|
+
from pyagent_context import ContextLifecycle
|
|
227
|
+
|
|
228
|
+
lifecycle = ContextLifecycle(consolidation_threshold=0.6)
|
|
229
|
+
|
|
230
|
+
# Remove expired items
|
|
231
|
+
new_ledger, expired = lifecycle.sweep_expired(ledger)
|
|
232
|
+
print(f"Removed {len(expired)} expired items")
|
|
233
|
+
|
|
234
|
+
# Apply freshness decay (old items get smaller token budgets)
|
|
235
|
+
decayed = lifecycle.apply_freshness_decay(ledger, half_life_seconds=3600)
|
|
236
|
+
|
|
237
|
+
# Merge similar items from the same source
|
|
238
|
+
consolidated = lifecycle.consolidate(ledger)
|
|
239
|
+
print(f"Consolidated: {len(ledger)} → {len(consolidated)} items")
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
## ContextRedactor — Sensitivity-Based Redaction
|
|
243
|
+
|
|
244
|
+
```python
|
|
245
|
+
from pyagent_context import ContextRedactor
|
|
246
|
+
from pyagent_context.item import Sensitivity
|
|
247
|
+
|
|
248
|
+
# Redact items above INTERNAL sensitivity
|
|
249
|
+
redactor = ContextRedactor(
|
|
250
|
+
max_sensitivity=Sensitivity.INTERNAL,
|
|
251
|
+
redaction_text="[REDACTED — CONFIDENTIAL]",
|
|
252
|
+
)
|
|
253
|
+
|
|
254
|
+
redacted_ledger = redactor.redact_ledger(ledger)
|
|
255
|
+
|
|
256
|
+
# Or exclude entirely instead of redacting
|
|
257
|
+
redactor = ContextRedactor(
|
|
258
|
+
max_sensitivity=Sensitivity.INTERNAL,
|
|
259
|
+
exclude_above=True,
|
|
260
|
+
)
|
|
261
|
+
filtered_ledger = redactor.redact_ledger(ledger)
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
## Integration with pyagent-patterns
|
|
265
|
+
|
|
266
|
+
```python
|
|
267
|
+
from pyagent_patterns.base import Agent, MockLLM
|
|
268
|
+
from pyagent_patterns.orchestration import Pipeline
|
|
269
|
+
from pyagent_context import ContextLedger, TrustLevel, WorkingMemory
|
|
270
|
+
|
|
271
|
+
ledger = ContextLedger()
|
|
272
|
+
|
|
273
|
+
# Before pattern run: seed with trusted context
|
|
274
|
+
ledger.add("User is asking about Q3 2025 earnings", "user", TrustLevel.USER_PROVIDED)
|
|
275
|
+
ledger.add("Tesla Q3 revenue was $25.2B", "database", TrustLevel.VERIFIED)
|
|
276
|
+
|
|
277
|
+
# Convert to messages and prepend to pattern input
|
|
278
|
+
context_messages = ledger.to_messages(max_tokens=2000)
|
|
279
|
+
|
|
280
|
+
# After pattern run: store results
|
|
281
|
+
ledger.add(result.output, "pipeline", TrustLevel.INFERRED)
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
## Full Documentation
|
|
285
|
+
|
|
286
|
+
See [pyagent.dev](https://pyagent.dev) for full API reference and integration guides.
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
pyagent_context/__init__.py,sha256=g87ZeL-Onj3A3xrvV9ohnJ6dMIVDHpZ98lR9dME4Z7A,1014
|
|
2
|
+
pyagent_context/compression.py,sha256=IstTaKIq9F2L2tXlVyuh3Gj-jsLipK0sYwlDG343kXg,4792
|
|
3
|
+
pyagent_context/item.py,sha256=GRS7RADw1NrOZX-FjDyyp3-MqDCRsinaSxeQYEndpeE,4161
|
|
4
|
+
pyagent_context/ledger.py,sha256=rC76sY1xJnl63TOnUY8pfVQTJDM_tgqX0XbXbaeGXnw,4260
|
|
5
|
+
pyagent_context/lifecycle.py,sha256=xgadoWdsk8qlo4ZGiVDhtl49v1YRKABIQuA_oqWC_2c,5531
|
|
6
|
+
pyagent_context/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
7
|
+
pyagent_context/redaction.py,sha256=fAT54ZYvzVRkupsHDL1iEiwdOAYNI5Hjo579AXX5We4,2459
|
|
8
|
+
pyagent_context/retrieval.py,sha256=hL2NSmbfgKOfNSRIxLaI4GSA20GTPa3bzzUyevLl2cU,3763
|
|
9
|
+
pyagent_context/memory/__init__.py,sha256=dqfzGRJCQN6_i-k2KiQYy_RC-222zePagfcpuvkfsnY,378
|
|
10
|
+
pyagent_context/memory/semantic.py,sha256=uNmmMNbXD8rl-xzRpZfyv-7edgUARqgxMNfgxWuR2JE,5324
|
|
11
|
+
pyagent_context/memory/session.py,sha256=a3ztBR_p0F79X-Z5pulxYHiu7iFolupjibMQ0TOVPLU,4827
|
|
12
|
+
pyagent_context/memory/working.py,sha256=6IoUiFCZaQ-rBJRCcdoV1pkiYykdcWRQCFl0-sRCUzg,1928
|
|
13
|
+
pyagent_context-0.1.0.dist-info/METADATA,sha256=sNomVCd9BwaG6VLHjh7hosMx6BzFan2xGmvDu4ABbBk,9544
|
|
14
|
+
pyagent_context-0.1.0.dist-info/WHEEL,sha256=mffPy8wBnZQn2VnJUU5jE99KsxaSfiyMHV9Yt0aLVxs,87
|
|
15
|
+
pyagent_context-0.1.0.dist-info/RECORD,,
|