jarvis-ai-assistant 0.1.222__py3-none-any.whl → 0.7.0__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.
Files changed (162) hide show
  1. jarvis/__init__.py +1 -1
  2. jarvis/jarvis_agent/__init__.py +1143 -245
  3. jarvis/jarvis_agent/agent_manager.py +97 -0
  4. jarvis/jarvis_agent/builtin_input_handler.py +12 -10
  5. jarvis/jarvis_agent/config_editor.py +57 -0
  6. jarvis/jarvis_agent/edit_file_handler.py +392 -99
  7. jarvis/jarvis_agent/event_bus.py +48 -0
  8. jarvis/jarvis_agent/events.py +157 -0
  9. jarvis/jarvis_agent/file_context_handler.py +79 -0
  10. jarvis/jarvis_agent/file_methodology_manager.py +117 -0
  11. jarvis/jarvis_agent/jarvis.py +1117 -147
  12. jarvis/jarvis_agent/main.py +78 -34
  13. jarvis/jarvis_agent/memory_manager.py +195 -0
  14. jarvis/jarvis_agent/methodology_share_manager.py +174 -0
  15. jarvis/jarvis_agent/prompt_manager.py +82 -0
  16. jarvis/jarvis_agent/prompts.py +46 -9
  17. jarvis/jarvis_agent/protocols.py +4 -1
  18. jarvis/jarvis_agent/rewrite_file_handler.py +141 -0
  19. jarvis/jarvis_agent/run_loop.py +146 -0
  20. jarvis/jarvis_agent/session_manager.py +9 -9
  21. jarvis/jarvis_agent/share_manager.py +228 -0
  22. jarvis/jarvis_agent/shell_input_handler.py +23 -3
  23. jarvis/jarvis_agent/stdio_redirect.py +295 -0
  24. jarvis/jarvis_agent/task_analyzer.py +212 -0
  25. jarvis/jarvis_agent/task_manager.py +154 -0
  26. jarvis/jarvis_agent/task_planner.py +496 -0
  27. jarvis/jarvis_agent/tool_executor.py +8 -4
  28. jarvis/jarvis_agent/tool_share_manager.py +139 -0
  29. jarvis/jarvis_agent/user_interaction.py +42 -0
  30. jarvis/jarvis_agent/utils.py +54 -0
  31. jarvis/jarvis_agent/web_bridge.py +189 -0
  32. jarvis/jarvis_agent/web_output_sink.py +53 -0
  33. jarvis/jarvis_agent/web_server.py +751 -0
  34. jarvis/jarvis_c2rust/__init__.py +26 -0
  35. jarvis/jarvis_c2rust/cli.py +613 -0
  36. jarvis/jarvis_c2rust/collector.py +258 -0
  37. jarvis/jarvis_c2rust/library_replacer.py +1122 -0
  38. jarvis/jarvis_c2rust/llm_module_agent.py +1300 -0
  39. jarvis/jarvis_c2rust/optimizer.py +960 -0
  40. jarvis/jarvis_c2rust/scanner.py +1681 -0
  41. jarvis/jarvis_c2rust/transpiler.py +2325 -0
  42. jarvis/jarvis_code_agent/build_validation_config.py +133 -0
  43. jarvis/jarvis_code_agent/code_agent.py +1605 -178
  44. jarvis/jarvis_code_agent/code_analyzer/__init__.py +62 -0
  45. jarvis/jarvis_code_agent/code_analyzer/base_language.py +74 -0
  46. jarvis/jarvis_code_agent/code_analyzer/build_validator/__init__.py +44 -0
  47. jarvis/jarvis_code_agent/code_analyzer/build_validator/base.py +102 -0
  48. jarvis/jarvis_code_agent/code_analyzer/build_validator/cmake.py +59 -0
  49. jarvis/jarvis_code_agent/code_analyzer/build_validator/detector.py +125 -0
  50. jarvis/jarvis_code_agent/code_analyzer/build_validator/fallback.py +69 -0
  51. jarvis/jarvis_code_agent/code_analyzer/build_validator/go.py +38 -0
  52. jarvis/jarvis_code_agent/code_analyzer/build_validator/java_gradle.py +44 -0
  53. jarvis/jarvis_code_agent/code_analyzer/build_validator/java_maven.py +38 -0
  54. jarvis/jarvis_code_agent/code_analyzer/build_validator/makefile.py +50 -0
  55. jarvis/jarvis_code_agent/code_analyzer/build_validator/nodejs.py +93 -0
  56. jarvis/jarvis_code_agent/code_analyzer/build_validator/python.py +129 -0
  57. jarvis/jarvis_code_agent/code_analyzer/build_validator/rust.py +54 -0
  58. jarvis/jarvis_code_agent/code_analyzer/build_validator/validator.py +154 -0
  59. jarvis/jarvis_code_agent/code_analyzer/build_validator.py +43 -0
  60. jarvis/jarvis_code_agent/code_analyzer/context_manager.py +363 -0
  61. jarvis/jarvis_code_agent/code_analyzer/context_recommender.py +18 -0
  62. jarvis/jarvis_code_agent/code_analyzer/dependency_analyzer.py +132 -0
  63. jarvis/jarvis_code_agent/code_analyzer/file_ignore.py +330 -0
  64. jarvis/jarvis_code_agent/code_analyzer/impact_analyzer.py +781 -0
  65. jarvis/jarvis_code_agent/code_analyzer/language_registry.py +185 -0
  66. jarvis/jarvis_code_agent/code_analyzer/language_support.py +89 -0
  67. jarvis/jarvis_code_agent/code_analyzer/languages/__init__.py +31 -0
  68. jarvis/jarvis_code_agent/code_analyzer/languages/c_cpp_language.py +231 -0
  69. jarvis/jarvis_code_agent/code_analyzer/languages/go_language.py +183 -0
  70. jarvis/jarvis_code_agent/code_analyzer/languages/python_language.py +219 -0
  71. jarvis/jarvis_code_agent/code_analyzer/languages/rust_language.py +209 -0
  72. jarvis/jarvis_code_agent/code_analyzer/llm_context_recommender.py +451 -0
  73. jarvis/jarvis_code_agent/code_analyzer/symbol_extractor.py +77 -0
  74. jarvis/jarvis_code_agent/code_analyzer/tree_sitter_extractor.py +48 -0
  75. jarvis/jarvis_code_agent/lint.py +275 -13
  76. jarvis/jarvis_code_agent/utils.py +142 -0
  77. jarvis/jarvis_code_analysis/checklists/loader.py +20 -6
  78. jarvis/jarvis_code_analysis/code_review.py +583 -548
  79. jarvis/jarvis_data/config_schema.json +339 -28
  80. jarvis/jarvis_git_squash/main.py +22 -13
  81. jarvis/jarvis_git_utils/git_commiter.py +171 -55
  82. jarvis/jarvis_mcp/sse_mcp_client.py +22 -15
  83. jarvis/jarvis_mcp/stdio_mcp_client.py +4 -4
  84. jarvis/jarvis_mcp/streamable_mcp_client.py +36 -16
  85. jarvis/jarvis_memory_organizer/memory_organizer.py +753 -0
  86. jarvis/jarvis_methodology/main.py +48 -63
  87. jarvis/jarvis_multi_agent/__init__.py +302 -43
  88. jarvis/jarvis_multi_agent/main.py +70 -24
  89. jarvis/jarvis_platform/ai8.py +40 -23
  90. jarvis/jarvis_platform/base.py +210 -49
  91. jarvis/jarvis_platform/human.py +11 -1
  92. jarvis/jarvis_platform/kimi.py +82 -76
  93. jarvis/jarvis_platform/openai.py +73 -1
  94. jarvis/jarvis_platform/registry.py +8 -15
  95. jarvis/jarvis_platform/tongyi.py +115 -101
  96. jarvis/jarvis_platform/yuanbao.py +89 -63
  97. jarvis/jarvis_platform_manager/main.py +194 -132
  98. jarvis/jarvis_platform_manager/service.py +122 -86
  99. jarvis/jarvis_rag/cli.py +156 -53
  100. jarvis/jarvis_rag/embedding_manager.py +155 -12
  101. jarvis/jarvis_rag/llm_interface.py +10 -13
  102. jarvis/jarvis_rag/query_rewriter.py +63 -12
  103. jarvis/jarvis_rag/rag_pipeline.py +222 -40
  104. jarvis/jarvis_rag/reranker.py +26 -3
  105. jarvis/jarvis_rag/retriever.py +270 -14
  106. jarvis/jarvis_sec/__init__.py +3605 -0
  107. jarvis/jarvis_sec/checkers/__init__.py +32 -0
  108. jarvis/jarvis_sec/checkers/c_checker.py +2680 -0
  109. jarvis/jarvis_sec/checkers/rust_checker.py +1108 -0
  110. jarvis/jarvis_sec/cli.py +116 -0
  111. jarvis/jarvis_sec/report.py +257 -0
  112. jarvis/jarvis_sec/status.py +264 -0
  113. jarvis/jarvis_sec/types.py +20 -0
  114. jarvis/jarvis_sec/workflow.py +219 -0
  115. jarvis/jarvis_smart_shell/main.py +405 -137
  116. jarvis/jarvis_stats/__init__.py +13 -0
  117. jarvis/jarvis_stats/cli.py +387 -0
  118. jarvis/jarvis_stats/stats.py +711 -0
  119. jarvis/jarvis_stats/storage.py +612 -0
  120. jarvis/jarvis_stats/visualizer.py +282 -0
  121. jarvis/jarvis_tools/ask_user.py +1 -0
  122. jarvis/jarvis_tools/base.py +18 -2
  123. jarvis/jarvis_tools/clear_memory.py +239 -0
  124. jarvis/jarvis_tools/cli/main.py +220 -144
  125. jarvis/jarvis_tools/execute_script.py +52 -12
  126. jarvis/jarvis_tools/file_analyzer.py +17 -12
  127. jarvis/jarvis_tools/generate_new_tool.py +46 -24
  128. jarvis/jarvis_tools/read_code.py +277 -18
  129. jarvis/jarvis_tools/read_symbols.py +141 -0
  130. jarvis/jarvis_tools/read_webpage.py +86 -13
  131. jarvis/jarvis_tools/registry.py +294 -90
  132. jarvis/jarvis_tools/retrieve_memory.py +227 -0
  133. jarvis/jarvis_tools/save_memory.py +194 -0
  134. jarvis/jarvis_tools/search_web.py +62 -28
  135. jarvis/jarvis_tools/sub_agent.py +205 -0
  136. jarvis/jarvis_tools/sub_code_agent.py +217 -0
  137. jarvis/jarvis_tools/virtual_tty.py +330 -62
  138. jarvis/jarvis_utils/builtin_replace_map.py +4 -5
  139. jarvis/jarvis_utils/clipboard.py +90 -0
  140. jarvis/jarvis_utils/config.py +607 -50
  141. jarvis/jarvis_utils/embedding.py +3 -0
  142. jarvis/jarvis_utils/fzf.py +57 -0
  143. jarvis/jarvis_utils/git_utils.py +251 -29
  144. jarvis/jarvis_utils/globals.py +174 -17
  145. jarvis/jarvis_utils/http.py +58 -79
  146. jarvis/jarvis_utils/input.py +899 -153
  147. jarvis/jarvis_utils/methodology.py +210 -83
  148. jarvis/jarvis_utils/output.py +220 -137
  149. jarvis/jarvis_utils/utils.py +1906 -135
  150. jarvis_ai_assistant-0.7.0.dist-info/METADATA +465 -0
  151. jarvis_ai_assistant-0.7.0.dist-info/RECORD +192 -0
  152. {jarvis_ai_assistant-0.1.222.dist-info → jarvis_ai_assistant-0.7.0.dist-info}/entry_points.txt +8 -2
  153. jarvis/jarvis_git_details/main.py +0 -265
  154. jarvis/jarvis_platform/oyi.py +0 -357
  155. jarvis/jarvis_tools/edit_file.py +0 -255
  156. jarvis/jarvis_tools/rewrite_file.py +0 -195
  157. jarvis_ai_assistant-0.1.222.dist-info/METADATA +0 -767
  158. jarvis_ai_assistant-0.1.222.dist-info/RECORD +0 -110
  159. /jarvis/{jarvis_git_details → jarvis_memory_organizer}/__init__.py +0 -0
  160. {jarvis_ai_assistant-0.1.222.dist-info → jarvis_ai_assistant-0.7.0.dist-info}/WHEEL +0 -0
  161. {jarvis_ai_assistant-0.1.222.dist-info → jarvis_ai_assistant-0.7.0.dist-info}/licenses/LICENSE +0 -0
  162. {jarvis_ai_assistant-0.1.222.dist-info → jarvis_ai_assistant-0.7.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,227 @@
1
+ # -*- coding: utf-8 -*-
2
+ import json
3
+ from pathlib import Path
4
+ from typing import Any, Dict, List, Optional
5
+
6
+ from jarvis.jarvis_utils.config import get_data_dir, get_max_input_token_count
7
+ from jarvis.jarvis_utils.output import OutputType, PrettyOutput
8
+ from jarvis.jarvis_utils.globals import get_short_term_memories
9
+ from jarvis.jarvis_utils.embedding import get_context_token_count
10
+
11
+
12
+ class RetrieveMemoryTool:
13
+ """检索记忆工具,用于从长短期记忆系统中检索信息"""
14
+
15
+ name = "retrieve_memory"
16
+ description = """从长短期记忆系统中检索信息。
17
+
18
+ 支持的记忆类型:
19
+ - project_long_term: 项目长期记忆(与当前项目相关的信息)
20
+ - global_long_term: 全局长期记忆(通用的信息、用户喜好、知识、方法等)
21
+ - short_term: 短期记忆(当前任务相关的信息)
22
+ - all: 从所有类型中检索
23
+
24
+ 可以通过标签过滤检索结果,支持多个标签(满足任一标签即可)
25
+ 注意:标签数量建议不要超过10个,以保证检索效率
26
+ """
27
+
28
+ parameters = {
29
+ "type": "object",
30
+ "properties": {
31
+ "memory_types": {
32
+ "type": "array",
33
+ "items": {
34
+ "type": "string",
35
+ "enum": [
36
+ "project_long_term",
37
+ "global_long_term",
38
+ "short_term",
39
+ "all",
40
+ ],
41
+ },
42
+ "description": "要检索的记忆类型列表,如果包含'all'则检索所有类型",
43
+ },
44
+ "tags": {
45
+ "type": "array",
46
+ "items": {"type": "string"},
47
+ "description": "用于过滤的标签列表(可选)",
48
+ },
49
+ "limit": {
50
+ "type": "integer",
51
+ "description": "返回结果的最大数量(可选,默认返回所有)",
52
+ "minimum": 1,
53
+ },
54
+ },
55
+ "required": ["memory_types"],
56
+ }
57
+
58
+ def __init__(self):
59
+ """初始化检索记忆工具"""
60
+ self.project_memory_dir = Path(".jarvis/memory")
61
+ self.global_memory_dir = Path(get_data_dir()) / "memory"
62
+
63
+ def _get_memory_dir(self, memory_type: str) -> Path:
64
+ """根据记忆类型获取存储目录"""
65
+ if memory_type == "project_long_term":
66
+ return self.project_memory_dir
67
+ elif memory_type in ["global_long_term", "short_term"]:
68
+ return self.global_memory_dir / memory_type
69
+ else:
70
+ raise ValueError(f"未知的记忆类型: {memory_type}")
71
+
72
+ def _retrieve_from_type(
73
+ self, memory_type: str, tags: Optional[List[str]] = None
74
+ ) -> List[Dict[str, Any]]:
75
+ """从指定类型中检索记忆"""
76
+ memories: List[Dict[str, Any]] = []
77
+
78
+ if memory_type == "short_term":
79
+ # 从全局变量获取短期记忆
80
+ memories = get_short_term_memories(tags)
81
+ else:
82
+ # 从文件系统获取长期记忆
83
+ memory_dir = self._get_memory_dir(memory_type)
84
+
85
+ if not memory_dir.exists():
86
+ return memories
87
+
88
+ # 遍历记忆文件
89
+ for memory_file in memory_dir.glob("*.json"):
90
+ try:
91
+ with open(memory_file, "r", encoding="utf-8") as f:
92
+ memory_data = json.load(f)
93
+
94
+ # 如果指定了标签,检查是否匹配
95
+ if tags:
96
+ memory_tags = memory_data.get("tags", [])
97
+ if not any(tag in memory_tags for tag in tags):
98
+ continue
99
+
100
+ memories.append(memory_data)
101
+ except Exception as e:
102
+ PrettyOutput.print(
103
+ f"读取记忆文件 {memory_file} 失败: {str(e)}", OutputType.WARNING
104
+ )
105
+
106
+ return memories
107
+
108
+ def execute(self, args: Dict[str, Any]) -> Dict[str, Any]:
109
+ """执行检索记忆操作"""
110
+ try:
111
+ memory_types = args.get("memory_types", [])
112
+ tags = args.get("tags", [])
113
+ limit = args.get("limit", None)
114
+
115
+ # 确定要检索的记忆类型
116
+ if "all" in memory_types:
117
+ types_to_search = [
118
+ "project_long_term",
119
+ "global_long_term",
120
+ "short_term",
121
+ ]
122
+ else:
123
+ types_to_search = memory_types
124
+
125
+ # 从各个类型中检索记忆
126
+ all_memories = []
127
+ for memory_type in types_to_search:
128
+ memories = self._retrieve_from_type(memory_type, tags)
129
+ all_memories.extend(memories)
130
+
131
+ # 按创建时间排序(最新的在前)
132
+ all_memories.sort(key=lambda x: x.get("created_at", ""), reverse=True)
133
+
134
+ # 获取最大输入token数的2/3作为记忆的token限制
135
+ max_input_tokens = get_max_input_token_count()
136
+ memory_token_limit = int(max_input_tokens * 2 / 3)
137
+
138
+ # 基于token限制和条数限制筛选记忆
139
+ filtered_memories: List[Dict[str, Any]] = []
140
+ total_tokens = 0
141
+
142
+ for memory in all_memories:
143
+ # 计算当前记忆的token数量
144
+ memory_content = json.dumps(memory, ensure_ascii=False)
145
+ memory_tokens = get_context_token_count(memory_content)
146
+
147
+ # 检查是否超过token限制
148
+ if total_tokens + memory_tokens > memory_token_limit:
149
+
150
+ break
151
+
152
+ # 检查是否超过50条限制
153
+ if len(filtered_memories) >= 50:
154
+
155
+ break
156
+
157
+ filtered_memories.append(memory)
158
+ total_tokens += memory_tokens
159
+
160
+ all_memories = filtered_memories
161
+
162
+ # 如果指定了额外的限制,只返回前N个
163
+ if limit and len(all_memories) > limit:
164
+ all_memories = all_memories[:limit]
165
+
166
+ # 打印结果摘要
167
+
168
+ if tags:
169
+ pass
170
+
171
+ # 格式化为Markdown输出
172
+ markdown_output = "# 记忆检索结果\n\n"
173
+ markdown_output += f"**检索到 {len(all_memories)} 条记忆**\n\n"
174
+
175
+ if tags:
176
+ markdown_output += f"**使用标签过滤**: {', '.join(tags)}\n\n"
177
+
178
+ markdown_output += f"**记忆类型**: {', '.join(types_to_search)}\n\n"
179
+
180
+ markdown_output += "---\n\n"
181
+
182
+ # 输出所有记忆
183
+ for i, memory in enumerate(all_memories):
184
+ markdown_output += f"## {i+1}. {memory.get('id', '未知ID')}\n\n"
185
+ markdown_output += f"**类型**: {memory.get('type', '未知类型')}\n\n"
186
+ markdown_output += f"**标签**: {', '.join(memory.get('tags', []))}\n\n"
187
+ markdown_output += (
188
+ f"**创建时间**: {memory.get('created_at', '未知时间')}\n\n"
189
+ )
190
+
191
+ # 内容部分
192
+ content = memory.get("content", "")
193
+ if content:
194
+ markdown_output += f"**内容**:\n\n{content}\n\n"
195
+
196
+ # 如果有额外的元数据
197
+ metadata = {
198
+ k: v
199
+ for k, v in memory.items()
200
+ if k not in ["id", "type", "tags", "created_at", "content"]
201
+ }
202
+ if metadata:
203
+ markdown_output += "**其他信息**:\n"
204
+ for key, value in metadata.items():
205
+ markdown_output += f"- {key}: {value}\n"
206
+ markdown_output += "\n"
207
+
208
+ markdown_output += "---\n\n"
209
+
210
+ # 如果记忆较多,在终端显示摘要
211
+ if len(all_memories) > 5:
212
+ # 静默模式下不再打印摘要,完整结果已包含在返回的markdown_output中
213
+ for i, memory in enumerate(all_memories[:5]):
214
+ content_preview = memory.get("content", "")[:100]
215
+ if len(memory.get("content", "")) > 100:
216
+ content_preview += "..."
217
+
218
+ return {
219
+ "success": True,
220
+ "stdout": markdown_output,
221
+ "stderr": "",
222
+ }
223
+
224
+ except Exception as e:
225
+ error_msg = f"检索记忆失败: {str(e)}"
226
+ PrettyOutput.print(error_msg, OutputType.ERROR)
227
+ return {"success": False, "stdout": "", "stderr": error_msg}
@@ -0,0 +1,194 @@
1
+ # -*- coding: utf-8 -*-
2
+ import json
3
+ import time
4
+ from datetime import datetime
5
+ from pathlib import Path
6
+ from typing import Any, Dict
7
+
8
+ from jarvis.jarvis_utils.config import get_data_dir
9
+ from jarvis.jarvis_utils.output import OutputType, PrettyOutput
10
+ from jarvis.jarvis_utils.globals import add_short_term_memory
11
+
12
+
13
+ class SaveMemoryTool:
14
+ """保存记忆工具,用于将信息保存到长短期记忆系统"""
15
+
16
+ name = "save_memory"
17
+ description = """保存信息到长短期记忆系统。
18
+
19
+ 支持批量保存多条记忆,可以同时保存不同类型的记忆。
20
+
21
+ 支持的记忆类型:
22
+ - project_long_term: 项目长期记忆(与当前项目相关的信息)
23
+ - global_long_term: 全局长期记忆(通用的信息、用户喜好、知识、方法等)
24
+ - short_term: 短期记忆(当前任务相关的信息)
25
+
26
+ 项目长期记忆存储在当前目录的 .jarvis/memory 下
27
+ 全局长期记忆和短期记忆存储在数据目录的 memory 子目录下
28
+ """
29
+
30
+ parameters = {
31
+ "type": "object",
32
+ "properties": {
33
+ "memories": {
34
+ "type": "array",
35
+ "items": {
36
+ "type": "object",
37
+ "properties": {
38
+ "memory_type": {
39
+ "type": "string",
40
+ "enum": [
41
+ "project_long_term",
42
+ "global_long_term",
43
+ "short_term",
44
+ ],
45
+ "description": "记忆类型",
46
+ },
47
+ "tags": {
48
+ "type": "array",
49
+ "items": {"type": "string"},
50
+ "description": "用于索引记忆的标签列表",
51
+ },
52
+ "content": {
53
+ "type": "string",
54
+ "description": "要保存的记忆内容",
55
+ },
56
+ },
57
+ "required": ["memory_type", "tags", "content"],
58
+ },
59
+ "description": "要保存的记忆列表",
60
+ }
61
+ },
62
+ "required": ["memories"],
63
+ }
64
+
65
+ def __init__(self):
66
+ """初始化保存记忆工具"""
67
+ self.project_memory_dir = Path(".jarvis/memory")
68
+ self.global_memory_dir = Path(get_data_dir()) / "memory"
69
+
70
+ def _get_memory_dir(self, memory_type: str) -> Path:
71
+ """根据记忆类型获取存储目录"""
72
+ if memory_type == "project_long_term":
73
+ return self.project_memory_dir
74
+ elif memory_type in ["global_long_term", "short_term"]:
75
+ return self.global_memory_dir / memory_type
76
+ else:
77
+ raise ValueError(f"未知的记忆类型: {memory_type}")
78
+
79
+ def _generate_memory_id(self) -> str:
80
+ """生成唯一的记忆ID"""
81
+ # 添加微秒级时间戳确保唯一性
82
+ time.sleep(0.001) # 确保不同记忆有不同的时间戳
83
+ return datetime.now().strftime("%Y%m%d_%H%M%S_%f")
84
+
85
+ def _save_single_memory(self, memory_data: Dict[str, Any]) -> Dict[str, Any]:
86
+ """保存单条记忆"""
87
+ memory_type = memory_data["memory_type"]
88
+ tags = memory_data.get("tags", [])
89
+ content = memory_data.get("content", "")
90
+
91
+ # 生成记忆ID
92
+ memory_id = self._generate_memory_id()
93
+
94
+ # 创建记忆对象
95
+ memory_obj = {
96
+ "id": memory_id,
97
+ "type": memory_type,
98
+ "tags": tags,
99
+ "content": content,
100
+ "created_at": datetime.now().isoformat(),
101
+ "updated_at": datetime.now().isoformat(),
102
+ }
103
+
104
+ if memory_type == "short_term":
105
+ # 短期记忆保存到全局变量
106
+ add_short_term_memory(memory_obj)
107
+
108
+ result = {
109
+ "memory_id": memory_id,
110
+ "memory_type": memory_type,
111
+ "tags": tags,
112
+ "storage": "memory",
113
+ "message": f"短期记忆已成功保存到内存,ID: {memory_id}",
114
+ }
115
+ else:
116
+ # 长期记忆保存到文件
117
+ # 获取存储目录并确保存在
118
+ memory_dir = self._get_memory_dir(memory_type)
119
+ memory_dir.mkdir(parents=True, exist_ok=True)
120
+
121
+ # 保存记忆文件
122
+ memory_file = memory_dir / f"{memory_id}.json"
123
+ with open(memory_file, "w", encoding="utf-8") as f:
124
+ json.dump(memory_obj, f, ensure_ascii=False, indent=2)
125
+
126
+ result = {
127
+ "memory_id": memory_id,
128
+ "memory_type": memory_type,
129
+ "tags": tags,
130
+ "file_path": str(memory_file),
131
+ "message": f"记忆已成功保存,ID: {memory_id}",
132
+ }
133
+
134
+ return result
135
+
136
+ def execute(self, args: Dict[str, Any]) -> Dict[str, Any]:
137
+ """执行保存记忆操作"""
138
+ try:
139
+ memories = args.get("memories", [])
140
+
141
+ if not memories:
142
+ return {
143
+ "success": False,
144
+ "stdout": "",
145
+ "stderr": "没有提供要保存的记忆",
146
+ }
147
+
148
+ results = []
149
+ success_count = 0
150
+ failed_count = 0
151
+
152
+ # 保存每条记忆
153
+ for i, memory_data in enumerate(memories):
154
+ try:
155
+ result = self._save_single_memory(memory_data)
156
+ results.append(result)
157
+ success_count += 1
158
+
159
+ # 打印单条记忆保存信息
160
+ memory_data["memory_type"]
161
+ memory_data.get("tags", [])
162
+
163
+ except Exception as e:
164
+ failed_count += 1
165
+ error_msg = f"保存第 {i+1} 条记忆失败: {str(e)}"
166
+ PrettyOutput.print(error_msg, OutputType.ERROR)
167
+ results.append(
168
+ {
169
+ "error": error_msg,
170
+ "memory_type": memory_data.get("memory_type", "unknown"),
171
+ "tags": memory_data.get("tags", []),
172
+ }
173
+ )
174
+
175
+ # 生成总结报告
176
+
177
+ # 构建返回结果
178
+ output = {
179
+ "total": len(memories),
180
+ "success": success_count,
181
+ "failed": failed_count,
182
+ "results": results,
183
+ }
184
+
185
+ return {
186
+ "success": True,
187
+ "stdout": json.dumps(output, ensure_ascii=False, indent=2),
188
+ "stderr": "",
189
+ }
190
+
191
+ except Exception as e:
192
+ error_msg = f"保存记忆失败: {str(e)}"
193
+ PrettyOutput.print(error_msg, OutputType.ERROR)
194
+ return {"success": False, "stdout": "", "stderr": error_msg}
@@ -1,19 +1,27 @@
1
1
  # -*- coding: utf-8 -*-
2
- """A tool for searching the web."""
2
+ """网络搜索工具。"""
3
3
  from typing import Any, Dict
4
4
 
5
- import httpx
6
- from bs4 import BeautifulSoup
7
- from ddgs import DDGS
5
+ import requests # type: ignore[import-untyped]
6
+ from markdownify import markdownify as md # type: ignore
7
+
8
+ # pylint: disable=import-error,missing-module-docstring
9
+ # fmt: off
10
+ from ddgs import DDGS # type: ignore[import-not-found]
11
+ # fmt: on
8
12
 
9
13
  from jarvis.jarvis_agent import Agent
10
14
  from jarvis.jarvis_platform.registry import PlatformRegistry
15
+ from jarvis.jarvis_utils.config import (
16
+ get_web_search_platform_name,
17
+ get_web_search_model_name,
18
+ )
11
19
  from jarvis.jarvis_utils.http import get as http_get
12
20
  from jarvis.jarvis_utils.output import OutputType, PrettyOutput
13
21
 
14
22
 
15
23
  class SearchWebTool:
16
- """A class to handle web searches."""
24
+ """处理网络搜索的类。"""
17
25
 
18
26
  name = "search_web"
19
27
  description = "搜索互联网上的信息"
@@ -24,10 +32,10 @@ class SearchWebTool:
24
32
 
25
33
  def _search_with_ddgs(self, query: str, agent: Agent) -> Dict[str, Any]:
26
34
  # pylint: disable=too-many-locals, broad-except
27
- """Performs a web search, scrapes content, and summarizes the results."""
35
+ """执行网络搜索、抓取内容并总结结果。"""
28
36
  try:
29
- PrettyOutput.print("▶️ 使用 DuckDuckGo 开始网页搜索...", OutputType.INFO)
30
- results = list(DDGS().text(query, max_results=5))
37
+
38
+ results = list(DDGS().text(query, max_results=50, page=3))
31
39
 
32
40
  if not results:
33
41
  return {
@@ -36,25 +44,32 @@ class SearchWebTool:
36
44
  "success": False,
37
45
  }
38
46
 
39
- urls = [r["href"] for r in results]
40
47
  full_content = ""
41
48
  visited_urls = []
49
+ visited_count = 0
50
+
51
+ for r in results:
52
+ if visited_count >= 10:
53
+
54
+ break
55
+
56
+ url = r["href"]
57
+ r.get("title", url)
42
58
 
43
- for url in urls:
44
59
  try:
45
- PrettyOutput.print(f"📄 正在抓取内容: {url}", OutputType.INFO)
46
- response = http_get(url, timeout=10.0, follow_redirects=True)
47
- soup = BeautifulSoup(response.text, "lxml")
48
- body = soup.find("body")
49
- if body:
50
- full_content += body.get_text(" ", strip=True) + "\n\n"
60
+
61
+ response = http_get(url, timeout=10.0, allow_redirects=True)
62
+ content = md(response.text, strip=["script", "style"])
63
+ if content:
64
+ full_content += content + "\n\n"
51
65
  visited_urls.append(url)
52
- except httpx.HTTPStatusError as e:
66
+ visited_count += 1
67
+ except requests.exceptions.HTTPError as e:
53
68
  PrettyOutput.print(
54
69
  f"⚠️ HTTP错误 {e.response.status_code} 访问 {url}",
55
70
  OutputType.WARNING,
56
71
  )
57
- except httpx.RequestError as e:
72
+ except requests.exceptions.RequestException as e:
58
73
  PrettyOutput.print(f"⚠️ 请求错误: {e}", OutputType.WARNING)
59
74
 
60
75
  if not full_content.strip():
@@ -64,12 +79,8 @@ class SearchWebTool:
64
79
  "success": False,
65
80
  }
66
81
 
67
- url_list_str = "\n".join(f" - {u}" for u in visited_urls)
68
- PrettyOutput.print(
69
- f"🔍 已成功访问并处理以下URL:\n{url_list_str}", OutputType.INFO
70
- )
82
+ "\n".join(f" - {u}" for u in visited_urls)
71
83
 
72
- PrettyOutput.print("🧠 正在总结内容...", OutputType.INFO)
73
84
  summary_prompt = f"请为查询“{query}”总结以下内容:\n\n{full_content}"
74
85
 
75
86
  if not agent.model:
@@ -124,6 +135,27 @@ class SearchWebTool:
124
135
  "success": False,
125
136
  }
126
137
 
138
+ # 检查是否配置了专门的 Web 搜索平台和模型
139
+ web_search_platform = get_web_search_platform_name()
140
+ web_search_model = get_web_search_model_name()
141
+
142
+ # 如果配置了专门的 Web 搜索平台和模型,优先使用
143
+ if web_search_platform and web_search_model:
144
+ model = PlatformRegistry().create_platform(web_search_platform)
145
+ if model:
146
+ model.set_model_name(web_search_model)
147
+ if model.support_web():
148
+ model.set_web(True)
149
+ model.set_suppress_output(False)
150
+ response = model.chat_until_success(query)
151
+ if response and response.strip():
152
+ return {
153
+ "stdout": response,
154
+ "stderr": "",
155
+ "success": True,
156
+ }
157
+
158
+ # 否则使用现有的模型选择流程
127
159
  if agent.model.support_web():
128
160
  model = PlatformRegistry().create_platform(agent.model.platform_name())
129
161
  if not model:
@@ -131,11 +163,13 @@ class SearchWebTool:
131
163
  model.set_model_name(agent.model.name())
132
164
  model.set_web(True)
133
165
  model.set_suppress_output(False)
134
- return {
135
- "stdout": model.chat_until_success(query),
136
- "stderr": "",
137
- "success": True,
138
- }
166
+ response = model.chat_until_success(query)
167
+ if response and response.strip():
168
+ return {
169
+ "stdout": response,
170
+ "stderr": "",
171
+ "success": True,
172
+ }
139
173
 
140
174
  return self._search_with_ddgs(query, agent)
141
175