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
|
@@ -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.
|
|
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
|
|
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
|
-
|
|
137
|
+
custom_manager = CommandManager(test_dir)
|
|
138
|
+
print(f"自定义命令目录: {custom_manager.commands_dir}")
|
|
130
139
|
|
|
131
|
-
|
|
132
|
-
|
|
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
|
|
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
|
-
#
|
|
335
|
-
|
|
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
|
-
|
|
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
|
添加对话到索引
|