chuk-ai-session-manager 0.7.1__py3-none-any.whl → 0.8.1__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 +259 -232
  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.1.dist-info}/METADATA +80 -4
  43. chuk_ai_session_manager-0.8.1.dist-info/RECORD +45 -0
  44. {chuk_ai_session_manager-0.7.1.dist-info → chuk_ai_session_manager-0.8.1.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.1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,407 @@
1
+ # chuk_ai_session_manager/procedural_memory/formatter.py
2
+ """
3
+ Context Formatter for Procedural Memory.
4
+
5
+ Handles formatting procedural memory for injection into model context.
6
+ This is the "paging" layer - deciding what tool knowledge to include
7
+ and how to present it.
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ from typing import Any, Optional
13
+
14
+ from pydantic import BaseModel, Field
15
+
16
+ from chuk_ai_session_manager.procedural_memory.models import (
17
+ ToolLogEntry,
18
+ ToolPattern,
19
+ )
20
+ from chuk_ai_session_manager.procedural_memory.manager import ToolMemoryManager
21
+
22
+
23
+ class FormatterConfig(BaseModel):
24
+ """Configuration for procedural memory formatting."""
25
+
26
+ # How many recent calls to include per tool
27
+ max_recent_calls: int = 3
28
+
29
+ # How many error patterns to show
30
+ max_error_patterns: int = 3
31
+
32
+ # How many success patterns to show
33
+ max_success_patterns: int = 3
34
+
35
+ # Whether to include argument details
36
+ include_args: bool = False
37
+
38
+ # Whether to include timing info
39
+ include_timing: bool = False
40
+
41
+ # Whether to show fix relationships
42
+ show_fix_relations: bool = True
43
+
44
+ # Compact mode (less verbose)
45
+ compact: bool = False
46
+
47
+
48
+ class ProceduralContextFormatter(BaseModel):
49
+ """
50
+ Formats procedural memory for injection into model context.
51
+
52
+ This class handles the "paging" decision - what to include and how.
53
+ It can format:
54
+ - Recent tool history for specific tools
55
+ - Patterns (errors, successes) for tools
56
+ - Full procedural context summary
57
+ """
58
+
59
+ config: FormatterConfig = Field(default_factory=FormatterConfig)
60
+
61
+ def format_for_tools(
62
+ self,
63
+ manager: ToolMemoryManager,
64
+ tool_names: list[str],
65
+ context_goal: Optional[str] = None,
66
+ ) -> str:
67
+ """
68
+ Format procedural memory for specific tools about to be called.
69
+
70
+ This is the main entry point for "just-in-time" paging.
71
+
72
+ Args:
73
+ manager: The tool memory manager
74
+ tool_names: Tools that are about to be called
75
+ context_goal: Current goal (for relevance filtering)
76
+
77
+ Returns:
78
+ Formatted context string ready for injection
79
+ """
80
+ sections = []
81
+
82
+ for tool_name in tool_names:
83
+ tool_section = self._format_tool_section(manager, tool_name, context_goal)
84
+ if tool_section:
85
+ sections.append(tool_section)
86
+
87
+ if not sections:
88
+ return ""
89
+
90
+ header = "<procedural_memory>\n"
91
+ footer = "\n</procedural_memory>"
92
+
93
+ return header + "\n\n".join(sections) + footer
94
+
95
+ def _format_tool_section(
96
+ self,
97
+ manager: ToolMemoryManager,
98
+ tool_name: str,
99
+ context_goal: Optional[str] = None,
100
+ ) -> Optional[str]:
101
+ """Format a single tool's procedural memory."""
102
+ lines = []
103
+
104
+ # Get recent calls for this tool
105
+ recent = manager.get_recent_calls(
106
+ tool_name=tool_name,
107
+ limit=self.config.max_recent_calls,
108
+ )
109
+
110
+ # Get pattern
111
+ pattern = manager.get_pattern(tool_name)
112
+
113
+ # Skip if no history
114
+ if not recent and not pattern:
115
+ return None
116
+
117
+ lines.append(f'<tool_memory name="{tool_name}">')
118
+
119
+ # Recent history
120
+ if recent:
121
+ lines.append(self._format_recent_calls(recent))
122
+
123
+ # Patterns
124
+ if pattern:
125
+ pattern_text = self._format_pattern(pattern, context_goal)
126
+ if pattern_text:
127
+ if recent:
128
+ lines.append("")
129
+ lines.append(pattern_text)
130
+
131
+ lines.append("</tool_memory>")
132
+
133
+ return "\n".join(lines)
134
+
135
+ def _format_recent_calls(self, calls: list[ToolLogEntry]) -> str:
136
+ """Format recent call history."""
137
+ lines = ["<recent_calls>"]
138
+
139
+ for entry in calls:
140
+ status = "✓" if entry.is_success() else "✗"
141
+ line = f" [{status}] {entry.result_summary}"
142
+
143
+ # Add fix info
144
+ if self.config.show_fix_relations:
145
+ if entry.is_fix() and entry.delta_args:
146
+ line += f" (fixed prior by: {self._format_delta(entry.delta_args)})"
147
+ elif entry.is_failure() and entry.was_fixed():
148
+ line += f" (fixed by {entry.fixed_by})"
149
+
150
+ # Add args if configured
151
+ if self.config.include_args and entry.arguments:
152
+ args_str = self._format_args(entry.arguments)
153
+ line += f"\n args: {args_str}"
154
+
155
+ # Add timing if configured
156
+ if self.config.include_timing and entry.execution_time_ms:
157
+ line += f" [{entry.execution_time_ms}ms]"
158
+
159
+ lines.append(line)
160
+
161
+ lines.append("</recent_calls>")
162
+ return "\n".join(lines)
163
+
164
+ def _format_pattern(
165
+ self,
166
+ pattern: ToolPattern,
167
+ context_goal: Optional[str] = None,
168
+ ) -> Optional[str]:
169
+ """Format aggregated patterns."""
170
+ lines = []
171
+
172
+ # Only show patterns if we have useful info
173
+ has_errors = bool(pattern.error_patterns)
174
+ has_successes = bool(pattern.success_patterns)
175
+
176
+ if not has_errors and not has_successes:
177
+ return None
178
+
179
+ lines.append("<patterns>")
180
+
181
+ # Stats (if not compact)
182
+ if not self.config.compact:
183
+ lines.append(
184
+ f" success_rate: {pattern.success_rate:.0%} "
185
+ f"({pattern.total_calls} calls)"
186
+ )
187
+
188
+ # Error patterns with fixes
189
+ if has_errors:
190
+ lines.append(" <common_errors>")
191
+ for ep in pattern.error_patterns[-self.config.max_error_patterns :]:
192
+ error_line = f" - {ep.error_type}"
193
+ if ep.count > 1:
194
+ error_line += f" (x{ep.count})"
195
+ if ep.typical_fix:
196
+ error_line += f" -> fix: {ep.typical_fix}"
197
+ lines.append(error_line)
198
+ lines.append(" </common_errors>")
199
+
200
+ # Success patterns (especially fixes)
201
+ if has_successes:
202
+ relevant = self._filter_relevant_successes(
203
+ pattern.success_patterns,
204
+ context_goal,
205
+ )
206
+ if relevant:
207
+ lines.append(" <success_hints>")
208
+ for sp in relevant[-self.config.max_success_patterns :]:
209
+ if sp.delta_that_fixed:
210
+ lines.append(
211
+ f" - Fixed by: {self._format_delta(sp.delta_that_fixed)}"
212
+ )
213
+ elif sp.arg_hints:
214
+ lines.append(f" - Typical args: {sp.arg_hints}")
215
+ lines.append(" </success_hints>")
216
+
217
+ lines.append("</patterns>")
218
+ return "\n".join(lines)
219
+
220
+ def _filter_relevant_successes(
221
+ self,
222
+ patterns: list,
223
+ context_goal: Optional[str],
224
+ ) -> list:
225
+ """Filter success patterns to those relevant to current goal."""
226
+ if not context_goal:
227
+ return patterns
228
+
229
+ goal_lower = context_goal.lower()
230
+ relevant = []
231
+
232
+ for sp in patterns:
233
+ # Include if no goal specified (general pattern)
234
+ if not sp.goal_match:
235
+ relevant.append(sp)
236
+ continue
237
+
238
+ # Include if goal matches
239
+ if goal_lower in sp.goal_match.lower():
240
+ relevant.append(sp)
241
+ continue
242
+
243
+ # Include if it's a fix (always useful)
244
+ if sp.delta_that_fixed:
245
+ relevant.append(sp)
246
+
247
+ return relevant
248
+
249
+ def _format_delta(self, delta: dict[str, Any]) -> str:
250
+ """Format arg delta compactly."""
251
+ parts = []
252
+
253
+ if "added" in delta:
254
+ added_keys = list(delta["added"].keys())
255
+ if len(added_keys) <= 2:
256
+ parts.append(f"+{', '.join(added_keys)}")
257
+ else:
258
+ parts.append(f"+{len(added_keys)} args")
259
+
260
+ if "removed" in delta:
261
+ removed = delta["removed"]
262
+ if len(removed) <= 2:
263
+ parts.append(f"-{', '.join(removed)}")
264
+ else:
265
+ parts.append(f"-{len(removed)} args")
266
+
267
+ if "changed" in delta:
268
+ changed_keys = list(delta["changed"].keys())
269
+ if len(changed_keys) <= 2:
270
+ changes = []
271
+ for k in changed_keys:
272
+ v = delta["changed"][k]
273
+ changes.append(
274
+ f"{k}:{self._abbrev(v['from'])}->{self._abbrev(v['to'])}"
275
+ )
276
+ parts.append(", ".join(changes))
277
+ else:
278
+ parts.append(f"~{len(changed_keys)} args")
279
+
280
+ return "; ".join(parts) if parts else "changes"
281
+
282
+ def _format_args(self, args: dict[str, Any], max_items: int = 3) -> str:
283
+ """Format arguments compactly."""
284
+ items = list(args.items())[:max_items]
285
+ formatted = [f"{k}={self._abbrev(v)}" for k, v in items]
286
+ if len(args) > max_items:
287
+ formatted.append(f"...+{len(args) - max_items}")
288
+ return ", ".join(formatted)
289
+
290
+ def _abbrev(self, value: Any, max_len: int = 15) -> str:
291
+ """Abbreviate a value."""
292
+ s = str(value)
293
+ if len(s) > max_len:
294
+ return s[: max_len - 2] + ".."
295
+ return s
296
+
297
+ # --- Full context formatting ---
298
+
299
+ def format_full_summary(
300
+ self,
301
+ manager: ToolMemoryManager,
302
+ max_tools: int = 5,
303
+ ) -> str:
304
+ """
305
+ Format a full summary of procedural memory.
306
+
307
+ Useful for periodic context refresh or debugging.
308
+ """
309
+ lines = ["<procedural_memory_summary>"]
310
+
311
+ stats = manager.get_stats()
312
+ lines.append(
313
+ f" Total: {stats['total_calls']} calls, "
314
+ f"{stats['success_rate']:.0%} success, "
315
+ f"{stats['total_fixes_detected']} fixes detected"
316
+ )
317
+
318
+ # Top tools by usage
319
+ patterns = manager.get_all_patterns()
320
+ sorted_tools = sorted(
321
+ patterns.items(),
322
+ key=lambda x: x[1].total_calls,
323
+ reverse=True,
324
+ )[:max_tools]
325
+
326
+ if sorted_tools:
327
+ lines.append("")
328
+ lines.append(" Most used tools:")
329
+ for tool_name, pattern in sorted_tools:
330
+ lines.append(
331
+ f" - {tool_name}: {pattern.total_calls} calls, "
332
+ f"{pattern.success_rate:.0%} success"
333
+ )
334
+ if pattern.error_patterns:
335
+ top_error = pattern.error_patterns[-1]
336
+ lines.append(f" last error: {top_error.error_type}")
337
+
338
+ lines.append("</procedural_memory_summary>")
339
+ return "\n".join(lines)
340
+
341
+ # --- Specialized formats ---
342
+
343
+ def format_error_guidance(
344
+ self,
345
+ manager: ToolMemoryManager,
346
+ tool_name: str,
347
+ error_type: str,
348
+ ) -> str:
349
+ """
350
+ Format guidance for handling a specific error.
351
+
352
+ Called when a tool fails - provides context on how
353
+ this error was handled before.
354
+ """
355
+ lines = [f'<error_guidance tool="{tool_name}" error="{error_type}">']
356
+
357
+ # Look for fix pattern
358
+ fix_delta = manager.get_fix_for_error(tool_name, error_type)
359
+ if fix_delta:
360
+ lines.append(f" Previous fix: {self._format_delta(fix_delta)}")
361
+
362
+ # Look for similar past failures that were fixed
363
+ similar = manager.search_calls(
364
+ tool_name=tool_name,
365
+ error_type=error_type,
366
+ only_fixed=True,
367
+ limit=3,
368
+ )
369
+
370
+ if similar:
371
+ lines.append(" Past fixes:")
372
+ for entry in similar:
373
+ if entry.fixed_by:
374
+ lines.append(f" - {entry.id} -> {entry.fixed_by}")
375
+ if entry.delta_args:
376
+ lines.append(
377
+ f" changed: {self._format_delta(entry.delta_args)}"
378
+ )
379
+
380
+ if len(lines) == 1:
381
+ lines.append(" No previous fixes found for this error type.")
382
+
383
+ lines.append("</error_guidance>")
384
+ return "\n".join(lines)
385
+
386
+ def format_success_template(
387
+ self,
388
+ manager: ToolMemoryManager,
389
+ tool_name: str,
390
+ goal: str,
391
+ ) -> Optional[str]:
392
+ """
393
+ Format a success template based on past successful calls.
394
+
395
+ Returns argument hints for achieving a goal.
396
+ """
397
+ arg_hints = manager.get_successful_args_for_goal(tool_name, goal)
398
+ if not arg_hints:
399
+ return None
400
+
401
+ lines = [f'<success_template tool="{tool_name}" goal="{goal}">']
402
+ lines.append(" Recommended args based on past success:")
403
+ for key, value in arg_hints.items():
404
+ lines.append(f" {key}: {value}")
405
+ lines.append("</success_template>")
406
+
407
+ return "\n".join(lines)