kweaver-dolphin 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.
Files changed (199) hide show
  1. DolphinLanguageSDK/__init__.py +58 -0
  2. dolphin/__init__.py +62 -0
  3. dolphin/cli/__init__.py +20 -0
  4. dolphin/cli/args/__init__.py +9 -0
  5. dolphin/cli/args/parser.py +567 -0
  6. dolphin/cli/builtin_agents/__init__.py +22 -0
  7. dolphin/cli/commands/__init__.py +4 -0
  8. dolphin/cli/interrupt/__init__.py +8 -0
  9. dolphin/cli/interrupt/handler.py +205 -0
  10. dolphin/cli/interrupt/keyboard.py +82 -0
  11. dolphin/cli/main.py +49 -0
  12. dolphin/cli/multimodal/__init__.py +34 -0
  13. dolphin/cli/multimodal/clipboard.py +327 -0
  14. dolphin/cli/multimodal/handler.py +249 -0
  15. dolphin/cli/multimodal/image_processor.py +214 -0
  16. dolphin/cli/multimodal/input_parser.py +149 -0
  17. dolphin/cli/runner/__init__.py +8 -0
  18. dolphin/cli/runner/runner.py +989 -0
  19. dolphin/cli/ui/__init__.py +10 -0
  20. dolphin/cli/ui/console.py +2795 -0
  21. dolphin/cli/ui/input.py +340 -0
  22. dolphin/cli/ui/layout.py +425 -0
  23. dolphin/cli/ui/stream_renderer.py +302 -0
  24. dolphin/cli/utils/__init__.py +8 -0
  25. dolphin/cli/utils/helpers.py +135 -0
  26. dolphin/cli/utils/version.py +49 -0
  27. dolphin/core/__init__.py +107 -0
  28. dolphin/core/agent/__init__.py +10 -0
  29. dolphin/core/agent/agent_state.py +69 -0
  30. dolphin/core/agent/base_agent.py +970 -0
  31. dolphin/core/code_block/__init__.py +0 -0
  32. dolphin/core/code_block/agent_init_block.py +0 -0
  33. dolphin/core/code_block/assign_block.py +98 -0
  34. dolphin/core/code_block/basic_code_block.py +1865 -0
  35. dolphin/core/code_block/explore_block.py +1327 -0
  36. dolphin/core/code_block/explore_block_v2.py +712 -0
  37. dolphin/core/code_block/explore_strategy.py +672 -0
  38. dolphin/core/code_block/judge_block.py +220 -0
  39. dolphin/core/code_block/prompt_block.py +32 -0
  40. dolphin/core/code_block/skill_call_deduplicator.py +291 -0
  41. dolphin/core/code_block/tool_block.py +129 -0
  42. dolphin/core/common/__init__.py +17 -0
  43. dolphin/core/common/constants.py +176 -0
  44. dolphin/core/common/enums.py +1173 -0
  45. dolphin/core/common/exceptions.py +133 -0
  46. dolphin/core/common/multimodal.py +539 -0
  47. dolphin/core/common/object_type.py +165 -0
  48. dolphin/core/common/output_format.py +432 -0
  49. dolphin/core/common/types.py +36 -0
  50. dolphin/core/config/__init__.py +16 -0
  51. dolphin/core/config/global_config.py +1289 -0
  52. dolphin/core/config/ontology_config.py +133 -0
  53. dolphin/core/context/__init__.py +12 -0
  54. dolphin/core/context/context.py +1580 -0
  55. dolphin/core/context/context_manager.py +161 -0
  56. dolphin/core/context/var_output.py +82 -0
  57. dolphin/core/context/variable_pool.py +356 -0
  58. dolphin/core/context_engineer/__init__.py +41 -0
  59. dolphin/core/context_engineer/config/__init__.py +5 -0
  60. dolphin/core/context_engineer/config/settings.py +402 -0
  61. dolphin/core/context_engineer/core/__init__.py +7 -0
  62. dolphin/core/context_engineer/core/budget_manager.py +327 -0
  63. dolphin/core/context_engineer/core/context_assembler.py +583 -0
  64. dolphin/core/context_engineer/core/context_manager.py +637 -0
  65. dolphin/core/context_engineer/core/tokenizer_service.py +260 -0
  66. dolphin/core/context_engineer/example/incremental_example.py +267 -0
  67. dolphin/core/context_engineer/example/traditional_example.py +334 -0
  68. dolphin/core/context_engineer/services/__init__.py +5 -0
  69. dolphin/core/context_engineer/services/compressor.py +399 -0
  70. dolphin/core/context_engineer/utils/__init__.py +6 -0
  71. dolphin/core/context_engineer/utils/context_utils.py +441 -0
  72. dolphin/core/context_engineer/utils/message_formatter.py +270 -0
  73. dolphin/core/context_engineer/utils/token_utils.py +139 -0
  74. dolphin/core/coroutine/__init__.py +15 -0
  75. dolphin/core/coroutine/context_snapshot.py +154 -0
  76. dolphin/core/coroutine/context_snapshot_profile.py +922 -0
  77. dolphin/core/coroutine/context_snapshot_store.py +268 -0
  78. dolphin/core/coroutine/execution_frame.py +145 -0
  79. dolphin/core/coroutine/execution_state_registry.py +161 -0
  80. dolphin/core/coroutine/resume_handle.py +101 -0
  81. dolphin/core/coroutine/step_result.py +101 -0
  82. dolphin/core/executor/__init__.py +18 -0
  83. dolphin/core/executor/debug_controller.py +630 -0
  84. dolphin/core/executor/dolphin_executor.py +1063 -0
  85. dolphin/core/executor/executor.py +624 -0
  86. dolphin/core/flags/__init__.py +27 -0
  87. dolphin/core/flags/definitions.py +49 -0
  88. dolphin/core/flags/manager.py +113 -0
  89. dolphin/core/hook/__init__.py +95 -0
  90. dolphin/core/hook/expression_evaluator.py +499 -0
  91. dolphin/core/hook/hook_dispatcher.py +380 -0
  92. dolphin/core/hook/hook_types.py +248 -0
  93. dolphin/core/hook/isolated_variable_pool.py +284 -0
  94. dolphin/core/interfaces.py +53 -0
  95. dolphin/core/llm/__init__.py +0 -0
  96. dolphin/core/llm/llm.py +495 -0
  97. dolphin/core/llm/llm_call.py +100 -0
  98. dolphin/core/llm/llm_client.py +1285 -0
  99. dolphin/core/llm/message_sanitizer.py +120 -0
  100. dolphin/core/logging/__init__.py +20 -0
  101. dolphin/core/logging/logger.py +526 -0
  102. dolphin/core/message/__init__.py +8 -0
  103. dolphin/core/message/compressor.py +749 -0
  104. dolphin/core/parser/__init__.py +8 -0
  105. dolphin/core/parser/parser.py +405 -0
  106. dolphin/core/runtime/__init__.py +10 -0
  107. dolphin/core/runtime/runtime_graph.py +926 -0
  108. dolphin/core/runtime/runtime_instance.py +446 -0
  109. dolphin/core/skill/__init__.py +14 -0
  110. dolphin/core/skill/context_retention.py +157 -0
  111. dolphin/core/skill/skill_function.py +686 -0
  112. dolphin/core/skill/skill_matcher.py +282 -0
  113. dolphin/core/skill/skillkit.py +700 -0
  114. dolphin/core/skill/skillset.py +72 -0
  115. dolphin/core/trajectory/__init__.py +10 -0
  116. dolphin/core/trajectory/recorder.py +189 -0
  117. dolphin/core/trajectory/trajectory.py +522 -0
  118. dolphin/core/utils/__init__.py +9 -0
  119. dolphin/core/utils/cache_kv.py +212 -0
  120. dolphin/core/utils/tools.py +340 -0
  121. dolphin/lib/__init__.py +93 -0
  122. dolphin/lib/debug/__init__.py +8 -0
  123. dolphin/lib/debug/visualizer.py +409 -0
  124. dolphin/lib/memory/__init__.py +28 -0
  125. dolphin/lib/memory/async_processor.py +220 -0
  126. dolphin/lib/memory/llm_calls.py +195 -0
  127. dolphin/lib/memory/manager.py +78 -0
  128. dolphin/lib/memory/sandbox.py +46 -0
  129. dolphin/lib/memory/storage.py +245 -0
  130. dolphin/lib/memory/utils.py +51 -0
  131. dolphin/lib/ontology/__init__.py +12 -0
  132. dolphin/lib/ontology/basic/__init__.py +0 -0
  133. dolphin/lib/ontology/basic/base.py +102 -0
  134. dolphin/lib/ontology/basic/concept.py +130 -0
  135. dolphin/lib/ontology/basic/object.py +11 -0
  136. dolphin/lib/ontology/basic/relation.py +63 -0
  137. dolphin/lib/ontology/datasource/__init__.py +27 -0
  138. dolphin/lib/ontology/datasource/datasource.py +66 -0
  139. dolphin/lib/ontology/datasource/oracle_datasource.py +338 -0
  140. dolphin/lib/ontology/datasource/sql.py +845 -0
  141. dolphin/lib/ontology/mapping.py +177 -0
  142. dolphin/lib/ontology/ontology.py +733 -0
  143. dolphin/lib/ontology/ontology_context.py +16 -0
  144. dolphin/lib/ontology/ontology_manager.py +107 -0
  145. dolphin/lib/skill_results/__init__.py +31 -0
  146. dolphin/lib/skill_results/cache_backend.py +559 -0
  147. dolphin/lib/skill_results/result_processor.py +181 -0
  148. dolphin/lib/skill_results/result_reference.py +179 -0
  149. dolphin/lib/skill_results/skillkit_hook.py +324 -0
  150. dolphin/lib/skill_results/strategies.py +328 -0
  151. dolphin/lib/skill_results/strategy_registry.py +150 -0
  152. dolphin/lib/skillkits/__init__.py +44 -0
  153. dolphin/lib/skillkits/agent_skillkit.py +155 -0
  154. dolphin/lib/skillkits/cognitive_skillkit.py +82 -0
  155. dolphin/lib/skillkits/env_skillkit.py +250 -0
  156. dolphin/lib/skillkits/mcp_adapter.py +616 -0
  157. dolphin/lib/skillkits/mcp_skillkit.py +771 -0
  158. dolphin/lib/skillkits/memory_skillkit.py +650 -0
  159. dolphin/lib/skillkits/noop_skillkit.py +31 -0
  160. dolphin/lib/skillkits/ontology_skillkit.py +89 -0
  161. dolphin/lib/skillkits/plan_act_skillkit.py +452 -0
  162. dolphin/lib/skillkits/resource/__init__.py +52 -0
  163. dolphin/lib/skillkits/resource/models/__init__.py +6 -0
  164. dolphin/lib/skillkits/resource/models/skill_config.py +109 -0
  165. dolphin/lib/skillkits/resource/models/skill_meta.py +127 -0
  166. dolphin/lib/skillkits/resource/resource_skillkit.py +393 -0
  167. dolphin/lib/skillkits/resource/skill_cache.py +215 -0
  168. dolphin/lib/skillkits/resource/skill_loader.py +395 -0
  169. dolphin/lib/skillkits/resource/skill_validator.py +406 -0
  170. dolphin/lib/skillkits/resource_skillkit.py +11 -0
  171. dolphin/lib/skillkits/search_skillkit.py +163 -0
  172. dolphin/lib/skillkits/sql_skillkit.py +274 -0
  173. dolphin/lib/skillkits/system_skillkit.py +509 -0
  174. dolphin/lib/skillkits/vm_skillkit.py +65 -0
  175. dolphin/lib/utils/__init__.py +9 -0
  176. dolphin/lib/utils/data_process.py +207 -0
  177. dolphin/lib/utils/handle_progress.py +178 -0
  178. dolphin/lib/utils/security.py +139 -0
  179. dolphin/lib/utils/text_retrieval.py +462 -0
  180. dolphin/lib/vm/__init__.py +11 -0
  181. dolphin/lib/vm/env_executor.py +895 -0
  182. dolphin/lib/vm/python_session_manager.py +453 -0
  183. dolphin/lib/vm/vm.py +610 -0
  184. dolphin/sdk/__init__.py +60 -0
  185. dolphin/sdk/agent/__init__.py +12 -0
  186. dolphin/sdk/agent/agent_factory.py +236 -0
  187. dolphin/sdk/agent/dolphin_agent.py +1106 -0
  188. dolphin/sdk/api/__init__.py +4 -0
  189. dolphin/sdk/runtime/__init__.py +8 -0
  190. dolphin/sdk/runtime/env.py +363 -0
  191. dolphin/sdk/skill/__init__.py +10 -0
  192. dolphin/sdk/skill/global_skills.py +706 -0
  193. dolphin/sdk/skill/traditional_toolkit.py +260 -0
  194. kweaver_dolphin-0.1.0.dist-info/METADATA +521 -0
  195. kweaver_dolphin-0.1.0.dist-info/RECORD +199 -0
  196. kweaver_dolphin-0.1.0.dist-info/WHEEL +5 -0
  197. kweaver_dolphin-0.1.0.dist-info/entry_points.txt +27 -0
  198. kweaver_dolphin-0.1.0.dist-info/licenses/LICENSE.txt +201 -0
  199. kweaver_dolphin-0.1.0.dist-info/top_level.txt +2 -0
@@ -0,0 +1,453 @@
1
+ """
2
+ Python Session Manager with Pickle Support
3
+
4
+ This module manages stateful Python execution sessions, allowing variables
5
+ and state to persist across multiple executions like Jupyter notebooks.
6
+ """
7
+
8
+ import ast
9
+ import logging
10
+ from typing import Dict, Any, Optional
11
+ import string
12
+ import random
13
+ from datetime import datetime, timedelta
14
+ from dataclasses import dataclass, field
15
+
16
+ logger = logging.getLogger(__name__)
17
+
18
+
19
+ @dataclass
20
+ class PythonSession:
21
+ """Represents a Python execution session with persistent state"""
22
+
23
+ session_id: str
24
+ created_at: datetime
25
+ last_accessed: datetime
26
+ namespace: Dict[str, Any] = field(default_factory=dict)
27
+ execution_count: int = 0
28
+ remote_pickle_path: Optional[str] = None
29
+
30
+ def is_expired(self, timeout_hours: int = 24) -> bool:
31
+ """Check if session has expired"""
32
+ return datetime.now() - self.last_accessed > timedelta(hours=timeout_hours)
33
+
34
+ def touch(self):
35
+ """Update last accessed time"""
36
+ self.last_accessed = datetime.now()
37
+
38
+
39
+ class PythonSessionManager:
40
+ """
41
+ Manages Python sessions with state persistence using pickle.
42
+
43
+ Sessions maintain variable state across multiple executions,
44
+ similar to Jupyter notebook cells.
45
+ """
46
+
47
+ def __init__(self, remote_session_dir: str = "/tmp/python_sessions"):
48
+ """
49
+ Initialize the session manager.
50
+
51
+ Args:
52
+ remote_session_dir: Directory on remote VM to store session pickles
53
+ """
54
+ self.remote_session_dir = remote_session_dir
55
+ self.sessions: Dict[str, PythonSession] = {}
56
+ self._cleanup_interval_calls = 0
57
+
58
+ def get_or_create_session(self, session_id: str = None) -> PythonSession:
59
+ """
60
+ Get an existing session or create a new one.
61
+
62
+ Args:
63
+ session_id: Session identifier. If None, creates a new session.
64
+
65
+ Returns:
66
+ PythonSession object
67
+ """
68
+ # Periodic cleanup every 100 calls
69
+ self._cleanup_interval_calls += 1
70
+ if self._cleanup_interval_calls >= 100:
71
+ self.cleanup_expired_sessions()
72
+ self._cleanup_interval_calls = 0
73
+
74
+ if session_id is None:
75
+ session_id = self._generate_session_id()
76
+
77
+ if session_id not in self.sessions:
78
+ session = PythonSession(
79
+ session_id=session_id,
80
+ created_at=datetime.now(),
81
+ last_accessed=datetime.now(),
82
+ remote_pickle_path=f"{self.remote_session_dir}/session_{session_id}.pkl",
83
+ )
84
+ self.sessions[session_id] = session
85
+ logger.info(f"Created new Python session: {session_id}")
86
+ else:
87
+ session = self.sessions[session_id]
88
+ session.touch()
89
+
90
+ return session
91
+
92
+ def _auto_capture_last_expr(self, code: str) -> str:
93
+ """
94
+ Automatically capture the last expression's value as return_value.
95
+
96
+ If the last statement in the code is a pure expression (not an assignment,
97
+ function definition, etc.), wrap it to assign to return_value.
98
+ This makes the behavior similar to Jupyter notebooks.
99
+
100
+ Args:
101
+ code: User's Python code
102
+
103
+ Returns:
104
+ Modified code with last expression captured, or original code if not applicable
105
+ """
106
+ if not code or not code.strip():
107
+ return code
108
+
109
+ try:
110
+ tree = ast.parse(code)
111
+ if not tree.body:
112
+ return code
113
+
114
+ last_stmt = tree.body[-1]
115
+
116
+ # Only process if the last statement is an expression (not assignment, etc.)
117
+ if isinstance(last_stmt, ast.Expr):
118
+ # Get the source lines
119
+ lines = code.split('\n')
120
+
121
+ # Find the start line of the last expression (1-indexed in AST)
122
+ last_expr_start = last_stmt.lineno - 1 # Convert to 0-indexed
123
+
124
+ # Get the expression text (may span multiple lines)
125
+ expr_lines = lines[last_expr_start:]
126
+ expr_text = '\n'.join(expr_lines).strip()
127
+
128
+ # Build new code: everything before + wrapped expression
129
+ prefix_lines = lines[:last_expr_start]
130
+ prefix = '\n'.join(prefix_lines)
131
+
132
+ # Wrap the expression to capture its value
133
+ wrapped_expr = f"return_value = ({expr_text})"
134
+
135
+ if prefix.strip():
136
+ return f"{prefix}\n{wrapped_expr}"
137
+ else:
138
+ return wrapped_expr
139
+
140
+ except SyntaxError:
141
+ # If code has syntax errors, return as-is and let the execution handle it
142
+ pass
143
+ except Exception as e:
144
+ logger.debug(f"Could not auto-capture last expression: {e}")
145
+
146
+ return code
147
+
148
+ def prepare_session_code(
149
+ self,
150
+ code: str,
151
+ session: PythonSession,
152
+ varDict: Optional[Dict[str, Any]] = None,
153
+ ) -> str:
154
+ """
155
+ Prepare Python code with session management.
156
+
157
+ This wraps the user code with pickle load/save operations
158
+ to maintain state across executions.
159
+
160
+ Args:
161
+ code: User's Python code
162
+ session: Session object
163
+ varDict: Optional variables to inject
164
+
165
+ Returns:
166
+ Complete Python code with session management
167
+ """
168
+ session.execution_count += 1
169
+
170
+ session_code = []
171
+
172
+ # Header
173
+ session_code.append("# -*- coding: utf-8 -*-")
174
+ session_code.append("import pickle")
175
+ session_code.append("import os")
176
+ session_code.append("import sys")
177
+ session_code.append("import itertools")
178
+ session_code.append("import warnings")
179
+ session_code.append("")
180
+ session_code.append(
181
+ "# Suppress DeprecationWarning for itertools pickle support"
182
+ )
183
+ session_code.append(
184
+ "warnings.filterwarnings('ignore', category=DeprecationWarning, module='.*itertools.*')"
185
+ )
186
+ session_code.append("")
187
+
188
+ # Session info
189
+ session_code.append(f"# Session: {session.session_id}")
190
+ session_code.append(f"# Execution: {session.execution_count}")
191
+ session_code.append("")
192
+
193
+ # Initialize namespace
194
+ session_code.append("# Initialize session namespace")
195
+ session_code.append("__session_namespace = {}")
196
+ session_code.append(
197
+ "__session_pickle_path = '{}'".format(session.remote_pickle_path)
198
+ )
199
+ session_code.append("")
200
+
201
+ # Load existing session state if available
202
+ session_code.append("# Load existing session state")
203
+ session_code.append("if os.path.exists(__session_pickle_path):")
204
+ session_code.append(" try:")
205
+ session_code.append(" with open(__session_pickle_path, 'rb') as f:")
206
+ session_code.append(" __session_data = pickle.load(f)")
207
+ session_code.append(" ")
208
+ session_code.append(
209
+ " # Handle both old format (dict) and new format (dict with variables/modules)"
210
+ )
211
+ session_code.append(
212
+ " if isinstance(__session_data, dict) and 'variables' in __session_data:"
213
+ )
214
+ session_code.append(
215
+ " __session_namespace = __session_data['variables']"
216
+ )
217
+ session_code.append(
218
+ " __session_modules = __session_data.get('modules', {})"
219
+ )
220
+ session_code.append(" else:")
221
+ session_code.append(" # Old format - treat as variables only")
222
+ session_code.append(" __session_namespace = __session_data")
223
+ session_code.append(" __session_modules = {}")
224
+ session_code.append(" ")
225
+ session_code.append(" # Restore modules first")
226
+ session_code.append(" __failed_modules = []")
227
+ session_code.append(
228
+ " for __mod_name, __mod_import_name in __session_modules.items():"
229
+ )
230
+ session_code.append(" try:")
231
+ session_code.append(" __module = __import__(__mod_import_name)")
232
+ session_code.append(" globals()[__mod_name] = __module")
233
+ session_code.append(" except ImportError:")
234
+ session_code.append(" __failed_modules.append(__mod_name)")
235
+ session_code.append(" ")
236
+ session_code.append(" if __failed_modules:")
237
+ session_code.append(
238
+ " print(f'Note: Some modules could not be auto-restored: {\", \".join(__failed_modules)}. Please re-import if needed.')"
239
+ )
240
+ session_code.append(" ")
241
+ session_code.append(" # Restore variables to global namespace")
242
+ session_code.append(
243
+ " for __key, __value in __session_namespace.items():"
244
+ )
245
+ session_code.append(" if not __key.startswith('__'):")
246
+ session_code.append(" globals()[__key] = __value")
247
+ session_code.append(
248
+ " print(f'Session restored: {len(__session_namespace)} variables, {len(__session_modules)} modules loaded')"
249
+ )
250
+ session_code.append(" except Exception as e:")
251
+ session_code.append(
252
+ " print(f'Warning: Could not load session state: {e}')"
253
+ )
254
+ session_code.append(" __session_namespace = {}")
255
+ session_code.append(" __session_modules = {}")
256
+ session_code.append("else:")
257
+ session_code.append(" print('Starting new session')")
258
+ session_code.append("")
259
+
260
+ # Inject varDict if provided
261
+ if varDict:
262
+ session_code.append("# Inject provided variables")
263
+ for key, value in varDict.items():
264
+ # Serialize the value safely
265
+ session_code.append(f"{key} = {repr(value)}")
266
+ session_code.append("")
267
+
268
+ # Add execution counter
269
+ session_code.append("# Execution counter")
270
+ session_code.append(f"__execution_count = {session.execution_count}")
271
+ session_code.append(
272
+ "print(f'[Session {}: Execution #{{__execution_count}}]')".format(
273
+ session.session_id[:8]
274
+ )
275
+ )
276
+ session_code.append("")
277
+
278
+ # Auto-capture last expression value (like Jupyter)
279
+ processed_code = self._auto_capture_last_expr(code)
280
+
281
+ # Execute user code
282
+ session_code.append("# User code execution")
283
+ session_code.append("try:")
284
+ # Indent user code
285
+ for line in processed_code.split("\n"):
286
+ if line.strip():
287
+ session_code.append(f" {line}")
288
+ else:
289
+ session_code.append("")
290
+ session_code.append("except Exception as e:")
291
+ session_code.append(" print(f'Error in user code: {e}')")
292
+ session_code.append(" import traceback")
293
+ session_code.append(" traceback.print_exc()")
294
+ session_code.append("")
295
+
296
+ # Save session state
297
+ session_code.append("# Save session state")
298
+ session_code.append("try:")
299
+ session_code.append(" # Ensure directory exists")
300
+ session_code.append(
301
+ " os.makedirs(os.path.dirname(__session_pickle_path), exist_ok=True)"
302
+ )
303
+ session_code.append(" ")
304
+ session_code.append(" # Collect all non-private variables and modules")
305
+ session_code.append(" __vars_to_save = {}")
306
+ session_code.append(" __modules_to_save = {}")
307
+ session_code.append(" for __key, __value in list(globals().items()):")
308
+ session_code.append(
309
+ " if not __key.startswith('__') and __key not in ['pickle', 'os', 'sys']:"
310
+ )
311
+ session_code.append(" # Handle modules separately")
312
+ session_code.append(
313
+ " if hasattr(__value, '__name__') and hasattr(__value, '__package__'):"
314
+ )
315
+ session_code.append(
316
+ " __modules_to_save[__key] = __value.__name__"
317
+ )
318
+ session_code.append(" else:")
319
+ session_code.append(
320
+ " # Skip user-defined functions as they cause pickle issues"
321
+ )
322
+ session_code.append(
323
+ " if callable(__value) and hasattr(__value, '__name__') and getattr(__value, '__module__', None) == '__main__':"
324
+ )
325
+ session_code.append(" # Skip user-defined functions")
326
+ session_code.append(" pass")
327
+ session_code.append(" else:")
328
+ session_code.append(" # Check if it's an itertools object")
329
+ session_code.append(
330
+ " if type(__value).__module__ == 'itertools':"
331
+ )
332
+ session_code.append(
333
+ " # Skip itertools objects to avoid deprecation warnings"
334
+ )
335
+ session_code.append(" pass")
336
+ session_code.append(" else:")
337
+ session_code.append(" try:")
338
+ session_code.append(
339
+ " # Test if object is pickleable"
340
+ )
341
+ session_code.append(
342
+ " with warnings.catch_warnings():"
343
+ )
344
+ session_code.append(
345
+ " warnings.simplefilter('ignore', DeprecationWarning)"
346
+ )
347
+ session_code.append(" pickle.dumps(__value)")
348
+ session_code.append(
349
+ " __vars_to_save[__key] = __value"
350
+ )
351
+ session_code.append(" except Exception:")
352
+ session_code.append(" # Skip non-pickleable objects")
353
+ session_code.append(" pass")
354
+ session_code.append(" ")
355
+ session_code.append(" # Save variables and modules info")
356
+ session_code.append(" __session_data = {")
357
+ session_code.append(" 'variables': __vars_to_save,")
358
+ session_code.append(" 'modules': __modules_to_save")
359
+ session_code.append(" }")
360
+ session_code.append(" with warnings.catch_warnings():")
361
+ session_code.append(
362
+ " warnings.simplefilter('ignore', DeprecationWarning)"
363
+ )
364
+ session_code.append(" with open(__session_pickle_path, 'wb') as f:")
365
+ session_code.append(" pickle.dump(__session_data, f)")
366
+ session_code.append(
367
+ " print(f'\\nSession saved: {len(__vars_to_save)} variables, {len(__modules_to_save)} modules persisted')"
368
+ )
369
+ session_code.append("except Exception as e:")
370
+ session_code.append(" print(f'Warning: Could not save session state: {e}')")
371
+ session_code.append("")
372
+
373
+ # Print final return value if it exists
374
+ session_code.append("# Output return value if defined")
375
+ session_code.append("if 'return_value' in globals():")
376
+ session_code.append(" print(f'\\nReturn value: {return_value}')")
377
+
378
+ return "\n".join(session_code)
379
+
380
+ def clear_session(self, session_id: str) -> bool:
381
+ """
382
+ Clear a specific session.
383
+
384
+ Args:
385
+ session_id: Session to clear
386
+
387
+ Returns:
388
+ True if session was cleared, False otherwise
389
+ """
390
+ if session_id in self.sessions:
391
+ del self.sessions[session_id]
392
+ logger.info(f"Cleared session: {session_id}")
393
+ return True
394
+ return False
395
+
396
+ def cleanup_expired_sessions(self, timeout_hours: int = 6):
397
+ """
398
+ Remove expired sessions.
399
+
400
+ Args:
401
+ timeout_hours: Sessions older than this are removed
402
+ """
403
+ expired = [
404
+ sid
405
+ for sid, session in self.sessions.items()
406
+ if session.is_expired(timeout_hours)
407
+ ]
408
+
409
+ for sid in expired:
410
+ self.clear_session(sid)
411
+
412
+ if expired:
413
+ logger.info(f"Cleaned up {len(expired)} expired sessions")
414
+
415
+ def get_session_info(self, session_id: str) -> Optional[Dict[str, Any]]:
416
+ """
417
+ Get information about a session.
418
+
419
+ Args:
420
+ session_id: Session identifier
421
+
422
+ Returns:
423
+ Session information dict or None if not found
424
+ """
425
+ if session_id not in self.sessions:
426
+ return None
427
+
428
+ session = self.sessions[session_id]
429
+ return {
430
+ "session_id": session.session_id,
431
+ "created_at": session.created_at.isoformat(),
432
+ "last_accessed": session.last_accessed.isoformat(),
433
+ "execution_count": session.execution_count,
434
+ "variable_count": len(session.namespace),
435
+ "variables": list(session.namespace.keys()),
436
+ }
437
+
438
+ def list_sessions(self) -> Dict[str, Dict[str, Any]]:
439
+ """
440
+ List all active sessions.
441
+
442
+ Returns:
443
+ Dict of session information
444
+ """
445
+ return {sid: self.get_session_info(sid) for sid in self.sessions.keys()}
446
+
447
+ def _generate_session_id(self) -> str:
448
+ """Generate a unique session ID"""
449
+ timestamp = datetime.now().strftime("%Y%m%d%H%M%S")
450
+ random_suffix = "".join(
451
+ random.choices(string.ascii_lowercase + string.digits, k=6)
452
+ )
453
+ return f"ps_{timestamp}_{random_suffix}"