jarvis-ai-assistant 0.3.30__py3-none-any.whl → 0.7.6__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/__init__.py +458 -152
- jarvis/jarvis_agent/agent_manager.py +17 -13
- jarvis/jarvis_agent/builtin_input_handler.py +2 -6
- jarvis/jarvis_agent/config_editor.py +2 -7
- jarvis/jarvis_agent/event_bus.py +82 -12
- jarvis/jarvis_agent/file_context_handler.py +329 -0
- jarvis/jarvis_agent/file_methodology_manager.py +3 -4
- jarvis/jarvis_agent/jarvis.py +628 -55
- jarvis/jarvis_agent/language_extractors/__init__.py +57 -0
- jarvis/jarvis_agent/language_extractors/c_extractor.py +21 -0
- jarvis/jarvis_agent/language_extractors/cpp_extractor.py +21 -0
- jarvis/jarvis_agent/language_extractors/go_extractor.py +21 -0
- jarvis/jarvis_agent/language_extractors/java_extractor.py +84 -0
- jarvis/jarvis_agent/language_extractors/javascript_extractor.py +79 -0
- jarvis/jarvis_agent/language_extractors/python_extractor.py +21 -0
- jarvis/jarvis_agent/language_extractors/rust_extractor.py +21 -0
- jarvis/jarvis_agent/language_extractors/typescript_extractor.py +84 -0
- jarvis/jarvis_agent/language_support_info.py +486 -0
- jarvis/jarvis_agent/main.py +34 -10
- jarvis/jarvis_agent/memory_manager.py +7 -16
- jarvis/jarvis_agent/methodology_share_manager.py +10 -16
- jarvis/jarvis_agent/prompt_manager.py +1 -1
- jarvis/jarvis_agent/prompts.py +193 -171
- jarvis/jarvis_agent/protocols.py +8 -12
- jarvis/jarvis_agent/run_loop.py +105 -9
- jarvis/jarvis_agent/session_manager.py +2 -3
- jarvis/jarvis_agent/share_manager.py +20 -22
- jarvis/jarvis_agent/shell_input_handler.py +1 -2
- jarvis/jarvis_agent/stdio_redirect.py +295 -0
- jarvis/jarvis_agent/task_analyzer.py +31 -6
- jarvis/jarvis_agent/task_manager.py +11 -27
- jarvis/jarvis_agent/tool_executor.py +2 -3
- jarvis/jarvis_agent/tool_share_manager.py +12 -24
- jarvis/jarvis_agent/utils.py +5 -1
- jarvis/jarvis_agent/web_bridge.py +189 -0
- jarvis/jarvis_agent/web_output_sink.py +53 -0
- jarvis/jarvis_agent/web_server.py +786 -0
- jarvis/jarvis_c2rust/__init__.py +26 -0
- jarvis/jarvis_c2rust/cli.py +575 -0
- jarvis/jarvis_c2rust/collector.py +250 -0
- jarvis/jarvis_c2rust/constants.py +26 -0
- jarvis/jarvis_c2rust/library_replacer.py +1254 -0
- jarvis/jarvis_c2rust/llm_module_agent.py +1272 -0
- jarvis/jarvis_c2rust/loaders.py +207 -0
- jarvis/jarvis_c2rust/models.py +28 -0
- jarvis/jarvis_c2rust/optimizer.py +2157 -0
- jarvis/jarvis_c2rust/scanner.py +1681 -0
- jarvis/jarvis_c2rust/transpiler.py +2983 -0
- jarvis/jarvis_c2rust/utils.py +385 -0
- jarvis/jarvis_code_agent/build_validation_config.py +132 -0
- jarvis/jarvis_code_agent/code_agent.py +1371 -220
- jarvis/jarvis_code_agent/code_analyzer/__init__.py +65 -0
- jarvis/jarvis_code_agent/code_analyzer/base_language.py +74 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/__init__.py +44 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/base.py +106 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/cmake.py +74 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/detector.py +125 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/fallback.py +72 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/go.py +70 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/java_gradle.py +53 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/java_maven.py +47 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/makefile.py +61 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/nodejs.py +110 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/python.py +154 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/rust.py +110 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/validator.py +153 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator.py +43 -0
- jarvis/jarvis_code_agent/code_analyzer/context_manager.py +648 -0
- jarvis/jarvis_code_agent/code_analyzer/context_recommender.py +18 -0
- jarvis/jarvis_code_agent/code_analyzer/dependency_analyzer.py +132 -0
- jarvis/jarvis_code_agent/code_analyzer/file_ignore.py +330 -0
- jarvis/jarvis_code_agent/code_analyzer/impact_analyzer.py +781 -0
- jarvis/jarvis_code_agent/code_analyzer/language_registry.py +185 -0
- jarvis/jarvis_code_agent/code_analyzer/language_support.py +110 -0
- jarvis/jarvis_code_agent/code_analyzer/languages/__init__.py +49 -0
- jarvis/jarvis_code_agent/code_analyzer/languages/c_cpp_language.py +299 -0
- jarvis/jarvis_code_agent/code_analyzer/languages/go_language.py +215 -0
- jarvis/jarvis_code_agent/code_analyzer/languages/java_language.py +212 -0
- jarvis/jarvis_code_agent/code_analyzer/languages/javascript_language.py +254 -0
- jarvis/jarvis_code_agent/code_analyzer/languages/python_language.py +269 -0
- jarvis/jarvis_code_agent/code_analyzer/languages/rust_language.py +281 -0
- jarvis/jarvis_code_agent/code_analyzer/languages/typescript_language.py +280 -0
- jarvis/jarvis_code_agent/code_analyzer/llm_context_recommender.py +605 -0
- jarvis/jarvis_code_agent/code_analyzer/structured_code.py +556 -0
- jarvis/jarvis_code_agent/code_analyzer/symbol_extractor.py +252 -0
- jarvis/jarvis_code_agent/code_analyzer/tree_sitter_extractor.py +58 -0
- jarvis/jarvis_code_agent/lint.py +501 -8
- jarvis/jarvis_code_agent/utils.py +141 -0
- jarvis/jarvis_code_analysis/code_review.py +493 -584
- jarvis/jarvis_data/config_schema.json +128 -12
- jarvis/jarvis_git_squash/main.py +4 -5
- jarvis/jarvis_git_utils/git_commiter.py +82 -75
- jarvis/jarvis_mcp/sse_mcp_client.py +22 -29
- jarvis/jarvis_mcp/stdio_mcp_client.py +12 -13
- jarvis/jarvis_mcp/streamable_mcp_client.py +15 -14
- jarvis/jarvis_memory_organizer/memory_organizer.py +55 -74
- jarvis/jarvis_methodology/main.py +32 -48
- jarvis/jarvis_multi_agent/__init__.py +287 -55
- jarvis/jarvis_multi_agent/main.py +36 -4
- jarvis/jarvis_platform/base.py +524 -202
- jarvis/jarvis_platform/human.py +7 -8
- jarvis/jarvis_platform/kimi.py +30 -36
- jarvis/jarvis_platform/openai.py +88 -25
- jarvis/jarvis_platform/registry.py +26 -10
- jarvis/jarvis_platform/tongyi.py +24 -25
- jarvis/jarvis_platform/yuanbao.py +32 -43
- jarvis/jarvis_platform_manager/main.py +66 -77
- jarvis/jarvis_platform_manager/service.py +8 -13
- jarvis/jarvis_rag/cli.py +53 -55
- jarvis/jarvis_rag/embedding_manager.py +13 -18
- jarvis/jarvis_rag/llm_interface.py +8 -9
- jarvis/jarvis_rag/query_rewriter.py +10 -21
- jarvis/jarvis_rag/rag_pipeline.py +24 -27
- jarvis/jarvis_rag/reranker.py +4 -5
- jarvis/jarvis_rag/retriever.py +28 -30
- jarvis/jarvis_sec/__init__.py +305 -0
- jarvis/jarvis_sec/agents.py +143 -0
- jarvis/jarvis_sec/analysis.py +276 -0
- jarvis/jarvis_sec/checkers/__init__.py +32 -0
- jarvis/jarvis_sec/checkers/c_checker.py +2680 -0
- jarvis/jarvis_sec/checkers/rust_checker.py +1108 -0
- jarvis/jarvis_sec/cli.py +139 -0
- jarvis/jarvis_sec/clustering.py +1439 -0
- jarvis/jarvis_sec/file_manager.py +427 -0
- jarvis/jarvis_sec/parsers.py +73 -0
- jarvis/jarvis_sec/prompts.py +268 -0
- jarvis/jarvis_sec/report.py +336 -0
- jarvis/jarvis_sec/review.py +453 -0
- jarvis/jarvis_sec/status.py +264 -0
- jarvis/jarvis_sec/types.py +20 -0
- jarvis/jarvis_sec/utils.py +499 -0
- jarvis/jarvis_sec/verification.py +848 -0
- jarvis/jarvis_sec/workflow.py +226 -0
- jarvis/jarvis_smart_shell/main.py +38 -87
- jarvis/jarvis_stats/cli.py +2 -2
- jarvis/jarvis_stats/stats.py +8 -8
- jarvis/jarvis_stats/storage.py +15 -21
- jarvis/jarvis_stats/visualizer.py +1 -1
- jarvis/jarvis_tools/clear_memory.py +3 -20
- jarvis/jarvis_tools/cli/main.py +21 -23
- jarvis/jarvis_tools/edit_file.py +1019 -132
- jarvis/jarvis_tools/execute_script.py +83 -25
- jarvis/jarvis_tools/file_analyzer.py +6 -9
- jarvis/jarvis_tools/generate_new_tool.py +14 -21
- jarvis/jarvis_tools/lsp_client.py +1552 -0
- jarvis/jarvis_tools/methodology.py +2 -3
- jarvis/jarvis_tools/read_code.py +1736 -35
- jarvis/jarvis_tools/read_symbols.py +140 -0
- jarvis/jarvis_tools/read_webpage.py +12 -13
- jarvis/jarvis_tools/registry.py +427 -200
- jarvis/jarvis_tools/retrieve_memory.py +20 -19
- jarvis/jarvis_tools/rewrite_file.py +72 -158
- jarvis/jarvis_tools/save_memory.py +3 -15
- jarvis/jarvis_tools/search_web.py +18 -18
- jarvis/jarvis_tools/sub_agent.py +36 -43
- jarvis/jarvis_tools/sub_code_agent.py +25 -26
- jarvis/jarvis_tools/virtual_tty.py +55 -33
- jarvis/jarvis_utils/clipboard.py +7 -10
- jarvis/jarvis_utils/config.py +232 -45
- jarvis/jarvis_utils/embedding.py +8 -5
- jarvis/jarvis_utils/fzf.py +8 -8
- jarvis/jarvis_utils/git_utils.py +225 -36
- jarvis/jarvis_utils/globals.py +3 -3
- jarvis/jarvis_utils/http.py +1 -1
- jarvis/jarvis_utils/input.py +99 -48
- jarvis/jarvis_utils/jsonnet_compat.py +465 -0
- jarvis/jarvis_utils/methodology.py +52 -48
- jarvis/jarvis_utils/utils.py +819 -491
- jarvis_ai_assistant-0.7.6.dist-info/METADATA +600 -0
- jarvis_ai_assistant-0.7.6.dist-info/RECORD +218 -0
- {jarvis_ai_assistant-0.3.30.dist-info → jarvis_ai_assistant-0.7.6.dist-info}/entry_points.txt +4 -0
- jarvis/jarvis_agent/config.py +0 -92
- jarvis/jarvis_agent/edit_file_handler.py +0 -296
- jarvis/jarvis_platform/ai8.py +0 -332
- jarvis/jarvis_tools/ask_user.py +0 -54
- jarvis_ai_assistant-0.3.30.dist-info/METADATA +0 -381
- jarvis_ai_assistant-0.3.30.dist-info/RECORD +0 -137
- {jarvis_ai_assistant-0.3.30.dist-info → jarvis_ai_assistant-0.7.6.dist-info}/WHEEL +0 -0
- {jarvis_ai_assistant-0.3.30.dist-info → jarvis_ai_assistant-0.7.6.dist-info}/licenses/LICENSE +0 -0
- {jarvis_ai_assistant-0.3.30.dist-info → jarvis_ai_assistant-0.7.6.dist-info}/top_level.txt +0 -0
jarvis/jarvis_platform/human.py
CHANGED
|
@@ -12,7 +12,6 @@ from typing import Generator, List, Tuple
|
|
|
12
12
|
from jarvis.jarvis_platform.base import BasePlatform
|
|
13
13
|
from jarvis.jarvis_utils.clipboard import copy_to_clipboard
|
|
14
14
|
from jarvis.jarvis_utils.input import get_multiline_input
|
|
15
|
-
from jarvis.jarvis_utils.output import OutputType, PrettyOutput
|
|
16
15
|
|
|
17
16
|
|
|
18
17
|
class HumanPlatform(BasePlatform):
|
|
@@ -39,7 +38,7 @@ class HumanPlatform(BasePlatform):
|
|
|
39
38
|
if model_name == "human":
|
|
40
39
|
self.model_name = model_name
|
|
41
40
|
else:
|
|
42
|
-
|
|
41
|
+
print(f"❌ 错误:不支持的模型: {model_name}")
|
|
43
42
|
|
|
44
43
|
def chat(self, message: str) -> Generator[str, None, None]:
|
|
45
44
|
"""发送消息并获取人类响应"""
|
|
@@ -66,7 +65,7 @@ class HumanPlatform(BasePlatform):
|
|
|
66
65
|
|
|
67
66
|
def upload_files(self, file_list: List[str]) -> bool:
|
|
68
67
|
"""文件上传功能,人类平台不需要实际处理"""
|
|
69
|
-
|
|
68
|
+
print("⚠️ 人类交互平台不支持文件上传")
|
|
70
69
|
return False
|
|
71
70
|
|
|
72
71
|
def delete_chat(self) -> bool:
|
|
@@ -88,10 +87,10 @@ class HumanPlatform(BasePlatform):
|
|
|
88
87
|
with open(file_path, "w", encoding="utf-8") as f:
|
|
89
88
|
json.dump(state, f, ensure_ascii=False, indent=4)
|
|
90
89
|
self._saved = True
|
|
91
|
-
|
|
90
|
+
print(f"✅ 会话已成功保存到 {file_path}")
|
|
92
91
|
return True
|
|
93
92
|
except Exception as e:
|
|
94
|
-
|
|
93
|
+
print(f"❌ 保存会话失败: {str(e)}")
|
|
95
94
|
return False
|
|
96
95
|
|
|
97
96
|
def restore(self, file_path: str) -> bool:
|
|
@@ -106,13 +105,13 @@ class HumanPlatform(BasePlatform):
|
|
|
106
105
|
self.first_message = state.get("first_message", True)
|
|
107
106
|
self._saved = True
|
|
108
107
|
|
|
109
|
-
|
|
108
|
+
print(f"✅ 从 {file_path} 成功恢复会话")
|
|
110
109
|
return True
|
|
111
110
|
except FileNotFoundError:
|
|
112
|
-
|
|
111
|
+
print(f"❌ 会话文件未找到: {file_path}")
|
|
113
112
|
return False
|
|
114
113
|
except Exception as e:
|
|
115
|
-
|
|
114
|
+
print(f"❌ 恢复会话失败: {str(e)}")
|
|
116
115
|
return False
|
|
117
116
|
|
|
118
117
|
def name(self) -> str:
|
jarvis/jarvis_platform/kimi.py
CHANGED
|
@@ -9,7 +9,6 @@ from typing import Dict, Generator, List, Tuple
|
|
|
9
9
|
|
|
10
10
|
from jarvis.jarvis_platform.base import BasePlatform
|
|
11
11
|
from jarvis.jarvis_utils import http
|
|
12
|
-
from jarvis.jarvis_utils.output import OutputType, PrettyOutput
|
|
13
12
|
from jarvis.jarvis_utils.utils import while_success
|
|
14
13
|
|
|
15
14
|
|
|
@@ -37,7 +36,7 @@ class KimiModel(BasePlatform):
|
|
|
37
36
|
self.chat_id = "" # 当前会话ID
|
|
38
37
|
self.api_key = os.getenv("KIMI_API_KEY") # 从环境变量获取API密钥
|
|
39
38
|
if not self.api_key:
|
|
40
|
-
|
|
39
|
+
print("⚠️ KIMI_API_KEY 未设置")
|
|
41
40
|
self.auth_header = f"Bearer {self.api_key}" # 认证头信息
|
|
42
41
|
self.uploaded_files = [] # 存储已上传文件的信息
|
|
43
42
|
self.chat_id = "" # 当前会话ID
|
|
@@ -65,18 +64,15 @@ class KimiModel(BasePlatform):
|
|
|
65
64
|
}
|
|
66
65
|
try:
|
|
67
66
|
response = while_success(
|
|
68
|
-
lambda: http.post(url, headers=headers, data=payload)
|
|
69
|
-
sleep_time=5,
|
|
67
|
+
lambda: http.post(url, headers=headers, data=payload)
|
|
70
68
|
)
|
|
71
69
|
if response.status_code != 200:
|
|
72
|
-
|
|
73
|
-
f"错误:创建会话失败:{response.json()}", OutputType.ERROR
|
|
74
|
-
)
|
|
70
|
+
print(f"❌ 错误:创建会话失败:{response.json()}")
|
|
75
71
|
return False
|
|
76
72
|
self.chat_id = response.json()["id"]
|
|
77
73
|
return True
|
|
78
74
|
except Exception as e:
|
|
79
|
-
|
|
75
|
+
print(f"❌ 错误:创建会话失败:{e}")
|
|
80
76
|
return False
|
|
81
77
|
|
|
82
78
|
def _get_presigned_url(self, filename: str, action: str) -> Dict:
|
|
@@ -93,8 +89,7 @@ class KimiModel(BasePlatform):
|
|
|
93
89
|
}
|
|
94
90
|
|
|
95
91
|
response = while_success(
|
|
96
|
-
lambda: http.post(url, headers=headers, data=payload)
|
|
97
|
-
)
|
|
92
|
+
lambda: http.post(url, headers=headers, data=payload) )
|
|
98
93
|
return response.json()
|
|
99
94
|
|
|
100
95
|
def support_upload_files(self) -> bool:
|
|
@@ -107,11 +102,11 @@ class KimiModel(BasePlatform):
|
|
|
107
102
|
with open(file_path, "rb") as f:
|
|
108
103
|
content = f.read()
|
|
109
104
|
response = while_success(
|
|
110
|
-
lambda: http.put(presigned_url, data=content)
|
|
105
|
+
lambda: http.put(presigned_url, data=content)
|
|
111
106
|
)
|
|
112
107
|
return response.status_code == 200
|
|
113
108
|
except Exception as e:
|
|
114
|
-
|
|
109
|
+
print(f"❌ 错误:上传文件失败:{e}")
|
|
115
110
|
return False
|
|
116
111
|
|
|
117
112
|
def _get_file_info(self, file_data: Dict, name: str, file_type: str) -> Dict:
|
|
@@ -134,8 +129,7 @@ class KimiModel(BasePlatform):
|
|
|
134
129
|
}
|
|
135
130
|
|
|
136
131
|
response = while_success(
|
|
137
|
-
lambda: http.post(url, headers=headers, data=payload)
|
|
138
|
-
)
|
|
132
|
+
lambda: http.post(url, headers=headers, data=payload) )
|
|
139
133
|
return response.json()
|
|
140
134
|
|
|
141
135
|
def _wait_for_parse(self, file_id: str) -> bool:
|
|
@@ -153,7 +147,6 @@ class KimiModel(BasePlatform):
|
|
|
153
147
|
payload = {"ids": [file_id]}
|
|
154
148
|
response_stream = while_success(
|
|
155
149
|
lambda: http.stream_post(url, headers=headers, json=payload),
|
|
156
|
-
sleep_time=5,
|
|
157
150
|
)
|
|
158
151
|
|
|
159
152
|
# 处理流式响应
|
|
@@ -171,7 +164,7 @@ class KimiModel(BasePlatform):
|
|
|
171
164
|
return True
|
|
172
165
|
elif status == "failed":
|
|
173
166
|
return False
|
|
174
|
-
except
|
|
167
|
+
except Exception:
|
|
175
168
|
continue
|
|
176
169
|
|
|
177
170
|
retry_count += 1
|
|
@@ -185,11 +178,11 @@ class KimiModel(BasePlatform):
|
|
|
185
178
|
return True
|
|
186
179
|
|
|
187
180
|
if not self.chat_id:
|
|
188
|
-
|
|
181
|
+
print("ℹ️ 正在创建聊天会话...")
|
|
189
182
|
if not self._create_chat():
|
|
190
|
-
|
|
183
|
+
print("❌ 创建聊天会话失败")
|
|
191
184
|
return False
|
|
192
|
-
|
|
185
|
+
print("✅ 创建聊天会话成功")
|
|
193
186
|
|
|
194
187
|
uploaded_files = []
|
|
195
188
|
for index, file_path in enumerate(file_list, 1):
|
|
@@ -220,22 +213,26 @@ class KimiModel(BasePlatform):
|
|
|
220
213
|
log_lines.append(f"文件处理完成: {file_name}")
|
|
221
214
|
else:
|
|
222
215
|
log_lines.append(f"文件解析失败: {file_name}")
|
|
223
|
-
|
|
216
|
+
joined_logs = '\n'.join(log_lines)
|
|
217
|
+
print(f"❌ {joined_logs}")
|
|
224
218
|
return False
|
|
225
219
|
else:
|
|
226
220
|
uploaded_files.append(file_info)
|
|
227
221
|
log_lines.append(f"图片处理完成: {file_name}")
|
|
228
222
|
else:
|
|
229
223
|
log_lines.append(f"文件上传失败: {file_name}")
|
|
230
|
-
|
|
224
|
+
joined_logs = '\n'.join(log_lines)
|
|
225
|
+
print(f"❌ {joined_logs}")
|
|
231
226
|
return False
|
|
232
227
|
|
|
233
228
|
# 成功路径统一输出本文件的处理日志
|
|
234
|
-
|
|
229
|
+
joined_logs = '\n'.join(log_lines)
|
|
230
|
+
print(f"ℹ️ {joined_logs}")
|
|
235
231
|
|
|
236
232
|
except Exception as e:
|
|
237
233
|
log_lines.append(f"处理文件出错 {file_path}: {str(e)}")
|
|
238
|
-
|
|
234
|
+
joined_logs = '\n'.join(log_lines)
|
|
235
|
+
print(f"❌ {joined_logs}")
|
|
239
236
|
return False
|
|
240
237
|
|
|
241
238
|
self.uploaded_files = uploaded_files
|
|
@@ -287,7 +284,6 @@ class KimiModel(BasePlatform):
|
|
|
287
284
|
# 使用新的stream_post接口发送消息请求,获取流式响应
|
|
288
285
|
response_stream = while_success(
|
|
289
286
|
lambda: http.stream_post(url, headers=headers, json=payload),
|
|
290
|
-
sleep_time=5,
|
|
291
287
|
)
|
|
292
288
|
|
|
293
289
|
# 处理流式响应
|
|
@@ -306,7 +302,7 @@ class KimiModel(BasePlatform):
|
|
|
306
302
|
text = data.get("text", "")
|
|
307
303
|
if text:
|
|
308
304
|
yield text
|
|
309
|
-
except
|
|
305
|
+
except Exception:
|
|
310
306
|
continue
|
|
311
307
|
|
|
312
308
|
return None
|
|
@@ -327,7 +323,7 @@ class KimiModel(BasePlatform):
|
|
|
327
323
|
|
|
328
324
|
try:
|
|
329
325
|
response = while_success(
|
|
330
|
-
lambda: http.delete(url, headers=headers)
|
|
326
|
+
lambda: http.delete(url, headers=headers)
|
|
331
327
|
)
|
|
332
328
|
if response.status_code == 200:
|
|
333
329
|
self.chat_id = ""
|
|
@@ -335,18 +331,16 @@ class KimiModel(BasePlatform):
|
|
|
335
331
|
self.first_chat = True # 重置first_chat标记
|
|
336
332
|
return True
|
|
337
333
|
else:
|
|
338
|
-
|
|
339
|
-
f"删除会话失败: HTTP {response.status_code}", OutputType.WARNING
|
|
340
|
-
)
|
|
334
|
+
print(f"⚠️ 删除会话失败: HTTP {response.status_code}")
|
|
341
335
|
return False
|
|
342
336
|
except Exception as e:
|
|
343
|
-
|
|
337
|
+
print(f"❌ 删除会话时发生错误: {str(e)}")
|
|
344
338
|
return False
|
|
345
339
|
|
|
346
340
|
def save(self, file_path: str) -> bool:
|
|
347
341
|
"""Save chat session to a file."""
|
|
348
342
|
if not self.chat_id:
|
|
349
|
-
|
|
343
|
+
print("⚠️ 没有活动的会话可供保存")
|
|
350
344
|
return False
|
|
351
345
|
|
|
352
346
|
state = {
|
|
@@ -361,10 +355,10 @@ class KimiModel(BasePlatform):
|
|
|
361
355
|
with open(file_path, "w", encoding="utf-8") as f:
|
|
362
356
|
json.dump(state, f, ensure_ascii=False, indent=4)
|
|
363
357
|
self._saved = True
|
|
364
|
-
|
|
358
|
+
print(f"✅ 会话已成功保存到 {file_path}")
|
|
365
359
|
return True
|
|
366
360
|
except Exception as e:
|
|
367
|
-
|
|
361
|
+
print(f"❌ 保存会话失败: {str(e)}")
|
|
368
362
|
return False
|
|
369
363
|
|
|
370
364
|
def restore(self, file_path: str) -> bool:
|
|
@@ -380,13 +374,13 @@ class KimiModel(BasePlatform):
|
|
|
380
374
|
self.uploaded_files = state.get("uploaded_files", [])
|
|
381
375
|
self._saved = True
|
|
382
376
|
|
|
383
|
-
|
|
377
|
+
print(f"✅ 从 {file_path} 成功恢复会话")
|
|
384
378
|
return True
|
|
385
379
|
except FileNotFoundError:
|
|
386
|
-
|
|
380
|
+
print(f"❌ 会话文件未找到: {file_path}")
|
|
387
381
|
return False
|
|
388
382
|
except Exception as e:
|
|
389
|
-
|
|
383
|
+
print(f"❌ 恢复会话失败: {str(e)}")
|
|
390
384
|
return False
|
|
391
385
|
|
|
392
386
|
def name(self) -> str:
|
jarvis/jarvis_platform/openai.py
CHANGED
|
@@ -6,7 +6,6 @@ from typing import Dict, Generator, List, Tuple
|
|
|
6
6
|
from openai import OpenAI
|
|
7
7
|
|
|
8
8
|
from jarvis.jarvis_platform.base import BasePlatform
|
|
9
|
-
from jarvis.jarvis_utils.output import OutputType, PrettyOutput
|
|
10
9
|
|
|
11
10
|
|
|
12
11
|
class OpenAIModel(BasePlatform):
|
|
@@ -19,12 +18,37 @@ class OpenAIModel(BasePlatform):
|
|
|
19
18
|
self.system_message = ""
|
|
20
19
|
self.api_key = os.getenv("OPENAI_API_KEY")
|
|
21
20
|
if not self.api_key:
|
|
22
|
-
|
|
21
|
+
print("⚠️ OPENAI_API_KEY 未设置")
|
|
23
22
|
|
|
24
23
|
self.base_url = os.getenv("OPENAI_API_BASE", "https://api.openai.com/v1")
|
|
25
24
|
self.model_name = os.getenv("JARVIS_MODEL") or "gpt-4o"
|
|
26
25
|
|
|
27
|
-
|
|
26
|
+
# Optional: Inject extra HTTP headers via environment variable
|
|
27
|
+
# Expected format: OPENAI_EXTRA_HEADERS='{"Header-Name": "value", "X-Trace": "abc"}'
|
|
28
|
+
headers_str = os.getenv("OPENAI_EXTRA_HEADERS")
|
|
29
|
+
self.extra_headers: Dict[str, str] = {}
|
|
30
|
+
if headers_str:
|
|
31
|
+
try:
|
|
32
|
+
parsed = json.loads(headers_str)
|
|
33
|
+
if isinstance(parsed, dict):
|
|
34
|
+
# Ensure all header keys/values are strings
|
|
35
|
+
self.extra_headers = {str(k): str(v) for k, v in parsed.items()}
|
|
36
|
+
else:
|
|
37
|
+
print("⚠️ OPENAI_EXTRA_HEADERS 应为 JSON 对象,如 {'X-Source':'jarvis'}")
|
|
38
|
+
except Exception as e:
|
|
39
|
+
print(f"⚠️ 解析 OPENAI_EXTRA_HEADERS 失败: {e}")
|
|
40
|
+
|
|
41
|
+
# Initialize OpenAI client, try to pass default headers if SDK supports it
|
|
42
|
+
try:
|
|
43
|
+
if self.extra_headers:
|
|
44
|
+
self.client = OpenAI(api_key=self.api_key, base_url=self.base_url, default_headers=self.extra_headers)
|
|
45
|
+
else:
|
|
46
|
+
self.client = OpenAI(api_key=self.api_key, base_url=self.base_url)
|
|
47
|
+
except TypeError:
|
|
48
|
+
# Fallback: SDK version may not support default_headers
|
|
49
|
+
self.client = OpenAI(api_key=self.api_key, base_url=self.base_url)
|
|
50
|
+
if self.extra_headers:
|
|
51
|
+
print("⚠️ 当前 OpenAI SDK 不支持 default_headers,未能注入额外 HTTP 头")
|
|
28
52
|
self.messages: List[Dict[str, str]] = []
|
|
29
53
|
self.system_message = ""
|
|
30
54
|
|
|
@@ -57,7 +81,7 @@ class OpenAIModel(BasePlatform):
|
|
|
57
81
|
model_list.append((model.id, model.id))
|
|
58
82
|
return model_list
|
|
59
83
|
except Exception as e:
|
|
60
|
-
|
|
84
|
+
print(f"❌ 获取模型列表失败:{str(e)}")
|
|
61
85
|
return []
|
|
62
86
|
|
|
63
87
|
def set_model_name(self, model_name: str):
|
|
@@ -100,26 +124,65 @@ class OpenAIModel(BasePlatform):
|
|
|
100
124
|
# Add user message to history
|
|
101
125
|
self.messages.append({"role": "user", "content": message})
|
|
102
126
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
127
|
+
# 累计完整响应
|
|
128
|
+
accumulated_response = ""
|
|
129
|
+
|
|
130
|
+
# 循环处理,直到不是因为长度限制而结束
|
|
131
|
+
while True:
|
|
132
|
+
response = self.client.chat.completions.create(
|
|
133
|
+
model=self.model_name, # Use the configured model name
|
|
134
|
+
messages=self.messages, # type: ignore
|
|
135
|
+
stream=True,
|
|
136
|
+
) # type: ignore
|
|
137
|
+
|
|
138
|
+
full_response = ""
|
|
139
|
+
finish_reason = None
|
|
140
|
+
|
|
141
|
+
for chunk in response:
|
|
142
|
+
if chunk.choices and len(chunk.choices) > 0:
|
|
143
|
+
choice = chunk.choices[0]
|
|
144
|
+
|
|
145
|
+
# 检查 finish_reason(通常在最后一个 chunk 中)
|
|
146
|
+
if choice.finish_reason:
|
|
147
|
+
finish_reason = choice.finish_reason
|
|
148
|
+
|
|
149
|
+
# 获取内容增量
|
|
150
|
+
if choice.delta and choice.delta.content:
|
|
151
|
+
text = choice.delta.content
|
|
152
|
+
full_response += text
|
|
153
|
+
accumulated_response += text
|
|
154
|
+
yield text
|
|
155
|
+
|
|
156
|
+
# 如果是因为长度限制而结束,继续获取剩余内容
|
|
157
|
+
if finish_reason == "length":
|
|
158
|
+
# 将已获取的内容追加到消息历史中,以便下次请求时模型知道已生成的内容
|
|
159
|
+
if self.messages and self.messages[-1].get("role") == "assistant":
|
|
160
|
+
# 追加到现有的 assistant 消息
|
|
161
|
+
self.messages[-1]["content"] += full_response
|
|
162
|
+
else:
|
|
163
|
+
# 创建新的 assistant 消息
|
|
164
|
+
self.messages.append({"role": "assistant", "content": full_response})
|
|
165
|
+
|
|
166
|
+
# 添加一个继续请求的用户消息,让模型继续生成
|
|
167
|
+
self.messages.append({"role": "user", "content": "请继续。"})
|
|
168
|
+
# 继续循环,获取剩余内容
|
|
169
|
+
continue
|
|
170
|
+
else:
|
|
171
|
+
# 正常结束(stop、null 或其他原因)
|
|
172
|
+
# 将完整响应添加到消息历史
|
|
173
|
+
if accumulated_response:
|
|
174
|
+
if self.messages and self.messages[-1].get("role") == "assistant":
|
|
175
|
+
# 如果最后一条是 assistant 消息,追加本次的内容
|
|
176
|
+
self.messages[-1]["content"] += full_response
|
|
177
|
+
else:
|
|
178
|
+
# 创建新的 assistant 消息,使用累计的完整响应
|
|
179
|
+
self.messages.append({"role": "assistant", "content": accumulated_response})
|
|
180
|
+
break
|
|
118
181
|
|
|
119
182
|
return None
|
|
120
183
|
|
|
121
184
|
except Exception as e:
|
|
122
|
-
|
|
185
|
+
print(f"❌ 对话失败:{str(e)}")
|
|
123
186
|
raise Exception(f"Chat failed: {str(e)}")
|
|
124
187
|
|
|
125
188
|
def name(self) -> str:
|
|
@@ -168,10 +231,10 @@ class OpenAIModel(BasePlatform):
|
|
|
168
231
|
with open(file_path, "w", encoding="utf-8") as f:
|
|
169
232
|
json.dump(state, f, ensure_ascii=False, indent=4)
|
|
170
233
|
self._saved = True
|
|
171
|
-
|
|
234
|
+
print(f"✅ 会话已成功保存到 {file_path}")
|
|
172
235
|
return True
|
|
173
236
|
except Exception as e:
|
|
174
|
-
|
|
237
|
+
print(f"❌ 保存会话失败: {str(e)}")
|
|
175
238
|
return False
|
|
176
239
|
|
|
177
240
|
def restore(self, file_path: str) -> bool:
|
|
@@ -185,13 +248,13 @@ class OpenAIModel(BasePlatform):
|
|
|
185
248
|
# atexit.register(self.delete_chat)
|
|
186
249
|
self._saved = True
|
|
187
250
|
|
|
188
|
-
|
|
251
|
+
print(f"✅ 从 {file_path} 成功恢复会话")
|
|
189
252
|
return True
|
|
190
253
|
except FileNotFoundError:
|
|
191
|
-
|
|
254
|
+
print(f"❌ 会话文件未找到: {file_path}")
|
|
192
255
|
return False
|
|
193
256
|
except Exception as e:
|
|
194
|
-
|
|
257
|
+
print(f"❌ 恢复会话失败: {str(e)}")
|
|
195
258
|
return False
|
|
196
259
|
|
|
197
260
|
def support_web(self) -> bool:
|
|
@@ -7,11 +7,14 @@ from typing import Dict, List, Optional, Type
|
|
|
7
7
|
|
|
8
8
|
from jarvis.jarvis_platform.base import BasePlatform
|
|
9
9
|
from jarvis.jarvis_utils.config import (
|
|
10
|
+
get_cheap_model_name,
|
|
11
|
+
get_cheap_platform_name,
|
|
10
12
|
get_data_dir,
|
|
11
13
|
get_normal_model_name,
|
|
12
14
|
get_normal_platform_name,
|
|
15
|
+
get_smart_model_name,
|
|
16
|
+
get_smart_platform_name,
|
|
13
17
|
)
|
|
14
|
-
from jarvis.jarvis_utils.output import OutputType, PrettyOutput
|
|
15
18
|
|
|
16
19
|
REQUIRED_METHODS = [
|
|
17
20
|
("chat", ["message"]), # 方法名和参数列表
|
|
@@ -42,7 +45,7 @@ class PlatformRegistry:
|
|
|
42
45
|
):
|
|
43
46
|
pass
|
|
44
47
|
except Exception as e:
|
|
45
|
-
|
|
48
|
+
print(f"❌ 创建平台目录失败: {str(e)}")
|
|
46
49
|
return ""
|
|
47
50
|
return user_platform_dir
|
|
48
51
|
|
|
@@ -77,10 +80,7 @@ class PlatformRegistry:
|
|
|
77
80
|
missing_methods.append(f"{method_name}(parameter mismatch)")
|
|
78
81
|
|
|
79
82
|
if missing_methods:
|
|
80
|
-
|
|
81
|
-
f"平台 {platform_class.__name__} 缺少必要的方法: {', '.join(missing_methods)}",
|
|
82
|
-
OutputType.WARNING,
|
|
83
|
-
)
|
|
83
|
+
print(f"⚠️ 平台 {platform_class.__name__} 缺少必要的方法: {', '.join(missing_methods)}")
|
|
84
84
|
return False
|
|
85
85
|
|
|
86
86
|
return True
|
|
@@ -99,7 +99,7 @@ class PlatformRegistry:
|
|
|
99
99
|
|
|
100
100
|
# 确保目录存在
|
|
101
101
|
if not os.path.exists(directory):
|
|
102
|
-
|
|
102
|
+
print(f"⚠️ 平台目录不存在: {directory}")
|
|
103
103
|
return platforms
|
|
104
104
|
|
|
105
105
|
# 获取目录的包名
|
|
@@ -149,7 +149,8 @@ class PlatformRegistry:
|
|
|
149
149
|
error_lines.append(f"加载平台 {module_name} 失败: {str(e)}")
|
|
150
150
|
|
|
151
151
|
if error_lines:
|
|
152
|
-
|
|
152
|
+
joined_errors = '\n'.join(error_lines)
|
|
153
|
+
print(f"❌ {joined_errors}")
|
|
153
154
|
return platforms
|
|
154
155
|
|
|
155
156
|
@staticmethod
|
|
@@ -179,13 +180,28 @@ class PlatformRegistry:
|
|
|
179
180
|
self.register_platform(platform_name, platform_class)
|
|
180
181
|
|
|
181
182
|
def get_normal_platform(self) -> BasePlatform:
|
|
183
|
+
"""获取正常操作的平台实例"""
|
|
182
184
|
platform_name = get_normal_platform_name()
|
|
183
185
|
model_name = get_normal_model_name()
|
|
184
186
|
platform = self.create_platform(platform_name)
|
|
185
187
|
platform.set_model_name(model_name) # type: ignore
|
|
186
188
|
return platform # type: ignore
|
|
187
189
|
|
|
190
|
+
def get_cheap_platform(self) -> BasePlatform:
|
|
191
|
+
"""获取廉价操作的平台实例"""
|
|
192
|
+
platform_name = get_cheap_platform_name()
|
|
193
|
+
model_name = get_cheap_model_name()
|
|
194
|
+
platform = self.create_platform(platform_name)
|
|
195
|
+
platform.set_model_name(model_name) # type: ignore
|
|
196
|
+
return platform # type: ignore
|
|
188
197
|
|
|
198
|
+
def get_smart_platform(self) -> BasePlatform:
|
|
199
|
+
"""获取智能操作的平台实例"""
|
|
200
|
+
platform_name = get_smart_platform_name()
|
|
201
|
+
model_name = get_smart_model_name()
|
|
202
|
+
platform = self.create_platform(platform_name)
|
|
203
|
+
platform.set_model_name(model_name) # type: ignore
|
|
204
|
+
return platform # type: ignore
|
|
189
205
|
|
|
190
206
|
def register_platform(self, name: str, platform_class: Type[BasePlatform]) -> None:
|
|
191
207
|
"""Register platform class
|
|
@@ -207,7 +223,7 @@ class PlatformRegistry:
|
|
|
207
223
|
BasePlatform: Platform instance
|
|
208
224
|
"""
|
|
209
225
|
if name not in self.platforms:
|
|
210
|
-
|
|
226
|
+
print(f"⚠️ 未找到平台: {name}")
|
|
211
227
|
return None
|
|
212
228
|
|
|
213
229
|
try:
|
|
@@ -215,7 +231,7 @@ class PlatformRegistry:
|
|
|
215
231
|
platform = self.platforms[name]()
|
|
216
232
|
return platform
|
|
217
233
|
except Exception as e:
|
|
218
|
-
|
|
234
|
+
print(f"❌ 创建平台失败: {str(e)}")
|
|
219
235
|
return None
|
|
220
236
|
|
|
221
237
|
def get_available_platforms(self) -> List[str]:
|