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.
- jarvis/__init__.py +1 -1
- jarvis/jarvis_agent/__init__.py +1143 -245
- jarvis/jarvis_agent/agent_manager.py +97 -0
- jarvis/jarvis_agent/builtin_input_handler.py +12 -10
- jarvis/jarvis_agent/config_editor.py +57 -0
- jarvis/jarvis_agent/edit_file_handler.py +392 -99
- jarvis/jarvis_agent/event_bus.py +48 -0
- jarvis/jarvis_agent/events.py +157 -0
- jarvis/jarvis_agent/file_context_handler.py +79 -0
- jarvis/jarvis_agent/file_methodology_manager.py +117 -0
- jarvis/jarvis_agent/jarvis.py +1117 -147
- jarvis/jarvis_agent/main.py +78 -34
- jarvis/jarvis_agent/memory_manager.py +195 -0
- jarvis/jarvis_agent/methodology_share_manager.py +174 -0
- jarvis/jarvis_agent/prompt_manager.py +82 -0
- jarvis/jarvis_agent/prompts.py +46 -9
- jarvis/jarvis_agent/protocols.py +4 -1
- jarvis/jarvis_agent/rewrite_file_handler.py +141 -0
- jarvis/jarvis_agent/run_loop.py +146 -0
- jarvis/jarvis_agent/session_manager.py +9 -9
- jarvis/jarvis_agent/share_manager.py +228 -0
- jarvis/jarvis_agent/shell_input_handler.py +23 -3
- jarvis/jarvis_agent/stdio_redirect.py +295 -0
- jarvis/jarvis_agent/task_analyzer.py +212 -0
- jarvis/jarvis_agent/task_manager.py +154 -0
- jarvis/jarvis_agent/task_planner.py +496 -0
- jarvis/jarvis_agent/tool_executor.py +8 -4
- jarvis/jarvis_agent/tool_share_manager.py +139 -0
- jarvis/jarvis_agent/user_interaction.py +42 -0
- jarvis/jarvis_agent/utils.py +54 -0
- jarvis/jarvis_agent/web_bridge.py +189 -0
- jarvis/jarvis_agent/web_output_sink.py +53 -0
- jarvis/jarvis_agent/web_server.py +751 -0
- jarvis/jarvis_c2rust/__init__.py +26 -0
- jarvis/jarvis_c2rust/cli.py +613 -0
- jarvis/jarvis_c2rust/collector.py +258 -0
- jarvis/jarvis_c2rust/library_replacer.py +1122 -0
- jarvis/jarvis_c2rust/llm_module_agent.py +1300 -0
- jarvis/jarvis_c2rust/optimizer.py +960 -0
- jarvis/jarvis_c2rust/scanner.py +1681 -0
- jarvis/jarvis_c2rust/transpiler.py +2325 -0
- jarvis/jarvis_code_agent/build_validation_config.py +133 -0
- jarvis/jarvis_code_agent/code_agent.py +1605 -178
- jarvis/jarvis_code_agent/code_analyzer/__init__.py +62 -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 +102 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/cmake.py +59 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/detector.py +125 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/fallback.py +69 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/go.py +38 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/java_gradle.py +44 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/java_maven.py +38 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/makefile.py +50 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/nodejs.py +93 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/python.py +129 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/rust.py +54 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/validator.py +154 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator.py +43 -0
- jarvis/jarvis_code_agent/code_analyzer/context_manager.py +363 -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 +89 -0
- jarvis/jarvis_code_agent/code_analyzer/languages/__init__.py +31 -0
- jarvis/jarvis_code_agent/code_analyzer/languages/c_cpp_language.py +231 -0
- jarvis/jarvis_code_agent/code_analyzer/languages/go_language.py +183 -0
- jarvis/jarvis_code_agent/code_analyzer/languages/python_language.py +219 -0
- jarvis/jarvis_code_agent/code_analyzer/languages/rust_language.py +209 -0
- jarvis/jarvis_code_agent/code_analyzer/llm_context_recommender.py +451 -0
- jarvis/jarvis_code_agent/code_analyzer/symbol_extractor.py +77 -0
- jarvis/jarvis_code_agent/code_analyzer/tree_sitter_extractor.py +48 -0
- jarvis/jarvis_code_agent/lint.py +275 -13
- jarvis/jarvis_code_agent/utils.py +142 -0
- jarvis/jarvis_code_analysis/checklists/loader.py +20 -6
- jarvis/jarvis_code_analysis/code_review.py +583 -548
- jarvis/jarvis_data/config_schema.json +339 -28
- jarvis/jarvis_git_squash/main.py +22 -13
- jarvis/jarvis_git_utils/git_commiter.py +171 -55
- jarvis/jarvis_mcp/sse_mcp_client.py +22 -15
- jarvis/jarvis_mcp/stdio_mcp_client.py +4 -4
- jarvis/jarvis_mcp/streamable_mcp_client.py +36 -16
- jarvis/jarvis_memory_organizer/memory_organizer.py +753 -0
- jarvis/jarvis_methodology/main.py +48 -63
- jarvis/jarvis_multi_agent/__init__.py +302 -43
- jarvis/jarvis_multi_agent/main.py +70 -24
- jarvis/jarvis_platform/ai8.py +40 -23
- jarvis/jarvis_platform/base.py +210 -49
- jarvis/jarvis_platform/human.py +11 -1
- jarvis/jarvis_platform/kimi.py +82 -76
- jarvis/jarvis_platform/openai.py +73 -1
- jarvis/jarvis_platform/registry.py +8 -15
- jarvis/jarvis_platform/tongyi.py +115 -101
- jarvis/jarvis_platform/yuanbao.py +89 -63
- jarvis/jarvis_platform_manager/main.py +194 -132
- jarvis/jarvis_platform_manager/service.py +122 -86
- jarvis/jarvis_rag/cli.py +156 -53
- jarvis/jarvis_rag/embedding_manager.py +155 -12
- jarvis/jarvis_rag/llm_interface.py +10 -13
- jarvis/jarvis_rag/query_rewriter.py +63 -12
- jarvis/jarvis_rag/rag_pipeline.py +222 -40
- jarvis/jarvis_rag/reranker.py +26 -3
- jarvis/jarvis_rag/retriever.py +270 -14
- jarvis/jarvis_sec/__init__.py +3605 -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 +116 -0
- jarvis/jarvis_sec/report.py +257 -0
- jarvis/jarvis_sec/status.py +264 -0
- jarvis/jarvis_sec/types.py +20 -0
- jarvis/jarvis_sec/workflow.py +219 -0
- jarvis/jarvis_smart_shell/main.py +405 -137
- jarvis/jarvis_stats/__init__.py +13 -0
- jarvis/jarvis_stats/cli.py +387 -0
- jarvis/jarvis_stats/stats.py +711 -0
- jarvis/jarvis_stats/storage.py +612 -0
- jarvis/jarvis_stats/visualizer.py +282 -0
- jarvis/jarvis_tools/ask_user.py +1 -0
- jarvis/jarvis_tools/base.py +18 -2
- jarvis/jarvis_tools/clear_memory.py +239 -0
- jarvis/jarvis_tools/cli/main.py +220 -144
- jarvis/jarvis_tools/execute_script.py +52 -12
- jarvis/jarvis_tools/file_analyzer.py +17 -12
- jarvis/jarvis_tools/generate_new_tool.py +46 -24
- jarvis/jarvis_tools/read_code.py +277 -18
- jarvis/jarvis_tools/read_symbols.py +141 -0
- jarvis/jarvis_tools/read_webpage.py +86 -13
- jarvis/jarvis_tools/registry.py +294 -90
- jarvis/jarvis_tools/retrieve_memory.py +227 -0
- jarvis/jarvis_tools/save_memory.py +194 -0
- jarvis/jarvis_tools/search_web.py +62 -28
- jarvis/jarvis_tools/sub_agent.py +205 -0
- jarvis/jarvis_tools/sub_code_agent.py +217 -0
- jarvis/jarvis_tools/virtual_tty.py +330 -62
- jarvis/jarvis_utils/builtin_replace_map.py +4 -5
- jarvis/jarvis_utils/clipboard.py +90 -0
- jarvis/jarvis_utils/config.py +607 -50
- jarvis/jarvis_utils/embedding.py +3 -0
- jarvis/jarvis_utils/fzf.py +57 -0
- jarvis/jarvis_utils/git_utils.py +251 -29
- jarvis/jarvis_utils/globals.py +174 -17
- jarvis/jarvis_utils/http.py +58 -79
- jarvis/jarvis_utils/input.py +899 -153
- jarvis/jarvis_utils/methodology.py +210 -83
- jarvis/jarvis_utils/output.py +220 -137
- jarvis/jarvis_utils/utils.py +1906 -135
- jarvis_ai_assistant-0.7.0.dist-info/METADATA +465 -0
- jarvis_ai_assistant-0.7.0.dist-info/RECORD +192 -0
- {jarvis_ai_assistant-0.1.222.dist-info → jarvis_ai_assistant-0.7.0.dist-info}/entry_points.txt +8 -2
- jarvis/jarvis_git_details/main.py +0 -265
- jarvis/jarvis_platform/oyi.py +0 -357
- jarvis/jarvis_tools/edit_file.py +0 -255
- jarvis/jarvis_tools/rewrite_file.py +0 -195
- jarvis_ai_assistant-0.1.222.dist-info/METADATA +0 -767
- jarvis_ai_assistant-0.1.222.dist-info/RECORD +0 -110
- /jarvis/{jarvis_git_details → jarvis_memory_organizer}/__init__.py +0 -0
- {jarvis_ai_assistant-0.1.222.dist-info → jarvis_ai_assistant-0.7.0.dist-info}/WHEEL +0 -0
- {jarvis_ai_assistant-0.1.222.dist-info → jarvis_ai_assistant-0.7.0.dist-info}/licenses/LICENSE +0 -0
- {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
|
-
"""
|
|
2
|
+
"""网络搜索工具。"""
|
|
3
3
|
from typing import Any, Dict
|
|
4
4
|
|
|
5
|
-
import
|
|
6
|
-
from
|
|
7
|
-
|
|
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
|
-
"""
|
|
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
|
-
"""
|
|
35
|
+
"""执行网络搜索、抓取内容并总结结果。"""
|
|
28
36
|
try:
|
|
29
|
-
|
|
30
|
-
results = list(DDGS().text(query, max_results=
|
|
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
|
-
|
|
46
|
-
response = http_get(url, timeout=10.0,
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
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
|
|