auto-coder 0.1.398__py3-none-any.whl → 0.1.400__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.
Potentially problematic release.
This version of auto-coder might be problematic. Click here for more details.
- auto_coder-0.1.400.dist-info/METADATA +396 -0
- {auto_coder-0.1.398.dist-info → auto_coder-0.1.400.dist-info}/RECORD +82 -29
- {auto_coder-0.1.398.dist-info → auto_coder-0.1.400.dist-info}/WHEEL +1 -1
- {auto_coder-0.1.398.dist-info → auto_coder-0.1.400.dist-info}/entry_points.txt +2 -0
- autocoder/agent/base_agentic/base_agent.py +2 -2
- autocoder/agent/base_agentic/tools/replace_in_file_tool_resolver.py +1 -1
- autocoder/agent/entry_command_agent/__init__.py +29 -0
- autocoder/agent/entry_command_agent/auto_tool.py +61 -0
- autocoder/agent/entry_command_agent/chat.py +475 -0
- autocoder/agent/entry_command_agent/designer.py +53 -0
- autocoder/agent/entry_command_agent/generate_command.py +50 -0
- autocoder/agent/entry_command_agent/project_reader.py +58 -0
- autocoder/agent/entry_command_agent/voice2text.py +71 -0
- autocoder/auto_coder.py +23 -548
- autocoder/auto_coder_runner.py +511 -8
- autocoder/chat/rules_command.py +1 -1
- autocoder/chat_auto_coder.py +6 -1
- autocoder/common/ac_style_command_parser/__init__.py +15 -0
- autocoder/common/ac_style_command_parser/example.py +7 -0
- autocoder/{command_parser.py → common/ac_style_command_parser/parser.py} +28 -45
- autocoder/common/ac_style_command_parser/test_parser.py +516 -0
- autocoder/common/auto_coder_lang.py +78 -0
- autocoder/common/command_completer_v2.py +1 -1
- autocoder/common/command_file_manager/examples.py +22 -8
- autocoder/common/command_file_manager/manager.py +37 -6
- autocoder/common/conversations/get_conversation_manager.py +143 -0
- autocoder/common/conversations/manager.py +122 -11
- autocoder/common/conversations/storage/index_manager.py +89 -0
- autocoder/common/pull_requests/__init__.py +256 -0
- autocoder/common/pull_requests/base_provider.py +191 -0
- autocoder/common/pull_requests/config.py +66 -0
- autocoder/common/pull_requests/example.py +1 -0
- autocoder/common/pull_requests/exceptions.py +46 -0
- autocoder/common/pull_requests/manager.py +201 -0
- autocoder/common/pull_requests/models.py +164 -0
- autocoder/common/pull_requests/providers/__init__.py +23 -0
- autocoder/common/pull_requests/providers/gitcode_provider.py +19 -0
- autocoder/common/pull_requests/providers/gitee_provider.py +20 -0
- autocoder/common/pull_requests/providers/github_provider.py +214 -0
- autocoder/common/pull_requests/providers/gitlab_provider.py +29 -0
- autocoder/common/pull_requests/test_module.py +1 -0
- autocoder/common/pull_requests/utils.py +344 -0
- autocoder/common/tokens/__init__.py +62 -0
- autocoder/common/tokens/counter.py +211 -0
- autocoder/common/tokens/file_detector.py +105 -0
- autocoder/common/tokens/filters.py +111 -0
- autocoder/common/tokens/models.py +28 -0
- autocoder/common/v2/agent/agentic_edit.py +312 -85
- autocoder/common/v2/agent/agentic_edit_types.py +11 -0
- autocoder/common/v2/code_auto_generate_editblock.py +10 -2
- autocoder/dispacher/__init__.py +10 -0
- autocoder/rags.py +0 -27
- autocoder/run_context.py +1 -0
- autocoder/sdk/__init__.py +188 -0
- autocoder/sdk/cli/__init__.py +15 -0
- autocoder/sdk/cli/__main__.py +26 -0
- autocoder/sdk/cli/completion_wrapper.py +38 -0
- autocoder/sdk/cli/formatters.py +211 -0
- autocoder/sdk/cli/handlers.py +175 -0
- autocoder/sdk/cli/install_completion.py +301 -0
- autocoder/sdk/cli/main.py +286 -0
- autocoder/sdk/cli/options.py +73 -0
- autocoder/sdk/constants.py +102 -0
- autocoder/sdk/core/__init__.py +20 -0
- autocoder/sdk/core/auto_coder_core.py +880 -0
- autocoder/sdk/core/bridge.py +500 -0
- autocoder/sdk/example.py +0 -0
- autocoder/sdk/exceptions.py +72 -0
- autocoder/sdk/models/__init__.py +19 -0
- autocoder/sdk/models/messages.py +209 -0
- autocoder/sdk/models/options.py +196 -0
- autocoder/sdk/models/responses.py +311 -0
- autocoder/sdk/session/__init__.py +32 -0
- autocoder/sdk/session/session.py +106 -0
- autocoder/sdk/session/session_manager.py +56 -0
- autocoder/sdk/utils/__init__.py +24 -0
- autocoder/sdk/utils/formatters.py +216 -0
- autocoder/sdk/utils/io_utils.py +302 -0
- autocoder/sdk/utils/validators.py +287 -0
- autocoder/version.py +2 -1
- auto_coder-0.1.398.dist-info/METADATA +0 -111
- autocoder/common/conversations/compatibility.py +0 -303
- autocoder/common/conversations/conversation_manager.py +0 -502
- autocoder/common/conversations/example.py +0 -152
- {auto_coder-0.1.398.dist-info → auto_coder-0.1.400.dist-info/licenses}/LICENSE +0 -0
- {auto_coder-0.1.398.dist-info → auto_coder-0.1.400.dist-info}/top_level.txt +0 -0
|
@@ -1,502 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Unified conversation manager for AutoCoder.
|
|
3
|
-
|
|
4
|
-
This module provides a centralized conversation management system that can be used
|
|
5
|
-
by different components of AutoCoder (agentic_edit, auto_command, etc.) instead of
|
|
6
|
-
having separate conversation handling implementations.
|
|
7
|
-
"""
|
|
8
|
-
|
|
9
|
-
import os
|
|
10
|
-
import json
|
|
11
|
-
import time
|
|
12
|
-
import uuid
|
|
13
|
-
from enum import Enum
|
|
14
|
-
from typing import Dict, List, Any, Optional, Union, Callable
|
|
15
|
-
from pydantic import BaseModel, Field
|
|
16
|
-
|
|
17
|
-
from autocoder.common import AutoCoderArgs
|
|
18
|
-
from autocoder.common.printer import Printer
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
class ConversationType(Enum):
|
|
22
|
-
"""Types of conversations supported by the manager."""
|
|
23
|
-
COMMAND = "command"
|
|
24
|
-
AGENTIC_EDIT = "agentic_edit"
|
|
25
|
-
CHAT = "chat"
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
class Message(BaseModel):
|
|
29
|
-
"""Base message model with role and content."""
|
|
30
|
-
role: str
|
|
31
|
-
content: Optional[str] = None
|
|
32
|
-
timestamp: Optional[int] = Field(default_factory=lambda: int(time.time()))
|
|
33
|
-
|
|
34
|
-
# Additional fields for tool calls
|
|
35
|
-
tool_calls: Optional[List[Dict[str, Any]]] = None
|
|
36
|
-
tool_call_id: Optional[str] = None
|
|
37
|
-
|
|
38
|
-
# For any additional metadata
|
|
39
|
-
metadata: Optional[Dict[str, Any]] = None
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
class Conversation(BaseModel):
|
|
43
|
-
"""Model representing a conversation with its messages."""
|
|
44
|
-
id: str = Field(default_factory=lambda: str(uuid.uuid4()))
|
|
45
|
-
type: ConversationType
|
|
46
|
-
messages: List[Message] = []
|
|
47
|
-
|
|
48
|
-
# For command conversations
|
|
49
|
-
archived_conversations: Optional[Dict[str, List[Message]]] = None
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
class ConversationManager:
|
|
53
|
-
"""
|
|
54
|
-
Unified conversation manager for AutoCoder.
|
|
55
|
-
|
|
56
|
-
This class handles conversation storage, retrieval, and operations for
|
|
57
|
-
different components of AutoCoder, replacing separate implementations.
|
|
58
|
-
"""
|
|
59
|
-
|
|
60
|
-
def __init__(self, args: AutoCoderArgs):
|
|
61
|
-
"""
|
|
62
|
-
Initialize the conversation manager.
|
|
63
|
-
|
|
64
|
-
Args:
|
|
65
|
-
args: AutoCoderArgs object containing configuration.
|
|
66
|
-
"""
|
|
67
|
-
self.args = args
|
|
68
|
-
self.project_dir = args.source_dir
|
|
69
|
-
self.memory_base_dir = os.path.join(self.project_dir, ".auto-coder", "memory", "conversations")
|
|
70
|
-
os.makedirs(self.memory_base_dir, exist_ok=True)
|
|
71
|
-
|
|
72
|
-
# Cache of loaded conversations by id
|
|
73
|
-
self._conversations: Dict[str, Conversation] = {}
|
|
74
|
-
|
|
75
|
-
# Map of event_id to conversation_id
|
|
76
|
-
self._event_mapping: Dict[str, str] = self._load_event_mapping()
|
|
77
|
-
|
|
78
|
-
def _get_conversation_dir(self, conv_type: ConversationType) -> str:
|
|
79
|
-
"""Get the directory for a specific conversation type."""
|
|
80
|
-
return os.path.join(self.memory_base_dir, conv_type.value)
|
|
81
|
-
|
|
82
|
-
def _get_conversation_path(self, conv_id: str, conv_type: ConversationType) -> str:
|
|
83
|
-
"""Get the file path for a specific conversation."""
|
|
84
|
-
conv_dir = self._get_conversation_dir(conv_type)
|
|
85
|
-
os.makedirs(conv_dir, exist_ok=True)
|
|
86
|
-
return os.path.join(conv_dir, f"{conv_id}.json")
|
|
87
|
-
|
|
88
|
-
def _load_event_mapping(self) -> Dict[str, str]:
|
|
89
|
-
"""Load the mapping of event_id to conversation_id."""
|
|
90
|
-
mapping_path = os.path.join(self.memory_base_dir, "event_mapping.json")
|
|
91
|
-
if os.path.exists(mapping_path):
|
|
92
|
-
try:
|
|
93
|
-
with open(mapping_path, "r", encoding="utf-8") as f:
|
|
94
|
-
return json.load(f)
|
|
95
|
-
except Exception as e:
|
|
96
|
-
print(f"Error loading event mapping: {e}")
|
|
97
|
-
return {}
|
|
98
|
-
|
|
99
|
-
def _save_event_mapping(self):
|
|
100
|
-
"""Save the mapping of event_id to conversation_id."""
|
|
101
|
-
mapping_path = os.path.join(self.memory_base_dir, "event_mapping.json")
|
|
102
|
-
try:
|
|
103
|
-
with open(mapping_path, "w", encoding="utf-8") as f:
|
|
104
|
-
json.dump(self._event_mapping, f, indent=2)
|
|
105
|
-
except Exception as e:
|
|
106
|
-
print(f"Error saving event mapping: {e}")
|
|
107
|
-
|
|
108
|
-
def create_conversation(
|
|
109
|
-
self,
|
|
110
|
-
conv_type: ConversationType,
|
|
111
|
-
event_id: Optional[str] = None,
|
|
112
|
-
conv_id: Optional[str] = None
|
|
113
|
-
) -> Conversation:
|
|
114
|
-
"""
|
|
115
|
-
Create a new conversation.
|
|
116
|
-
|
|
117
|
-
Args:
|
|
118
|
-
conv_type: Type of the conversation.
|
|
119
|
-
event_id: Optional event ID to associate with this conversation.
|
|
120
|
-
conv_id: Optional conversation ID. If not provided, a UUID will be generated.
|
|
121
|
-
|
|
122
|
-
Returns:
|
|
123
|
-
The created Conversation object.
|
|
124
|
-
"""
|
|
125
|
-
conv_id = conv_id or str(uuid.uuid4())
|
|
126
|
-
|
|
127
|
-
# Create conversation with appropriate initial structure
|
|
128
|
-
if conv_type == ConversationType.COMMAND:
|
|
129
|
-
conversation = Conversation(
|
|
130
|
-
id=conv_id,
|
|
131
|
-
type=conv_type,
|
|
132
|
-
archived_conversations={}
|
|
133
|
-
)
|
|
134
|
-
else:
|
|
135
|
-
conversation = Conversation(
|
|
136
|
-
id=conv_id,
|
|
137
|
-
type=conv_type
|
|
138
|
-
)
|
|
139
|
-
|
|
140
|
-
# Save conversation
|
|
141
|
-
self._conversations[conv_id] = conversation
|
|
142
|
-
self._save_conversation(conversation)
|
|
143
|
-
|
|
144
|
-
# Map event_id to conversation_id if provided
|
|
145
|
-
if event_id:
|
|
146
|
-
self._event_mapping[event_id] = conv_id
|
|
147
|
-
self._save_event_mapping()
|
|
148
|
-
|
|
149
|
-
return conversation
|
|
150
|
-
|
|
151
|
-
def get_conversation(
|
|
152
|
-
self,
|
|
153
|
-
event_id: Optional[str] = None,
|
|
154
|
-
conv_id: Optional[str] = None,
|
|
155
|
-
conv_type: Optional[ConversationType] = None,
|
|
156
|
-
create_if_not_exists: bool = True
|
|
157
|
-
) -> Optional[Conversation]:
|
|
158
|
-
"""
|
|
159
|
-
Get a conversation by event_id or conv_id.
|
|
160
|
-
|
|
161
|
-
Args:
|
|
162
|
-
event_id: Event ID associated with the conversation.
|
|
163
|
-
conv_id: Conversation ID.
|
|
164
|
-
conv_type: Type of conversation to create if not exists.
|
|
165
|
-
create_if_not_exists: Whether to create a new conversation if not found.
|
|
166
|
-
|
|
167
|
-
Returns:
|
|
168
|
-
The Conversation object or None if not found and create_if_not_exists is False.
|
|
169
|
-
"""
|
|
170
|
-
# Try to find by event_id first
|
|
171
|
-
if event_id and event_id in self._event_mapping:
|
|
172
|
-
conv_id = self._event_mapping[event_id]
|
|
173
|
-
|
|
174
|
-
# Return conversation from cache if available
|
|
175
|
-
if conv_id and conv_id in self._conversations:
|
|
176
|
-
return self._conversations[conv_id]
|
|
177
|
-
|
|
178
|
-
# Try to load from disk if we have conv_id
|
|
179
|
-
if conv_id:
|
|
180
|
-
for type_value in ConversationType:
|
|
181
|
-
conv_path = self._get_conversation_path(conv_id, type_value)
|
|
182
|
-
if os.path.exists(conv_path):
|
|
183
|
-
conversation = self._load_conversation(conv_path, type_value)
|
|
184
|
-
if conversation:
|
|
185
|
-
self._conversations[conv_id] = conversation
|
|
186
|
-
return conversation
|
|
187
|
-
|
|
188
|
-
# Create new if requested
|
|
189
|
-
if create_if_not_exists and conv_type:
|
|
190
|
-
return self.create_conversation(conv_type, event_id, conv_id)
|
|
191
|
-
|
|
192
|
-
return None
|
|
193
|
-
|
|
194
|
-
def add_message(
|
|
195
|
-
self,
|
|
196
|
-
conv_id: str,
|
|
197
|
-
role: str,
|
|
198
|
-
content: Optional[str] = None,
|
|
199
|
-
**kwargs
|
|
200
|
-
) -> Message:
|
|
201
|
-
"""
|
|
202
|
-
Add a message to a conversation.
|
|
203
|
-
|
|
204
|
-
Args:
|
|
205
|
-
conv_id: Conversation ID.
|
|
206
|
-
role: Role of the message sender (user, assistant, tool, etc.).
|
|
207
|
-
content: Content of the message.
|
|
208
|
-
**kwargs: Additional message fields.
|
|
209
|
-
|
|
210
|
-
Returns:
|
|
211
|
-
The created Message object.
|
|
212
|
-
"""
|
|
213
|
-
conversation = self._conversations.get(conv_id)
|
|
214
|
-
if not conversation:
|
|
215
|
-
raise ValueError(f"Conversation {conv_id} not found")
|
|
216
|
-
|
|
217
|
-
message = Message(role=role, content=content, **kwargs)
|
|
218
|
-
conversation.messages.append(message)
|
|
219
|
-
self._save_conversation(conversation)
|
|
220
|
-
return message
|
|
221
|
-
|
|
222
|
-
def add_message_by_event_id(
|
|
223
|
-
self,
|
|
224
|
-
event_id: str,
|
|
225
|
-
role: str,
|
|
226
|
-
content: Optional[str] = None,
|
|
227
|
-
**kwargs
|
|
228
|
-
) -> Optional[Message]:
|
|
229
|
-
"""
|
|
230
|
-
Add a message to a conversation identified by event_id.
|
|
231
|
-
|
|
232
|
-
Args:
|
|
233
|
-
event_id: Event ID associated with the conversation.
|
|
234
|
-
role: Role of the message sender.
|
|
235
|
-
content: Content of the message.
|
|
236
|
-
**kwargs: Additional message fields.
|
|
237
|
-
|
|
238
|
-
Returns:
|
|
239
|
-
The created Message object or None if conversation not found.
|
|
240
|
-
"""
|
|
241
|
-
if event_id not in self._event_mapping:
|
|
242
|
-
return None
|
|
243
|
-
|
|
244
|
-
conv_id = self._event_mapping[event_id]
|
|
245
|
-
return self.add_message(conv_id, role, content, **kwargs)
|
|
246
|
-
|
|
247
|
-
def add_user_message(self, conv_id: str, content: str) -> Message:
|
|
248
|
-
"""Add a user message to a conversation."""
|
|
249
|
-
return self.add_message(conv_id, "user", content)
|
|
250
|
-
|
|
251
|
-
def add_assistant_message(self, conv_id: str, content: str) -> Message:
|
|
252
|
-
"""Add an assistant message to a conversation."""
|
|
253
|
-
return self.add_message(conv_id, "assistant", content)
|
|
254
|
-
|
|
255
|
-
def add_tool_call_message(self, conv_id: str, tool_calls: List[Dict[str, Any]], content: Optional[str] = None) -> Message:
|
|
256
|
-
"""Add a tool call message to a conversation."""
|
|
257
|
-
return self.add_message(conv_id, "assistant", content, tool_calls=tool_calls)
|
|
258
|
-
|
|
259
|
-
def add_tool_result_message(self, conv_id: str, tool_call_id: str, content: Any) -> Message:
|
|
260
|
-
"""Add a tool result message to a conversation."""
|
|
261
|
-
return self.add_message(conv_id, "tool", content, tool_call_id=tool_call_id)
|
|
262
|
-
|
|
263
|
-
def append_to_last_message(self, conv_id: str, content: str, role: Optional[str] = None) -> bool:
|
|
264
|
-
"""
|
|
265
|
-
Append content to the last message in the conversation.
|
|
266
|
-
|
|
267
|
-
Args:
|
|
268
|
-
conv_id: Conversation ID.
|
|
269
|
-
content: Content to append.
|
|
270
|
-
role: If specified, only append if the last message has this role.
|
|
271
|
-
|
|
272
|
-
Returns:
|
|
273
|
-
True if appended successfully, False otherwise.
|
|
274
|
-
"""
|
|
275
|
-
conversation = self._conversations.get(conv_id)
|
|
276
|
-
if not conversation or not conversation.messages:
|
|
277
|
-
return False
|
|
278
|
-
|
|
279
|
-
last_message = conversation.messages[-1]
|
|
280
|
-
if role and last_message.role != role:
|
|
281
|
-
return False
|
|
282
|
-
|
|
283
|
-
# Append to content, creating it if it doesn't exist
|
|
284
|
-
if last_message.content is None:
|
|
285
|
-
last_message.content = content
|
|
286
|
-
else:
|
|
287
|
-
last_message.content += content
|
|
288
|
-
|
|
289
|
-
self._save_conversation(conversation)
|
|
290
|
-
return True
|
|
291
|
-
|
|
292
|
-
def get_history(self, conv_id: str, max_pairs: int = -1) -> List[Message]:
|
|
293
|
-
"""
|
|
294
|
-
Get the conversation history.
|
|
295
|
-
|
|
296
|
-
If max_pairs == -1, return all messages as-is (no pairing/merging).
|
|
297
|
-
If max_pairs != -1, return the latest max_pairs user-assistant pairs,
|
|
298
|
-
with pairing/merging logic for all conversation types.
|
|
299
|
-
|
|
300
|
-
Args:
|
|
301
|
-
conv_id: Conversation ID.
|
|
302
|
-
max_pairs: Maximum number of user-assistant pairs to return.
|
|
303
|
-
If -1, returns all messages directly.
|
|
304
|
-
|
|
305
|
-
Returns:
|
|
306
|
-
List of messages representing the conversation history.
|
|
307
|
-
"""
|
|
308
|
-
conversation = self._conversations.get(conv_id)
|
|
309
|
-
if not conversation:
|
|
310
|
-
return []
|
|
311
|
-
|
|
312
|
-
if max_pairs == -1:
|
|
313
|
-
# No pairing/merging, return all messages as-is
|
|
314
|
-
return conversation.messages
|
|
315
|
-
|
|
316
|
-
# Pairing/merging logic for all conversation types when max_pairs != -1
|
|
317
|
-
paired_history = []
|
|
318
|
-
pair_count = 0
|
|
319
|
-
pending_assistant = None
|
|
320
|
-
pending_user = None
|
|
321
|
-
|
|
322
|
-
# Traverse history in reverse to collect latest pairs with merging
|
|
323
|
-
for msg in reversed(conversation.messages):
|
|
324
|
-
role = msg.role
|
|
325
|
-
if role == "assistant":
|
|
326
|
-
if pending_assistant is None:
|
|
327
|
-
pending_assistant = msg
|
|
328
|
-
else:
|
|
329
|
-
# Merge with previous assistant
|
|
330
|
-
prev_content = pending_assistant.content or ""
|
|
331
|
-
curr_content = msg.content or ""
|
|
332
|
-
merged_content = (curr_content.strip() + "\n" + prev_content.strip()).strip()
|
|
333
|
-
pending_assistant.content = merged_content
|
|
334
|
-
elif role == "user":
|
|
335
|
-
if pending_user is None:
|
|
336
|
-
pending_user = msg
|
|
337
|
-
else:
|
|
338
|
-
# Merge with previous user
|
|
339
|
-
prev_content = pending_user.content or ""
|
|
340
|
-
curr_content = msg.content or ""
|
|
341
|
-
merged_content = (curr_content.strip() + "\n" + prev_content.strip()).strip()
|
|
342
|
-
pending_user.content = merged_content
|
|
343
|
-
|
|
344
|
-
if pending_assistant is not None:
|
|
345
|
-
# Have a full pair, insert in order
|
|
346
|
-
paired_history.insert(0, pending_user)
|
|
347
|
-
paired_history.insert(1, pending_assistant)
|
|
348
|
-
pair_count += 1
|
|
349
|
-
pending_assistant = None
|
|
350
|
-
pending_user = None
|
|
351
|
-
if pair_count >= max_pairs:
|
|
352
|
-
break
|
|
353
|
-
else:
|
|
354
|
-
# User without assistant yet, continue accumulating
|
|
355
|
-
continue
|
|
356
|
-
else:
|
|
357
|
-
# Ignore other roles
|
|
358
|
-
continue
|
|
359
|
-
|
|
360
|
-
# Ensure last message is assistant, drop trailing user if unpaired
|
|
361
|
-
if paired_history and paired_history[-1].role == "user":
|
|
362
|
-
paired_history.pop()
|
|
363
|
-
|
|
364
|
-
return paired_history
|
|
365
|
-
|
|
366
|
-
def get_history_as_dict_list(self, conv_id: str, max_pairs: int = 20) -> List[Dict[str, Any]]:
|
|
367
|
-
"""
|
|
368
|
-
Get conversation history as a list of dictionaries suitable for LLM APIs.
|
|
369
|
-
|
|
370
|
-
Args:
|
|
371
|
-
conv_id: Conversation ID.
|
|
372
|
-
max_pairs: Maximum number of user-assistant pairs to return.
|
|
373
|
-
|
|
374
|
-
Returns:
|
|
375
|
-
List of dictionaries with 'role' and 'content' keys.
|
|
376
|
-
"""
|
|
377
|
-
history = self.get_history(conv_id, max_pairs)
|
|
378
|
-
return [msg.model_dump(exclude={"timestamp", "metadata"}) for msg in history]
|
|
379
|
-
|
|
380
|
-
def clear_conversation(self, conv_id: str) -> bool:
|
|
381
|
-
"""
|
|
382
|
-
Clear all messages from a conversation.
|
|
383
|
-
|
|
384
|
-
Args:
|
|
385
|
-
conv_id: Conversation ID.
|
|
386
|
-
|
|
387
|
-
Returns:
|
|
388
|
-
True if cleared successfully, False otherwise.
|
|
389
|
-
"""
|
|
390
|
-
conversation = self._conversations.get(conv_id)
|
|
391
|
-
if not conversation:
|
|
392
|
-
return False
|
|
393
|
-
|
|
394
|
-
conversation.messages = []
|
|
395
|
-
self._save_conversation(conversation)
|
|
396
|
-
return True
|
|
397
|
-
|
|
398
|
-
def archive_conversation(self, conv_id: str) -> bool:
|
|
399
|
-
"""
|
|
400
|
-
Archive a command conversation.
|
|
401
|
-
|
|
402
|
-
Args:
|
|
403
|
-
conv_id: Conversation ID.
|
|
404
|
-
|
|
405
|
-
Returns:
|
|
406
|
-
True if archived successfully, False otherwise.
|
|
407
|
-
"""
|
|
408
|
-
conversation = self._conversations.get(conv_id)
|
|
409
|
-
if not conversation or conversation.type != ConversationType.COMMAND:
|
|
410
|
-
return False
|
|
411
|
-
|
|
412
|
-
# For COMMAND type, archive current messages
|
|
413
|
-
if conversation.messages:
|
|
414
|
-
timestamp = str(int(time.time()))
|
|
415
|
-
conversation.archived_conversations = conversation.archived_conversations or {}
|
|
416
|
-
conversation.archived_conversations[timestamp] = conversation.messages
|
|
417
|
-
conversation.messages = []
|
|
418
|
-
self._save_conversation(conversation)
|
|
419
|
-
|
|
420
|
-
return True
|
|
421
|
-
|
|
422
|
-
def _save_conversation(self, conversation: Conversation) -> None:
|
|
423
|
-
"""Save a conversation to disk."""
|
|
424
|
-
conv_path = self._get_conversation_path(conversation.id, conversation.type)
|
|
425
|
-
try:
|
|
426
|
-
os.makedirs(os.path.dirname(conv_path), exist_ok=True)
|
|
427
|
-
with open(conv_path, "w", encoding="utf-8") as f:
|
|
428
|
-
f.write(conversation.model_dump_json(indent=2))
|
|
429
|
-
except Exception as e:
|
|
430
|
-
print(f"Error saving conversation {conversation.id}: {e}")
|
|
431
|
-
|
|
432
|
-
def _load_conversation(self, conv_path: str, conv_type: ConversationType) -> Optional[Conversation]:
|
|
433
|
-
"""Load a conversation from disk."""
|
|
434
|
-
try:
|
|
435
|
-
with open(conv_path, "r", encoding="utf-8") as f:
|
|
436
|
-
data = json.load(f)
|
|
437
|
-
# Ensure type is correct
|
|
438
|
-
data["type"] = conv_type.value
|
|
439
|
-
return Conversation.model_validate(data)
|
|
440
|
-
except Exception as e:
|
|
441
|
-
print(f"Error loading conversation from {conv_path}: {e}")
|
|
442
|
-
return None
|
|
443
|
-
|
|
444
|
-
def delete_conversation(self, conv_id: str) -> bool:
|
|
445
|
-
"""
|
|
446
|
-
Delete a conversation.
|
|
447
|
-
|
|
448
|
-
Args:
|
|
449
|
-
conv_id: Conversation ID.
|
|
450
|
-
|
|
451
|
-
Returns:
|
|
452
|
-
True if deleted successfully, False otherwise.
|
|
453
|
-
"""
|
|
454
|
-
conversation = self._conversations.get(conv_id)
|
|
455
|
-
if not conversation:
|
|
456
|
-
return False
|
|
457
|
-
|
|
458
|
-
# Remove from cache
|
|
459
|
-
if conv_id in self._conversations:
|
|
460
|
-
del self._conversations[conv_id]
|
|
461
|
-
|
|
462
|
-
# Remove from disk
|
|
463
|
-
conv_path = self._get_conversation_path(conv_id, conversation.type)
|
|
464
|
-
if os.path.exists(conv_path):
|
|
465
|
-
try:
|
|
466
|
-
os.remove(conv_path)
|
|
467
|
-
except Exception as e:
|
|
468
|
-
print(f"Error deleting conversation file {conv_path}: {e}")
|
|
469
|
-
return False
|
|
470
|
-
|
|
471
|
-
# Remove from event mapping
|
|
472
|
-
event_ids_to_remove = []
|
|
473
|
-
for event_id, mapped_conv_id in self._event_mapping.items():
|
|
474
|
-
if mapped_conv_id == conv_id:
|
|
475
|
-
event_ids_to_remove.append(event_id)
|
|
476
|
-
|
|
477
|
-
for event_id in event_ids_to_remove:
|
|
478
|
-
del self._event_mapping[event_id]
|
|
479
|
-
|
|
480
|
-
if event_ids_to_remove:
|
|
481
|
-
self._save_event_mapping()
|
|
482
|
-
|
|
483
|
-
return True
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
# Factory function to get or create a conversation manager
|
|
487
|
-
_manager_instance = None
|
|
488
|
-
|
|
489
|
-
def get_conversation_manager(args: AutoCoderArgs) -> ConversationManager:
|
|
490
|
-
"""
|
|
491
|
-
Get or create a ConversationManager instance.
|
|
492
|
-
|
|
493
|
-
Args:
|
|
494
|
-
args: AutoCoderArgs object.
|
|
495
|
-
|
|
496
|
-
Returns:
|
|
497
|
-
A ConversationManager instance.
|
|
498
|
-
"""
|
|
499
|
-
global _manager_instance
|
|
500
|
-
if _manager_instance is None:
|
|
501
|
-
_manager_instance = ConversationManager(args)
|
|
502
|
-
return _manager_instance
|
|
@@ -1,152 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Example usage of the unified conversation manager.
|
|
3
|
-
|
|
4
|
-
This file demonstrates how to use the unified conversation manager
|
|
5
|
-
for different conversation types (command, agentic_edit, chat).
|
|
6
|
-
"""
|
|
7
|
-
|
|
8
|
-
from autocoder.common import AutoCoderArgs
|
|
9
|
-
from autocoder.common.conversations.conversation_manager import (
|
|
10
|
-
ConversationManager,
|
|
11
|
-
ConversationType,
|
|
12
|
-
get_conversation_manager,
|
|
13
|
-
Message
|
|
14
|
-
)
|
|
15
|
-
from autocoder.common.conversations.compatibility import (
|
|
16
|
-
load_command_conversation,
|
|
17
|
-
save_command_conversation,
|
|
18
|
-
save_to_command_memory_file,
|
|
19
|
-
get_agentic_conversation
|
|
20
|
-
)
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
def example_command_conversation(args: AutoCoderArgs):
|
|
24
|
-
"""Example of using the manager for command conversations."""
|
|
25
|
-
# Get the conversation manager
|
|
26
|
-
manager = get_conversation_manager(args)
|
|
27
|
-
|
|
28
|
-
# Create a command conversation (or get existing one)
|
|
29
|
-
conversation = manager.get_conversation(
|
|
30
|
-
conv_id="command_example",
|
|
31
|
-
conv_type=ConversationType.COMMAND,
|
|
32
|
-
create_if_not_exists=True
|
|
33
|
-
)
|
|
34
|
-
|
|
35
|
-
# Add messages
|
|
36
|
-
manager.add_user_message(conversation.id, "What's the weather like today?")
|
|
37
|
-
manager.add_assistant_message(conversation.id, "I'm sorry, I don't have access to real-time weather data.")
|
|
38
|
-
|
|
39
|
-
# Get conversation history
|
|
40
|
-
history = manager.get_history_as_dict_list(conversation.id)
|
|
41
|
-
print("Command conversation history:")
|
|
42
|
-
for msg in history:
|
|
43
|
-
print(f"{msg['role']}: {msg['content']}")
|
|
44
|
-
|
|
45
|
-
# Archive the conversation (useful for command conversations)
|
|
46
|
-
manager.archive_conversation(conversation.id)
|
|
47
|
-
|
|
48
|
-
# Add new messages
|
|
49
|
-
manager.add_user_message(conversation.id, "What time is it?")
|
|
50
|
-
manager.add_assistant_message(conversation.id, "I don't have access to real-time clock information.")
|
|
51
|
-
|
|
52
|
-
# Using compatibility functions
|
|
53
|
-
old_format = load_command_conversation(args, conversation.id)
|
|
54
|
-
print(f"\nLoaded in old format: {len(old_format.current_conversation)} current messages, {len(old_format.history)} archived conversations")
|
|
55
|
-
|
|
56
|
-
# Save in old format
|
|
57
|
-
save_command_conversation(args, old_format, conversation.id)
|
|
58
|
-
|
|
59
|
-
# Simple save function (common in auto_command.py)
|
|
60
|
-
save_to_command_memory_file(args, "How are you?", "I'm doing well, thank you!", conversation.id)
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
def example_agentic_edit_conversation(args: AutoCoderArgs):
|
|
64
|
-
"""Example of using the manager for agentic edit conversations."""
|
|
65
|
-
# Using the compatibility wrapper
|
|
66
|
-
agentic_conv = get_agentic_conversation(
|
|
67
|
-
args,
|
|
68
|
-
conversation_name="agentic_example",
|
|
69
|
-
event_id="event_123"
|
|
70
|
-
)
|
|
71
|
-
|
|
72
|
-
# Add messages
|
|
73
|
-
agentic_conv.add_user_message("Please help me refactor this code.")
|
|
74
|
-
agentic_conv.add_assistant_message("I'll help you refactor your code. Let me analyze it first.")
|
|
75
|
-
|
|
76
|
-
# Add tool call and result
|
|
77
|
-
tool_calls = [{
|
|
78
|
-
"id": "tool-1",
|
|
79
|
-
"type": "function",
|
|
80
|
-
"function": {
|
|
81
|
-
"name": "read_file",
|
|
82
|
-
"arguments": {"path": "example.py"}
|
|
83
|
-
}
|
|
84
|
-
}]
|
|
85
|
-
|
|
86
|
-
agentic_conv.add_assistant_tool_call_message(tool_calls, "I need to read the file first.")
|
|
87
|
-
agentic_conv.add_tool_result_message("tool-1", "print('Hello, world!')")
|
|
88
|
-
|
|
89
|
-
# Get history
|
|
90
|
-
history = agentic_conv.get_history()
|
|
91
|
-
print("\nAgentic edit conversation history:")
|
|
92
|
-
for msg in history:
|
|
93
|
-
role = msg["role"]
|
|
94
|
-
content = msg.get("content", "")
|
|
95
|
-
tool_calls = msg.get("tool_calls", [])
|
|
96
|
-
tool_call_id = msg.get("tool_call_id", "")
|
|
97
|
-
|
|
98
|
-
if role == "assistant" and tool_calls:
|
|
99
|
-
print(f"{role}: {content} [Tool calls: {len(tool_calls)}]")
|
|
100
|
-
elif role == "tool":
|
|
101
|
-
print(f"{role} ({tool_call_id}): {content}")
|
|
102
|
-
else:
|
|
103
|
-
print(f"{role}: {content}")
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
def example_chat_conversation(args: AutoCoderArgs, event_id: str):
|
|
107
|
-
"""Example of using the manager for chat conversations with event_id."""
|
|
108
|
-
# Get the conversation manager
|
|
109
|
-
manager = get_conversation_manager(args)
|
|
110
|
-
|
|
111
|
-
# Get or create a conversation by event_id
|
|
112
|
-
conversation = manager.get_conversation(
|
|
113
|
-
event_id=event_id,
|
|
114
|
-
conv_type=ConversationType.CHAT,
|
|
115
|
-
create_if_not_exists=True
|
|
116
|
-
)
|
|
117
|
-
|
|
118
|
-
# Add messages
|
|
119
|
-
manager.add_user_message(conversation.id, "Tell me about Python's asyncio.")
|
|
120
|
-
manager.add_assistant_message(conversation.id, "Python's asyncio is a library to write concurrent code using the async/await syntax.")
|
|
121
|
-
|
|
122
|
-
# Add another message
|
|
123
|
-
manager.add_message_by_event_id(event_id, "user", "How does it compare to threading?")
|
|
124
|
-
|
|
125
|
-
# Get history
|
|
126
|
-
history = manager.get_history_as_dict_list(conversation.id)
|
|
127
|
-
print("\nChat conversation history (event_id: {event_id}):")
|
|
128
|
-
for msg in history:
|
|
129
|
-
print(f"{msg['role']}: {msg['content']}")
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
def main():
|
|
133
|
-
"""Main function to run examples."""
|
|
134
|
-
# Create dummy args
|
|
135
|
-
args = AutoCoderArgs(source_dir=".")
|
|
136
|
-
|
|
137
|
-
# Run examples
|
|
138
|
-
example_command_conversation(args)
|
|
139
|
-
example_agentic_edit_conversation(args)
|
|
140
|
-
example_chat_conversation(args, "user_session_123")
|
|
141
|
-
|
|
142
|
-
# Show retrieval by event_id
|
|
143
|
-
manager = get_conversation_manager(args)
|
|
144
|
-
conversation = manager.get_conversation(event_id="user_session_123")
|
|
145
|
-
if conversation:
|
|
146
|
-
print(f"\nSuccessfully retrieved conversation by event_id: {conversation.id}")
|
|
147
|
-
else:
|
|
148
|
-
print("\nFailed to retrieve conversation by event_id")
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
if __name__ == "__main__":
|
|
152
|
-
main()
|
|
File without changes
|
|
File without changes
|