generic-llm-memorizer 0.1.0__tar.gz

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.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 jlesbegu
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,250 @@
1
+ Metadata-Version: 2.4
2
+ Name: generic-llm-memorizer
3
+ Version: 0.1.0
4
+ Summary: A library for managing LLM conversations and context
5
+ Author-email: Your Name <your.email@example.com>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/yourusername/generic-llm-memorizer
8
+ Project-URL: Documentation, https://github.com/yourusername/generic-llm-memorizer#readme
9
+ Project-URL: Repository, https://github.com/yourusername/generic-llm-memorizer
10
+ Keywords: llm,conversation,chat,ai,prompt,context-management
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
16
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
17
+ Requires-Python: >=3.12
18
+ Description-Content-Type: text/markdown
19
+ License-File: LICENSE
20
+ Requires-Dist: pydantic-ai>=1.76.0
21
+ Requires-Dist: pytest>=9.0.2
22
+ Requires-Dist: pytest-asyncio>=1.3.0
23
+ Dynamic: license-file
24
+
25
+ # Generic LLM Memorizer
26
+
27
+ A Python library for managing LLM conversations and context with automatic fact extraction and structured data handling.
28
+
29
+ ## Installation
30
+
31
+ ```bash
32
+ pip install generic-llm-memorizer
33
+ ```
34
+
35
+ ## Usage
36
+
37
+ ### Basic Conversation Management
38
+
39
+ ```python
40
+ from generic_llm_memorizer import Conversation
41
+
42
+ # Create a new conversation
43
+ conv = Conversation(system_prompt="You are a helpful assistant.")
44
+
45
+ # Add messages
46
+ conv.add_user_message("Hello, how are you?")
47
+ conv.add_assistant_message("I'm doing well, thank you! How can I help you today?")
48
+
49
+ # Get conversation history
50
+ messages = conv.get_messages()
51
+
52
+ # Format for LLM API (expects list of dicts with role and content)
53
+ from generic_llm_memorizer import format_conversation_for_prompt
54
+ formatted = format_conversation_for_prompt(conv)
55
+ # Output: [{"role": "system", "content": "You are a helpful assistant."}, ...]
56
+ ```
57
+
58
+ ```python
59
+ from generic_llm_memorizer import Conversation
60
+
61
+ # Create a new conversation
62
+ conv = Conversation(system_prompt="You are a helpful assistant.")
63
+
64
+ # Add messages
65
+ conv.add_user_message("Hello, how are you?")
66
+ conv.add_assistant_message("I'm doing well, thank you! How can I help you today?")
67
+
68
+ # Get conversation history
69
+ messages = conv.get_messages()
70
+
71
+ # Format for LLM API (expects list of dicts with role and content)
72
+ from generic_llm_memorizer import format_conversation_for_prompt
73
+ formatted = format_conversation_for_prompt(conv)
74
+ # Output: [{"role": "system", "content": "You are a helpful assistant."}, ...]
75
+ ```
76
+
77
+ ### Persisting Conversations
78
+
79
+ ```python
80
+ from generic_llm_memorizer import Conversation
81
+
82
+ # Save conversation to JSON
83
+ conv_json = conv.to_json()
84
+ with open("conversation.json", "w") as f:
85
+ f.write(conv_json)
86
+
87
+ # Load conversation from JSON
88
+ with open("conversation.json", "r") as f:
89
+ conv_json = f.read()
90
+ loaded_conv = Conversation.from_json(conv_json)
91
+ ```
92
+
93
+ ```python
94
+ from generic_llm_memorizer import Conversation
95
+
96
+ # Save conversation to JSON
97
+ conv_json = conv.to_json()
98
+ with open("conversation.json", "w") as f:
99
+ f.write(conv_json)
100
+
101
+ # Load conversation from JSON
102
+ with open("conversation.json", "r") as f:
103
+ conv_json = f.read()
104
+ loaded_conv = Conversation.from_json(conv_json)
105
+ ```
106
+
107
+ ### Utility Functions
108
+
109
+ ## Extending with Custom Models
110
+
111
+ You can create your own Pydantic models for domain-specific extractions:
112
+
113
+ ```python
114
+ from pydantic import BaseModel
115
+ from typing import List
116
+ from enum import Enum
117
+
118
+ class Priority(str, Enum):
119
+ LOW = "low"
120
+ MEDIUM = "medium"
121
+ HIGH = "high"
122
+
123
+ class Task(BaseModel):
124
+ title: str
125
+ description: str
126
+ priority: Priority
127
+ assignee: str
128
+
129
+ class TaskList(BaseModel):
130
+ tasks: List[Task]
131
+
132
+ # Use with the generic llm_call function
133
+ tasks = llm_call("Extract tasks from this conversation", TaskList)
134
+ ```
135
+
136
+ ### Advanced Usage with Structured Data Extraction
137
+
138
+ The library now supports a generic `llm_call` function that works with Pydantic models for structured data extraction:
139
+
140
+ ```python
141
+ from typing import Type, TypeVar
142
+ from pydantic import BaseModel
143
+ from generic_llm_memorizer.conversation import Conversation
144
+ from generic_llm_memorizer.memory import Summary, Memory, Fact
145
+
146
+ # Define a generic type variable bounded by BaseModel
147
+ T = TypeVar("T", bound=BaseModel)
148
+
149
+ def llm_call(prompt: str, model_class: Type[T]) -> T:
150
+ """
151
+ Generic LLM call function that returns structured data.
152
+
153
+ Args:
154
+ prompt: The prompt to send to the LLM
155
+ model_class: The Pydantic model class to parse the response into
156
+
157
+ Returns:
158
+ An instance of the specified model_class
159
+ """
160
+ # In a real implementation, this would call an actual LLM API
161
+ # and parse the response into the specified Pydantic model
162
+
163
+ # Mock implementation for demonstration
164
+ if model_class == Summary:
165
+ return Summary(content="Mock summary of the conversation")
166
+ elif model_class == Memory:
167
+ return Memory(facts=[
168
+ Fact(
169
+ content="Example fact extracted from conversation",
170
+ kind="example",
171
+ timestamp="2023-01-01T10:00:00Z",
172
+ validity="forever"
173
+ )
174
+ ])
175
+
176
+ # Return a default instance of the model
177
+ return model_class()
178
+
179
+ # Usage with LLMMemorizer
180
+ from generic_llm_memorizer.llm_memorizer import LLMMemorizer
181
+
182
+ conversation = Conversation(system_prompt="You are a helpful assistant")
183
+ conversation.add_user_message("Hello, my name is John Doe and I'm a software engineer")
184
+
185
+ memorizer = LLMMemorizer(
186
+ user_id="user_123",
187
+ session_id="session_456",
188
+ llm_call=llm_call,
189
+ conversation=conversation
190
+ )
191
+
192
+ # Extract structured facts
193
+ memory = memorizer.extract_facts()
194
+ print(f"Extracted {len(memory.facts)} facts")
195
+
196
+ # Generate structured summary
197
+ summary = memorizer.summarize()
198
+ print(f"Summary: {summary.content}")
199
+
200
+ ## API Reference
201
+
202
+ ```python
203
+ from generic_llm_memorizer import extract_last_user_message, extract_last_assistant_message
204
+
205
+ # Extract last user message
206
+ last_user = extract_last_user_message(conv)
207
+
208
+ # Extract last assistant message
209
+ last_assistant = extract_last_assistant_message(conv)
210
+ ```
211
+
212
+ ## API Reference
213
+
214
+ ### Conversation Class
215
+
216
+ Manages a conversation with an LLM.
217
+
218
+ #### Methods
219
+
220
+ - `add_message(role, content, metadata=None)`: Add a message to the conversation
221
+ - `add_user_message(content, metadata=None)`: Add a user message
222
+ - `add_assistant_message(content, metadata=None)`: Add an assistant message
223
+ - `get_messages()`: Get all messages
224
+ - `get_recent_messages(count)`: Get the most recent messages
225
+ - `clear()`: Clear all messages
226
+ - `to_dict()`: Convert to dictionary format
227
+ - `to_json(indent=2)`: Convert to JSON string
228
+ - `from_dict(data)`: Create from dictionary data
229
+ - `from_json(json_str)`: Create from JSON string
230
+
231
+ ### Message Dataclass
232
+
233
+ Represents a single message in a conversation.
234
+
235
+ #### Attributes
236
+
237
+ - `role`: Role of the message sender ("user", "assistant", "system", etc.)
238
+ - `content`: Message content
239
+ - `timestamp`: ISO format timestamp (automatically set)
240
+ - `metadata`: Additional metadata dictionary
241
+
242
+ ### Utility Functions
243
+
244
+ - `format_conversation_for_prompt(conversation)`: Format conversation for LLM API
245
+ - `extract_last_user_message(conversation)`: Get last user message content
246
+ - `extract_last_assistant_message(conversation)`: Get last assistant message content
247
+
248
+ ## License
249
+
250
+ MIT
@@ -0,0 +1,226 @@
1
+ # Generic LLM Memorizer
2
+
3
+ A Python library for managing LLM conversations and context with automatic fact extraction and structured data handling.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ pip install generic-llm-memorizer
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ### Basic Conversation Management
14
+
15
+ ```python
16
+ from generic_llm_memorizer import Conversation
17
+
18
+ # Create a new conversation
19
+ conv = Conversation(system_prompt="You are a helpful assistant.")
20
+
21
+ # Add messages
22
+ conv.add_user_message("Hello, how are you?")
23
+ conv.add_assistant_message("I'm doing well, thank you! How can I help you today?")
24
+
25
+ # Get conversation history
26
+ messages = conv.get_messages()
27
+
28
+ # Format for LLM API (expects list of dicts with role and content)
29
+ from generic_llm_memorizer import format_conversation_for_prompt
30
+ formatted = format_conversation_for_prompt(conv)
31
+ # Output: [{"role": "system", "content": "You are a helpful assistant."}, ...]
32
+ ```
33
+
34
+ ```python
35
+ from generic_llm_memorizer import Conversation
36
+
37
+ # Create a new conversation
38
+ conv = Conversation(system_prompt="You are a helpful assistant.")
39
+
40
+ # Add messages
41
+ conv.add_user_message("Hello, how are you?")
42
+ conv.add_assistant_message("I'm doing well, thank you! How can I help you today?")
43
+
44
+ # Get conversation history
45
+ messages = conv.get_messages()
46
+
47
+ # Format for LLM API (expects list of dicts with role and content)
48
+ from generic_llm_memorizer import format_conversation_for_prompt
49
+ formatted = format_conversation_for_prompt(conv)
50
+ # Output: [{"role": "system", "content": "You are a helpful assistant."}, ...]
51
+ ```
52
+
53
+ ### Persisting Conversations
54
+
55
+ ```python
56
+ from generic_llm_memorizer import Conversation
57
+
58
+ # Save conversation to JSON
59
+ conv_json = conv.to_json()
60
+ with open("conversation.json", "w") as f:
61
+ f.write(conv_json)
62
+
63
+ # Load conversation from JSON
64
+ with open("conversation.json", "r") as f:
65
+ conv_json = f.read()
66
+ loaded_conv = Conversation.from_json(conv_json)
67
+ ```
68
+
69
+ ```python
70
+ from generic_llm_memorizer import Conversation
71
+
72
+ # Save conversation to JSON
73
+ conv_json = conv.to_json()
74
+ with open("conversation.json", "w") as f:
75
+ f.write(conv_json)
76
+
77
+ # Load conversation from JSON
78
+ with open("conversation.json", "r") as f:
79
+ conv_json = f.read()
80
+ loaded_conv = Conversation.from_json(conv_json)
81
+ ```
82
+
83
+ ### Utility Functions
84
+
85
+ ## Extending with Custom Models
86
+
87
+ You can create your own Pydantic models for domain-specific extractions:
88
+
89
+ ```python
90
+ from pydantic import BaseModel
91
+ from typing import List
92
+ from enum import Enum
93
+
94
+ class Priority(str, Enum):
95
+ LOW = "low"
96
+ MEDIUM = "medium"
97
+ HIGH = "high"
98
+
99
+ class Task(BaseModel):
100
+ title: str
101
+ description: str
102
+ priority: Priority
103
+ assignee: str
104
+
105
+ class TaskList(BaseModel):
106
+ tasks: List[Task]
107
+
108
+ # Use with the generic llm_call function
109
+ tasks = llm_call("Extract tasks from this conversation", TaskList)
110
+ ```
111
+
112
+ ### Advanced Usage with Structured Data Extraction
113
+
114
+ The library now supports a generic `llm_call` function that works with Pydantic models for structured data extraction:
115
+
116
+ ```python
117
+ from typing import Type, TypeVar
118
+ from pydantic import BaseModel
119
+ from generic_llm_memorizer.conversation import Conversation
120
+ from generic_llm_memorizer.memory import Summary, Memory, Fact
121
+
122
+ # Define a generic type variable bounded by BaseModel
123
+ T = TypeVar("T", bound=BaseModel)
124
+
125
+ def llm_call(prompt: str, model_class: Type[T]) -> T:
126
+ """
127
+ Generic LLM call function that returns structured data.
128
+
129
+ Args:
130
+ prompt: The prompt to send to the LLM
131
+ model_class: The Pydantic model class to parse the response into
132
+
133
+ Returns:
134
+ An instance of the specified model_class
135
+ """
136
+ # In a real implementation, this would call an actual LLM API
137
+ # and parse the response into the specified Pydantic model
138
+
139
+ # Mock implementation for demonstration
140
+ if model_class == Summary:
141
+ return Summary(content="Mock summary of the conversation")
142
+ elif model_class == Memory:
143
+ return Memory(facts=[
144
+ Fact(
145
+ content="Example fact extracted from conversation",
146
+ kind="example",
147
+ timestamp="2023-01-01T10:00:00Z",
148
+ validity="forever"
149
+ )
150
+ ])
151
+
152
+ # Return a default instance of the model
153
+ return model_class()
154
+
155
+ # Usage with LLMMemorizer
156
+ from generic_llm_memorizer.llm_memorizer import LLMMemorizer
157
+
158
+ conversation = Conversation(system_prompt="You are a helpful assistant")
159
+ conversation.add_user_message("Hello, my name is John Doe and I'm a software engineer")
160
+
161
+ memorizer = LLMMemorizer(
162
+ user_id="user_123",
163
+ session_id="session_456",
164
+ llm_call=llm_call,
165
+ conversation=conversation
166
+ )
167
+
168
+ # Extract structured facts
169
+ memory = memorizer.extract_facts()
170
+ print(f"Extracted {len(memory.facts)} facts")
171
+
172
+ # Generate structured summary
173
+ summary = memorizer.summarize()
174
+ print(f"Summary: {summary.content}")
175
+
176
+ ## API Reference
177
+
178
+ ```python
179
+ from generic_llm_memorizer import extract_last_user_message, extract_last_assistant_message
180
+
181
+ # Extract last user message
182
+ last_user = extract_last_user_message(conv)
183
+
184
+ # Extract last assistant message
185
+ last_assistant = extract_last_assistant_message(conv)
186
+ ```
187
+
188
+ ## API Reference
189
+
190
+ ### Conversation Class
191
+
192
+ Manages a conversation with an LLM.
193
+
194
+ #### Methods
195
+
196
+ - `add_message(role, content, metadata=None)`: Add a message to the conversation
197
+ - `add_user_message(content, metadata=None)`: Add a user message
198
+ - `add_assistant_message(content, metadata=None)`: Add an assistant message
199
+ - `get_messages()`: Get all messages
200
+ - `get_recent_messages(count)`: Get the most recent messages
201
+ - `clear()`: Clear all messages
202
+ - `to_dict()`: Convert to dictionary format
203
+ - `to_json(indent=2)`: Convert to JSON string
204
+ - `from_dict(data)`: Create from dictionary data
205
+ - `from_json(json_str)`: Create from JSON string
206
+
207
+ ### Message Dataclass
208
+
209
+ Represents a single message in a conversation.
210
+
211
+ #### Attributes
212
+
213
+ - `role`: Role of the message sender ("user", "assistant", "system", etc.)
214
+ - `content`: Message content
215
+ - `timestamp`: ISO format timestamp (automatically set)
216
+ - `metadata`: Additional metadata dictionary
217
+
218
+ ### Utility Functions
219
+
220
+ - `format_conversation_for_prompt(conversation)`: Format conversation for LLM API
221
+ - `extract_last_user_message(conversation)`: Get last user message content
222
+ - `extract_last_assistant_message(conversation)`: Get last assistant message content
223
+
224
+ ## License
225
+
226
+ MIT
@@ -0,0 +1,17 @@
1
+ """
2
+ Generic LLM Memorizer - LLM Conversation Management Library
3
+ """
4
+
5
+ from .conversation import Conversation, Message, format_conversation_for_prompt, extract_last_user_message, extract_last_assistant_message
6
+
7
+ __version__ = "0.1.0"
8
+ __author__ = "Your Name"
9
+ __email__ = "your.email@example.com"
10
+
11
+ __all__ = [
12
+ "Conversation",
13
+ "Message",
14
+ "format_conversation_for_prompt",
15
+ "extract_last_user_message",
16
+ "extract_last_assistant_message"
17
+ ]
@@ -0,0 +1,170 @@
1
+ """
2
+ Conversation management for LLM interactions.
3
+ Provides classes and functions to manage LLM conversation history and context.
4
+ """
5
+
6
+ import json
7
+ from dataclasses import asdict, dataclass
8
+ from datetime import datetime
9
+ from typing import Any, Dict, List, Optional
10
+
11
+
12
+ @dataclass
13
+ class Message:
14
+ """Represents a single message in a conversation."""
15
+
16
+ role: str # "user", "assistant", "system", etc.
17
+ content: str
18
+ timestamp: Optional[str] = None
19
+ metadata: Optional[Dict[str, Any]] = None
20
+
21
+ def __post_init__(self):
22
+ if self.timestamp is None:
23
+ self.timestamp = datetime.now().isoformat()
24
+ if self.metadata is None:
25
+ self.metadata = {}
26
+
27
+
28
+ class Conversation:
29
+ """Manages a conversation with an LLM."""
30
+
31
+ def __init__(self, session_id: str, system_prompt: Optional[str] = None):
32
+ self.session_id = session_id
33
+ self.messages: List[Message] = []
34
+
35
+ if system_prompt:
36
+ self.add_message("system", system_prompt)
37
+
38
+ def add_message(
39
+ self, role: str, content: str, metadata: Optional[Dict[str, Any]] = None
40
+ ) -> Message:
41
+ """Add a message to the conversation."""
42
+ message = Message(role=role, content=content, metadata=metadata)
43
+ self.messages.append(message)
44
+ return message
45
+
46
+ def add_messages(self, messages: List[Message]) -> None:
47
+ """Add multiple messages to the conversation."""
48
+ self.messages.extend(messages)
49
+
50
+ def insert_summary(self, summary: str) -> None:
51
+ """Insert a summary message into the conversation."""
52
+ return self.messages.insert(0, Message(role="assistant", content=summary))
53
+
54
+ def add_user_message(
55
+ self, content: str, metadata: Optional[Dict[str, Any]] = None
56
+ ) -> Message:
57
+ """Add a user message to the conversation."""
58
+ return self.add_message("user", content, metadata)
59
+
60
+ def add_assistant_message(
61
+ self, content: str, metadata: Optional[Dict[str, Any]] = None
62
+ ) -> Message:
63
+ """Add an assistant message to the conversation."""
64
+ return self.add_message("assistant", content, metadata)
65
+
66
+ def get_messages(self) -> List[Message]:
67
+ """Get all messages in the conversation."""
68
+ return self.messages.copy()
69
+
70
+ def get_recent_messages(self, count: int) -> List[Message]:
71
+ """Get the most recent messages."""
72
+ return (
73
+ self.messages[-count:]
74
+ if count <= len(self.messages)
75
+ else self.messages.copy()
76
+ )
77
+
78
+ def keep_recent_messages(self, count: int):
79
+ """Keep only the most recent messages."""
80
+ if count < 0:
81
+ raise ValueError("Count must be non-negative")
82
+ self.messages = self.messages[-count:]
83
+
84
+ def delete_old_messages(self, count: int):
85
+ """Delete the oldest messages from the conversation."""
86
+ if count < 0:
87
+ raise ValueError("Count must be non-negative")
88
+ self.messages = self.messages[count:]
89
+
90
+ def clear(self):
91
+ """Clear all messages from the conversation."""
92
+ self.messages.clear()
93
+
94
+ def to_dict(self) -> Dict[str, Any]:
95
+ """Convert conversation to dictionary format."""
96
+ return {
97
+ "session_id": self.session_id,
98
+ "messages": [asdict(msg) for msg in self.messages],
99
+ }
100
+
101
+ def to_json(self, indent: int = 2) -> str:
102
+ """Convert conversation to JSON string."""
103
+ return json.dumps(self.to_dict(), indent=indent)
104
+
105
+ @classmethod
106
+ def from_dict(cls, session_id: str, data: List[Dict[str, Any]]) -> "Conversation":
107
+ """Create a conversation from dictionary data."""
108
+ conversation = cls(session_id=session_id)
109
+ conversation.messages = [Message(**msg_dict) for msg_dict in data]
110
+ return conversation
111
+
112
+ @classmethod
113
+ def from_json(cls, json_str: str) -> "Conversation":
114
+ """Create a conversation from JSON string."""
115
+ data = json.loads(json_str)
116
+ return cls.from_dict(data["session_id"], data["messages"])
117
+
118
+ def __len__(self) -> int:
119
+ return len(self.messages)
120
+
121
+ def __iter__(self):
122
+ return iter(self.messages)
123
+
124
+
125
+ def format_conversation_for_prompt(conversation: Conversation) -> List[Dict[str, str]]:
126
+ """
127
+ Format conversation for use with LLM APIs that expect a list of message dicts.
128
+
129
+ Args:
130
+ conversation: Conversation object to format
131
+
132
+ Returns:
133
+ List of dictionaries with 'role' and 'content' keys
134
+ """
135
+ return [
136
+ {"role": msg.role, "content": msg.content}
137
+ for msg in conversation.get_messages()
138
+ ]
139
+
140
+
141
+ def extract_last_user_message(conversation: Conversation) -> Optional[str]:
142
+ """
143
+ Extract the last user message from a conversation.
144
+
145
+ Args:
146
+ conversation: Conversation object
147
+
148
+ Returns:
149
+ Content of the last user message, or None if no user messages exist
150
+ """
151
+ for msg in reversed(conversation.messages):
152
+ if msg.role == "user":
153
+ return msg.content
154
+ return None
155
+
156
+
157
+ def extract_last_assistant_message(conversation: Conversation) -> Optional[str]:
158
+ """
159
+ Extract the last assistant message from a conversation.
160
+
161
+ Args:
162
+ conversation: Conversation object
163
+
164
+ Returns:
165
+ Content of the last assistant message, or None if no assistant messages exist
166
+ """
167
+ for msg in reversed(conversation.messages):
168
+ if msg.role == "assistant":
169
+ return msg.content
170
+ return None