chuk-ai-session-manager 0.3__py3-none-any.whl → 0.4.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.
@@ -37,7 +37,8 @@ async def build_prompt_from_session(
37
37
  max_tokens: Optional[int] = None,
38
38
  model: str = "gpt-3.5-turbo",
39
39
  include_parent_context: bool = False,
40
- current_query: Optional[str] = None
40
+ current_query: Optional[str] = None,
41
+ max_history: int = 5 # Add this parameter for conversation strategy
41
42
  ) -> List[Dict[str, str]]:
42
43
  """
43
44
  Build a prompt for the next LLM call from a Session asynchronously.
@@ -49,6 +50,7 @@ async def build_prompt_from_session(
49
50
  model: Model to use for token counting
50
51
  include_parent_context: Whether to include context from parent sessions
51
52
  current_query: Current user query for relevance-based context selection
53
+ max_history: Maximum number of messages to include for conversation strategy
52
54
 
53
55
  Returns:
54
56
  A list of message dictionaries suitable for LLM API calls
@@ -72,7 +74,7 @@ async def build_prompt_from_session(
72
74
  elif strategy == PromptStrategy.TOOL_FOCUSED:
73
75
  return await _build_tool_focused_prompt(session)
74
76
  elif strategy == PromptStrategy.CONVERSATION:
75
- return await _build_conversation_prompt(session, max_history=5)
77
+ return await _build_conversation_prompt(session, max_history)
76
78
  elif strategy == PromptStrategy.HIERARCHICAL:
77
79
  return await _build_hierarchical_prompt(session, include_parent_context)
78
80
  else:
@@ -112,7 +114,7 @@ async def _build_minimal_prompt(session: Session) -> List[Dict[str, str]]:
112
114
 
113
115
  if assistant_msg is None:
114
116
  # Only the user message exists so far
115
- return [{"role": "user", "content": first_user.message}] if first_user else []
117
+ return [{"role": "user", "content": _extract_content(first_user.message)}] if first_user else []
116
118
 
117
119
  # Children of that assistant
118
120
  children = [
@@ -126,11 +128,7 @@ async def _build_minimal_prompt(session: Session) -> List[Dict[str, str]]:
126
128
  # Assemble prompt
127
129
  prompt: List[Dict[str, str]] = []
128
130
  if first_user:
129
- # Handle both string messages and dict messages
130
- user_content = first_user.message
131
- if isinstance(user_content, dict) and "content" in user_content:
132
- user_content = user_content["content"]
133
- prompt.append({"role": "user", "content": user_content})
131
+ prompt.append({"role": "user", "content": _extract_content(first_user.message)})
134
132
 
135
133
  # ALWAYS add the assistant marker - but strip its free text
136
134
  prompt.append({"role": "assistant", "content": None})
@@ -166,6 +164,24 @@ async def _build_minimal_prompt(session: Session) -> List[Dict[str, str]]:
166
164
  return prompt
167
165
 
168
166
 
167
+ def _extract_content(message: Any) -> str:
168
+ """
169
+ Extract content string from a message that could be a string or dict.
170
+
171
+ Args:
172
+ message: The message content (string, dict, or other)
173
+
174
+ Returns:
175
+ The extracted content as a string
176
+ """
177
+ if isinstance(message, str):
178
+ return message
179
+ elif isinstance(message, dict) and "content" in message:
180
+ return message["content"]
181
+ else:
182
+ return str(message)
183
+
184
+
169
185
  async def _build_task_focused_prompt(session: Session) -> List[Dict[str, str]]:
170
186
  """
171
187
  Build a task-focused prompt.
@@ -201,17 +217,11 @@ async def _build_task_focused_prompt(session: Session) -> List[Dict[str, str]]:
201
217
  prompt = []
202
218
 
203
219
  # Always include the first user message (the main task)
204
- first_content = first_user.message
205
- if isinstance(first_content, dict) and "content" in first_content:
206
- first_content = first_content["content"]
207
- prompt.append({"role": "user", "content": first_content})
220
+ prompt.append({"role": "user", "content": _extract_content(first_user.message)})
208
221
 
209
222
  # Include the latest user message if different from the first
210
223
  if latest_user and latest_user.id != first_user.id:
211
- latest_content = latest_user.message
212
- if isinstance(latest_content, dict) and "content" in latest_content:
213
- latest_content = latest_content["content"]
214
- prompt.append({"role": "user", "content": latest_content})
224
+ prompt.append({"role": "user", "content": _extract_content(latest_user.message)})
215
225
 
216
226
  # Include assistant response placeholder
217
227
  if assistant_msg:
@@ -274,10 +284,7 @@ async def _build_tool_focused_prompt(session: Session) -> List[Dict[str, str]]:
274
284
  prompt = []
275
285
 
276
286
  # Include user message
277
- user_content = latest_user.message
278
- if isinstance(user_content, dict) and "content" in user_content:
279
- user_content = user_content["content"]
280
- prompt.append({"role": "user", "content": user_content})
287
+ prompt.append({"role": "user", "content": _extract_content(latest_user.message)})
281
288
 
282
289
  # Include assistant placeholder
283
290
  if assistant_msg:
@@ -334,17 +341,17 @@ async def _build_conversation_prompt(
334
341
 
335
342
  # Build the conversation history
336
343
  prompt = []
337
- for msg in recent_messages:
344
+ for i, msg in enumerate(recent_messages):
338
345
  role = "user" if msg.source == EventSource.USER else "assistant"
339
- content = msg.message
346
+ content = _extract_content(msg.message)
340
347
 
341
- # Handle different message formats
342
- if isinstance(content, dict) and "content" in content:
343
- content = content["content"]
344
-
345
- # For the last assistant message, set content to None
346
- if role == "assistant" and msg == recent_messages[-1] and msg.source != EventSource.USER:
347
- content = None
348
+ # For the last assistant message, set content to None and add tool calls
349
+ if (role == "assistant" and
350
+ msg == recent_messages[-1] and
351
+ msg.source != EventSource.USER):
352
+
353
+ # Add the message first with None content
354
+ prompt.append({"role": role, "content": None})
348
355
 
349
356
  # Add tool call results for this assistant message
350
357
  tool_calls = [
@@ -352,9 +359,6 @@ async def _build_conversation_prompt(
352
359
  if e.type == EventType.TOOL_CALL and e.metadata.get("parent_event_id") == msg.id
353
360
  ]
354
361
 
355
- # Add the message first, then tools
356
- prompt.append({"role": role, "content": content})
357
-
358
362
  # Add tool results
359
363
  for tc in tool_calls:
360
364
  if isinstance(tc.message, dict):
@@ -366,11 +370,9 @@ async def _build_conversation_prompt(
366
370
  "name": tool_name,
367
371
  "content": json.dumps(tool_result, default=str),
368
372
  })
369
-
370
- # Skip adding this message again
371
- continue
372
-
373
- prompt.append({"role": role, "content": content})
373
+ else:
374
+ # Regular message
375
+ prompt.append({"role": role, "content": content})
374
376
 
375
377
  return prompt
376
378
 
@@ -391,32 +393,38 @@ async def _build_hierarchical_prompt(
391
393
 
392
394
  # If parent context is enabled and session has a parent
393
395
  if include_parent_context and session.parent_id:
394
- # Get the storage backend and create store
395
- backend = get_backend()
396
- store = ChukSessionsStore(backend)
397
- parent = await store.get(session.parent_id)
398
-
399
- if parent:
400
- # Find the most recent summary in parent
401
- summary_event = next(
402
- (e for e in reversed(parent.events)
403
- if e.type == EventType.SUMMARY),
404
- None
405
- )
396
+ try:
397
+ # Get the storage backend and create store
398
+ backend = get_backend()
399
+ store = ChukSessionsStore(backend)
400
+ parent = await store.get(session.parent_id)
406
401
 
407
- if summary_event:
408
- # Extract summary content
409
- summary_content = summary_event.message
410
- if isinstance(summary_content, dict) and "note" in summary_content:
411
- summary_content = summary_content["note"]
412
- elif isinstance(summary_content, dict) and "content" in summary_content:
413
- summary_content = summary_content["content"]
414
-
415
- # Add parent context at the beginning
416
- prompt.insert(0, {
417
- "role": "system",
418
- "content": f"Context from previous conversation: {summary_content}"
419
- })
402
+ if parent:
403
+ # Find the most recent summary in parent
404
+ summary_event = next(
405
+ (e for e in reversed(parent.events)
406
+ if e.type == EventType.SUMMARY),
407
+ None
408
+ )
409
+
410
+ if summary_event:
411
+ # Extract summary content
412
+ summary_content = summary_event.message
413
+ if isinstance(summary_content, dict) and "note" in summary_content:
414
+ summary_content = summary_content["note"]
415
+ elif isinstance(summary_content, dict) and "content" in summary_content:
416
+ summary_content = summary_content["content"]
417
+ else:
418
+ summary_content = str(summary_content)
419
+
420
+ # Add parent context at the beginning
421
+ prompt.insert(0, {
422
+ "role": "system",
423
+ "content": f"Context from previous conversation: {summary_content}"
424
+ })
425
+ except Exception as e:
426
+ # If we can't load parent context, just continue with minimal prompt
427
+ logger.warning(f"Could not load parent context for session {session.parent_id}: {e}")
420
428
 
421
429
  return prompt
422
430
 
@@ -1,4 +1,4 @@
1
- # chuk_ai_session_manager/session_storage.py
1
+ # src/chuk_ai_session_manager/session_storage.py
2
2
  """
3
3
  CHUK Sessions storage backend for AI Session Manager.
4
4
 
@@ -11,7 +11,18 @@ import json
11
11
  import logging
12
12
  from typing import Any, Dict, List, Optional
13
13
 
14
- from chuk_sessions import SessionManager as ChukSessionManager
14
+ # Import the correct class from chuk_sessions
15
+ # We need to determine the correct import based on what's actually available
16
+ try:
17
+ from chuk_sessions import SessionManager as ChukSessionManager
18
+ except ImportError as e:
19
+ raise ImportError(
20
+ f"Cannot import SessionManager from chuk_sessions: {e}\n"
21
+ "Please ensure chuk_sessions is properly installed with: uv add chuk-sessions\n"
22
+ "Or check the available classes in chuk_sessions by running:\n"
23
+ "python -c \"import chuk_sessions; print(dir(chuk_sessions))\""
24
+ ) from e
25
+
15
26
  from chuk_ai_session_manager.models.session import Session
16
27
 
17
28
  logger = logging.getLogger(__name__)
@@ -45,19 +56,21 @@ class SessionStorage:
45
56
  return self._cache[session_id]
46
57
 
47
58
  try:
48
- if not await self.chuk.validate_session(session_id):
49
- return None
50
-
59
+ # Get session info from CHUK Sessions
51
60
  info = await self.chuk.get_session_info(session_id)
52
61
  if not info:
53
62
  return None
54
63
 
64
+ # Check if it's an AI session manager session
55
65
  custom_metadata = info.get('custom_metadata', {})
56
- ai_session_json = custom_metadata.get('ai_session_data')
66
+ if custom_metadata.get('session_type') != 'ai_session_manager':
67
+ return None
57
68
 
69
+ ai_session_json = custom_metadata.get('ai_session_data')
58
70
  if not ai_session_json:
59
71
  return None
60
72
 
73
+ # Parse the JSON data
61
74
  session_data = json.loads(ai_session_json)
62
75
  ai_session = Session.model_validate(session_data)
63
76
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: chuk-ai-session-manager
3
- Version: 0.3
3
+ Version: 0.4.1
4
4
  Summary: Session manager for AI applications
5
5
  Requires-Python: >=3.11
6
6
  Description-Content-Type: text/markdown
@@ -14,6 +14,7 @@ Requires-Dist: redis>=4.0.0; extra == "redis"
14
14
  Provides-Extra: dev
15
15
  Requires-Dist: pytest>=7.0.0; extra == "dev"
16
16
  Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
17
+ Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
17
18
  Requires-Dist: redis>=4.0.0; extra == "dev"
18
19
  Requires-Dist: black>=23.0.0; extra == "dev"
19
20
  Requires-Dist: isort>=5.12.0; extra == "dev"
@@ -311,36 +312,6 @@ pip install chuk-ai-session-manager
311
312
  | **Production Ready** | Requires additional work | Built for production |
312
313
  | **Learning Curve** | Steep | 5 minutes to productivity |
313
314
 
314
- ## 🚀 Migration Guides
315
-
316
- ### From LangChain Memory
317
- ```python
318
- # Old LangChain way
319
- from langchain.memory import ConversationBufferMemory
320
- memory = ConversationBufferMemory()
321
- memory.save_context({"input": "Hi"}, {"output": "Hello"})
322
-
323
- # New CHUK way (much simpler!)
324
- from chuk_ai_session_manager import track_conversation
325
- await track_conversation("Hi", "Hello")
326
- ```
327
-
328
- ### From Manual Session Management
329
- ```python
330
- # Old manual way
331
- conversations = {}
332
- def save_conversation(user_id, message, response):
333
- if user_id not in conversations:
334
- conversations[user_id] = []
335
- conversations[user_id].append({"user": message, "ai": response})
336
-
337
- # New CHUK way
338
- from chuk_ai_session_manager import SessionManager
339
- sm = SessionManager(session_id=user_id)
340
- await sm.user_says(message)
341
- await sm.ai_responds(response)
342
- ```
343
-
344
315
  ## 📖 More Examples
345
316
 
346
317
  Check out the `/examples` directory for complete working examples:
@@ -362,16 +333,9 @@ Check out the `/examples` directory for complete working examples:
362
333
  - ✅ Complete conversation analytics and observability
363
334
  - ✅ Framework-agnostic solution that works with any LLM library
364
335
 
365
- **Consider alternatives if you:**
366
- - ❌ Only need basic in-memory conversation history
367
- - ❌ Are locked into a specific framework (LangChain, etc.)
368
- - ❌ Don't need cost tracking or analytics
369
- - ❌ Are building simple, stateless AI applications
370
-
371
336
  ## 🤝 Community & Support
372
337
 
373
338
  - 📖 **Documentation**: [Full docs with tutorials](link-to-docs)
374
- - 💬 **Discord**: Join our community for help and discussions
375
339
  - 🐛 **Issues**: Report bugs on GitHub
376
340
  - 💡 **Feature Requests**: Suggest new features
377
341
  - 📧 **Support**: enterprise@chuk.dev for production support
@@ -0,0 +1,22 @@
1
+ chuk_ai_session_manager/__init__.py,sha256=Kg8_fqwkV88b0f9H8Q45MDqaZdHHTo3Wf0Oko89DhHg,10217
2
+ chuk_ai_session_manager/exceptions.py,sha256=WqrrUZuOAiUmz7tKnSnk0y222U_nV9a8LyaXLayn2fg,4420
3
+ chuk_ai_session_manager/infinite_conversation.py,sha256=7j3caMnsX27M5rjj4oOkqiy_2AfcupWwsAWRflnKiSo,12092
4
+ chuk_ai_session_manager/sample_tools.py,sha256=U-jTGveTJ95uSnA4jB30fJQJG3K-TGxN9jcOY6qVHZQ,8179
5
+ chuk_ai_session_manager/session_aware_tool_processor.py,sha256=iVe3d-qfp5QGkdNrgfZeRYoOjd8nLZ0g6K7HW1thFE8,7274
6
+ chuk_ai_session_manager/session_manager.py,sha256=AauyE2XTqAM53fGxpJD73nVwxB3cWH0jMlBXakYTAD0,29195
7
+ chuk_ai_session_manager/session_prompt_builder.py,sha256=Jeg_MWses_hFtHtDL7ZQl6EdSNVmVIIrLDrWEoPumfM,17613
8
+ chuk_ai_session_manager/session_storage.py,sha256=wb6SPwOKVoov2cGmK6DuorpPM7xbjnCRHrA6VTvG7GE,6427
9
+ chuk_ai_session_manager/api/__init__.py,sha256=Lo_BoDW2rSn0Zw-CbjahOxc6ykjjTpucxHZo5FA2Gnc,41
10
+ chuk_ai_session_manager/api/simple_api.py,sha256=aUDH2KzkI_M2eUiW9DAQo2KmUYaVa5u25-gSIF3PzGM,10504
11
+ chuk_ai_session_manager/models/__init__.py,sha256=H1rRuDQDRf821JPUWUn5Zgwvc5BAqcEGekkHEmX-IgE,1167
12
+ chuk_ai_session_manager/models/event_source.py,sha256=mn_D16sXMa6nAX-5BzssygJPz6VF24GRe-3IaH7bTnI,196
13
+ chuk_ai_session_manager/models/event_type.py,sha256=TPPvAz-PlXVtrwXDNVFVnhdt1yEfgDGmKDGt8ArYcTk,275
14
+ chuk_ai_session_manager/models/session.py,sha256=yUPuu6yt8HbBebkIejpgSDGTLhZjtLOxFRkWG2C6XKM,12193
15
+ chuk_ai_session_manager/models/session_event.py,sha256=RTghC9_sDHzD8qdgEYCoclJzpVo8cE6B2f7jlsDOVr0,9006
16
+ chuk_ai_session_manager/models/session_metadata.py,sha256=KFG7lc_E0BQTP2OD9Y529elVGJXppDUMqz8vVONW0rw,1510
17
+ chuk_ai_session_manager/models/session_run.py,sha256=uhMM4-WSrqOUsiWQPnyakInd-foZhxI-YnSHSWiZZwE,4369
18
+ chuk_ai_session_manager/models/token_usage.py,sha256=M9Qwmeb2woILaSRwA2SIAiG-sIwC3cL_1H-y3NjW5Ik,11436
19
+ chuk_ai_session_manager-0.4.1.dist-info/METADATA,sha256=0PvxzwOw2HG8XujmXkMInNVAlrhF149rxyLzXTUROAg,11136
20
+ chuk_ai_session_manager-0.4.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
21
+ chuk_ai_session_manager-0.4.1.dist-info/top_level.txt,sha256=5RinqD0v-niHuLYePUREX4gEWTlrpgtUg0RfexVRBMk,24
22
+ chuk_ai_session_manager-0.4.1.dist-info/RECORD,,
File without changes