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.

Files changed (86) hide show
  1. auto_coder-0.1.400.dist-info/METADATA +396 -0
  2. {auto_coder-0.1.398.dist-info → auto_coder-0.1.400.dist-info}/RECORD +82 -29
  3. {auto_coder-0.1.398.dist-info → auto_coder-0.1.400.dist-info}/WHEEL +1 -1
  4. {auto_coder-0.1.398.dist-info → auto_coder-0.1.400.dist-info}/entry_points.txt +2 -0
  5. autocoder/agent/base_agentic/base_agent.py +2 -2
  6. autocoder/agent/base_agentic/tools/replace_in_file_tool_resolver.py +1 -1
  7. autocoder/agent/entry_command_agent/__init__.py +29 -0
  8. autocoder/agent/entry_command_agent/auto_tool.py +61 -0
  9. autocoder/agent/entry_command_agent/chat.py +475 -0
  10. autocoder/agent/entry_command_agent/designer.py +53 -0
  11. autocoder/agent/entry_command_agent/generate_command.py +50 -0
  12. autocoder/agent/entry_command_agent/project_reader.py +58 -0
  13. autocoder/agent/entry_command_agent/voice2text.py +71 -0
  14. autocoder/auto_coder.py +23 -548
  15. autocoder/auto_coder_runner.py +511 -8
  16. autocoder/chat/rules_command.py +1 -1
  17. autocoder/chat_auto_coder.py +6 -1
  18. autocoder/common/ac_style_command_parser/__init__.py +15 -0
  19. autocoder/common/ac_style_command_parser/example.py +7 -0
  20. autocoder/{command_parser.py → common/ac_style_command_parser/parser.py} +28 -45
  21. autocoder/common/ac_style_command_parser/test_parser.py +516 -0
  22. autocoder/common/auto_coder_lang.py +78 -0
  23. autocoder/common/command_completer_v2.py +1 -1
  24. autocoder/common/command_file_manager/examples.py +22 -8
  25. autocoder/common/command_file_manager/manager.py +37 -6
  26. autocoder/common/conversations/get_conversation_manager.py +143 -0
  27. autocoder/common/conversations/manager.py +122 -11
  28. autocoder/common/conversations/storage/index_manager.py +89 -0
  29. autocoder/common/pull_requests/__init__.py +256 -0
  30. autocoder/common/pull_requests/base_provider.py +191 -0
  31. autocoder/common/pull_requests/config.py +66 -0
  32. autocoder/common/pull_requests/example.py +1 -0
  33. autocoder/common/pull_requests/exceptions.py +46 -0
  34. autocoder/common/pull_requests/manager.py +201 -0
  35. autocoder/common/pull_requests/models.py +164 -0
  36. autocoder/common/pull_requests/providers/__init__.py +23 -0
  37. autocoder/common/pull_requests/providers/gitcode_provider.py +19 -0
  38. autocoder/common/pull_requests/providers/gitee_provider.py +20 -0
  39. autocoder/common/pull_requests/providers/github_provider.py +214 -0
  40. autocoder/common/pull_requests/providers/gitlab_provider.py +29 -0
  41. autocoder/common/pull_requests/test_module.py +1 -0
  42. autocoder/common/pull_requests/utils.py +344 -0
  43. autocoder/common/tokens/__init__.py +62 -0
  44. autocoder/common/tokens/counter.py +211 -0
  45. autocoder/common/tokens/file_detector.py +105 -0
  46. autocoder/common/tokens/filters.py +111 -0
  47. autocoder/common/tokens/models.py +28 -0
  48. autocoder/common/v2/agent/agentic_edit.py +312 -85
  49. autocoder/common/v2/agent/agentic_edit_types.py +11 -0
  50. autocoder/common/v2/code_auto_generate_editblock.py +10 -2
  51. autocoder/dispacher/__init__.py +10 -0
  52. autocoder/rags.py +0 -27
  53. autocoder/run_context.py +1 -0
  54. autocoder/sdk/__init__.py +188 -0
  55. autocoder/sdk/cli/__init__.py +15 -0
  56. autocoder/sdk/cli/__main__.py +26 -0
  57. autocoder/sdk/cli/completion_wrapper.py +38 -0
  58. autocoder/sdk/cli/formatters.py +211 -0
  59. autocoder/sdk/cli/handlers.py +175 -0
  60. autocoder/sdk/cli/install_completion.py +301 -0
  61. autocoder/sdk/cli/main.py +286 -0
  62. autocoder/sdk/cli/options.py +73 -0
  63. autocoder/sdk/constants.py +102 -0
  64. autocoder/sdk/core/__init__.py +20 -0
  65. autocoder/sdk/core/auto_coder_core.py +880 -0
  66. autocoder/sdk/core/bridge.py +500 -0
  67. autocoder/sdk/example.py +0 -0
  68. autocoder/sdk/exceptions.py +72 -0
  69. autocoder/sdk/models/__init__.py +19 -0
  70. autocoder/sdk/models/messages.py +209 -0
  71. autocoder/sdk/models/options.py +196 -0
  72. autocoder/sdk/models/responses.py +311 -0
  73. autocoder/sdk/session/__init__.py +32 -0
  74. autocoder/sdk/session/session.py +106 -0
  75. autocoder/sdk/session/session_manager.py +56 -0
  76. autocoder/sdk/utils/__init__.py +24 -0
  77. autocoder/sdk/utils/formatters.py +216 -0
  78. autocoder/sdk/utils/io_utils.py +302 -0
  79. autocoder/sdk/utils/validators.py +287 -0
  80. autocoder/version.py +2 -1
  81. auto_coder-0.1.398.dist-info/METADATA +0 -111
  82. autocoder/common/conversations/compatibility.py +0 -303
  83. autocoder/common/conversations/conversation_manager.py +0 -502
  84. autocoder/common/conversations/example.py +0 -152
  85. {auto_coder-0.1.398.dist-info → auto_coder-0.1.400.dist-info/licenses}/LICENSE +0 -0
  86. {auto_coder-0.1.398.dist-info → auto_coder-0.1.400.dist-info}/top_level.txt +0 -0
@@ -849,6 +849,84 @@ MESSAGES = {
849
849
  "/agent/edit/apply_changes":{
850
850
  "en":"Commit the changes in preview steps",
851
851
  "zh":"提交前面步骤的修改"
852
+ },
853
+ "/agent/edit/pull_request/branch_name_failed": {
854
+ "en": "Unable to get current branch name, skipping PR creation",
855
+ "zh": "无法获取当前分支名,跳过 PR 创建"
856
+ },
857
+ "/agent/edit/pull_request/title": {
858
+ "en": "AutoCoder: {{query}}",
859
+ "zh": "AutoCoder: {{query}}"
860
+ },
861
+ "/agent/edit/pull_request/default_query": {
862
+ "en": "Code auto generation",
863
+ "zh": "代码自动生成"
864
+ },
865
+ "/agent/edit/pull_request/description": {
866
+ "en": """## 🤖 AutoCoder Generated Pull Request
867
+
868
+ **Task Description**: {{query}}
869
+
870
+ ### 📝 Change Summary
871
+ - Modified {{file_count}} files
872
+ - Commit Hash: `{{commit_hash}}`
873
+
874
+ ### 📂 Changed Files List
875
+ {{file_list}}
876
+
877
+ ### ⚙️ Generation Configuration
878
+ - Source Branch: `{{source_branch}}`
879
+ - Target Branch: `{{target_branch}}`
880
+ - Auto-generated Time: {{timestamp}}
881
+
882
+ ### 🔍 Next Steps
883
+ - [ ] Code Review
884
+ - [ ] Test Verification
885
+ - [ ] Merge to Main Branch
886
+
887
+ ---
888
+ *This PR was automatically created by AutoCoder*
889
+ """,
890
+ "zh": """## 🤖 AutoCoder 自动生成的 Pull Request
891
+
892
+ **任务描述**: {{query}}
893
+
894
+ ### 📝 变更摘要
895
+ - 共修改 {{file_count}} 个文件
896
+ - 提交哈希: `{{commit_hash}}`
897
+
898
+ ### 📂 变更文件列表
899
+ {{file_list}}
900
+
901
+ ### ⚙️ 生成配置
902
+ - 源分支: `{{source_branch}}`
903
+ - 目标分支: `{{target_branch}}`
904
+ - 自动生成时间: {{timestamp}}
905
+
906
+ ### 🔍 下一步
907
+ - [ ] 代码审查
908
+ - [ ] 测试验证
909
+ - [ ] 合并到主分支
910
+
911
+ ---
912
+ *此 PR 由 AutoCoder 自动创建*
913
+ """
914
+ },
915
+ "/agent/edit/pull_request/creating": {
916
+ "en": "Creating Pull Request: {{title}}",
917
+ "zh": "正在创建 Pull Request: {{title}}"
918
+ },
919
+ "/agent/edit/pull_request/success": {
920
+ "en": "✅ Pull Request created successfully",
921
+ "zh": "✅ Pull Request 创建成功"
922
+ },
923
+ "/agent/edit/pull_request/failed": {
924
+ "en": "❌ Pull Request creation failed: {{error}}",
925
+ "zh": "❌ Pull Request 创建失败: {{error}}"
926
+ },
927
+ "/agent/edit/pull_request/exception": {
928
+ "en": "❌ Exception occurred while creating Pull Request: {{error}}",
929
+ "zh": "❌ 创建 Pull Request 时发生异常: {{error}}"
852
930
  }
853
931
  }
854
932
 
@@ -22,7 +22,7 @@ COMMAND_HIERARCHY = {
22
22
  "/mcp": {"/add": {}, "/remove": {}, "/list": {}, "/list_running": {}, "/refresh": {}, "/info": {}},
23
23
  "/lib": {"/add": {}, "/remove": {}, "/list": {}, "/set-proxy": {}, "/refresh": {}, "/get": {}},
24
24
  "/models": {"/chat": {}, "/add": {}, "/add_model": {}, "/remove": {}, "/list": {}, "/speed": {}, "/speed-test": {}, "/input_price": {}, "/output_price": {}, "/activate": {}},
25
- "/auto": {},
25
+ "/auto": {"/new": {}, "/resume": {}, "/list": {},"/command":{}},
26
26
  "/shell": {"/chat": {}},
27
27
  "/active_context": {"/list": {}, "/run": {}},
28
28
  "/index": {"/query": {}, "/build": {}, "/export": {}, "/import": {}},
@@ -9,7 +9,7 @@ import sys
9
9
  import json
10
10
  from typing import Dict, Set, List
11
11
 
12
- from autocoder.common.command_manager import (
12
+ from autocoder.common.command_file_manager import (
13
13
  CommandManager, CommandFile, JinjaVariable, CommandFileAnalysisResult
14
14
  )
15
15
 
@@ -122,16 +122,30 @@ def get_all_variables_example(manager: CommandManager):
122
122
  print()
123
123
 
124
124
 
125
- def main():
126
- """主函数"""
127
- # 设置测试环境
125
+ def initialization_examples():
126
+ """初始化示例"""
127
+ print("\n=== CommandManager 初始化示例 ===")
128
+
129
+ # 方式1: 使用默认目录(工作目录下的.autocodercommands目录)
130
+ print("方式1: 使用默认目录")
131
+ default_manager = CommandManager()
132
+ print(f"默认命令目录: {default_manager.commands_dir}")
133
+
134
+ # 方式2: 指定自定义目录
135
+ print("\n方式2: 指定自定义目录")
128
136
  test_dir = setup_test_environment()
129
- print(f"测试目录: {test_dir}")
137
+ custom_manager = CommandManager(test_dir)
138
+ print(f"自定义命令目录: {custom_manager.commands_dir}")
130
139
 
131
- # 创建命令管理器
132
- manager = CommandManager(test_dir)
140
+ return custom_manager
141
+
142
+
143
+ def main():
144
+ """主函数"""
145
+ # 展示初始化示例
146
+ manager = initialization_examples()
133
147
 
134
- # 运行示例
148
+ # 运行其他示例
135
149
  list_command_files_example(manager)
136
150
  read_command_file_example(manager)
137
151
  analyze_command_file_example(manager)
@@ -5,8 +5,9 @@
5
5
  """
6
6
 
7
7
  import os
8
- import logging
9
- from typing import Dict, List, Optional, Set, Tuple
8
+ from loguru import logger
9
+ from typing import Dict, List, Optional, Set, Tuple, Any
10
+ from byzerllm.utils import format_str_jinja2
10
11
 
11
12
  from autocoder.common.command_file_manager.models import (
12
13
  CommandFile, JinjaVariable, CommandFileAnalysisResult, ListCommandsResult
@@ -16,19 +17,19 @@ from autocoder.common.command_file_manager.utils import (
16
17
  analyze_command_file, is_command_file
17
18
  )
18
19
 
19
- logger = logging.getLogger(__name__)
20
-
21
20
 
22
21
  class CommandManager:
23
22
  """命令管理器,提供高层次的API接口"""
24
23
 
25
- def __init__(self, commands_dir: str):
24
+ def __init__(self, commands_dir: Optional[str] = None):
26
25
  """
27
26
  初始化命令管理器
28
27
 
29
28
  Args:
30
- commands_dir: 命令文件目录路径
29
+ commands_dir: 命令文件目录路径,如果为None则默认使用工作目录下的.autocodercommands目录
31
30
  """
31
+ if commands_dir is None:
32
+ commands_dir = os.path.join(os.getcwd(), ".autocodercommands")
32
33
  self.commands_dir = os.path.abspath(commands_dir)
33
34
 
34
35
  # 确保目录存在
@@ -97,6 +98,36 @@ class CommandManager:
97
98
  logger.error(f"读取命令文件时出错: {str(e)}")
98
99
  return None
99
100
 
101
+ def read_command_file_with_render(self, file_name: str, render_variables: Dict[str, Any] = {}) -> Optional[str]:
102
+ """
103
+ 读取指定的命令文件并使用 Jinja2 进行渲染
104
+
105
+ Args:
106
+ file_name: 命令文件名或相对路径
107
+ render_variables: 用于 Jinja2 渲染的变量字典,如果为 None 则使用空字典
108
+
109
+ Returns:
110
+ Optional[str]: 渲染后的文件内容,如果文件不存在或渲染失败则返回None
111
+ """
112
+ if render_variables is None:
113
+ render_variables = {}
114
+
115
+ # 首先读取命令文件
116
+ command_file = self.read_command_file(file_name)
117
+ if command_file is None:
118
+ return None
119
+
120
+ try:
121
+ # 使用 format_str_jinja2 进行渲染
122
+ rendered_content = format_str_jinja2(command_file.content, **render_variables)
123
+
124
+ logger.info(f"成功渲染命令文件: {file_name}, 使用变量: {render_variables}")
125
+ return rendered_content
126
+
127
+ except Exception as e:
128
+ logger.error(f"渲染命令文件时出错: {file_name}, 错误: {str(e)}")
129
+ return None
130
+
100
131
  def analyze_command_file(self, file_name: str) -> Optional[CommandFileAnalysisResult]:
101
132
  """
102
133
  分析指定的命令文件,提取其中的Jinja2变量
@@ -0,0 +1,143 @@
1
+ import os
2
+ import threading
3
+ from typing import Optional
4
+ from .manager import PersistConversationManager
5
+ from .config import ConversationManagerConfig
6
+
7
+
8
+ class ConversationManagerSingleton:
9
+ """对话管理器的单例类,确保全局只有一个实例"""
10
+
11
+ _instance: Optional[PersistConversationManager] = None
12
+ _lock = threading.Lock()
13
+ _config: Optional[ConversationManagerConfig] = None
14
+
15
+ @classmethod
16
+ def get_instance(cls, config: Optional[ConversationManagerConfig] = None) -> PersistConversationManager:
17
+ """
18
+ 获取对话管理器实例
19
+
20
+ Args:
21
+ config: 配置对象,如果为None则使用默认配置
22
+
23
+ Returns:
24
+ PersistConversationManager实例
25
+ """
26
+ if cls._instance is None:
27
+ with cls._lock:
28
+ if cls._instance is None:
29
+ if config is None:
30
+ config = cls._get_default_config()
31
+ cls._config = config
32
+ cls._instance = PersistConversationManager(config)
33
+ return cls._instance
34
+
35
+ @classmethod
36
+ def reset_instance(cls, config: Optional[ConversationManagerConfig] = None):
37
+ """
38
+ 重置实例,用于测试或配置更改时
39
+
40
+ Args:
41
+ config: 新的配置对象
42
+ """
43
+ with cls._lock:
44
+ cls._instance = None
45
+ cls._config = None
46
+ if config is not None:
47
+ cls._instance = PersistConversationManager(config)
48
+ cls._config = config
49
+
50
+ @classmethod
51
+ def _get_default_config(cls) -> ConversationManagerConfig:
52
+ """获取默认配置"""
53
+ # 默认存储路径为当前工作目录下的 .auto-coder/conversations
54
+ default_storage_path = os.path.join(os.getcwd(), ".auto-coder", "conversations")
55
+
56
+ return ConversationManagerConfig(
57
+ storage_path=default_storage_path,
58
+ max_cache_size=100,
59
+ cache_ttl=300.0,
60
+ lock_timeout=10.0,
61
+ backup_enabled=True,
62
+ backup_interval=3600.0,
63
+ max_backups=10
64
+ )
65
+
66
+ @classmethod
67
+ def get_config(cls) -> Optional[ConversationManagerConfig]:
68
+ """获取当前使用的配置"""
69
+ return cls._config
70
+
71
+
72
+ def get_conversation_manager(config: Optional[ConversationManagerConfig] = None) -> PersistConversationManager:
73
+ """
74
+ 获取全局对话管理器实例
75
+
76
+ 这是一个便捷函数,内部使用单例模式确保全局只有一个实例。
77
+ 首次调用时会创建实例,后续调用会返回同一个实例。
78
+
79
+ Args:
80
+ config: 可选的配置对象。如果为None,将使用默认配置。
81
+ 注意:只有在首次调用时,config参数才会生效。
82
+
83
+ Returns:
84
+ PersistConversationManager: 对话管理器实例
85
+
86
+ Example:
87
+ ```python
88
+ # 使用默认配置
89
+ manager = get_conversation_manager()
90
+
91
+ # 使用自定义配置(仅在首次调用时生效)
92
+ config = ConversationManagerConfig(
93
+ storage_path="./my_conversations",
94
+ max_cache_size=200
95
+ )
96
+ manager = get_conversation_manager(config)
97
+
98
+ # 创建对话
99
+ conv_id = manager.create_conversation(
100
+ name="测试对话",
101
+ description="这是一个测试对话"
102
+ )
103
+ ```
104
+ """
105
+ return ConversationManagerSingleton.get_instance(config)
106
+
107
+
108
+ def reset_conversation_manager(config: Optional[ConversationManagerConfig] = None):
109
+ """
110
+ 重置全局对话管理器实例
111
+
112
+ 用于测试或需要更改配置时重置实例。
113
+
114
+ Args:
115
+ config: 新的配置对象,如果为None则在下次调用get_conversation_manager时使用默认配置
116
+
117
+ Example:
118
+ ```python
119
+ # 重置为默认配置
120
+ reset_conversation_manager()
121
+
122
+ # 重置为新配置
123
+ new_config = ConversationManagerConfig(storage_path="./new_path")
124
+ reset_conversation_manager(new_config)
125
+ ```
126
+ """
127
+ ConversationManagerSingleton.reset_instance(config)
128
+
129
+
130
+ def get_conversation_manager_config() -> Optional[ConversationManagerConfig]:
131
+ """
132
+ 获取当前对话管理器使用的配置
133
+
134
+ Returns:
135
+ 当前配置对象,如果还未初始化则返回None
136
+ """
137
+ return ConversationManagerSingleton.get_config()
138
+
139
+
140
+ # 便捷别名
141
+ get_manager = get_conversation_manager
142
+ reset_manager = reset_conversation_manager
143
+ get_manager_config = get_conversation_manager_config
@@ -331,21 +331,22 @@ class PersistConversationManager:
331
331
  List of conversation data
332
332
  """
333
333
  try:
334
- # Get conversations from index
335
- conversations = self.index_manager.list_conversations()
334
+ # Convert sort_desc boolean to sort_order string
335
+ sort_order = 'desc' if sort_desc else 'asc'
336
+
337
+ # Get conversations from index with sorting and pagination
338
+ conversations = self.index_manager.list_conversations(
339
+ limit=limit,
340
+ offset=offset,
341
+ sort_by=sort_by,
342
+ sort_order=sort_order
343
+ )
336
344
 
337
345
  # Apply filters if provided
338
346
  if filters:
339
347
  conversations = self.filter_manager.apply_filters(conversations, filters)
340
348
 
341
- # Sort conversations
342
- conversations = self.index_manager.sort_conversations(
343
- conversations, sort_by, sort_desc
344
- )
345
-
346
- # Apply pagination
347
- end_idx = offset + limit if limit else None
348
- return conversations[offset:end_idx]
349
+ return conversations
349
350
 
350
351
  except Exception as e:
351
352
  raise ConversationManagerError(f"Failed to list conversations: {e}")
@@ -815,6 +816,7 @@ class PersistConversationManager:
815
816
  **self._stats,
816
817
  'cache_stats': cache_stats,
817
818
  'total_conversations': len(self.index_manager.list_conversations()),
819
+ 'current_conversation_id': self.get_current_conversation_id(),
818
820
  'storage_path': self.config.storage_path
819
821
  }
820
822
 
@@ -914,4 +916,113 @@ class PersistConversationManager:
914
916
  try:
915
917
  self.index_manager._save_index()
916
918
  except Exception:
917
- pass # Ignore errors during cleanup
919
+ pass # Ignore errors during cleanup
920
+
921
+ # Current Conversation Management Methods
922
+
923
+ def set_current_conversation(self, conversation_id: str) -> bool:
924
+ """
925
+ 设置当前对话。
926
+
927
+ Args:
928
+ conversation_id: 要设置为当前对话的ID
929
+
930
+ Returns:
931
+ True if setting was successful
932
+
933
+ Raises:
934
+ ConversationNotFoundError: 如果对话不存在
935
+ """
936
+ try:
937
+ # 验证对话是否存在
938
+ conversation_data = self.get_conversation(conversation_id)
939
+ if not conversation_data:
940
+ raise ConversationNotFoundError(conversation_id)
941
+
942
+ # 设置当前对话
943
+ success = self.index_manager.set_current_conversation(conversation_id)
944
+ if not success:
945
+ raise ConversationManagerError(f"Failed to set current conversation: {conversation_id}")
946
+
947
+ return True
948
+
949
+ except ConversationNotFoundError:
950
+ raise
951
+ except Exception as e:
952
+ raise ConversationManagerError(f"Failed to set current conversation {conversation_id}: {e}")
953
+
954
+ def get_current_conversation_id(self) -> Optional[str]:
955
+ """
956
+ 获取当前对话ID。
957
+
958
+ Returns:
959
+ 当前对话ID,如果未设置返回None
960
+ """
961
+ try:
962
+ return self.index_manager.get_current_conversation_id()
963
+ except Exception as e:
964
+ raise ConversationManagerError(f"Failed to get current conversation ID: {e}")
965
+
966
+ def get_current_conversation(self) -> Optional[Dict[str, Any]]:
967
+ """
968
+ 获取当前对话的完整数据。
969
+
970
+ Returns:
971
+ 当前对话的数据字典,如果未设置或对话不存在返回None
972
+ """
973
+ try:
974
+ current_id = self.get_current_conversation_id()
975
+ if not current_id:
976
+ return None
977
+
978
+ return self.get_conversation(current_id)
979
+
980
+ except Exception as e:
981
+ raise ConversationManagerError(f"Failed to get current conversation: {e}")
982
+
983
+ def clear_current_conversation(self) -> bool:
984
+ """
985
+ 清除当前对话设置。
986
+
987
+ Returns:
988
+ True if clearing was successful
989
+ """
990
+ try:
991
+ success = self.index_manager.clear_current_conversation()
992
+ if not success:
993
+ raise ConversationManagerError("Failed to clear current conversation")
994
+
995
+ return True
996
+
997
+ except Exception as e:
998
+ raise ConversationManagerError(f"Failed to clear current conversation: {e}")
999
+
1000
+ def append_message_to_current(
1001
+ self,
1002
+ role: str,
1003
+ content: Union[str, Dict[str, Any], List[Any]],
1004
+ metadata: Optional[Dict[str, Any]] = None
1005
+ ) -> str:
1006
+ """
1007
+ 向当前对话添加消息。
1008
+
1009
+ Args:
1010
+ role: 消息角色
1011
+ content: 消息内容
1012
+ metadata: 可选的消息元数据
1013
+
1014
+ Returns:
1015
+ 消息ID
1016
+
1017
+ Raises:
1018
+ ConversationManagerError: 如果没有设置当前对话或添加失败
1019
+ """
1020
+ try:
1021
+ current_id = self.get_current_conversation_id()
1022
+ if not current_id:
1023
+ raise ConversationManagerError("No current conversation set")
1024
+
1025
+ return self.append_message(current_id, role, content, metadata)
1026
+
1027
+ except Exception as e:
1028
+ raise ConversationManagerError(f"Failed to append message to current conversation: {e}")
@@ -26,10 +26,12 @@ class IndexManager:
26
26
  """
27
27
  self.index_path = Path(index_path)
28
28
  self.index_file = self.index_path / "conversations.idx"
29
+ self.config_file = self.index_path / "config.json"
29
30
  self.lock_file = self.index_path / "index.lock"
30
31
 
31
32
  self._ensure_index_directory()
32
33
  self._load_index()
34
+ self._load_config()
33
35
 
34
36
  def _ensure_index_directory(self):
35
37
  """确保索引目录存在"""
@@ -47,6 +49,18 @@ class IndexManager:
47
49
  # 如果索引损坏,重建空索引
48
50
  self._index_data = {}
49
51
 
52
+ def _load_config(self):
53
+ """加载配置数据"""
54
+ try:
55
+ if self.config_file.exists():
56
+ with open(self.config_file, 'r', encoding='utf-8') as f:
57
+ self._config_data = json.load(f)
58
+ else:
59
+ self._config_data = {}
60
+ except (json.JSONDecodeError, OSError, IOError):
61
+ # 如果配置损坏,重建空配置
62
+ self._config_data = {}
63
+
50
64
  def _save_index(self) -> bool:
51
65
  """
52
66
  保存索引数据
@@ -68,6 +82,81 @@ class IndexManager:
68
82
  except (OSError, IOError):
69
83
  return False
70
84
 
85
+ def _save_config(self) -> bool:
86
+ """
87
+ 保存配置数据
88
+
89
+ Returns:
90
+ bool: 保存成功返回True
91
+ """
92
+ try:
93
+ # 使用临时文件进行原子写入
94
+ temp_file = self.config_file.with_suffix('.tmp')
95
+
96
+ with open(temp_file, 'w', encoding='utf-8') as f:
97
+ json.dump(self._config_data, f, ensure_ascii=False, indent=2)
98
+
99
+ # 原子重命名
100
+ temp_file.replace(self.config_file)
101
+ return True
102
+
103
+ except (OSError, IOError):
104
+ return False
105
+
106
+ def set_current_conversation(self, conversation_id: Optional[str]) -> bool:
107
+ """
108
+ 设置当前对话ID
109
+
110
+ Args:
111
+ conversation_id: 对话ID,None表示清除当前对话
112
+
113
+ Returns:
114
+ bool: 设置成功返回True
115
+ """
116
+ try:
117
+ # 重新加载配置以获取最新数据
118
+ self._load_config()
119
+
120
+ # 设置或清除当前对话ID
121
+ if conversation_id is None:
122
+ self._config_data.pop('current_conversation_id', None)
123
+ else:
124
+ self._config_data['current_conversation_id'] = conversation_id
125
+
126
+ # 更新时间戳
127
+ self._config_data['last_updated'] = time.time()
128
+
129
+ # 保存配置
130
+ return self._save_config()
131
+
132
+ except Exception:
133
+ return False
134
+
135
+ def get_current_conversation_id(self) -> Optional[str]:
136
+ """
137
+ 获取当前对话ID
138
+
139
+ Returns:
140
+ Optional[str]: 当前对话ID,未设置返回None
141
+ """
142
+ try:
143
+ # 重新加载配置以获取最新数据
144
+ self._load_config()
145
+
146
+ return self._config_data.get('current_conversation_id')
147
+
148
+ except Exception:
149
+ return None
150
+
151
+ def clear_current_conversation(self) -> bool:
152
+ """
153
+ 清除当前对话设置
154
+
155
+ Returns:
156
+ bool: 清除成功返回True
157
+ """
158
+ return self.set_current_conversation(None)
159
+
71
160
  def add_conversation(self, conversation_metadata: Dict[str, Any]) -> bool:
72
161
  """
73
162
  添加对话到索引