jarvis-ai-assistant 0.2.6__py3-none-any.whl → 0.2.7__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 +12 -1
- jarvis/jarvis_agent/jarvis.py +239 -7
- jarvis/jarvis_code_agent/code_agent.py +1 -0
- jarvis/jarvis_data/config_schema.json +5 -0
- jarvis/jarvis_stats/storage.py +2 -2
- jarvis/jarvis_tools/clear_memory.py +252 -0
- jarvis/jarvis_tools/retrieve_memory.py +17 -11
- jarvis/jarvis_tools/save_memory.py +136 -75
- jarvis/jarvis_utils/config.py +10 -0
- jarvis/jarvis_utils/input.py +5 -3
- jarvis/jarvis_utils/methodology.py +41 -6
- jarvis/jarvis_utils/output.py +1 -1
- {jarvis_ai_assistant-0.2.6.dist-info → jarvis_ai_assistant-0.2.7.dist-info}/METADATA +1 -1
- {jarvis_ai_assistant-0.2.6.dist-info → jarvis_ai_assistant-0.2.7.dist-info}/RECORD +19 -18
- {jarvis_ai_assistant-0.2.6.dist-info → jarvis_ai_assistant-0.2.7.dist-info}/WHEEL +0 -0
- {jarvis_ai_assistant-0.2.6.dist-info → jarvis_ai_assistant-0.2.7.dist-info}/entry_points.txt +0 -0
- {jarvis_ai_assistant-0.2.6.dist-info → jarvis_ai_assistant-0.2.7.dist-info}/licenses/LICENSE +0 -0
- {jarvis_ai_assistant-0.2.6.dist-info → jarvis_ai_assistant-0.2.7.dist-info}/top_level.txt +0 -0
jarvis/__init__.py
CHANGED
jarvis/jarvis_agent/__init__.py
CHANGED
@@ -614,8 +614,19 @@ class Agent:
|
|
614
614
|
memory_tags = get_all_memory_tags()
|
615
615
|
memory_tags_prompt = ""
|
616
616
|
|
617
|
+
# 检查是否有save_memory工具
|
618
|
+
tool_registry = self.get_tool_registry()
|
619
|
+
has_save_memory = False
|
620
|
+
if tool_registry:
|
621
|
+
tool_names = [tool.name for tool in tool_registry.tools.values()]
|
622
|
+
has_save_memory = "save_memory" in tool_names
|
623
|
+
|
624
|
+
# 如果有save_memory工具,添加记录关键信息的提示
|
625
|
+
if has_save_memory:
|
626
|
+
memory_tags_prompt = "\n\n💡 提示:在分析任务之前,建议使用 save_memory 工具将关键信息记录下来,便于后续检索和复用。"
|
627
|
+
|
617
628
|
if any(tags for tags in memory_tags.values()):
|
618
|
-
memory_tags_prompt
|
629
|
+
memory_tags_prompt += "\n\n系统中存在以下记忆标签,你可以使用 retrieve_memory 工具检索相关记忆:"
|
619
630
|
for memory_type, tags in memory_tags.items():
|
620
631
|
if tags:
|
621
632
|
type_name = {
|
jarvis/jarvis_agent/jarvis.py
CHANGED
@@ -4,7 +4,7 @@ import shutil
|
|
4
4
|
import subprocess
|
5
5
|
import sys
|
6
6
|
from pathlib import Path
|
7
|
-
from typing import Dict, Optional
|
7
|
+
from typing import Dict, Optional, List
|
8
8
|
|
9
9
|
import typer
|
10
10
|
import yaml # type: ignore
|
@@ -91,13 +91,19 @@ def _select_task(tasks: Dict[str, str]) -> str:
|
|
91
91
|
selected_task = tasks[task_names[choice - 1]]
|
92
92
|
PrettyOutput.print(f"将要执行任务:\n {selected_task}", OutputType.INFO)
|
93
93
|
# 询问是否需要补充信息
|
94
|
-
need_additional = user_confirm(
|
94
|
+
need_additional = user_confirm(
|
95
|
+
"需要为此任务添加补充信息吗?", default=False
|
96
|
+
)
|
95
97
|
if need_additional:
|
96
98
|
additional_input = get_multiline_input("请输入补充信息:")
|
97
99
|
if additional_input:
|
98
|
-
selected_task =
|
100
|
+
selected_task = (
|
101
|
+
f"{selected_task}\n\n补充信息:\n{additional_input}"
|
102
|
+
)
|
99
103
|
return selected_task
|
100
|
-
PrettyOutput.print(
|
104
|
+
PrettyOutput.print(
|
105
|
+
"无效的选择。请选择列表中的一个号码。", OutputType.WARNING
|
106
|
+
)
|
101
107
|
|
102
108
|
except (KeyboardInterrupt, EOFError):
|
103
109
|
return ""
|
@@ -183,6 +189,217 @@ def _get_and_run_task(agent: Agent, task_content: Optional[str] = None) -> None:
|
|
183
189
|
raise typer.Exit(code=0)
|
184
190
|
|
185
191
|
|
192
|
+
def _parse_selection(selection_str: str, max_value: int) -> List[int]:
|
193
|
+
"""解析用户输入的选择字符串,支持逗号分隔和范围选择
|
194
|
+
|
195
|
+
例如: "1,2,3,4-9,20" -> [1, 2, 3, 4, 5, 6, 7, 8, 9, 20]
|
196
|
+
"""
|
197
|
+
selected: set[int] = set()
|
198
|
+
parts = selection_str.split(",")
|
199
|
+
|
200
|
+
for part in parts:
|
201
|
+
part = part.strip()
|
202
|
+
if "-" in part:
|
203
|
+
# 处理范围选择
|
204
|
+
try:
|
205
|
+
start_str, end_str = part.split("-")
|
206
|
+
start_num = int(start_str.strip())
|
207
|
+
end_num = int(end_str.strip())
|
208
|
+
if 1 <= start_num <= max_value and 1 <= end_num <= max_value:
|
209
|
+
selected.update(range(start_num, end_num + 1))
|
210
|
+
except ValueError:
|
211
|
+
continue
|
212
|
+
else:
|
213
|
+
# 处理单个数字
|
214
|
+
try:
|
215
|
+
num = int(part)
|
216
|
+
if 1 <= num <= max_value:
|
217
|
+
selected.add(num)
|
218
|
+
except ValueError:
|
219
|
+
continue
|
220
|
+
|
221
|
+
return sorted(list(selected))
|
222
|
+
|
223
|
+
|
224
|
+
def _handle_share_methodology(config_file: Optional[str] = None) -> None:
|
225
|
+
"""处理方法论分享功能"""
|
226
|
+
from jarvis.jarvis_utils.config import (
|
227
|
+
get_central_methodology_repo,
|
228
|
+
get_methodology_dirs,
|
229
|
+
get_data_dir,
|
230
|
+
)
|
231
|
+
import glob
|
232
|
+
import json
|
233
|
+
import shutil
|
234
|
+
|
235
|
+
# 获取中心方法论仓库配置
|
236
|
+
central_repo = get_central_methodology_repo()
|
237
|
+
if not central_repo:
|
238
|
+
PrettyOutput.print(
|
239
|
+
"错误:未配置中心方法论仓库(JARVIS_CENTRAL_METHODOLOGY_REPO)",
|
240
|
+
OutputType.ERROR,
|
241
|
+
)
|
242
|
+
PrettyOutput.print("请在配置文件中设置中心方法论仓库的Git地址", OutputType.INFO)
|
243
|
+
raise typer.Exit(code=1)
|
244
|
+
|
245
|
+
# 克隆或更新中心方法论仓库
|
246
|
+
central_repo_path = os.path.join(get_data_dir(), "central_methodology_repo")
|
247
|
+
if not os.path.exists(central_repo_path):
|
248
|
+
PrettyOutput.print(f"正在克隆中心方法论仓库...", OutputType.INFO)
|
249
|
+
subprocess.run(["git", "clone", central_repo, central_repo_path], check=True)
|
250
|
+
else:
|
251
|
+
PrettyOutput.print(f"正在更新中心方法论仓库...", OutputType.INFO)
|
252
|
+
subprocess.run(["git", "pull"], cwd=central_repo_path, check=True)
|
253
|
+
|
254
|
+
# 获取中心仓库中已有的方法论
|
255
|
+
existing_methodologies = {} # 改为字典,存储 problem_type -> content 的映射
|
256
|
+
for filepath in glob.glob(os.path.join(central_repo_path, "*.json")):
|
257
|
+
try:
|
258
|
+
with open(filepath, "r", encoding="utf-8", errors="ignore") as f:
|
259
|
+
methodology = json.load(f)
|
260
|
+
problem_type = methodology.get("problem_type", "")
|
261
|
+
content = methodology.get("content", "")
|
262
|
+
if problem_type and content:
|
263
|
+
existing_methodologies[problem_type] = content
|
264
|
+
except Exception:
|
265
|
+
pass
|
266
|
+
|
267
|
+
# 获取所有方法论目录
|
268
|
+
from jarvis.jarvis_utils.methodology import _get_methodology_directory
|
269
|
+
|
270
|
+
methodology_dirs = [_get_methodology_directory()] + get_methodology_dirs()
|
271
|
+
|
272
|
+
# 收集所有方法论文件(排除中心仓库目录和已存在的方法论)
|
273
|
+
all_methodologies = {}
|
274
|
+
methodology_files = []
|
275
|
+
seen_problem_types = set() # 用于去重
|
276
|
+
|
277
|
+
for directory in set(methodology_dirs):
|
278
|
+
# 跳过中心仓库目录
|
279
|
+
if os.path.abspath(directory) == os.path.abspath(central_repo_path):
|
280
|
+
continue
|
281
|
+
|
282
|
+
if not os.path.isdir(directory):
|
283
|
+
continue
|
284
|
+
|
285
|
+
for filepath in glob.glob(os.path.join(directory, "*.json")):
|
286
|
+
try:
|
287
|
+
with open(filepath, "r", encoding="utf-8", errors="ignore") as f:
|
288
|
+
methodology = json.load(f)
|
289
|
+
problem_type = methodology.get("problem_type", "")
|
290
|
+
content = methodology.get("content", "")
|
291
|
+
# 基于内容判断是否已存在于中心仓库
|
292
|
+
is_duplicate = False
|
293
|
+
if problem_type in existing_methodologies:
|
294
|
+
# 如果problem_type相同,比较内容
|
295
|
+
if (
|
296
|
+
content.strip()
|
297
|
+
== existing_methodologies[problem_type].strip()
|
298
|
+
):
|
299
|
+
is_duplicate = True
|
300
|
+
|
301
|
+
# 排除已存在于中心仓库的方法论(基于内容),以及本地重复的方法论
|
302
|
+
if (
|
303
|
+
problem_type
|
304
|
+
and content
|
305
|
+
and not is_duplicate
|
306
|
+
and problem_type not in seen_problem_types
|
307
|
+
):
|
308
|
+
methodology_files.append(
|
309
|
+
{
|
310
|
+
"path": filepath,
|
311
|
+
"problem_type": problem_type,
|
312
|
+
"directory": directory,
|
313
|
+
}
|
314
|
+
)
|
315
|
+
all_methodologies[problem_type] = methodology
|
316
|
+
seen_problem_types.add(problem_type)
|
317
|
+
except Exception:
|
318
|
+
pass
|
319
|
+
|
320
|
+
if not methodology_files:
|
321
|
+
PrettyOutput.print(
|
322
|
+
"没有找到新的方法论文件(所有方法论可能已存在于中心仓库)",
|
323
|
+
OutputType.WARNING,
|
324
|
+
)
|
325
|
+
raise typer.Exit(code=0)
|
326
|
+
|
327
|
+
# 显示可选的方法论
|
328
|
+
methodology_list = ["\n可分享的方法论(已排除中心仓库中已有的):"]
|
329
|
+
for i, meth in enumerate(methodology_files, 1):
|
330
|
+
dir_name = os.path.basename(meth["directory"])
|
331
|
+
methodology_list.append(f"[{i}] {meth['problem_type']} (来自: {dir_name})")
|
332
|
+
|
333
|
+
# 一次性打印所有方法论
|
334
|
+
PrettyOutput.print("\n".join(methodology_list), OutputType.INFO)
|
335
|
+
|
336
|
+
# 让用户选择要分享的方法论
|
337
|
+
while True:
|
338
|
+
try:
|
339
|
+
choice_str = prompt(
|
340
|
+
"\n请选择要分享的方法论编号(支持格式: 1,2,3,4-9,20 或 all):"
|
341
|
+
).strip()
|
342
|
+
if choice_str == "0":
|
343
|
+
raise typer.Exit(code=0)
|
344
|
+
|
345
|
+
selected_methodologies = []
|
346
|
+
if choice_str.lower() == "all":
|
347
|
+
selected_methodologies = methodology_files
|
348
|
+
else:
|
349
|
+
selected_indices = _parse_selection(choice_str, len(methodology_files))
|
350
|
+
if not selected_indices:
|
351
|
+
PrettyOutput.print("无效的选择", OutputType.WARNING)
|
352
|
+
continue
|
353
|
+
selected_methodologies = [
|
354
|
+
methodology_files[i - 1] for i in selected_indices
|
355
|
+
]
|
356
|
+
|
357
|
+
# 确认操作
|
358
|
+
share_list = ["\n将要分享以下方法论到中心仓库:"]
|
359
|
+
for meth in selected_methodologies:
|
360
|
+
share_list.append(f"- {meth['problem_type']}")
|
361
|
+
PrettyOutput.print("\n".join(share_list), OutputType.INFO)
|
362
|
+
|
363
|
+
if not user_confirm("确认分享这些方法论吗?"):
|
364
|
+
continue
|
365
|
+
|
366
|
+
# 复制选中的方法论到中心仓库
|
367
|
+
copied_list = []
|
368
|
+
for meth in selected_methodologies:
|
369
|
+
src_file = meth["path"]
|
370
|
+
dst_file = os.path.join(central_repo_path, os.path.basename(src_file))
|
371
|
+
shutil.copy2(src_file, dst_file)
|
372
|
+
copied_list.append(f"已复制: {meth['problem_type']}")
|
373
|
+
|
374
|
+
# 一次性显示所有复制结果
|
375
|
+
if copied_list:
|
376
|
+
PrettyOutput.print("\n".join(copied_list), OutputType.SUCCESS)
|
377
|
+
|
378
|
+
# 提交并推送更改
|
379
|
+
PrettyOutput.print("\n正在提交更改...", OutputType.INFO)
|
380
|
+
subprocess.run(["git", "add", "."], cwd=central_repo_path, check=True)
|
381
|
+
|
382
|
+
commit_msg = f"Add {len(selected_methodologies)} methodology(ies) from local collection"
|
383
|
+
subprocess.run(
|
384
|
+
["git", "commit", "-m", commit_msg], cwd=central_repo_path, check=True
|
385
|
+
)
|
386
|
+
|
387
|
+
PrettyOutput.print("正在推送到远程仓库...", OutputType.INFO)
|
388
|
+
subprocess.run(["git", "push"], cwd=central_repo_path, check=True)
|
389
|
+
|
390
|
+
PrettyOutput.print("\n方法论已成功分享到中心仓库!", OutputType.SUCCESS)
|
391
|
+
break
|
392
|
+
|
393
|
+
except ValueError:
|
394
|
+
PrettyOutput.print("请输入有效的数字", OutputType.WARNING)
|
395
|
+
except subprocess.CalledProcessError as e:
|
396
|
+
PrettyOutput.print(f"Git操作失败: {str(e)}", OutputType.ERROR)
|
397
|
+
raise typer.Exit(code=1)
|
398
|
+
except Exception as e:
|
399
|
+
PrettyOutput.print(f"分享方法论时出错: {str(e)}", OutputType.ERROR)
|
400
|
+
raise typer.Exit(code=1)
|
401
|
+
|
402
|
+
|
186
403
|
@app.callback(invoke_without_command=True)
|
187
404
|
def run_cli(
|
188
405
|
ctx: typer.Context,
|
@@ -191,17 +408,24 @@ def run_cli(
|
|
191
408
|
"--llm_type",
|
192
409
|
help="使用的LLM类型,可选值:'normal'(普通)或 'thinking'(思考模式)",
|
193
410
|
),
|
194
|
-
task: Optional[str] = typer.Option(
|
411
|
+
task: Optional[str] = typer.Option(
|
412
|
+
None, "-t", "--task", help="从命令行直接输入任务内容"
|
413
|
+
),
|
195
414
|
model_group: Optional[str] = typer.Option(
|
196
415
|
None, "--llm_group", help="使用的模型组,覆盖配置文件中的设置"
|
197
416
|
),
|
198
|
-
config_file: Optional[str] = typer.Option(
|
417
|
+
config_file: Optional[str] = typer.Option(
|
418
|
+
None, "-f", "--config", help="自定义配置文件路径"
|
419
|
+
),
|
199
420
|
restore_session: bool = typer.Option(
|
200
421
|
False,
|
201
422
|
"--restore-session",
|
202
423
|
help="从 .jarvis/saved_session.json 恢复会话",
|
203
424
|
),
|
204
425
|
edit: bool = typer.Option(False, "-e", "--edit", help="编辑配置文件"),
|
426
|
+
share_methodology: bool = typer.Option(
|
427
|
+
False, "--share-methodology", help="分享本地方法论到中心方法论仓库"
|
428
|
+
),
|
205
429
|
) -> None:
|
206
430
|
"""Jarvis AI assistant command-line interface."""
|
207
431
|
if ctx.invoked_subcommand is not None:
|
@@ -209,7 +433,15 @@ def run_cli(
|
|
209
433
|
|
210
434
|
_handle_edit_mode(edit, config_file)
|
211
435
|
|
212
|
-
|
436
|
+
# 处理方法论分享
|
437
|
+
if share_methodology:
|
438
|
+
init_env("", config_file=config_file) # 初始化配置但不显示欢迎信息
|
439
|
+
_handle_share_methodology(config_file)
|
440
|
+
raise typer.Exit(code=0)
|
441
|
+
|
442
|
+
init_env(
|
443
|
+
"欢迎使用 Jarvis AI 助手,您的智能助理已准备就绪!", config_file=config_file
|
444
|
+
)
|
213
445
|
|
214
446
|
try:
|
215
447
|
agent = _initialize_agent(llm_type, model_group, restore_session)
|
@@ -225,6 +225,11 @@
|
|
225
225
|
},
|
226
226
|
"default": []
|
227
227
|
},
|
228
|
+
"JARVIS_CENTRAL_METHODOLOGY_REPO": {
|
229
|
+
"type": "string",
|
230
|
+
"description": "中心方法论Git仓库地址,该仓库会自动添加到方法论加载路径中",
|
231
|
+
"default": ""
|
232
|
+
},
|
228
233
|
"JARVIS_PRINT_PROMPT": {
|
229
234
|
"type": "boolean",
|
230
235
|
"description": "是否打印提示",
|
jarvis/jarvis_stats/storage.py
CHANGED
@@ -245,7 +245,7 @@ class StatsStorage:
|
|
245
245
|
metrics_from_meta = set(meta.get("metrics", {}).keys())
|
246
246
|
|
247
247
|
# 扫描所有数据文件获取实际存在的指标
|
248
|
-
metrics_from_data = set()
|
248
|
+
metrics_from_data: set[str] = set()
|
249
249
|
for data_file in self.data_dir.glob("stats_*.json"):
|
250
250
|
try:
|
251
251
|
data = self._load_json(data_file)
|
@@ -285,7 +285,7 @@ class StatsStorage:
|
|
285
285
|
return {}
|
286
286
|
|
287
287
|
# 聚合数据
|
288
|
-
aggregated = defaultdict(
|
288
|
+
aggregated: Dict[str, Dict[str, Any]] = defaultdict(
|
289
289
|
lambda: {
|
290
290
|
"count": 0,
|
291
291
|
"sum": 0,
|
@@ -0,0 +1,252 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
import json
|
3
|
+
import shutil
|
4
|
+
from pathlib import Path
|
5
|
+
from typing import Any, Dict, List, Optional
|
6
|
+
|
7
|
+
from jarvis.jarvis_utils.config import get_data_dir
|
8
|
+
from jarvis.jarvis_utils.output import OutputType, PrettyOutput
|
9
|
+
from jarvis.jarvis_utils.globals import (
|
10
|
+
clear_short_term_memories,
|
11
|
+
get_short_term_memories,
|
12
|
+
short_term_memories,
|
13
|
+
)
|
14
|
+
|
15
|
+
|
16
|
+
class ClearMemoryTool:
|
17
|
+
"""清除记忆工具,用于批量清除指定的记忆"""
|
18
|
+
|
19
|
+
name = "clear_memory"
|
20
|
+
description = """批量清除指定的记忆。
|
21
|
+
|
22
|
+
支持的清除方式:
|
23
|
+
1. 按记忆类型清除所有记忆
|
24
|
+
2. 按标签清除特定记忆
|
25
|
+
3. 按记忆ID清除单个记忆
|
26
|
+
|
27
|
+
支持的记忆类型:
|
28
|
+
- project_long_term: 项目长期记忆
|
29
|
+
- global_long_term: 全局长期记忆
|
30
|
+
- short_term: 短期记忆
|
31
|
+
- all: 所有类型的记忆
|
32
|
+
|
33
|
+
注意:清除操作不可恢复,请谨慎使用
|
34
|
+
"""
|
35
|
+
|
36
|
+
parameters = {
|
37
|
+
"type": "object",
|
38
|
+
"properties": {
|
39
|
+
"memory_types": {
|
40
|
+
"type": "array",
|
41
|
+
"items": {
|
42
|
+
"type": "string",
|
43
|
+
"enum": [
|
44
|
+
"project_long_term",
|
45
|
+
"global_long_term",
|
46
|
+
"short_term",
|
47
|
+
"all",
|
48
|
+
],
|
49
|
+
},
|
50
|
+
"description": "要清除的记忆类型列表",
|
51
|
+
},
|
52
|
+
"tags": {
|
53
|
+
"type": "array",
|
54
|
+
"items": {"type": "string"},
|
55
|
+
"description": "要清除的记忆标签列表(可选,如果指定则只清除带有这些标签的记忆)",
|
56
|
+
},
|
57
|
+
"memory_ids": {
|
58
|
+
"type": "array",
|
59
|
+
"items": {"type": "string"},
|
60
|
+
"description": "要清除的具体记忆ID列表(可选)",
|
61
|
+
},
|
62
|
+
"confirm": {
|
63
|
+
"type": "boolean",
|
64
|
+
"description": "确认清除操作(必须为true才会执行清除)",
|
65
|
+
"default": False,
|
66
|
+
},
|
67
|
+
},
|
68
|
+
"required": ["memory_types", "confirm"],
|
69
|
+
}
|
70
|
+
|
71
|
+
def __init__(self):
|
72
|
+
"""初始化清除记忆工具"""
|
73
|
+
self.project_memory_dir = Path(".jarvis/memory")
|
74
|
+
self.global_memory_dir = Path(get_data_dir()) / "memory"
|
75
|
+
|
76
|
+
def _get_memory_dir(self, memory_type: str) -> Path:
|
77
|
+
"""根据记忆类型获取存储目录"""
|
78
|
+
if memory_type == "project_long_term":
|
79
|
+
return self.project_memory_dir
|
80
|
+
elif memory_type in ["global_long_term", "short_term"]:
|
81
|
+
return self.global_memory_dir / memory_type
|
82
|
+
else:
|
83
|
+
raise ValueError(f"未知的记忆类型: {memory_type}")
|
84
|
+
|
85
|
+
def _clear_short_term_memories(
|
86
|
+
self, tags: Optional[List[str]] = None, memory_ids: Optional[List[str]] = None
|
87
|
+
) -> Dict[str, int]:
|
88
|
+
"""清除短期记忆"""
|
89
|
+
global short_term_memories
|
90
|
+
|
91
|
+
initial_count = len(short_term_memories)
|
92
|
+
removed_count = 0
|
93
|
+
|
94
|
+
if memory_ids:
|
95
|
+
# 按ID清除
|
96
|
+
new_memories = []
|
97
|
+
for memory in short_term_memories:
|
98
|
+
if memory.get("id") not in memory_ids:
|
99
|
+
new_memories.append(memory)
|
100
|
+
else:
|
101
|
+
removed_count += 1
|
102
|
+
short_term_memories[:] = new_memories
|
103
|
+
elif tags:
|
104
|
+
# 按标签清除
|
105
|
+
new_memories = []
|
106
|
+
for memory in short_term_memories:
|
107
|
+
memory_tags = memory.get("tags", [])
|
108
|
+
if not any(tag in memory_tags for tag in tags):
|
109
|
+
new_memories.append(memory)
|
110
|
+
else:
|
111
|
+
removed_count += 1
|
112
|
+
short_term_memories[:] = new_memories
|
113
|
+
else:
|
114
|
+
# 清除所有
|
115
|
+
clear_short_term_memories()
|
116
|
+
removed_count = initial_count
|
117
|
+
|
118
|
+
return {"total": initial_count, "removed": removed_count}
|
119
|
+
|
120
|
+
def _clear_long_term_memories(
|
121
|
+
self,
|
122
|
+
memory_type: str,
|
123
|
+
tags: Optional[List[str]] = None,
|
124
|
+
memory_ids: Optional[List[str]] = None,
|
125
|
+
) -> Dict[str, int]:
|
126
|
+
"""清除长期记忆"""
|
127
|
+
memory_dir = self._get_memory_dir(memory_type)
|
128
|
+
|
129
|
+
if not memory_dir.exists():
|
130
|
+
return {"total": 0, "removed": 0}
|
131
|
+
|
132
|
+
total_count = 0
|
133
|
+
removed_count = 0
|
134
|
+
|
135
|
+
# 获取所有记忆文件
|
136
|
+
memory_files = list(memory_dir.glob("*.json"))
|
137
|
+
total_count = len(memory_files)
|
138
|
+
|
139
|
+
for memory_file in memory_files:
|
140
|
+
try:
|
141
|
+
# 读取记忆内容
|
142
|
+
with open(memory_file, "r", encoding="utf-8") as f:
|
143
|
+
memory_data = json.load(f)
|
144
|
+
|
145
|
+
should_remove = False
|
146
|
+
|
147
|
+
if memory_ids:
|
148
|
+
# 按ID判断
|
149
|
+
if memory_data.get("id") in memory_ids:
|
150
|
+
should_remove = True
|
151
|
+
elif tags:
|
152
|
+
# 按标签判断
|
153
|
+
memory_tags = memory_data.get("tags", [])
|
154
|
+
if any(tag in memory_tags for tag in tags):
|
155
|
+
should_remove = True
|
156
|
+
else:
|
157
|
+
# 清除所有
|
158
|
+
should_remove = True
|
159
|
+
|
160
|
+
if should_remove:
|
161
|
+
memory_file.unlink()
|
162
|
+
removed_count += 1
|
163
|
+
|
164
|
+
except Exception as e:
|
165
|
+
PrettyOutput.print(
|
166
|
+
f"处理记忆文件 {memory_file} 时出错: {str(e)}", OutputType.WARNING
|
167
|
+
)
|
168
|
+
|
169
|
+
# 如果目录为空,可以删除目录
|
170
|
+
if not any(memory_dir.iterdir()) and memory_dir != self.project_memory_dir:
|
171
|
+
memory_dir.rmdir()
|
172
|
+
|
173
|
+
return {"total": total_count, "removed": removed_count}
|
174
|
+
|
175
|
+
def execute(self, args: Dict[str, Any]) -> Dict[str, Any]:
|
176
|
+
"""执行清除记忆操作"""
|
177
|
+
try:
|
178
|
+
memory_types = args.get("memory_types", [])
|
179
|
+
tags = args.get("tags", [])
|
180
|
+
memory_ids = args.get("memory_ids", [])
|
181
|
+
confirm = args.get("confirm", False)
|
182
|
+
|
183
|
+
if not confirm:
|
184
|
+
return {
|
185
|
+
"success": False,
|
186
|
+
"stdout": "",
|
187
|
+
"stderr": "必须设置 confirm=true 才能执行清除操作",
|
188
|
+
}
|
189
|
+
|
190
|
+
# 确定要清除的记忆类型
|
191
|
+
if "all" in memory_types:
|
192
|
+
types_to_clear = ["project_long_term", "global_long_term", "short_term"]
|
193
|
+
else:
|
194
|
+
types_to_clear = memory_types
|
195
|
+
|
196
|
+
# 统计结果
|
197
|
+
results = {}
|
198
|
+
total_removed = 0
|
199
|
+
|
200
|
+
# 清除各类型的记忆
|
201
|
+
for memory_type in types_to_clear:
|
202
|
+
if memory_type == "short_term":
|
203
|
+
result = self._clear_short_term_memories(tags, memory_ids)
|
204
|
+
else:
|
205
|
+
result = self._clear_long_term_memories(
|
206
|
+
memory_type, tags, memory_ids
|
207
|
+
)
|
208
|
+
|
209
|
+
results[memory_type] = result
|
210
|
+
total_removed += result["removed"]
|
211
|
+
|
212
|
+
# 生成结果报告
|
213
|
+
PrettyOutput.print(
|
214
|
+
f"记忆清除完成,共清除 {total_removed} 条记忆", OutputType.SUCCESS
|
215
|
+
)
|
216
|
+
|
217
|
+
# 详细报告
|
218
|
+
report = f"# 记忆清除报告\n\n"
|
219
|
+
report += f"**总计清除**: {total_removed} 条记忆\n\n"
|
220
|
+
|
221
|
+
if tags:
|
222
|
+
report += f"**使用标签过滤**: {', '.join(tags)}\n\n"
|
223
|
+
|
224
|
+
if memory_ids:
|
225
|
+
report += f"**指定记忆ID**: {', '.join(memory_ids)}\n\n"
|
226
|
+
|
227
|
+
report += "## 详细结果\n\n"
|
228
|
+
|
229
|
+
for memory_type, result in results.items():
|
230
|
+
report += f"### {memory_type}\n"
|
231
|
+
report += f"- 原有记忆: {result['total']} 条\n"
|
232
|
+
report += f"- 已清除: {result['removed']} 条\n"
|
233
|
+
report += f"- 剩余: {result['total'] - result['removed']} 条\n\n"
|
234
|
+
|
235
|
+
# 在终端显示摘要
|
236
|
+
for memory_type, result in results.items():
|
237
|
+
if result["removed"] > 0:
|
238
|
+
PrettyOutput.print(
|
239
|
+
f"{memory_type}: 清除了 {result['removed']}/{result['total']} 条记忆",
|
240
|
+
OutputType.INFO,
|
241
|
+
)
|
242
|
+
|
243
|
+
return {
|
244
|
+
"success": True,
|
245
|
+
"stdout": report,
|
246
|
+
"stderr": "",
|
247
|
+
}
|
248
|
+
|
249
|
+
except Exception as e:
|
250
|
+
error_msg = f"清除记忆失败: {str(e)}"
|
251
|
+
PrettyOutput.print(error_msg, OutputType.ERROR)
|
252
|
+
return {"success": False, "stdout": "", "stderr": error_msg}
|
@@ -22,6 +22,7 @@ class RetrieveMemoryTool:
|
|
22
22
|
- all: 从所有类型中检索
|
23
23
|
|
24
24
|
可以通过标签过滤检索结果,支持多个标签(满足任一标签即可)
|
25
|
+
注意:标签数量建议不要超过10个,以保证检索效率
|
25
26
|
"""
|
26
27
|
|
27
28
|
parameters = {
|
@@ -149,35 +150,40 @@ class RetrieveMemoryTool:
|
|
149
150
|
# 格式化为Markdown输出
|
150
151
|
markdown_output = f"# 记忆检索结果\n\n"
|
151
152
|
markdown_output += f"**检索到 {len(all_memories)} 条记忆**\n\n"
|
152
|
-
|
153
|
+
|
153
154
|
if tags:
|
154
155
|
markdown_output += f"**使用标签过滤**: {', '.join(tags)}\n\n"
|
155
|
-
|
156
|
+
|
156
157
|
markdown_output += f"**记忆类型**: {', '.join(types_to_search)}\n\n"
|
157
|
-
|
158
|
+
|
158
159
|
markdown_output += "---\n\n"
|
159
|
-
|
160
|
+
|
160
161
|
# 输出所有记忆
|
161
162
|
for i, memory in enumerate(all_memories):
|
162
163
|
markdown_output += f"## {i+1}. {memory.get('id', '未知ID')}\n\n"
|
163
164
|
markdown_output += f"**类型**: {memory.get('type', '未知类型')}\n\n"
|
164
165
|
markdown_output += f"**标签**: {', '.join(memory.get('tags', []))}\n\n"
|
165
|
-
markdown_output +=
|
166
|
-
|
166
|
+
markdown_output += (
|
167
|
+
f"**创建时间**: {memory.get('created_at', '未知时间')}\n\n"
|
168
|
+
)
|
169
|
+
|
167
170
|
# 内容部分
|
168
|
-
content = memory.get(
|
171
|
+
content = memory.get("content", "")
|
169
172
|
if content:
|
170
173
|
markdown_output += f"**内容**:\n\n{content}\n\n"
|
171
|
-
|
174
|
+
|
172
175
|
# 如果有额外的元数据
|
173
|
-
metadata = {
|
174
|
-
|
176
|
+
metadata = {
|
177
|
+
k: v
|
178
|
+
for k, v in memory.items()
|
179
|
+
if k not in ["id", "type", "tags", "created_at", "content"]
|
180
|
+
}
|
175
181
|
if metadata:
|
176
182
|
markdown_output += f"**其他信息**:\n"
|
177
183
|
for key, value in metadata.items():
|
178
184
|
markdown_output += f"- {key}: {value}\n"
|
179
185
|
markdown_output += "\n"
|
180
|
-
|
186
|
+
|
181
187
|
markdown_output += "---\n\n"
|
182
188
|
|
183
189
|
# 如果记忆较多,在终端显示摘要
|
@@ -1,5 +1,6 @@
|
|
1
1
|
# -*- coding: utf-8 -*-
|
2
2
|
import json
|
3
|
+
import time
|
3
4
|
from datetime import datetime
|
4
5
|
from pathlib import Path
|
5
6
|
from typing import Any, Dict, List
|
@@ -15,6 +16,8 @@ class SaveMemoryTool:
|
|
15
16
|
name = "save_memory"
|
16
17
|
description = """保存信息到长短期记忆系统。
|
17
18
|
|
19
|
+
支持批量保存多条记忆,可以同时保存不同类型的记忆。
|
20
|
+
|
18
21
|
支持的记忆类型:
|
19
22
|
- project_long_term: 项目长期记忆(与当前项目相关的信息)
|
20
23
|
- global_long_term: 全局长期记忆(通用的信息、用户喜好、知识、方法等)
|
@@ -27,19 +30,36 @@ class SaveMemoryTool:
|
|
27
30
|
parameters = {
|
28
31
|
"type": "object",
|
29
32
|
"properties": {
|
30
|
-
"
|
31
|
-
"type": "string",
|
32
|
-
"enum": ["project_long_term", "global_long_term", "short_term"],
|
33
|
-
"description": "记忆类型",
|
34
|
-
},
|
35
|
-
"tags": {
|
33
|
+
"memories": {
|
36
34
|
"type": "array",
|
37
|
-
"items": {
|
38
|
-
|
39
|
-
|
40
|
-
|
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
|
+
}
|
41
61
|
},
|
42
|
-
"required": ["
|
62
|
+
"required": ["memories"],
|
43
63
|
}
|
44
64
|
|
45
65
|
def __init__(self):
|
@@ -58,81 +78,122 @@ class SaveMemoryTool:
|
|
58
78
|
|
59
79
|
def _generate_memory_id(self) -> str:
|
60
80
|
"""生成唯一的记忆ID"""
|
81
|
+
# 添加微秒级时间戳确保唯一性
|
82
|
+
time.sleep(0.001) # 确保不同记忆有不同的时间戳
|
61
83
|
return datetime.now().strftime("%Y%m%d_%H%M%S_%f")
|
62
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
|
+
|
63
136
|
def execute(self, args: Dict[str, Any]) -> Dict[str, Any]:
|
64
137
|
"""执行保存记忆操作"""
|
65
138
|
try:
|
66
|
-
|
67
|
-
tags = args.get("tags", [])
|
68
|
-
content = args.get("content", "")
|
139
|
+
memories = args.get("memories", [])
|
69
140
|
|
70
|
-
|
71
|
-
|
141
|
+
if not memories:
|
142
|
+
return {
|
143
|
+
"success": False,
|
144
|
+
"stdout": "",
|
145
|
+
"stderr": "没有提供要保存的记忆",
|
146
|
+
}
|
72
147
|
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
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_type = memory_data["memory_type"]
|
161
|
+
tags = memory_data.get("tags", [])
|
162
|
+
PrettyOutput.print(
|
163
|
+
f"[{i+1}/{len(memories)}] {memory_type} 记忆已保存\n"
|
164
|
+
f"ID: {result['memory_id']}\n"
|
165
|
+
f"标签: {', '.join(tags)}",
|
166
|
+
OutputType.SUCCESS,
|
167
|
+
)
|
168
|
+
except Exception as e:
|
169
|
+
failed_count += 1
|
170
|
+
error_msg = f"保存第 {i+1} 条记忆失败: {str(e)}"
|
171
|
+
PrettyOutput.print(error_msg, OutputType.ERROR)
|
172
|
+
results.append(
|
173
|
+
{
|
174
|
+
"error": error_msg,
|
175
|
+
"memory_type": memory_data.get("memory_type", "unknown"),
|
176
|
+
"tags": memory_data.get("tags", []),
|
177
|
+
}
|
178
|
+
)
|
179
|
+
|
180
|
+
# 生成总结报告
|
181
|
+
PrettyOutput.print(
|
182
|
+
f"\n批量保存完成:成功 {success_count} 条,失败 {failed_count} 条",
|
183
|
+
OutputType.INFO,
|
184
|
+
)
|
185
|
+
|
186
|
+
# 构建返回结果
|
187
|
+
output = {
|
188
|
+
"total": len(memories),
|
189
|
+
"success": success_count,
|
190
|
+
"failed": failed_count,
|
191
|
+
"results": results,
|
81
192
|
}
|
82
193
|
|
83
|
-
if memory_type == "short_term":
|
84
|
-
# 短期记忆保存到全局变量
|
85
|
-
add_short_term_memory(memory_data)
|
86
|
-
|
87
|
-
# 打印成功信息
|
88
|
-
PrettyOutput.print(
|
89
|
-
f"短期记忆已保存\n"
|
90
|
-
f"ID: {memory_id}\n"
|
91
|
-
f"类型: {memory_type}\n"
|
92
|
-
f"标签: {', '.join(tags)}\n"
|
93
|
-
f"存储位置: 内存(非持久化)",
|
94
|
-
OutputType.SUCCESS,
|
95
|
-
)
|
96
|
-
|
97
|
-
result = {
|
98
|
-
"memory_id": memory_id,
|
99
|
-
"memory_type": memory_type,
|
100
|
-
"tags": tags,
|
101
|
-
"storage": "memory",
|
102
|
-
"message": f"短期记忆已成功保存到内存,ID: {memory_id}",
|
103
|
-
}
|
104
|
-
else:
|
105
|
-
# 长期记忆保存到文件
|
106
|
-
# 获取存储目录并确保存在
|
107
|
-
memory_dir = self._get_memory_dir(memory_type)
|
108
|
-
memory_dir.mkdir(parents=True, exist_ok=True)
|
109
|
-
|
110
|
-
# 保存记忆文件
|
111
|
-
memory_file = memory_dir / f"{memory_id}.json"
|
112
|
-
with open(memory_file, "w", encoding="utf-8") as f:
|
113
|
-
json.dump(memory_data, f, ensure_ascii=False, indent=2)
|
114
|
-
|
115
|
-
# 打印成功信息
|
116
|
-
PrettyOutput.print(
|
117
|
-
f"记忆已保存\n"
|
118
|
-
f"ID: {memory_id}\n"
|
119
|
-
f"类型: {memory_type}\n"
|
120
|
-
f"标签: {', '.join(tags)}\n"
|
121
|
-
f"位置: {memory_file}",
|
122
|
-
OutputType.SUCCESS,
|
123
|
-
)
|
124
|
-
|
125
|
-
result = {
|
126
|
-
"memory_id": memory_id,
|
127
|
-
"memory_type": memory_type,
|
128
|
-
"tags": tags,
|
129
|
-
"file_path": str(memory_file),
|
130
|
-
"message": f"记忆已成功保存,ID: {memory_id}",
|
131
|
-
}
|
132
|
-
|
133
194
|
return {
|
134
195
|
"success": True,
|
135
|
-
"stdout": json.dumps(
|
196
|
+
"stdout": json.dumps(output, ensure_ascii=False, indent=2),
|
136
197
|
"stderr": "",
|
137
198
|
}
|
138
199
|
|
jarvis/jarvis_utils/config.py
CHANGED
@@ -309,6 +309,16 @@ def get_methodology_dirs() -> List[str]:
|
|
309
309
|
return GLOBAL_CONFIG_DATA.get("JARVIS_METHODOLOGY_DIRS", [])
|
310
310
|
|
311
311
|
|
312
|
+
def get_central_methodology_repo() -> str:
|
313
|
+
"""
|
314
|
+
获取中心方法论Git仓库地址。
|
315
|
+
|
316
|
+
返回:
|
317
|
+
str: 中心方法论Git仓库地址,如果未配置则返回空字符串
|
318
|
+
"""
|
319
|
+
return GLOBAL_CONFIG_DATA.get("JARVIS_CENTRAL_METHODOLOGY_REPO", "")
|
320
|
+
|
321
|
+
|
312
322
|
def is_print_prompt() -> bool:
|
313
323
|
"""
|
314
324
|
获取是否打印提示。
|
jarvis/jarvis_utils/input.py
CHANGED
@@ -216,9 +216,11 @@ def _get_multiline_input_internal(tip: str) -> str:
|
|
216
216
|
@bindings.add("enter")
|
217
217
|
def _(event):
|
218
218
|
if event.current_buffer.complete_state:
|
219
|
-
event.current_buffer.
|
220
|
-
|
221
|
-
|
219
|
+
completion = event.current_buffer.complete_state.current_completion
|
220
|
+
if completion:
|
221
|
+
event.current_buffer.apply_completion(completion)
|
222
|
+
else:
|
223
|
+
event.current_buffer.insert_text("\n")
|
222
224
|
else:
|
223
225
|
event.current_buffer.insert_text("\n")
|
224
226
|
|
@@ -15,11 +15,16 @@ from typing import Any, Dict, List, Optional
|
|
15
15
|
|
16
16
|
from jarvis.jarvis_platform.base import BasePlatform
|
17
17
|
from jarvis.jarvis_platform.registry import PlatformRegistry
|
18
|
-
from jarvis.jarvis_utils.config import
|
18
|
+
from jarvis.jarvis_utils.config import (
|
19
|
+
get_data_dir,
|
20
|
+
get_methodology_dirs,
|
21
|
+
get_central_methodology_repo,
|
22
|
+
)
|
19
23
|
from jarvis.jarvis_utils.globals import get_agent, current_agent_name
|
20
24
|
from jarvis.jarvis_utils.output import OutputType, PrettyOutput
|
21
25
|
from jarvis.jarvis_utils.utils import is_context_overflow, daily_check_git_updates
|
22
26
|
|
27
|
+
|
23
28
|
def _get_methodology_directory() -> str:
|
24
29
|
"""
|
25
30
|
获取方法论目录路径,如果不存在则创建
|
@@ -46,6 +51,29 @@ def _load_all_methodologies() -> Dict[str, str]:
|
|
46
51
|
all_methodologies = {}
|
47
52
|
methodology_dirs = [_get_methodology_directory()] + get_methodology_dirs()
|
48
53
|
|
54
|
+
# 如果配置了中心方法论仓库,将其添加到加载路径
|
55
|
+
central_repo = get_central_methodology_repo()
|
56
|
+
if central_repo:
|
57
|
+
# 中心方法论仓库存储在数据目录下的特定位置
|
58
|
+
central_repo_path = os.path.join(get_data_dir(), "central_methodology_repo")
|
59
|
+
methodology_dirs.append(central_repo_path)
|
60
|
+
|
61
|
+
# 确保中心方法论仓库被克隆/更新
|
62
|
+
if not os.path.exists(central_repo_path):
|
63
|
+
try:
|
64
|
+
import subprocess
|
65
|
+
|
66
|
+
PrettyOutput.print(
|
67
|
+
f"正在克隆中心方法论仓库: {central_repo}", OutputType.INFO
|
68
|
+
)
|
69
|
+
subprocess.run(
|
70
|
+
["git", "clone", central_repo, central_repo_path], check=True
|
71
|
+
)
|
72
|
+
except Exception as e:
|
73
|
+
PrettyOutput.print(
|
74
|
+
f"克隆中心方法论仓库失败: {str(e)}", OutputType.ERROR
|
75
|
+
)
|
76
|
+
|
49
77
|
# --- 全局每日更新检查 ---
|
50
78
|
daily_check_git_updates(methodology_dirs, "methodologies")
|
51
79
|
|
@@ -53,7 +81,9 @@ def _load_all_methodologies() -> Dict[str, str]:
|
|
53
81
|
|
54
82
|
for directory in set(methodology_dirs): # Use set to avoid duplicates
|
55
83
|
if not os.path.isdir(directory):
|
56
|
-
PrettyOutput.print(
|
84
|
+
PrettyOutput.print(
|
85
|
+
f"警告: 方法论目录不存在或不是一个目录: {directory}", OutputType.WARNING
|
86
|
+
)
|
57
87
|
continue
|
58
88
|
|
59
89
|
for filepath in glob.glob(os.path.join(directory, "*.json")):
|
@@ -64,7 +94,7 @@ def _load_all_methodologies() -> Dict[str, str]:
|
|
64
94
|
content = methodology.get("content", "")
|
65
95
|
if problem_type and content:
|
66
96
|
if problem_type in all_methodologies:
|
67
|
-
|
97
|
+
pass
|
68
98
|
all_methodologies[problem_type] = content
|
69
99
|
except Exception as e:
|
70
100
|
filename = os.path.basename(filepath)
|
@@ -211,8 +241,9 @@ def load_methodology(user_input: str, tool_registery: Optional[Any] = None) -> s
|
|
211
241
|
|
212
242
|
# 从响应中提取<NUM>标签内的内容
|
213
243
|
import re
|
214
|
-
|
215
|
-
|
244
|
+
|
245
|
+
num_match = re.search(r"<NUM>(.*?)</NUM>", response, re.DOTALL)
|
246
|
+
|
216
247
|
if not num_match:
|
217
248
|
# 如果没有找到<NUM>标签,尝试直接解析响应
|
218
249
|
selected_indices_str = response
|
@@ -226,7 +257,11 @@ def load_methodology(user_input: str, tool_registery: Optional[Any] = None) -> s
|
|
226
257
|
selected_methodologies = {}
|
227
258
|
try:
|
228
259
|
if selected_indices_str:
|
229
|
-
indices = [
|
260
|
+
indices = [
|
261
|
+
int(idx.strip())
|
262
|
+
for idx in selected_indices_str.split(",")
|
263
|
+
if idx.strip().isdigit()
|
264
|
+
]
|
230
265
|
for idx in indices:
|
231
266
|
if 1 <= idx <= len(methodology_titles):
|
232
267
|
title = methodology_titles[idx - 1]
|
jarvis/jarvis_utils/output.py
CHANGED
@@ -204,7 +204,7 @@ class PrettyOutput:
|
|
204
204
|
color="red", frame=True, bgcolor="#2b1c1c", meta={"icon": "❌"}
|
205
205
|
),
|
206
206
|
OutputType.INFO: RichStyle(
|
207
|
-
color="
|
207
|
+
color="bright_cyan", frame=True, bgcolor="#2b2b1c", meta={"icon": "ℹ️"}
|
208
208
|
),
|
209
209
|
OutputType.PLANNING: RichStyle(
|
210
210
|
color="purple",
|
@@ -1,8 +1,8 @@
|
|
1
|
-
jarvis/__init__.py,sha256=
|
2
|
-
jarvis/jarvis_agent/__init__.py,sha256=
|
1
|
+
jarvis/__init__.py,sha256=2oEyA1qaR5QI1ZfAHJn9_9z3DRmOtWPUEtfnySBHunk,73
|
2
|
+
jarvis/jarvis_agent/__init__.py,sha256=feyq1jxhgzAy8y0X7nNidmFTm8gkpJdhikzmphZNU7M,26945
|
3
3
|
jarvis/jarvis_agent/builtin_input_handler.py,sha256=Qs4LAr4xdKLBJpQE81YP4CkucAop86ms0iVoKa1nnso,2468
|
4
4
|
jarvis/jarvis_agent/edit_file_handler.py,sha256=w-byNJ4TN_SlV3djjfFC7OksySOFGrM8ku49w662dzc,11854
|
5
|
-
jarvis/jarvis_agent/jarvis.py,sha256=
|
5
|
+
jarvis/jarvis_agent/jarvis.py,sha256=Z4Alt7J5cOKUkdkPuSqPD-Zy338Zg8IizJZ3SzCRWbg,17696
|
6
6
|
jarvis/jarvis_agent/main.py,sha256=56pLVy6v-3ZdyPCcWXdRkgbjmYsoIfC7zrA6B7sYivU,3334
|
7
7
|
jarvis/jarvis_agent/output_handler.py,sha256=P7oWpXBGFfOsWq7cIhS_z9crkQ19ES7qU5pM92KKjAs,1172
|
8
8
|
jarvis/jarvis_agent/prompt_builder.py,sha256=PH1fPDVa8z_RXkoXHJFNDf8PQjUoLNLYwkh2lC__p40,1705
|
@@ -12,7 +12,7 @@ jarvis/jarvis_agent/session_manager.py,sha256=DnvI9rWkVmkyO1XfKZyo9lTn4ajg4ccwzE
|
|
12
12
|
jarvis/jarvis_agent/shell_input_handler.py,sha256=1IboqdxcJuoIqRpmDU10GugR9fWXUHyCEbVF4nIWbyo,1328
|
13
13
|
jarvis/jarvis_agent/tool_executor.py,sha256=nIq-sPNgrtimtM-IHpN09cWmId8jDzWRdCFoRzXnnoo,1721
|
14
14
|
jarvis/jarvis_code_agent/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
15
|
-
jarvis/jarvis_code_agent/code_agent.py,sha256=
|
15
|
+
jarvis/jarvis_code_agent/code_agent.py,sha256=t1i9XPihW7xrsYn6I86_QvCocVgHgIg0ORQ-ADcheeM,29132
|
16
16
|
jarvis/jarvis_code_agent/lint.py,sha256=LZPsfyZPMo7Wm7LN4osZocuNJwZx1ojacO3MlF870x8,4009
|
17
17
|
jarvis/jarvis_code_analysis/code_review.py,sha256=TMov1pqDe1bg0vM1ndnYeW9ejHrRN_jMroo3T4L9yag,32368
|
18
18
|
jarvis/jarvis_code_analysis/checklists/__init__.py,sha256=LIXAYa1sW3l7foP6kohLWnE98I_EQ0T7z5bYKHq6rJA,78
|
@@ -35,7 +35,7 @@ jarvis/jarvis_code_analysis/checklists/shell.py,sha256=aRFYhQQvTgbYd-uY5pc8UHIUA
|
|
35
35
|
jarvis/jarvis_code_analysis/checklists/sql.py,sha256=vR0T6qC7b4dURjJVAd7kSVxyvZEQXPG1Jqc2sNTGp5c,2355
|
36
36
|
jarvis/jarvis_code_analysis/checklists/swift.py,sha256=TPx4I6Gupvs6tSerRKmTSKEPQpOLEbH2Y7LXg1uBgxc,2566
|
37
37
|
jarvis/jarvis_code_analysis/checklists/web.py,sha256=25gGD7pDadZQybNFvALYxWvK0VRjGQb1NVJQElwjyk0,3943
|
38
|
-
jarvis/jarvis_data/config_schema.json,sha256=
|
38
|
+
jarvis/jarvis_data/config_schema.json,sha256=ml7Z3dhjh227K1RsBPVTGJYSXFDG3p3OR-nTb_cxK88,10317
|
39
39
|
jarvis/jarvis_data/tiktoken/9b5ad71b2ce5302211f9c61530b329a4922fc6a4,sha256=Ijkht27pm96ZW3_3OFE-7xAPtR0YyTWXoRO8_-hlsqc,1681126
|
40
40
|
jarvis/jarvis_git_squash/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
41
41
|
jarvis/jarvis_git_squash/main.py,sha256=6PECdAbTbrsJBRLK1pXBh4hdJ_LADh-XXSic1xJi97E,2255
|
@@ -73,11 +73,12 @@ jarvis/jarvis_smart_shell/main.py,sha256=ReCC9bWPlgl84ylI0uvdzlE3J6fS0XzFSLOpQQy
|
|
73
73
|
jarvis/jarvis_stats/__init__.py,sha256=jJzgP43nxzLbNGs8Do4Jfta1PNCJMf1Oq9YTPd6EnFM,342
|
74
74
|
jarvis/jarvis_stats/cli.py,sha256=KqLH-9Kd_YlBJSke3QXY90XnFmiH2kYkRacL8ygtSsM,12649
|
75
75
|
jarvis/jarvis_stats/stats.py,sha256=qLyOJvWAv0fgV7oohAUSQ2W2E1Hr4wWgEQXDOiI-4Cg,17674
|
76
|
-
jarvis/jarvis_stats/storage.py,sha256=
|
76
|
+
jarvis/jarvis_stats/storage.py,sha256=0hs-TkmvWavsf6J2LLOLXyyZzVK8g77jecRnt89MzYE,12724
|
77
77
|
jarvis/jarvis_stats/visualizer.py,sha256=ZIBmGELzs6c7qM01tQql1HF6eFKn6HDGVQfKXRUUIY0,8529
|
78
78
|
jarvis/jarvis_tools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
79
79
|
jarvis/jarvis_tools/ask_user.py,sha256=M6DdLNryCE8y1JcdZHEifUgZkPUEPNKc-zDW5p0Mb1k,2029
|
80
80
|
jarvis/jarvis_tools/base.py,sha256=tFVmK6ppsImW2BzHZmrNmMRiOJdW-4aZP6Me3VxdYcA,1194
|
81
|
+
jarvis/jarvis_tools/clear_memory.py,sha256=HQMK70UJhhDgPPHozGaTpYizzQblUzYRwPbvD1z3z6o,8730
|
81
82
|
jarvis/jarvis_tools/edit_file.py,sha256=hM345E9rxS-EkqCZpwwizL6fmPdTadtB798tEO5Ce3g,10417
|
82
83
|
jarvis/jarvis_tools/execute_script.py,sha256=gMarE5yCCSPU6Dp6HlcL2KT-2xCzR-1p-oQNlYOJK58,6157
|
83
84
|
jarvis/jarvis_tools/file_analyzer.py,sha256=aVe1jBSp0YmlypihxrGADJpYrU_7CxDETxGUNySuSlI,4044
|
@@ -86,29 +87,29 @@ jarvis/jarvis_tools/methodology.py,sha256=_K4GIDUodGEma3SvNRo7Qs5rliijgNespVLyAP
|
|
86
87
|
jarvis/jarvis_tools/read_code.py,sha256=EnI-R-5HyIQYhMD391nZWXHIuHHBF-OJIRE0QpLcPX4,6417
|
87
88
|
jarvis/jarvis_tools/read_webpage.py,sha256=NmDUboVZd4CGHBPRFK6dp3uqVhuGopW1bOi3TcaLDF4,2092
|
88
89
|
jarvis/jarvis_tools/registry.py,sha256=8qhZgmmGIXJsYwtpsp_Ls8woT0qmBrthF8lIuQqOu7c,28614
|
89
|
-
jarvis/jarvis_tools/retrieve_memory.py,sha256=
|
90
|
+
jarvis/jarvis_tools/retrieve_memory.py,sha256=zItXPSMGzaPyH-4p_CP3z24fBYFnCK3OQJctgVunZMI,8288
|
90
91
|
jarvis/jarvis_tools/rewrite_file.py,sha256=eG_WKg6cVAXmuGwUqlWkcuyay5S8DOzEi8vZCmX3O8w,7255
|
91
|
-
jarvis/jarvis_tools/save_memory.py,sha256=
|
92
|
+
jarvis/jarvis_tools/save_memory.py,sha256=DjeFb38OtK9Y_RpWYHz8vL72JdauXZTlc_Y0FUQBtiM,7486
|
92
93
|
jarvis/jarvis_tools/search_web.py,sha256=zh6EYLQPIQneoz27Hheh-fifMeMNhrTVldXKMSsMz2Y,5801
|
93
94
|
jarvis/jarvis_tools/virtual_tty.py,sha256=LTsg1PlsPvgaLShUaxpAKwTpyjXRr0l0qSREI7Q-fBc,26349
|
94
95
|
jarvis/jarvis_tools/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
95
96
|
jarvis/jarvis_tools/cli/main.py,sha256=GsfZJ4OS4Hvxh0H2XiLkgbzm-ajBsb4c0LyjuIAAatE,7718
|
96
97
|
jarvis/jarvis_utils/__init__.py,sha256=67h0ldisGlh3oK4DAeNEL2Bl_VsI3tSmfclasyVlueM,850
|
97
98
|
jarvis/jarvis_utils/builtin_replace_map.py,sha256=4BurljGuiG_I93EBs7mlFlPm9wYC_4CmdTG5tQWpF6g,1712
|
98
|
-
jarvis/jarvis_utils/config.py,sha256
|
99
|
+
jarvis/jarvis_utils/config.py,sha256=ESvxmUxbxX8B80ft1H2qQsdyu73xAYvr3Dql7CYnzwI,13178
|
99
100
|
jarvis/jarvis_utils/embedding.py,sha256=oEOEM2qf16DMYwPsQe6srET9BknyjOdY2ef0jsp3Or8,2714
|
100
101
|
jarvis/jarvis_utils/file_processors.py,sha256=XiM248SHS7lLgQDCbORVFWqinbVDUawYxWDOsLXDxP8,3043
|
101
102
|
jarvis/jarvis_utils/git_utils.py,sha256=dkC0HcUdm_rF5vXNoLByne3mGykZEviD3Lo_SYbwROU,21667
|
102
103
|
jarvis/jarvis_utils/globals.py,sha256=lpS1lmWRnLgqOeoyhZlktdZK9SK8YMekc6XWamho0Jw,8561
|
103
104
|
jarvis/jarvis_utils/http.py,sha256=eRhV3-GYuWmQ0ogq9di9WMlQkFcVb1zGCrySnOgT1x0,4392
|
104
|
-
jarvis/jarvis_utils/input.py,sha256=
|
105
|
-
jarvis/jarvis_utils/methodology.py,sha256=
|
106
|
-
jarvis/jarvis_utils/output.py,sha256=
|
105
|
+
jarvis/jarvis_utils/input.py,sha256=KMAbSubWHo2z-gACf5PyQSGozJKXQby-fwcfzobJld0,9598
|
106
|
+
jarvis/jarvis_utils/methodology.py,sha256=I8BTBijpApzUtRG20_Wu1Vuv0I0OoYOzshec6CMOPX8,10231
|
107
|
+
jarvis/jarvis_utils/output.py,sha256=QRLlKObQKT0KuRSeZRqYb7NlTQvsd1oZXZ41WxeWEuU,10894
|
107
108
|
jarvis/jarvis_utils/tag.py,sha256=f211opbbbTcSyzCDwuIK_oCnKhXPNK-RknYyGzY1yD0,431
|
108
109
|
jarvis/jarvis_utils/utils.py,sha256=b7t2BsoPEU_3ZhippcQW9qwQD0PO-sGkjjtZ4DvwI2s,37056
|
109
|
-
jarvis_ai_assistant-0.2.
|
110
|
-
jarvis_ai_assistant-0.2.
|
111
|
-
jarvis_ai_assistant-0.2.
|
112
|
-
jarvis_ai_assistant-0.2.
|
113
|
-
jarvis_ai_assistant-0.2.
|
114
|
-
jarvis_ai_assistant-0.2.
|
110
|
+
jarvis_ai_assistant-0.2.7.dist-info/licenses/LICENSE,sha256=AGgVgQmTqFvaztRtCAXsAMryUymB18gZif7_l2e1XOg,1063
|
111
|
+
jarvis_ai_assistant-0.2.7.dist-info/METADATA,sha256=NQFGWTpKdTU1HU9yjEjoFwSYz7DiAulBpVnB70WZDkg,16807
|
112
|
+
jarvis_ai_assistant-0.2.7.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
113
|
+
jarvis_ai_assistant-0.2.7.dist-info/entry_points.txt,sha256=8cwi1VxZGU5UeSZMFiH-jG6NK95Asjukj5SBLBrGiGo,1257
|
114
|
+
jarvis_ai_assistant-0.2.7.dist-info/top_level.txt,sha256=1BOxyWfzOP_ZXj8rVTDnNCJ92bBGB0rwq8N1PCpoMIs,7
|
115
|
+
jarvis_ai_assistant-0.2.7.dist-info/RECORD,,
|
File without changes
|
{jarvis_ai_assistant-0.2.6.dist-info → jarvis_ai_assistant-0.2.7.dist-info}/entry_points.txt
RENAMED
File without changes
|
{jarvis_ai_assistant-0.2.6.dist-info → jarvis_ai_assistant-0.2.7.dist-info}/licenses/LICENSE
RENAMED
File without changes
|
File without changes
|