jarvis-ai-assistant 0.2.1__py3-none-any.whl → 0.2.2__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.
- jarvis/__init__.py +1 -1
- jarvis/jarvis_agent/jarvis.py +61 -59
- jarvis/jarvis_agent/main.py +42 -40
- jarvis/jarvis_code_agent/code_agent.py +35 -31
- jarvis/jarvis_code_analysis/code_review.py +73 -39
- jarvis/jarvis_git_squash/main.py +16 -12
- jarvis/jarvis_git_utils/git_commiter.py +25 -20
- jarvis/jarvis_methodology/main.py +34 -49
- jarvis/jarvis_multi_agent/main.py +28 -23
- jarvis/jarvis_platform/ai8.py +31 -22
- jarvis/jarvis_platform/kimi.py +31 -61
- jarvis/jarvis_platform/tongyi.py +62 -76
- jarvis/jarvis_platform/yuanbao.py +44 -50
- jarvis/jarvis_platform_manager/main.py +55 -90
- jarvis/jarvis_smart_shell/main.py +58 -87
- jarvis/jarvis_tools/cli/main.py +120 -153
- jarvis/jarvis_tools/registry.py +1 -7
- jarvis/jarvis_tools/search_web.py +12 -10
- jarvis/jarvis_utils/http.py +58 -79
- jarvis/jarvis_utils/output.py +1 -1
- jarvis_ai_assistant-0.2.2.dist-info/METADATA +228 -0
- {jarvis_ai_assistant-0.2.1.dist-info → jarvis_ai_assistant-0.2.2.dist-info}/RECORD +26 -29
- {jarvis_ai_assistant-0.2.1.dist-info → jarvis_ai_assistant-0.2.2.dist-info}/entry_points.txt +0 -2
- jarvis/jarvis_git_details/__init__.py +0 -0
- jarvis/jarvis_git_details/main.py +0 -265
- jarvis/jarvis_platform/oyi.py +0 -357
- jarvis_ai_assistant-0.2.1.dist-info/METADATA +0 -845
- {jarvis_ai_assistant-0.2.1.dist-info → jarvis_ai_assistant-0.2.2.dist-info}/WHEEL +0 -0
- {jarvis_ai_assistant-0.2.1.dist-info → jarvis_ai_assistant-0.2.2.dist-info}/licenses/LICENSE +0 -0
- {jarvis_ai_assistant-0.2.1.dist-info → jarvis_ai_assistant-0.2.2.dist-info}/top_level.txt +0 -0
@@ -1,265 +0,0 @@
|
|
1
|
-
# -*- coding: utf-8 -*-
|
2
|
-
"""Git Commit分析工具模块
|
3
|
-
|
4
|
-
该模块提供了一个GitCommitAnalyzer类,用于获取和分析指定Git commit的详细信息,
|
5
|
-
包括提交信息、修改内容以及详细的功能、原因和逻辑分析。
|
6
|
-
"""
|
7
|
-
|
8
|
-
import os
|
9
|
-
import re
|
10
|
-
import subprocess
|
11
|
-
from typing import Any, Dict
|
12
|
-
|
13
|
-
from jarvis.jarvis_agent import Agent
|
14
|
-
from jarvis.jarvis_platform.registry import PlatformRegistry
|
15
|
-
from jarvis.jarvis_tools.registry import ToolRegistry
|
16
|
-
from jarvis.jarvis_utils.output import OutputType, PrettyOutput
|
17
|
-
from jarvis.jarvis_utils.tag import ct, ot
|
18
|
-
from jarvis.jarvis_utils.utils import init_env
|
19
|
-
|
20
|
-
|
21
|
-
class GitCommitAnalyzer:
|
22
|
-
"""Git Commit分析器
|
23
|
-
|
24
|
-
该类用于获取和分析指定Git commit的详细信息,包括:
|
25
|
-
- 完整的提交信息
|
26
|
-
- 修改的文件列表和状态
|
27
|
-
- 修改的功能、原因和逻辑分析
|
28
|
-
"""
|
29
|
-
|
30
|
-
def execute(self, args: Dict[str, Any]) -> Dict[str, Any]:
|
31
|
-
"""执行commit分析
|
32
|
-
|
33
|
-
Args:
|
34
|
-
args: 包含commit_sha/commit_range和root_dir的参数字典
|
35
|
-
commit_sha: 单个commit的SHA
|
36
|
-
commit_range: 两个commit的SHA范围,格式为"commit1..commit2"
|
37
|
-
root_dir: 代码库根目录
|
38
|
-
|
39
|
-
Returns:
|
40
|
-
包含分析结果的字典
|
41
|
-
"""
|
42
|
-
try:
|
43
|
-
commit_sha = args.get("commit_sha")
|
44
|
-
commit_range = args.get("commit_range")
|
45
|
-
root_dir = args.get("root_dir", ".")
|
46
|
-
|
47
|
-
if commit_range:
|
48
|
-
return self.analyze_commit_range(commit_range, root_dir)
|
49
|
-
elif commit_sha:
|
50
|
-
return self.analyze_single_commit(commit_sha, root_dir)
|
51
|
-
else:
|
52
|
-
raise ValueError("Either commit_sha or commit_range must be provided")
|
53
|
-
except Exception as e:
|
54
|
-
return {
|
55
|
-
"success": False,
|
56
|
-
"stdout": {},
|
57
|
-
"stderr": f"Failed to analyze commit: {str(e)}",
|
58
|
-
}
|
59
|
-
|
60
|
-
def analyze_single_commit(self, commit_sha: str, root_dir: str) -> Dict[str, Any]:
|
61
|
-
"""分析单个commit
|
62
|
-
|
63
|
-
Args:
|
64
|
-
commit_sha: commit的SHA
|
65
|
-
root_dir: 代码库根目录
|
66
|
-
|
67
|
-
Returns:
|
68
|
-
包含分析结果的字典
|
69
|
-
"""
|
70
|
-
original_dir = os.getcwd()
|
71
|
-
try:
|
72
|
-
os.chdir(root_dir)
|
73
|
-
|
74
|
-
# 获取commit详细信息
|
75
|
-
commit_info = subprocess.check_output(
|
76
|
-
f"git show {commit_sha} --pretty=fuller", shell=True, text=True
|
77
|
-
)
|
78
|
-
|
79
|
-
# 获取commit修改内容
|
80
|
-
diff_content = subprocess.check_output(
|
81
|
-
f"git show {commit_sha} --patch", shell=True, text=True
|
82
|
-
)
|
83
|
-
|
84
|
-
# 分析commit的功能、原因和逻辑
|
85
|
-
analysis_result = self._analyze_diff_content(diff_content)
|
86
|
-
|
87
|
-
return {
|
88
|
-
"success": True,
|
89
|
-
"stdout": {
|
90
|
-
"commit_info": commit_info,
|
91
|
-
"diff_content": diff_content,
|
92
|
-
"analysis_result": analysis_result,
|
93
|
-
},
|
94
|
-
"stderr": "",
|
95
|
-
}
|
96
|
-
except subprocess.CalledProcessError as error:
|
97
|
-
return {
|
98
|
-
"success": False,
|
99
|
-
"stdout": {},
|
100
|
-
"stderr": f"Failed to analyze commit: {str(error)}",
|
101
|
-
}
|
102
|
-
finally:
|
103
|
-
os.chdir(original_dir)
|
104
|
-
|
105
|
-
def analyze_commit_range(self, commit_range: str, root_dir: str) -> Dict[str, Any]:
|
106
|
-
"""分析两个commit之间的代码变更
|
107
|
-
|
108
|
-
Args:
|
109
|
-
commit_range: 两个commit的SHA范围,格式为"commit1..commit2"
|
110
|
-
root_dir: 代码库根目录
|
111
|
-
|
112
|
-
Returns:
|
113
|
-
包含分析结果的字典
|
114
|
-
"""
|
115
|
-
original_dir = os.getcwd()
|
116
|
-
try:
|
117
|
-
os.chdir(root_dir)
|
118
|
-
|
119
|
-
# 获取commit范围差异
|
120
|
-
diff_content = subprocess.check_output(
|
121
|
-
f"git diff {commit_range} --patch", shell=True, text=True
|
122
|
-
)
|
123
|
-
|
124
|
-
# 获取commit范围信息
|
125
|
-
commit_info = subprocess.check_output(
|
126
|
-
f"git log {commit_range} --pretty=fuller", shell=True, text=True
|
127
|
-
)
|
128
|
-
|
129
|
-
# 使用相同的分析方法处理差异内容
|
130
|
-
analysis_result = self._analyze_diff_content(diff_content)
|
131
|
-
|
132
|
-
return {
|
133
|
-
"success": True,
|
134
|
-
"stdout": {
|
135
|
-
"commit_info": commit_info,
|
136
|
-
"diff_content": diff_content,
|
137
|
-
"analysis_result": analysis_result,
|
138
|
-
},
|
139
|
-
"stderr": "",
|
140
|
-
}
|
141
|
-
except subprocess.CalledProcessError as error:
|
142
|
-
return {
|
143
|
-
"success": False,
|
144
|
-
"stdout": {},
|
145
|
-
"stderr": f"Failed to analyze commit range: {str(error)}",
|
146
|
-
}
|
147
|
-
finally:
|
148
|
-
os.chdir(original_dir)
|
149
|
-
|
150
|
-
def _analyze_diff_content(self, diff_content: str) -> str:
|
151
|
-
"""分析diff内容并生成报告
|
152
|
-
|
153
|
-
Args:
|
154
|
-
diff_content: git diff或git show的输出内容
|
155
|
-
|
156
|
-
Returns:
|
157
|
-
分析结果字符串
|
158
|
-
"""
|
159
|
-
system_prompt = """你是一位资深代码分析专家,拥有多年代码审查和重构经验。你需要对Git commit进行深入分析,包括:
|
160
|
-
1. 修改的功能:明确说明本次commit实现或修改了哪些功能
|
161
|
-
2. 修改的原因:分析为什么要进行这些修改(如修复bug、优化性能、添加新功能等)
|
162
|
-
3. 修改的逻辑:详细说明代码修改的具体实现逻辑和思路
|
163
|
-
4. 影响范围:评估本次修改可能影响的其他模块或功能
|
164
|
-
5. 代码质量:分析代码风格、可读性和可维护性
|
165
|
-
6. 测试覆盖:评估是否需要添加或修改测试用例
|
166
|
-
7. 最佳实践:检查代码是否符合行业最佳实践和项目规范
|
167
|
-
|
168
|
-
请确保分析内容:
|
169
|
-
- 准确反映commit的实际修改
|
170
|
-
- 提供足够的技术细节
|
171
|
-
- 保持结构清晰,便于理解
|
172
|
-
- 重点关注关键修改和潜在风险"""
|
173
|
-
|
174
|
-
tool_registry = ToolRegistry()
|
175
|
-
agent = Agent(
|
176
|
-
system_prompt=system_prompt,
|
177
|
-
name="Commit Analysis Agent",
|
178
|
-
summary_prompt=f"""请生成一份详细的commit分析报告,包含以下内容:
|
179
|
-
{ot("REPORT")}
|
180
|
-
# 功能分析
|
181
|
-
[说明本次commit实现或修改了哪些功能]
|
182
|
-
|
183
|
-
# 修改原因
|
184
|
-
[分析进行这些修改的原因,如修复bug、优化性能、添加新功能等]
|
185
|
-
|
186
|
-
# 实现逻辑
|
187
|
-
[详细说明代码修改的具体实现逻辑和思路]
|
188
|
-
|
189
|
-
# 影响范围
|
190
|
-
[评估本次修改可能影响的其他模块或功能]
|
191
|
-
|
192
|
-
# 代码质量
|
193
|
-
[分析代码风格、可读性和可维护性]
|
194
|
-
|
195
|
-
# 测试覆盖
|
196
|
-
[评估是否需要添加或修改测试用例]
|
197
|
-
|
198
|
-
# 最佳实践
|
199
|
-
[检查代码是否符合行业最佳实践和项目规范]
|
200
|
-
{ct("REPORT")}""",
|
201
|
-
output_handler=[tool_registry],
|
202
|
-
llm_type="normal",
|
203
|
-
auto_complete=True,
|
204
|
-
)
|
205
|
-
|
206
|
-
return agent.run(diff_content)
|
207
|
-
|
208
|
-
|
209
|
-
def extract_analysis_report(result: str) -> str:
|
210
|
-
"""从分析结果中提取报告内容
|
211
|
-
|
212
|
-
Args:
|
213
|
-
result: 包含REPORT标签的完整分析结果字符串
|
214
|
-
|
215
|
-
Returns:
|
216
|
-
提取的报告内容,如果未找到REPORT标签则返回空字符串
|
217
|
-
"""
|
218
|
-
search_match = re.search(
|
219
|
-
ot("REPORT") + r"\n(.*?)\n" + ct("REPORT"), result, re.DOTALL
|
220
|
-
)
|
221
|
-
if search_match:
|
222
|
-
return search_match.group(1)
|
223
|
-
return result
|
224
|
-
|
225
|
-
|
226
|
-
def main():
|
227
|
-
"""主函数,用于命令行接口"""
|
228
|
-
import argparse
|
229
|
-
|
230
|
-
init_env("欢迎使用 Jarvis-GitCommitAnalyzer,您的Git Commit分析助手已准备就绪!")
|
231
|
-
|
232
|
-
parser = argparse.ArgumentParser(description="Git Commit Analyzer")
|
233
|
-
group = parser.add_mutually_exclusive_group(required=True)
|
234
|
-
group.add_argument("commit", nargs="?", help="Commit SHA to analyze")
|
235
|
-
group.add_argument(
|
236
|
-
"--range", type=str, help="Commit range to analyze (commit1..commit2)"
|
237
|
-
)
|
238
|
-
parser.add_argument(
|
239
|
-
"--root-dir", type=str, help="Root directory of the codebase", default="."
|
240
|
-
)
|
241
|
-
|
242
|
-
args = parser.parse_args()
|
243
|
-
|
244
|
-
analyzer = GitCommitAnalyzer()
|
245
|
-
if args.range:
|
246
|
-
result = analyzer.execute(
|
247
|
-
{"commit_range": args.range, "root_dir": args.root_dir}
|
248
|
-
)
|
249
|
-
else:
|
250
|
-
result = analyzer.execute(
|
251
|
-
{"commit_sha": args.commit, "root_dir": args.root_dir}
|
252
|
-
)
|
253
|
-
|
254
|
-
if result["success"]:
|
255
|
-
PrettyOutput.section("Commit Information:", OutputType.SUCCESS)
|
256
|
-
PrettyOutput.print(result["stdout"]["commit_info"], OutputType.CODE)
|
257
|
-
PrettyOutput.section("Analysis Report:", OutputType.SUCCESS)
|
258
|
-
report = extract_analysis_report(result["stdout"]["analysis_result"])
|
259
|
-
PrettyOutput.print(report, OutputType.SUCCESS, lang="markdown")
|
260
|
-
else:
|
261
|
-
PrettyOutput.print(result["stderr"], OutputType.WARNING)
|
262
|
-
|
263
|
-
|
264
|
-
if __name__ == "__main__":
|
265
|
-
main()
|
jarvis/jarvis_platform/oyi.py
DELETED
@@ -1,357 +0,0 @@
|
|
1
|
-
import mimetypes
|
2
|
-
import os
|
3
|
-
from typing import Dict, Generator, List, Tuple
|
4
|
-
from jarvis.jarvis_platform.base import BasePlatform
|
5
|
-
import json
|
6
|
-
|
7
|
-
from jarvis.jarvis_utils import http
|
8
|
-
from jarvis.jarvis_utils.output import OutputType, PrettyOutput
|
9
|
-
from jarvis.jarvis_utils.utils import while_success
|
10
|
-
|
11
|
-
|
12
|
-
class OyiModel(BasePlatform):
|
13
|
-
"""Oyi model implementation"""
|
14
|
-
|
15
|
-
BASE_URL = "https://api-10086.rcouyi.com"
|
16
|
-
|
17
|
-
def get_model_list(self) -> List[Tuple[str, str]]:
|
18
|
-
"""Get model list"""
|
19
|
-
self.get_available_models()
|
20
|
-
return [(name, info["desc"]) for name, info in self.models.items()]
|
21
|
-
|
22
|
-
def __init__(self):
|
23
|
-
"""Initialize model"""
|
24
|
-
super().__init__()
|
25
|
-
self.models = {}
|
26
|
-
self.messages = []
|
27
|
-
self.system_prompt = ""
|
28
|
-
self.conversation = None
|
29
|
-
self.first_chat = True
|
30
|
-
|
31
|
-
self.token = os.getenv("OYI_API_KEY")
|
32
|
-
if not self.token:
|
33
|
-
PrettyOutput.print("OYI_API_KEY 未设置", OutputType.WARNING)
|
34
|
-
|
35
|
-
self.model_name = os.getenv("JARVIS_MODEL") or "deepseek-chat"
|
36
|
-
if self.model_name not in [m.split()[0] for m in self.get_available_models()]:
|
37
|
-
PrettyOutput.print(
|
38
|
-
f"警告: 选择的模型 {self.model_name} 不在可用列表中", OutputType.WARNING
|
39
|
-
)
|
40
|
-
|
41
|
-
def set_model_name(self, model_name: str):
|
42
|
-
"""Set model name"""
|
43
|
-
|
44
|
-
self.model_name = model_name
|
45
|
-
|
46
|
-
def create_conversation(self) -> bool:
|
47
|
-
"""Create a new conversation"""
|
48
|
-
try:
|
49
|
-
headers = {
|
50
|
-
"Authorization": f"Bearer {self.token}",
|
51
|
-
"Content-Type": "application/json",
|
52
|
-
"Accept": "application/json",
|
53
|
-
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36",
|
54
|
-
}
|
55
|
-
|
56
|
-
payload = {
|
57
|
-
"id": 0,
|
58
|
-
"roleId": 0,
|
59
|
-
"title": "New conversation",
|
60
|
-
"isLock": False,
|
61
|
-
"systemMessage": "",
|
62
|
-
"params": json.dumps(
|
63
|
-
{
|
64
|
-
"model": self.model_name,
|
65
|
-
"is_webSearch": True,
|
66
|
-
"message": [],
|
67
|
-
"systemMessage": None,
|
68
|
-
"requestMsgCount": 65536,
|
69
|
-
"temperature": 0.8,
|
70
|
-
"speechVoice": "Alloy",
|
71
|
-
"max_tokens": 8192,
|
72
|
-
"chatPluginIds": [],
|
73
|
-
}
|
74
|
-
),
|
75
|
-
}
|
76
|
-
|
77
|
-
response = while_success(
|
78
|
-
lambda: http.post(
|
79
|
-
f"{self.BASE_URL}/chatapi/chat/save", headers=headers, json=payload
|
80
|
-
),
|
81
|
-
sleep_time=5,
|
82
|
-
)
|
83
|
-
|
84
|
-
data = response.json()
|
85
|
-
if data["code"] == 200 and data["type"] == "success":
|
86
|
-
self.conversation = data
|
87
|
-
return True
|
88
|
-
else:
|
89
|
-
PrettyOutput.print(
|
90
|
-
f"创建会话失败: {data['message']}", OutputType.WARNING
|
91
|
-
)
|
92
|
-
return False
|
93
|
-
|
94
|
-
except Exception as e:
|
95
|
-
PrettyOutput.print(f"创建会话失败: {str(e)}", OutputType.ERROR)
|
96
|
-
return False
|
97
|
-
|
98
|
-
def set_system_prompt(self, message: str):
|
99
|
-
"""Set system message"""
|
100
|
-
self.system_prompt = message
|
101
|
-
|
102
|
-
def chat(self, message: str) -> Generator[str, None, None]:
|
103
|
-
"""Execute chat with the model
|
104
|
-
|
105
|
-
Args:
|
106
|
-
message: User input message
|
107
|
-
|
108
|
-
Returns:
|
109
|
-
str: Model response
|
110
|
-
"""
|
111
|
-
try:
|
112
|
-
# 确保有会话ID
|
113
|
-
if not self.conversation:
|
114
|
-
if not self.create_conversation():
|
115
|
-
raise Exception("Failed to create conversation")
|
116
|
-
|
117
|
-
# 1. 发送消息
|
118
|
-
headers = {
|
119
|
-
"Authorization": f"Bearer {self.token}",
|
120
|
-
"Content-Type": "application/json",
|
121
|
-
"Accept": "application/json, text/plain, */*",
|
122
|
-
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36",
|
123
|
-
"Origin": "https://ai.rcouyi.com",
|
124
|
-
"Referer": "https://ai.rcouyi.com/",
|
125
|
-
}
|
126
|
-
|
127
|
-
payload = {
|
128
|
-
"topicId": (
|
129
|
-
self.conversation["result"]["id"] if self.conversation else None
|
130
|
-
),
|
131
|
-
"messages": self.messages,
|
132
|
-
"content": message,
|
133
|
-
"contentFiles": [],
|
134
|
-
}
|
135
|
-
|
136
|
-
# 如果有上传的文件,添加到请求中
|
137
|
-
if self.first_chat:
|
138
|
-
message = self.system_prompt + "\n" + message
|
139
|
-
payload["content"] = message
|
140
|
-
self.first_chat = False
|
141
|
-
|
142
|
-
self.messages.append({"role": "user", "content": message})
|
143
|
-
|
144
|
-
# 发送消息
|
145
|
-
response = while_success(
|
146
|
-
lambda: http.post(
|
147
|
-
f"{self.BASE_URL}/chatapi/chat/message",
|
148
|
-
headers=headers,
|
149
|
-
json=payload,
|
150
|
-
),
|
151
|
-
sleep_time=5,
|
152
|
-
)
|
153
|
-
|
154
|
-
data = response.json()
|
155
|
-
if data["code"] != 200 or data["type"] != "success":
|
156
|
-
error_msg = f"聊天失败: {data.get('message', '未知错误')}"
|
157
|
-
PrettyOutput.print(error_msg, OutputType.WARNING)
|
158
|
-
raise Exception(error_msg)
|
159
|
-
|
160
|
-
message_id = data["result"][-1]
|
161
|
-
|
162
|
-
# 获取响应内容
|
163
|
-
response = while_success(
|
164
|
-
lambda: http.stream_post(
|
165
|
-
f"{self.BASE_URL}/chatapi/chat/message/{message_id}",
|
166
|
-
headers=headers,
|
167
|
-
),
|
168
|
-
sleep_time=5,
|
169
|
-
)
|
170
|
-
|
171
|
-
full_response = ""
|
172
|
-
bin = b""
|
173
|
-
for chunk in response:
|
174
|
-
if chunk:
|
175
|
-
bin += chunk
|
176
|
-
try:
|
177
|
-
text = bin.decode("utf-8")
|
178
|
-
except UnicodeDecodeError:
|
179
|
-
continue
|
180
|
-
full_response += text
|
181
|
-
bin = b""
|
182
|
-
yield text
|
183
|
-
|
184
|
-
self.messages.append({"role": "assistant", "content": full_response})
|
185
|
-
return None
|
186
|
-
except Exception as e:
|
187
|
-
PrettyOutput.print(f"聊天失败: {str(e)}", OutputType.ERROR)
|
188
|
-
raise e
|
189
|
-
|
190
|
-
def name(self) -> str:
|
191
|
-
"""Return model name"""
|
192
|
-
return self.model_name
|
193
|
-
|
194
|
-
@classmethod
|
195
|
-
def platform_name(cls) -> str:
|
196
|
-
"""Return platform name"""
|
197
|
-
return "oyi"
|
198
|
-
|
199
|
-
def delete_chat(self) -> bool:
|
200
|
-
"""Delete current chat session"""
|
201
|
-
try:
|
202
|
-
if not self.conversation:
|
203
|
-
return True
|
204
|
-
|
205
|
-
headers = {
|
206
|
-
"Authorization": f"Bearer {self.token}",
|
207
|
-
"Content-Type": "application/json",
|
208
|
-
"Accept": "application/json, text/plain, */*",
|
209
|
-
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36",
|
210
|
-
"Origin": "https://ai.rcouyi.com",
|
211
|
-
"Referer": "https://ai.rcouyi.com/",
|
212
|
-
}
|
213
|
-
|
214
|
-
response = while_success(
|
215
|
-
lambda: http.post(
|
216
|
-
f"{self.BASE_URL}/chatapi/chat/{self.conversation['result']['id']}", # type: ignore
|
217
|
-
headers=headers,
|
218
|
-
json={},
|
219
|
-
),
|
220
|
-
sleep_time=5,
|
221
|
-
)
|
222
|
-
|
223
|
-
data = response.json()
|
224
|
-
if data["code"] == 200 and data["type"] == "success":
|
225
|
-
self.messages = []
|
226
|
-
self.conversation = None
|
227
|
-
self.first_chat = True
|
228
|
-
return True
|
229
|
-
else:
|
230
|
-
error_msg = f"删除会话失败: {data.get('message', '未知错误')}"
|
231
|
-
PrettyOutput.print(error_msg, OutputType.WARNING)
|
232
|
-
return False
|
233
|
-
|
234
|
-
except Exception as e:
|
235
|
-
PrettyOutput.print(f"删除会话失败: {str(e)}", OutputType.ERROR)
|
236
|
-
return False
|
237
|
-
|
238
|
-
def save(self, file_path: str) -> bool:
|
239
|
-
"""Save chat session to a file."""
|
240
|
-
if not self.conversation:
|
241
|
-
PrettyOutput.print("没有活动的会话可供保存", OutputType.WARNING)
|
242
|
-
return False
|
243
|
-
|
244
|
-
state = {
|
245
|
-
"conversation": self.conversation,
|
246
|
-
"messages": self.messages,
|
247
|
-
"model_name": self.model_name,
|
248
|
-
"system_prompt": self.system_prompt,
|
249
|
-
"first_chat": self.first_chat,
|
250
|
-
}
|
251
|
-
|
252
|
-
try:
|
253
|
-
with open(file_path, "w", encoding="utf-8") as f:
|
254
|
-
json.dump(state, f, ensure_ascii=False, indent=4)
|
255
|
-
self._saved = True
|
256
|
-
PrettyOutput.print(f"会话已成功保存到 {file_path}", OutputType.SUCCESS)
|
257
|
-
return True
|
258
|
-
except Exception as e:
|
259
|
-
PrettyOutput.print(f"保存会话失败: {str(e)}", OutputType.ERROR)
|
260
|
-
return False
|
261
|
-
|
262
|
-
def restore(self, file_path: str) -> bool:
|
263
|
-
"""Restore chat session from a file."""
|
264
|
-
try:
|
265
|
-
with open(file_path, "r", encoding="utf-8") as f:
|
266
|
-
state = json.load(f)
|
267
|
-
|
268
|
-
self.conversation = state.get("conversation")
|
269
|
-
self.messages = state.get("messages", [])
|
270
|
-
self.model_name = state.get("model_name", "deepseek-chat")
|
271
|
-
self.system_prompt = state.get("system_prompt", "")
|
272
|
-
self.first_chat = state.get("first_chat", True)
|
273
|
-
self._saved = True
|
274
|
-
|
275
|
-
PrettyOutput.print(f"从 {file_path} 成功恢复会话", OutputType.SUCCESS)
|
276
|
-
return True
|
277
|
-
except FileNotFoundError:
|
278
|
-
PrettyOutput.print(f"会话文件未找到: {file_path}", OutputType.ERROR)
|
279
|
-
return False
|
280
|
-
except Exception as e:
|
281
|
-
PrettyOutput.print(f"恢复会话失败: {str(e)}", OutputType.ERROR)
|
282
|
-
return False
|
283
|
-
|
284
|
-
def get_available_models(self) -> List[str]:
|
285
|
-
"""Get available model list
|
286
|
-
|
287
|
-
Returns:
|
288
|
-
List[str]: Available model name list
|
289
|
-
"""
|
290
|
-
try:
|
291
|
-
if self.models:
|
292
|
-
return list(self.models.keys())
|
293
|
-
|
294
|
-
headers = {
|
295
|
-
"Content-Type": "application/json",
|
296
|
-
"Accept": "application/json, text/plain, */*",
|
297
|
-
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36",
|
298
|
-
"Origin": "https://ai.rcouyi.com",
|
299
|
-
"Referer": "https://ai.rcouyi.com/",
|
300
|
-
}
|
301
|
-
|
302
|
-
response = while_success(
|
303
|
-
lambda: http.get(
|
304
|
-
"https://ai.rcouyi.com/config/system.json", headers=headers
|
305
|
-
),
|
306
|
-
sleep_time=5,
|
307
|
-
)
|
308
|
-
|
309
|
-
data = response.json()
|
310
|
-
|
311
|
-
# 保存模型信息
|
312
|
-
self.models = {
|
313
|
-
model["value"]: model
|
314
|
-
for model in data.get("model", [])
|
315
|
-
if model.get("enable", False) # 只保存启用的模型
|
316
|
-
}
|
317
|
-
|
318
|
-
# 格式化显示
|
319
|
-
models = []
|
320
|
-
for model in self.models.values():
|
321
|
-
# 基本信息
|
322
|
-
model_name = model["value"]
|
323
|
-
model_str = model["label"]
|
324
|
-
|
325
|
-
# 添加后缀标签
|
326
|
-
suffix = model.get("suffix", [])
|
327
|
-
if suffix:
|
328
|
-
# 处理新格式的suffix (字典列表)
|
329
|
-
if suffix and isinstance(suffix[0], dict):
|
330
|
-
suffix_str = ", ".join(s.get("tag", "") for s in suffix)
|
331
|
-
# 处理旧格式的suffix (字符串列表)
|
332
|
-
else:
|
333
|
-
suffix_str = ", ".join(str(s) for s in suffix)
|
334
|
-
model_str += f" ({suffix_str})"
|
335
|
-
|
336
|
-
# 添加描述或提示
|
337
|
-
info = model.get("tooltip") or model.get("description", "")
|
338
|
-
if info:
|
339
|
-
model_str += f" - {info}"
|
340
|
-
|
341
|
-
model["desc"] = model_str
|
342
|
-
models.append(model_name)
|
343
|
-
|
344
|
-
return sorted(models)
|
345
|
-
|
346
|
-
except Exception as e:
|
347
|
-
PrettyOutput.print(f"获取模型列表失败: {str(e)}", OutputType.WARNING)
|
348
|
-
return []
|
349
|
-
|
350
|
-
def support_upload_files(self) -> bool:
|
351
|
-
return False
|
352
|
-
|
353
|
-
def support_web(self) -> bool:
|
354
|
-
return False
|
355
|
-
|
356
|
-
def upload_files(self, file_list: List[str]) -> bool:
|
357
|
-
return False
|