jarvis-ai-assistant 0.3.1__py3-none-any.whl → 0.3.3__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 +6 -6
- jarvis/jarvis_agent/jarvis.py +4 -4
- jarvis/jarvis_agent/main.py +3 -4
- jarvis/jarvis_agent/share_manager.py +43 -0
- jarvis/jarvis_code_agent/code_agent.py +6 -4
- jarvis/jarvis_code_analysis/code_review.py +117 -12
- jarvis/jarvis_git_utils/git_commiter.py +63 -9
- jarvis/jarvis_memory_organizer/__init__.py +0 -0
- jarvis/jarvis_memory_organizer/memory_organizer.py +729 -0
- jarvis/jarvis_platform/base.py +9 -0
- jarvis/jarvis_platform/kimi.py +20 -0
- jarvis/jarvis_platform/openai.py +27 -0
- jarvis/jarvis_platform/tongyi.py +19 -0
- jarvis/jarvis_platform/yuanbao.py +18 -0
- jarvis/jarvis_platform_manager/main.py +131 -30
- jarvis/jarvis_stats/storage.py +4 -1
- jarvis/jarvis_utils/globals.py +16 -10
- jarvis/jarvis_utils/utils.py +49 -29
- {jarvis_ai_assistant-0.3.1.dist-info → jarvis_ai_assistant-0.3.3.dist-info}/METADATA +40 -1
- {jarvis_ai_assistant-0.3.1.dist-info → jarvis_ai_assistant-0.3.3.dist-info}/RECORD +25 -23
- {jarvis_ai_assistant-0.3.1.dist-info → jarvis_ai_assistant-0.3.3.dist-info}/entry_points.txt +2 -0
- {jarvis_ai_assistant-0.3.1.dist-info → jarvis_ai_assistant-0.3.3.dist-info}/WHEEL +0 -0
- {jarvis_ai_assistant-0.3.1.dist-info → jarvis_ai_assistant-0.3.3.dist-info}/licenses/LICENSE +0 -0
- {jarvis_ai_assistant-0.3.1.dist-info → jarvis_ai_assistant-0.3.3.dist-info}/top_level.txt +0 -0
jarvis/jarvis_platform/base.py
CHANGED
@@ -255,6 +255,15 @@ class BasePlatform(ABC):
|
|
255
255
|
"""Get env default values"""
|
256
256
|
return {}
|
257
257
|
|
258
|
+
@classmethod
|
259
|
+
def get_env_config_guide(cls) -> Dict[str, str]:
|
260
|
+
"""Get environment variable configuration guide
|
261
|
+
|
262
|
+
Returns:
|
263
|
+
Dict[str, str]: A dictionary mapping env key names to their configuration instructions
|
264
|
+
"""
|
265
|
+
return {}
|
266
|
+
|
258
267
|
def set_suppress_output(self, suppress: bool):
|
259
268
|
"""Set whether to suppress output"""
|
260
269
|
self.suppress_output = suppress
|
jarvis/jarvis_platform/kimi.py
CHANGED
@@ -405,3 +405,23 @@ class KimiModel(BasePlatform):
|
|
405
405
|
List[str]: 环境变量键的列表
|
406
406
|
"""
|
407
407
|
return ["KIMI_API_KEY"]
|
408
|
+
|
409
|
+
@classmethod
|
410
|
+
def get_env_config_guide(cls) -> Dict[str, str]:
|
411
|
+
"""
|
412
|
+
获取环境变量配置指导
|
413
|
+
|
414
|
+
返回:
|
415
|
+
Dict[str, str]: 环境变量名到配置指导的映射
|
416
|
+
"""
|
417
|
+
return {
|
418
|
+
"KIMI_API_KEY": (
|
419
|
+
"1. 登录 Kimi 网页版: https://kimi.moonshot.cn/\n"
|
420
|
+
"2. 打开浏览器开发者工具 (F12)\n"
|
421
|
+
'3. 切换到"网络"(Network)标签页\n'
|
422
|
+
"4. 在 Kimi 中发送一条消息\n"
|
423
|
+
"5. 找到 stream 请求\n"
|
424
|
+
'6. 在"请求标头"中找到 authorization 字段\n'
|
425
|
+
"7. 复制 Bearer 后面的 API Key 部分"
|
426
|
+
)
|
427
|
+
}
|
jarvis/jarvis_platform/openai.py
CHANGED
@@ -222,6 +222,33 @@ class OpenAIModel(BasePlatform):
|
|
222
222
|
"""
|
223
223
|
return ["OPENAI_API_KEY", "OPENAI_API_BASE"]
|
224
224
|
|
225
|
+
@classmethod
|
226
|
+
def get_env_config_guide(cls) -> Dict[str, str]:
|
227
|
+
"""
|
228
|
+
获取环境变量配置指导
|
229
|
+
|
230
|
+
返回:
|
231
|
+
Dict[str, str]: 环境变量名到配置指导的映射
|
232
|
+
"""
|
233
|
+
return {
|
234
|
+
"OPENAI_API_KEY": (
|
235
|
+
"请输入您的 OpenAI API Key:\n"
|
236
|
+
"获取方式一(官方):\n"
|
237
|
+
"1. 登录 OpenAI 平台: https://platform.openai.com/\n"
|
238
|
+
"2. 进入 API Keys 页面\n"
|
239
|
+
"3. 创建新的 API Key 或使用现有的\n"
|
240
|
+
"4. 复制 API Key (以 sk- 开头)\n"
|
241
|
+
"\n获取方式二(第三方代理):\n"
|
242
|
+
"如果使用第三方代理服务,请从代理服务商处获取 API Key"
|
243
|
+
),
|
244
|
+
"OPENAI_API_BASE": (
|
245
|
+
"请输入 API Base URL:\n"
|
246
|
+
"- 官方 API: https://api.openai.com/v1\n"
|
247
|
+
"- 如使用代理或第三方服务,请输入对应的 Base URL\n"
|
248
|
+
"- 例如: https://your-proxy.com/v1"
|
249
|
+
),
|
250
|
+
}
|
251
|
+
|
225
252
|
@classmethod
|
226
253
|
def get_env_defaults(cls) -> Dict[str, str]:
|
227
254
|
"""
|
jarvis/jarvis_platform/tongyi.py
CHANGED
@@ -567,3 +567,22 @@ class TongyiPlatform(BasePlatform):
|
|
567
567
|
List[str]: 环境变量键的列表
|
568
568
|
"""
|
569
569
|
return ["TONGYI_COOKIES"]
|
570
|
+
|
571
|
+
@classmethod
|
572
|
+
def get_env_config_guide(cls) -> Dict[str, str]:
|
573
|
+
"""
|
574
|
+
获取环境变量配置指导
|
575
|
+
|
576
|
+
返回:
|
577
|
+
Dict[str, str]: 环境变量名到配置指导的映射
|
578
|
+
"""
|
579
|
+
return {
|
580
|
+
"TONGYI_COOKIES": (
|
581
|
+
"1. 登录通义千问网页版: https://tongyi.aliyun.com/\n"
|
582
|
+
"2. 打开浏览器开发者工具 (F12)\n"
|
583
|
+
'3. 切换到"网络"(Network)标签页\n'
|
584
|
+
"4. 刷新页面或发送一条消息\n"
|
585
|
+
"5. 找到 conversation 请求或任意发往 api.tongyi.com 的请求\n"
|
586
|
+
'6. 在"请求标头"中复制完整的 Cookie 值'
|
587
|
+
)
|
588
|
+
}
|
@@ -633,3 +633,21 @@ class YuanbaoPlatform(BasePlatform):
|
|
633
633
|
List[str]: 环境变量键的列表
|
634
634
|
"""
|
635
635
|
return ["YUANBAO_COOKIES"]
|
636
|
+
|
637
|
+
@classmethod
|
638
|
+
def get_env_config_guide(cls) -> Dict[str, str]:
|
639
|
+
"""
|
640
|
+
获取环境变量配置指导
|
641
|
+
|
642
|
+
返回:
|
643
|
+
Dict[str, str]: 环境变量名到配置指导的映射
|
644
|
+
"""
|
645
|
+
return {
|
646
|
+
"YUANBAO_COOKIES": (
|
647
|
+
"1. 登录腾讯元宝网页版: https://yuanbao.tencent.com/\n"
|
648
|
+
"2. 打开浏览器开发者工具 (F12)\n"
|
649
|
+
'3. 切换到"网络"(Network)标签页\n'
|
650
|
+
"4. 刷新页面,找到任意一个发往 yuanbao.tencent.com 的请求\n"
|
651
|
+
'5. 在"请求标头"中复制完整的 Cookie 值'
|
652
|
+
)
|
653
|
+
}
|
@@ -8,6 +8,12 @@ import sys
|
|
8
8
|
from typing import Any, Dict, List, Optional
|
9
9
|
|
10
10
|
import typer
|
11
|
+
from jarvis.jarvis_utils.config import (
|
12
|
+
get_normal_platform_name,
|
13
|
+
get_normal_model_name,
|
14
|
+
get_thinking_platform_name,
|
15
|
+
get_thinking_model_name,
|
16
|
+
)
|
11
17
|
|
12
18
|
from jarvis.jarvis_platform.registry import PlatformRegistry
|
13
19
|
from jarvis.jarvis_utils.input import get_multiline_input, get_single_line_input
|
@@ -19,22 +25,26 @@ app = typer.Typer(help="Jarvis AI 平台")
|
|
19
25
|
|
20
26
|
|
21
27
|
@app.command("info")
|
22
|
-
def list_platforms(
|
23
|
-
|
28
|
+
def list_platforms(
|
29
|
+
platform: Optional[str] = typer.Option(
|
30
|
+
None, "--platform", "-p", help="指定要查看的平台"
|
31
|
+
)
|
32
|
+
) -> None:
|
33
|
+
"""列出所有支持的平台和模型,或指定平台的详细信息。"""
|
24
34
|
registry = PlatformRegistry.get_global_platform_registry()
|
25
|
-
|
35
|
+
platform_names = [platform] if platform else registry.get_available_platforms()
|
26
36
|
|
27
37
|
PrettyOutput.section("Supported platforms and models", OutputType.SUCCESS)
|
28
38
|
|
29
|
-
for platform_name in
|
39
|
+
for platform_name in platform_names:
|
30
40
|
try:
|
31
41
|
# Create platform instance
|
32
|
-
|
33
|
-
if not
|
42
|
+
platform_instance = registry.create_platform(platform_name)
|
43
|
+
if not platform_instance:
|
34
44
|
continue
|
35
45
|
|
36
46
|
# Get the list of models supported by the platform
|
37
|
-
models =
|
47
|
+
models = platform_instance.get_model_list()
|
38
48
|
|
39
49
|
# Print platform name
|
40
50
|
PrettyOutput.section(f"{platform_name}", OutputType.SUCCESS)
|
@@ -55,13 +65,25 @@ def list_platforms() -> None:
|
|
55
65
|
PrettyOutput.print(f"创建 {platform_name} 平台失败", OutputType.WARNING)
|
56
66
|
|
57
67
|
|
58
|
-
def chat_with_model(
|
59
|
-
""
|
68
|
+
def chat_with_model(
|
69
|
+
platform_name: str, model_name: str, system_prompt: str, llm_type: str = "normal"
|
70
|
+
) -> None:
|
71
|
+
"""与指定平台和模型进行对话。
|
72
|
+
|
73
|
+
参数:
|
74
|
+
platform_name: 平台名称
|
75
|
+
model_name: 模型名称
|
76
|
+
system_prompt: 系统提示语
|
77
|
+
llm_type: LLM类型,可选值:'normal'(普通)或 'thinking'(思考模式)
|
78
|
+
"""
|
60
79
|
registry = PlatformRegistry.get_global_platform_registry()
|
61
80
|
conversation_history: List[Dict[str, str]] = [] # 存储对话记录
|
62
81
|
|
63
82
|
# Create platform instance
|
64
83
|
platform = registry.create_platform(platform_name)
|
84
|
+
if platform:
|
85
|
+
platform.set_model_name(model_name)
|
86
|
+
|
65
87
|
if not platform:
|
66
88
|
PrettyOutput.print(f"创建平台 {platform_name} 失败", OutputType.WARNING)
|
67
89
|
return
|
@@ -97,10 +119,16 @@ def chat_with_model(platform_name: str, model_name: str, system_prompt: str) ->
|
|
97
119
|
PrettyOutput.print("检测到空输入,退出聊天", OutputType.INFO)
|
98
120
|
break
|
99
121
|
|
122
|
+
# Parse command and arguments
|
123
|
+
stripped_input = user_input.strip()
|
124
|
+
parts = stripped_input.split(None, 1)
|
125
|
+
command = parts[0] if parts else ""
|
126
|
+
args = parts[1] if len(parts) > 1 else ""
|
127
|
+
|
100
128
|
# Check if it is a clear session command
|
101
|
-
if
|
129
|
+
if command == "/clear":
|
102
130
|
try:
|
103
|
-
platform.reset()
|
131
|
+
platform.reset() # type: ignore[no-untyped-call] # type: ignore[no-untyped-call] # type: ignore[no-untyped-call]
|
104
132
|
platform.set_model_name(model_name) # Reinitialize session
|
105
133
|
conversation_history = [] # 重置对话记录
|
106
134
|
PrettyOutput.print("会话已清除", OutputType.SUCCESS)
|
@@ -109,9 +137,9 @@ def chat_with_model(platform_name: str, model_name: str, system_prompt: str) ->
|
|
109
137
|
continue
|
110
138
|
|
111
139
|
# Check if it is an upload command
|
112
|
-
if
|
140
|
+
if command == "/upload":
|
113
141
|
try:
|
114
|
-
file_path =
|
142
|
+
file_path = args
|
115
143
|
if not file_path:
|
116
144
|
PrettyOutput.print(
|
117
145
|
'请指定要上传的文件路径,例如: /upload /path/to/file 或 /upload "/path/with spaces/file"',
|
@@ -139,9 +167,9 @@ def chat_with_model(platform_name: str, model_name: str, system_prompt: str) ->
|
|
139
167
|
continue
|
140
168
|
|
141
169
|
# Check if it is a save command
|
142
|
-
if
|
170
|
+
if command == "/save":
|
143
171
|
try:
|
144
|
-
file_path =
|
172
|
+
file_path = args
|
145
173
|
if not file_path:
|
146
174
|
PrettyOutput.print(
|
147
175
|
"请指定保存文件名,例如: /save last_message.txt",
|
@@ -170,9 +198,9 @@ def chat_with_model(platform_name: str, model_name: str, system_prompt: str) ->
|
|
170
198
|
continue
|
171
199
|
|
172
200
|
# Check if it is a saveall command
|
173
|
-
if
|
201
|
+
if command == "/saveall":
|
174
202
|
try:
|
175
|
-
file_path =
|
203
|
+
file_path = args
|
176
204
|
if not file_path:
|
177
205
|
PrettyOutput.print(
|
178
206
|
"请指定保存文件名,例如: /saveall all_conversations.txt",
|
@@ -201,9 +229,9 @@ def chat_with_model(platform_name: str, model_name: str, system_prompt: str) ->
|
|
201
229
|
continue
|
202
230
|
|
203
231
|
# Check if it is a save_session command
|
204
|
-
if
|
232
|
+
if command == "/save_session":
|
205
233
|
try:
|
206
|
-
file_path =
|
234
|
+
file_path = args
|
207
235
|
if not file_path:
|
208
236
|
PrettyOutput.print(
|
209
237
|
"请指定保存会话的文件名,例如: /save_session session.json",
|
@@ -228,9 +256,9 @@ def chat_with_model(platform_name: str, model_name: str, system_prompt: str) ->
|
|
228
256
|
continue
|
229
257
|
|
230
258
|
# Check if it is a load_session command
|
231
|
-
if
|
259
|
+
if command == "/load_session":
|
232
260
|
try:
|
233
|
-
file_path =
|
261
|
+
file_path = args
|
234
262
|
if not file_path:
|
235
263
|
PrettyOutput.print(
|
236
264
|
"请指定加载会话的文件名,例如: /load_session session.json",
|
@@ -256,18 +284,18 @@ def chat_with_model(platform_name: str, model_name: str, system_prompt: str) ->
|
|
256
284
|
continue
|
257
285
|
|
258
286
|
# Check if it is a shell command
|
259
|
-
if
|
287
|
+
if command == "/shell":
|
260
288
|
try:
|
261
|
-
|
262
|
-
if not
|
289
|
+
shell_command = args
|
290
|
+
if not shell_command:
|
263
291
|
PrettyOutput.print(
|
264
292
|
"请指定要执行的shell命令,例如: /shell ls -l",
|
265
293
|
OutputType.WARNING,
|
266
294
|
)
|
267
295
|
continue
|
268
296
|
|
269
|
-
PrettyOutput.print(f"执行命令: {
|
270
|
-
return_code = os.system(
|
297
|
+
PrettyOutput.print(f"执行命令: {shell_command}", OutputType.INFO)
|
298
|
+
return_code = os.system(shell_command)
|
271
299
|
if return_code == 0:
|
272
300
|
PrettyOutput.print("命令执行完成", OutputType.SUCCESS)
|
273
301
|
else:
|
@@ -332,11 +360,32 @@ def chat_command(
|
|
332
360
|
None, "--platform", "-p", help="指定要使用的平台"
|
333
361
|
),
|
334
362
|
model: Optional[str] = typer.Option(None, "--model", "-m", help="指定要使用的模型"),
|
363
|
+
llm_type: str = typer.Option(
|
364
|
+
"normal",
|
365
|
+
"-t",
|
366
|
+
"--llm_type",
|
367
|
+
help="使用的LLM类型,可选值:'normal'(普通)或 'thinking'(思考模式)",
|
368
|
+
),
|
369
|
+
llm_group: Optional[str] = typer.Option(
|
370
|
+
None, "-g", "--llm_group", help="使用的模型组,覆盖配置文件中的设置"
|
371
|
+
),
|
335
372
|
) -> None:
|
336
373
|
"""与指定平台和模型聊天。"""
|
374
|
+
# 如果未提供平台或模型参数,则从config获取默认值
|
375
|
+
platform = platform or (
|
376
|
+
get_thinking_platform_name(llm_group)
|
377
|
+
if llm_type == "thinking"
|
378
|
+
else get_normal_platform_name(llm_group)
|
379
|
+
)
|
380
|
+
model = model or (
|
381
|
+
get_thinking_model_name(llm_group)
|
382
|
+
if llm_type == "thinking"
|
383
|
+
else get_normal_model_name(llm_group)
|
384
|
+
)
|
385
|
+
|
337
386
|
if not validate_platform_model(platform, model):
|
338
387
|
return
|
339
|
-
chat_with_model(platform, model, "")
|
388
|
+
chat_with_model(platform, model, "", llm_type)
|
340
389
|
|
341
390
|
|
342
391
|
@app.command("service")
|
@@ -351,6 +400,9 @@ def service_command(
|
|
351
400
|
),
|
352
401
|
) -> None:
|
353
402
|
"""启动OpenAI兼容的API服务。"""
|
403
|
+
# 如果未提供平台或模型参数,则从config获取默认值
|
404
|
+
platform = platform or get_normal_platform_name()
|
405
|
+
model = model or get_normal_model_name()
|
354
406
|
start_service(host=host, port=port, default_platform=platform, default_model=model)
|
355
407
|
|
356
408
|
|
@@ -392,6 +444,15 @@ def role_command(
|
|
392
444
|
model: Optional[str] = typer.Option(
|
393
445
|
None, "--model", "-m", help="指定要使用的模型,覆盖角色配置"
|
394
446
|
),
|
447
|
+
llm_type: Optional[str] = typer.Option(
|
448
|
+
None,
|
449
|
+
"-t",
|
450
|
+
"--llm_type",
|
451
|
+
help="使用的LLM类型,可选值:'normal'(普通)或 'thinking'(思考模式),覆盖角色配置",
|
452
|
+
),
|
453
|
+
llm_group: Optional[str] = typer.Option(
|
454
|
+
None, "-g", "--llm_group", help="使用的模型组,覆盖配置文件中的设置"
|
455
|
+
),
|
395
456
|
) -> None:
|
396
457
|
"""加载角色配置文件并开始对话。"""
|
397
458
|
config_path = os.path.expanduser(config_file)
|
@@ -418,14 +479,54 @@ def role_command(
|
|
418
479
|
PrettyOutput.print("无效的选择", OutputType.ERROR)
|
419
480
|
return
|
420
481
|
|
482
|
+
# 获取llm_type,优先使用命令行参数,否则使用角色配置,默认为normal
|
483
|
+
role_llm_type = llm_type or selected_role.get("llm_type", "normal")
|
484
|
+
|
421
485
|
# 初始化平台和模型
|
422
|
-
|
423
|
-
|
486
|
+
# 如果提供了platform或model参数,优先使用命令行参数
|
487
|
+
# 否则,如果提供了llm_group,根据llm_type从配置中获取
|
488
|
+
# 最后才使用角色配置中的platform和model
|
489
|
+
if platform:
|
490
|
+
platform_name = platform
|
491
|
+
elif llm_group:
|
492
|
+
platform_name = (
|
493
|
+
get_thinking_platform_name(llm_group)
|
494
|
+
if role_llm_type == "thinking"
|
495
|
+
else get_normal_platform_name(llm_group)
|
496
|
+
)
|
497
|
+
else:
|
498
|
+
platform_name = selected_role.get("platform")
|
499
|
+
if not platform_name:
|
500
|
+
# 如果角色配置中没有platform,使用默认配置
|
501
|
+
platform_name = (
|
502
|
+
get_thinking_platform_name()
|
503
|
+
if role_llm_type == "thinking"
|
504
|
+
else get_normal_platform_name()
|
505
|
+
)
|
506
|
+
|
507
|
+
if model:
|
508
|
+
model_name = model
|
509
|
+
elif llm_group:
|
510
|
+
model_name = (
|
511
|
+
get_thinking_model_name(llm_group)
|
512
|
+
if role_llm_type == "thinking"
|
513
|
+
else get_normal_model_name(llm_group)
|
514
|
+
)
|
515
|
+
else:
|
516
|
+
model_name = selected_role.get("model")
|
517
|
+
if not model_name:
|
518
|
+
# 如果角色配置中没有model,使用默认配置
|
519
|
+
model_name = (
|
520
|
+
get_thinking_model_name()
|
521
|
+
if role_llm_type == "thinking"
|
522
|
+
else get_normal_model_name()
|
523
|
+
)
|
524
|
+
|
424
525
|
system_prompt = selected_role.get("system_prompt", "")
|
425
526
|
|
426
527
|
# 开始对话
|
427
528
|
PrettyOutput.print(f"已选择角色: {selected_role['name']}", OutputType.SUCCESS)
|
428
|
-
chat_with_model(platform_name, model_name, system_prompt)
|
529
|
+
chat_with_model(platform_name, model_name, system_prompt, role_llm_type)
|
429
530
|
|
430
531
|
|
431
532
|
def main() -> None:
|
jarvis/jarvis_stats/storage.py
CHANGED
@@ -12,6 +12,7 @@ from typing import Dict, List, Optional, Any
|
|
12
12
|
from collections import defaultdict
|
13
13
|
import sys
|
14
14
|
import time
|
15
|
+
import uuid
|
15
16
|
|
16
17
|
|
17
18
|
class StatsStorage:
|
@@ -76,7 +77,9 @@ class StatsStorage:
|
|
76
77
|
def _save_json(self, filepath: Path, data: Dict):
|
77
78
|
"""保存JSON文件"""
|
78
79
|
# 使用临时文件+重命名的原子操作来避免并发写入问题
|
79
|
-
|
80
|
+
# 使用唯一的临时文件名避免并发冲突
|
81
|
+
temp_suffix = f".tmp.{uuid.uuid4().hex[:8]}"
|
82
|
+
temp_filepath = filepath.with_suffix(temp_suffix)
|
80
83
|
max_retries = 3
|
81
84
|
|
82
85
|
for attempt in range(max_retries):
|
jarvis/jarvis_utils/globals.py
CHANGED
@@ -236,20 +236,26 @@ def get_short_term_memories(tags: Optional[List[str]] = None) -> List[Dict[str,
|
|
236
236
|
tags: 用于过滤的标签列表(可选)
|
237
237
|
|
238
238
|
返回:
|
239
|
-
List[Dict[str, Any]]:
|
239
|
+
List[Dict[str, Any]]: 符合条件的短期记忆列表,按创建时间降序排列
|
240
240
|
"""
|
241
241
|
global short_term_memories
|
242
|
-
if not tags:
|
243
|
-
return short_term_memories.copy()
|
244
242
|
|
245
|
-
#
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
243
|
+
# 获取记忆副本
|
244
|
+
memories_copy = short_term_memories.copy()
|
245
|
+
|
246
|
+
# 按标签过滤(如果提供了标签)
|
247
|
+
if tags:
|
248
|
+
filtered_memories = []
|
249
|
+
for memory in memories_copy:
|
250
|
+
memory_tags = memory.get("tags", [])
|
251
|
+
if any(tag in memory_tags for tag in tags):
|
252
|
+
filtered_memories.append(memory)
|
253
|
+
memories_copy = filtered_memories
|
254
|
+
|
255
|
+
# 按创建时间排序(最新的在前)
|
256
|
+
memories_copy.sort(key=lambda x: x.get("created_at", ""), reverse=True)
|
251
257
|
|
252
|
-
return
|
258
|
+
return memories_copy
|
253
259
|
|
254
260
|
|
255
261
|
def clear_short_term_memories() -> None:
|
jarvis/jarvis_utils/utils.py
CHANGED
@@ -26,6 +26,7 @@ from jarvis.jarvis_utils.config import (
|
|
26
26
|
)
|
27
27
|
from jarvis.jarvis_utils.embedding import get_context_token_count
|
28
28
|
from jarvis.jarvis_utils.globals import get_in_chat, get_interrupt, set_interrupt
|
29
|
+
from jarvis.jarvis_utils.input import user_confirm
|
29
30
|
from jarvis.jarvis_utils.output import OutputType, PrettyOutput
|
30
31
|
|
31
32
|
g_config_file = None
|
@@ -57,6 +58,8 @@ COMMAND_MAPPING = {
|
|
57
58
|
"jrg": "jarvis-rag",
|
58
59
|
# 统计
|
59
60
|
"jst": "jarvis-stats",
|
61
|
+
# 记忆整理
|
62
|
+
"jmo": "jarvis-memory-organizer",
|
60
63
|
}
|
61
64
|
|
62
65
|
|
@@ -189,20 +192,12 @@ def _show_usage_stats() -> None:
|
|
189
192
|
|
190
193
|
# 计算采纳率并添加到统计中
|
191
194
|
commit_stats = categorized_stats["commit"]["metrics"]
|
192
|
-
#
|
193
|
-
generated_commits = commit_stats.get(
|
194
|
-
|
195
|
-
)
|
196
|
-
accepted_commits = commit_stats.get(
|
197
|
-
"commits_accepted",
|
198
|
-
commit_stats.get("commit_accepted", commit_stats.get("commit_adopted", 0)),
|
199
|
-
)
|
200
|
-
rejected_commits = commit_stats.get(
|
201
|
-
"commits_rejected", commit_stats.get("commit_rejected", 0)
|
202
|
-
)
|
195
|
+
# 使用精确的指标名称
|
196
|
+
generated_commits = commit_stats.get("commits_generated", 0)
|
197
|
+
accepted_commits = commit_stats.get("commits_accepted", 0)
|
203
198
|
|
204
|
-
# 如果有 generated
|
205
|
-
if generated_commits > 0
|
199
|
+
# 如果有 generated,则计算采纳率
|
200
|
+
if generated_commits > 0:
|
206
201
|
adoption_rate = (accepted_commits / generated_commits) * 100
|
207
202
|
categorized_stats["adoption"]["metrics"][
|
208
203
|
"adoption_rate"
|
@@ -210,17 +205,6 @@ def _show_usage_stats() -> None:
|
|
210
205
|
categorized_stats["adoption"]["metrics"][
|
211
206
|
"commits_status"
|
212
207
|
] = f"{accepted_commits}/{generated_commits}"
|
213
|
-
elif accepted_commits > 0 or rejected_commits > 0:
|
214
|
-
# 否则使用 accepted 和 rejected 计算
|
215
|
-
total_commits = accepted_commits + rejected_commits
|
216
|
-
if total_commits > 0:
|
217
|
-
adoption_rate = (accepted_commits / total_commits) * 100
|
218
|
-
categorized_stats["adoption"]["metrics"][
|
219
|
-
"adoption_rate"
|
220
|
-
] = f"{adoption_rate:.1f}%"
|
221
|
-
categorized_stats["adoption"]["metrics"][
|
222
|
-
"commits_status"
|
223
|
-
] = f"{accepted_commits}/{total_commits}"
|
224
208
|
|
225
209
|
# 构建输出
|
226
210
|
has_data = False
|
@@ -518,11 +502,22 @@ def _interactive_config_setup(config_file_path: Path):
|
|
518
502
|
env_vars = {}
|
519
503
|
required_keys = platform_class.get_required_env_keys()
|
520
504
|
defaults = platform_class.get_env_defaults()
|
505
|
+
config_guide = platform_class.get_env_config_guide()
|
521
506
|
if required_keys:
|
522
507
|
PrettyOutput.print(
|
523
508
|
f"请输入 {platform_name} 平台所需的配置信息:", OutputType.INFO
|
524
509
|
)
|
510
|
+
|
511
|
+
# 如果有配置指导,先显示总体说明
|
512
|
+
if config_guide:
|
513
|
+
PrettyOutput.print(f"\n配置获取方法:", OutputType.INFO)
|
514
|
+
|
525
515
|
for key in required_keys:
|
516
|
+
# 显示该环境变量的配置指导
|
517
|
+
if key in config_guide and config_guide[key]:
|
518
|
+
PrettyOutput.print(f"\n{key} 获取方法:", OutputType.INFO)
|
519
|
+
PrettyOutput.print(config_guide[key], OutputType.INFO)
|
520
|
+
|
526
521
|
default_value = defaults.get(key, "")
|
527
522
|
prompt_text = f" - {key}"
|
528
523
|
if default_value:
|
@@ -998,11 +993,36 @@ def _pull_git_repo(repo_path: Path, repo_type: str):
|
|
998
993
|
timeout=10,
|
999
994
|
)
|
1000
995
|
if status_result.stdout:
|
1001
|
-
|
1002
|
-
f"检测到 '{repo_path.name}'
|
1003
|
-
|
1004
|
-
|
1005
|
-
|
996
|
+
if user_confirm(
|
997
|
+
f"检测到 '{repo_path.name}' 存在未提交的更改,是否放弃这些更改并更新?"
|
998
|
+
):
|
999
|
+
try:
|
1000
|
+
subprocess.run(
|
1001
|
+
["git", "checkout", "."],
|
1002
|
+
cwd=repo_path,
|
1003
|
+
capture_output=True,
|
1004
|
+
text=True,
|
1005
|
+
encoding="utf-8",
|
1006
|
+
errors="replace",
|
1007
|
+
check=True,
|
1008
|
+
timeout=10,
|
1009
|
+
)
|
1010
|
+
except (
|
1011
|
+
subprocess.CalledProcessError,
|
1012
|
+
subprocess.TimeoutExpired,
|
1013
|
+
FileNotFoundError,
|
1014
|
+
) as e:
|
1015
|
+
PrettyOutput.print(
|
1016
|
+
f"放弃 '{repo_path.name}' 的更改失败: {str(e)}",
|
1017
|
+
OutputType.ERROR,
|
1018
|
+
)
|
1019
|
+
return
|
1020
|
+
else:
|
1021
|
+
PrettyOutput.print(
|
1022
|
+
f"跳过更新 '{repo_path.name}' 以保留未提交的更改。",
|
1023
|
+
OutputType.INFO,
|
1024
|
+
)
|
1025
|
+
return
|
1006
1026
|
|
1007
1027
|
# 获取更新前的commit hash
|
1008
1028
|
before_hash_result = subprocess.run(
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: jarvis-ai-assistant
|
3
|
-
Version: 0.3.
|
3
|
+
Version: 0.3.3
|
4
4
|
Summary: Jarvis: An AI assistant that uses tools to interact with the system
|
5
5
|
Home-page: https://github.com/skyfireitdiy/Jarvis
|
6
6
|
Author: skyfire
|
@@ -202,27 +202,66 @@ iex ((New-Object System.Net.WebClient).DownloadString('https://raw.githubusercon
|
|
202
202
|
|
203
203
|
#### 手动安装
|
204
204
|
```bash
|
205
|
+
# 1. 克隆仓库
|
205
206
|
git clone https://github.com/skyfireitdiy/Jarvis
|
207
|
+
|
208
|
+
# 2. 进入项目目录
|
206
209
|
cd Jarvis
|
210
|
+
|
211
|
+
# 3. 安装项目为可编辑模式
|
207
212
|
pip3 install -e .
|
208
213
|
```
|
214
|
+
> **提示**: 使用 `-e .` (可编辑模式) 安装后,您对源码的任何修改都会立刻生效,非常适合开发者。
|
215
|
+
|
209
216
|
或者从PyPI安装 (可能不是最新版):
|
210
217
|
```bash
|
211
218
|
pip3 install jarvis-ai-assistant
|
212
219
|
```
|
213
220
|
|
221
|
+
**通过 uv 安装 (推荐)**
|
222
|
+
```bash
|
223
|
+
# 1. 安装 uv (如果未安装)
|
224
|
+
curl -LsSf https://astral.sh/uv/install.sh | sh
|
225
|
+
|
226
|
+
# 2. 克隆仓库
|
227
|
+
git clone https://github.com/skyfireitdiy/Jarvis
|
228
|
+
|
229
|
+
# 3. 进入项目目录
|
230
|
+
cd Jarvis
|
231
|
+
|
232
|
+
# 4. 创建虚拟环境并安装
|
233
|
+
uv venv
|
234
|
+
|
235
|
+
# 安装基础功能
|
236
|
+
uv pip install .
|
237
|
+
|
238
|
+
# 可选: 安装RAG功能(包含PyTorch等较重的依赖)
|
239
|
+
# uv pip install .[rag]
|
240
|
+
```
|
241
|
+
|
242
|
+
> **提示**: 安装完成后,建议将虚拟环境激活命令添加到您的 shell 配置文件中:
|
243
|
+
> - Bash/Zsh: 在 ~/.bashrc 或 ~/.zshrc 中添加 `source /path/to/Jarvis/.venv/bin/activate`
|
244
|
+
> - Fish: 在 ~/.config/fish/config.fish 中添加 `source /path/to/Jarvis/.venv/bin/activate.fish`
|
245
|
+
|
214
246
|
### 基本使用
|
215
247
|
Jarvis 包含一系列专注于不同任务的工具。以下是主要命令及其快捷方式:
|
216
248
|
|
217
249
|
| 命令 | 快捷方式 | 功能描述 |
|
218
250
|
|------|----------|----------|
|
219
251
|
| `jarvis` | `jvs` | 通用AI代理,适用于多种任务 |
|
252
|
+
| `jarvis-agent` | `ja` | AI代理基础功能,处理会话和任务 |
|
220
253
|
| `jarvis-code-agent` | `jca` | 专注于代码分析、修改和生成的代码代理 |
|
254
|
+
| `jarvis-code-review` | `jcr` | 智能代码审查工具 |
|
221
255
|
| `jarvis-git-commit` | `jgc` | 自动化分析代码变更并生成规范的Git提交信息 |
|
256
|
+
| `jarvis-git-squash` | `jgs` | Git提交历史整理工具 |
|
222
257
|
| `jarvis-platform-manager` | `jpm` | 管理和测试不同的大语言模型平台 |
|
258
|
+
| `jarvis-multi-agent` | `jma` | 多智能体协作系统 |
|
259
|
+
| `jarvis-tool` | `jt` | 工具管理与调用系统 |
|
260
|
+
| `jarvis-methodology` | `jm` | 方法论知识库管理 |
|
223
261
|
| `jarvis-rag` | `jrg` | 构建和查询本地化的RAG知识库 |
|
224
262
|
| `jarvis-smart-shell` | `jss` | 实验性的智能Shell功能 |
|
225
263
|
| `jarvis-stats` | `jst` | 通用统计模块,支持记录和可视化任意指标数据 |
|
264
|
+
| `jarvis-memory-organizer` | `jmo` | 记忆管理工具,支持整理、合并、导入导出记忆 |
|
226
265
|
|
227
266
|
更多详细用法和参数,请查阅我们的 [**使用指南**](docs/jarvis_book/4.使用指南.md)。
|
228
267
|
|