chuk-ai-session-manager 0.7.1__py3-none-any.whl → 0.8__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.
Files changed (46) hide show
  1. chuk_ai_session_manager/__init__.py +84 -40
  2. chuk_ai_session_manager/api/__init__.py +1 -1
  3. chuk_ai_session_manager/api/simple_api.py +53 -59
  4. chuk_ai_session_manager/exceptions.py +31 -17
  5. chuk_ai_session_manager/guards/__init__.py +118 -0
  6. chuk_ai_session_manager/guards/bindings.py +217 -0
  7. chuk_ai_session_manager/guards/cache.py +163 -0
  8. chuk_ai_session_manager/guards/manager.py +819 -0
  9. chuk_ai_session_manager/guards/models.py +498 -0
  10. chuk_ai_session_manager/guards/ungrounded.py +159 -0
  11. chuk_ai_session_manager/infinite_conversation.py +86 -79
  12. chuk_ai_session_manager/memory/__init__.py +247 -0
  13. chuk_ai_session_manager/memory/artifacts_bridge.py +469 -0
  14. chuk_ai_session_manager/memory/context_packer.py +347 -0
  15. chuk_ai_session_manager/memory/fault_handler.py +507 -0
  16. chuk_ai_session_manager/memory/manifest.py +307 -0
  17. chuk_ai_session_manager/memory/models.py +1084 -0
  18. chuk_ai_session_manager/memory/mutation_log.py +186 -0
  19. chuk_ai_session_manager/memory/pack_cache.py +206 -0
  20. chuk_ai_session_manager/memory/page_table.py +275 -0
  21. chuk_ai_session_manager/memory/prefetcher.py +192 -0
  22. chuk_ai_session_manager/memory/tlb.py +247 -0
  23. chuk_ai_session_manager/memory/vm_prompts.py +238 -0
  24. chuk_ai_session_manager/memory/working_set.py +574 -0
  25. chuk_ai_session_manager/models/__init__.py +21 -9
  26. chuk_ai_session_manager/models/event_source.py +3 -1
  27. chuk_ai_session_manager/models/event_type.py +10 -1
  28. chuk_ai_session_manager/models/session.py +103 -68
  29. chuk_ai_session_manager/models/session_event.py +69 -68
  30. chuk_ai_session_manager/models/session_metadata.py +9 -10
  31. chuk_ai_session_manager/models/session_run.py +21 -22
  32. chuk_ai_session_manager/models/token_usage.py +76 -76
  33. chuk_ai_session_manager/procedural_memory/__init__.py +70 -0
  34. chuk_ai_session_manager/procedural_memory/formatter.py +407 -0
  35. chuk_ai_session_manager/procedural_memory/manager.py +523 -0
  36. chuk_ai_session_manager/procedural_memory/models.py +371 -0
  37. chuk_ai_session_manager/sample_tools.py +79 -46
  38. chuk_ai_session_manager/session_aware_tool_processor.py +27 -16
  39. chuk_ai_session_manager/session_manager.py +238 -197
  40. chuk_ai_session_manager/session_prompt_builder.py +163 -111
  41. chuk_ai_session_manager/session_storage.py +45 -52
  42. {chuk_ai_session_manager-0.7.1.dist-info → chuk_ai_session_manager-0.8.dist-info}/METADATA +79 -3
  43. chuk_ai_session_manager-0.8.dist-info/RECORD +45 -0
  44. {chuk_ai_session_manager-0.7.1.dist-info → chuk_ai_session_manager-0.8.dist-info}/WHEEL +1 -1
  45. chuk_ai_session_manager-0.7.1.dist-info/RECORD +0 -22
  46. {chuk_ai_session_manager-0.7.1.dist-info → chuk_ai_session_manager-0.8.dist-info}/top_level.txt +0 -0
@@ -2,24 +2,27 @@
2
2
  """
3
3
  Session model for the chuk session manager with improved async support.
4
4
  """
5
+
5
6
  from __future__ import annotations
6
- from datetime import datetime, timezone
7
+ from datetime import datetime
7
8
  from typing import Any, Dict, List, Optional, Generic, TypeVar, Union
8
9
  from uuid import uuid4
9
10
  from pydantic import BaseModel, Field, model_validator
10
- import asyncio
11
11
 
12
12
  # Import models that Session depends on
13
13
  from chuk_ai_session_manager.models.session_metadata import SessionMetadata
14
14
  from chuk_ai_session_manager.models.session_event import SessionEvent
15
15
  from chuk_ai_session_manager.models.token_usage import TokenUsage, TokenSummary
16
+
16
17
  # Import SessionRun and RunStatus directly to avoid circular import
17
18
  from chuk_ai_session_manager.models.session_run import SessionRun, RunStatus
18
19
 
19
- MessageT = TypeVar('MessageT')
20
+ MessageT = TypeVar("MessageT")
21
+
20
22
 
21
23
  class Session(BaseModel, Generic[MessageT]):
22
24
  """A standalone conversation session with hierarchical support and async methods."""
25
+
23
26
  id: str = Field(default_factory=lambda: str(uuid4()))
24
27
  metadata: SessionMetadata = Field(default_factory=SessionMetadata)
25
28
 
@@ -30,31 +33,35 @@ class Session(BaseModel, Generic[MessageT]):
30
33
  runs: List[SessionRun] = Field(default_factory=list)
31
34
  events: List[SessionEvent[MessageT]] = Field(default_factory=list)
32
35
  state: Dict[str, Any] = Field(default_factory=dict)
33
-
36
+
34
37
  # Token tracking
35
38
  token_summary: TokenSummary = Field(default_factory=TokenSummary)
36
39
 
37
40
  @model_validator(mode="after")
38
41
  def _sync_hierarchy(cls, model: Session) -> Session:
39
42
  """After creation, sync this session with its parent in the store.
40
-
41
- Note: This is synchronous for compatibility with Pydantic.
43
+
44
+ Note: This is synchronous for compatibility with Pydantic.
42
45
  For async parent syncing, use async_init() after creation.
43
46
  """
44
47
  # This validator will be called during model creation,
45
48
  # but won't actually sync with storage - that requires async
46
49
  return model
47
-
50
+
48
51
  async def async_init(self) -> None:
49
52
  """
50
53
  Initialize async components of the session.
51
-
54
+
52
55
  Call this after creating a new session to properly set up
53
56
  parent-child relationships in the async storage.
54
57
  """
55
58
  if self.parent_id:
56
59
  # Import here to avoid circular import
57
- from chuk_ai_session_manager.session_storage import get_backend, ChukSessionsStore
60
+ from chuk_ai_session_manager.session_storage import (
61
+ get_backend,
62
+ ChukSessionsStore,
63
+ )
64
+
58
65
  backend = get_backend()
59
66
  store = ChukSessionsStore(backend)
60
67
  parent = await store.get(self.parent_id)
@@ -76,12 +83,12 @@ class Session(BaseModel, Generic[MessageT]):
76
83
  if run.status == RunStatus.RUNNING:
77
84
  return run
78
85
  return None
79
-
86
+
80
87
  @property
81
88
  def total_tokens(self) -> int:
82
89
  """Return the total number of tokens used in this session."""
83
90
  return self.token_summary.total_tokens
84
-
91
+
85
92
  @property
86
93
  def total_cost(self) -> float:
87
94
  """Return the total estimated cost of this session."""
@@ -92,7 +99,11 @@ class Session(BaseModel, Generic[MessageT]):
92
99
  if child_id not in self.child_ids:
93
100
  self.child_ids.append(child_id)
94
101
  # Save the updated session
95
- from chuk_ai_session_manager.session_storage import get_backend, ChukSessionsStore
102
+ from chuk_ai_session_manager.session_storage import (
103
+ get_backend,
104
+ ChukSessionsStore,
105
+ )
106
+
96
107
  backend = get_backend()
97
108
  store = ChukSessionsStore(backend)
98
109
  await store.save(self)
@@ -102,7 +113,11 @@ class Session(BaseModel, Generic[MessageT]):
102
113
  if child_id in self.child_ids:
103
114
  self.child_ids.remove(child_id)
104
115
  # Save the updated session
105
- from chuk_ai_session_manager.session_storage import get_backend, ChukSessionsStore
116
+ from chuk_ai_session_manager.session_storage import (
117
+ get_backend,
118
+ ChukSessionsStore,
119
+ )
120
+
106
121
  backend = get_backend()
107
122
  store = ChukSessionsStore(backend)
108
123
  await store.save(self)
@@ -111,12 +126,16 @@ class Session(BaseModel, Generic[MessageT]):
111
126
  """Fetch ancestor sessions from store asynchronously."""
112
127
  result: List[Session] = []
113
128
  current = self.parent_id
114
-
129
+
115
130
  # Import here to avoid circular import
116
- from chuk_ai_session_manager.session_storage import get_backend, ChukSessionsStore
131
+ from chuk_ai_session_manager.session_storage import (
132
+ get_backend,
133
+ ChukSessionsStore,
134
+ )
135
+
117
136
  backend = get_backend()
118
137
  store = ChukSessionsStore(backend)
119
-
138
+
120
139
  while current:
121
140
  parent = await store.get(current)
122
141
  if not parent:
@@ -129,12 +148,16 @@ class Session(BaseModel, Generic[MessageT]):
129
148
  """Fetch all descendant sessions from store in DFS order asynchronously."""
130
149
  result: List[Session] = []
131
150
  stack = list(self.child_ids)
132
-
151
+
133
152
  # Import here to avoid circular import
134
- from chuk_ai_session_manager.session_storage import get_backend, ChukSessionsStore
153
+ from chuk_ai_session_manager.session_storage import (
154
+ get_backend,
155
+ ChukSessionsStore,
156
+ )
157
+
135
158
  backend = get_backend()
136
159
  store = ChukSessionsStore(backend)
137
-
160
+
138
161
  while stack:
139
162
  cid = stack.pop()
140
163
  child = await store.get(cid)
@@ -142,160 +165,166 @@ class Session(BaseModel, Generic[MessageT]):
142
165
  result.append(child)
143
166
  stack.extend(child.child_ids)
144
167
  return result
145
-
168
+
146
169
  async def add_event(self, event: SessionEvent[MessageT]) -> None:
147
170
  """
148
171
  Add an event to the session and update token tracking asynchronously.
149
-
172
+
150
173
  Args:
151
174
  event: The event to add
152
175
  """
153
176
  # Add the event
154
177
  self.events.append(event)
155
-
178
+
156
179
  # Update token summary if this event has token usage
157
180
  if event.token_usage:
158
181
  await self.token_summary.add_usage(event.token_usage)
159
-
182
+
160
183
  async def add_event_and_save(self, event: SessionEvent[MessageT]) -> None:
161
184
  """
162
185
  Add an event to the session, update token tracking, and save the session.
163
-
186
+
164
187
  Args:
165
188
  event: The event to add
166
189
  """
167
190
  # Add the event asynchronously
168
191
  await self.add_event(event)
169
-
192
+
170
193
  # Save the session
171
- from chuk_ai_session_manager.session_storage import get_backend, ChukSessionsStore
194
+ from chuk_ai_session_manager.session_storage import (
195
+ get_backend,
196
+ ChukSessionsStore,
197
+ )
198
+
172
199
  backend = get_backend()
173
200
  store = ChukSessionsStore(backend)
174
201
  await store.save(self)
175
-
202
+
176
203
  async def get_token_usage_by_source(self) -> Dict[str, TokenSummary]:
177
204
  """
178
205
  Get token usage statistics grouped by event source asynchronously.
179
-
206
+
180
207
  Returns:
181
208
  A dictionary mapping event sources to token summaries
182
209
  """
183
210
  result: Dict[str, TokenSummary] = {}
184
-
211
+
185
212
  for event in self.events:
186
213
  if not event.token_usage:
187
214
  continue
188
-
215
+
189
216
  # Use the string value of the enum for the key
190
- source = event.source.value if hasattr(event.source, 'value') else str(event.source)
217
+ source = (
218
+ event.source.value
219
+ if hasattr(event.source, "value")
220
+ else str(event.source)
221
+ )
191
222
  if source not in result:
192
223
  result[source] = TokenSummary()
193
-
224
+
194
225
  await result[source].add_usage(event.token_usage)
195
-
226
+
196
227
  return result
197
-
228
+
198
229
  async def get_token_usage_by_run(self) -> Dict[str, TokenSummary]:
199
230
  """
200
231
  Get token usage statistics grouped by run asynchronously.
201
-
232
+
202
233
  Returns:
203
234
  A dictionary mapping run IDs to token summaries
204
235
  """
205
236
  result: Dict[str, TokenSummary] = {}
206
-
237
+
207
238
  # Add an entry for events without a run
208
239
  result["no_run"] = TokenSummary()
209
-
240
+
210
241
  for event in self.events:
211
242
  if not event.token_usage:
212
243
  continue
213
-
244
+
214
245
  run_id = event.task_id or "no_run"
215
246
  if run_id not in result:
216
247
  result[run_id] = TokenSummary()
217
-
248
+
218
249
  await result[run_id].add_usage(event.token_usage)
219
-
250
+
220
251
  return result
221
-
252
+
222
253
  async def count_message_tokens(
223
- self,
224
- message: Union[str, Dict[str, Any]],
225
- model: str = "gpt-3.5-turbo"
254
+ self, message: Union[str, Dict[str, Any]], model: str = "gpt-3.5-turbo"
226
255
  ) -> int:
227
256
  """
228
257
  Count tokens in a message asynchronously.
229
-
258
+
230
259
  Args:
231
260
  message: The message to count tokens for (string or dict)
232
261
  model: The model to use for counting
233
-
262
+
234
263
  Returns:
235
264
  The number of tokens in the message
236
265
  """
237
266
  # If message is already a string, count directly
238
267
  if isinstance(message, str):
239
268
  return await TokenUsage.count_tokens(message, model)
240
-
269
+
241
270
  # If it's a dict (like OpenAI messages), extract content
242
271
  if isinstance(message, dict) and "content" in message:
243
272
  return await TokenUsage.count_tokens(message["content"], model)
244
-
273
+
245
274
  # If it's some other object, convert to string and count
246
275
  return await TokenUsage.count_tokens(str(message), model)
247
-
276
+
248
277
  async def set_state(self, key: str, value: Any) -> None:
249
278
  """
250
279
  Set a state value asynchronously.
251
-
280
+
252
281
  Args:
253
282
  key: The state key to set
254
283
  value: The value to set
255
284
  """
256
285
  self.state[key] = value
257
-
286
+
258
287
  # Auto-save if needed (could be added as an option)
259
288
  # from chuk_ai_session_manager.chuk_sessions_storage import get_backend, ChukSessionsStore
260
289
  # backend = get_backend()
261
290
  # store = ChukSessionsStore(backend)
262
291
  # await store.save(self)
263
-
292
+
264
293
  async def get_state(self, key: str, default: Any = None) -> Any:
265
294
  """
266
295
  Get a state value asynchronously.
267
-
296
+
268
297
  Args:
269
298
  key: The state key to retrieve
270
299
  default: Default value to return if key not found
271
-
300
+
272
301
  Returns:
273
302
  The state value or default if not found
274
303
  """
275
304
  return self.state.get(key, default)
276
-
305
+
277
306
  async def has_state(self, key: str) -> bool:
278
307
  """
279
308
  Check if a state key exists asynchronously.
280
-
309
+
281
310
  Args:
282
311
  key: The state key to check
283
-
312
+
284
313
  Returns:
285
314
  True if the key exists in state
286
315
  """
287
316
  return key in self.state
288
-
317
+
289
318
  async def remove_state(self, key: str) -> None:
290
319
  """
291
320
  Remove a state key-value pair asynchronously.
292
-
321
+
293
322
  Args:
294
323
  key: The state key to remove
295
324
  """
296
325
  if key in self.state:
297
326
  del self.state[key]
298
-
327
+
299
328
  # Auto-save if needed (could be added as an option)
300
329
  # from chuk_ai_session_manager.chuk_sessions_storage import get_backend, ChukSessionsStore
301
330
  # backend = get_backend()
@@ -303,15 +332,17 @@ class Session(BaseModel, Generic[MessageT]):
303
332
  # await store.save(self)
304
333
 
305
334
  @classmethod
306
- async def create(cls, session_id: Optional[str] = None, parent_id: Optional[str] = None, **kwargs) -> Session:
335
+ async def create(
336
+ cls, session_id: Optional[str] = None, parent_id: Optional[str] = None, **kwargs
337
+ ) -> Session:
307
338
  """
308
339
  Create a new session asynchronously, handling parent-child relationships.
309
-
340
+
310
341
  Args:
311
342
  session_id: Optional session ID to use (if not provided, generates a new one)
312
343
  parent_id: Optional parent session ID
313
344
  **kwargs: Additional arguments for Session initialization
314
-
345
+
315
346
  Returns:
316
347
  A new Session instance with parent-child relationships set up
317
348
  """
@@ -320,13 +351,17 @@ class Session(BaseModel, Generic[MessageT]):
320
351
  session = cls(id=session_id, parent_id=parent_id, **kwargs)
321
352
  else:
322
353
  session = cls(parent_id=parent_id, **kwargs)
323
-
354
+
324
355
  await session.async_init()
325
-
356
+
326
357
  # Save the new session
327
- from chuk_ai_session_manager.session_storage import get_backend, ChukSessionsStore
358
+ from chuk_ai_session_manager.session_storage import (
359
+ get_backend,
360
+ ChukSessionsStore,
361
+ )
362
+
328
363
  backend = get_backend()
329
364
  store = ChukSessionsStore(backend)
330
365
  await store.save(session)
331
-
332
- return session
366
+
367
+ return session