jarvis-ai-assistant 0.1.89__py3-none-any.whl → 0.1.91__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.
Potentially problematic release.
This version of jarvis-ai-assistant might be problematic. Click here for more details.
- jarvis/__init__.py +1 -1
- jarvis/agent.py +3 -3
- jarvis/jarvis_codebase/main.py +3 -2
- jarvis/jarvis_platform/main.py +121 -0
- jarvis/jarvis_rag/main.py +42 -14
- jarvis/jarvis_smart_shell/main.py +0 -6
- jarvis/models/ai8.py +36 -44
- jarvis/models/base.py +10 -1
- jarvis/models/kimi.py +7 -2
- jarvis/models/ollama.py +7 -12
- jarvis/models/openai.py +12 -3
- jarvis/models/oyi.py +15 -21
- jarvis/models/registry.py +12 -1
- jarvis/tools/ask_user.py +4 -14
- jarvis/tools/codebase_qa.py +1 -1
- jarvis/tools/rag.py +134 -0
- jarvis/tools/thinker.py +203 -0
- jarvis/utils.py +3 -5
- {jarvis_ai_assistant-0.1.89.dist-info → jarvis_ai_assistant-0.1.91.dist-info}/METADATA +4 -7
- jarvis_ai_assistant-0.1.91.dist-info/RECORD +41 -0
- {jarvis_ai_assistant-0.1.89.dist-info → jarvis_ai_assistant-0.1.91.dist-info}/entry_points.txt +1 -1
- jarvis/jarvis_coder/git_utils.py +0 -67
- jarvis/jarvis_coder/main.py +0 -358
- jarvis/jarvis_coder/model_utils.py +0 -30
- jarvis/jarvis_coder/patch_handler.py +0 -390
- jarvis/tools/coder.py +0 -69
- jarvis_ai_assistant-0.1.89.dist-info/RECORD +0 -43
- /jarvis/{jarvis_coder → jarvis_platform}/__init__.py +0 -0
- {jarvis_ai_assistant-0.1.89.dist-info → jarvis_ai_assistant-0.1.91.dist-info}/LICENSE +0 -0
- {jarvis_ai_assistant-0.1.89.dist-info → jarvis_ai_assistant-0.1.91.dist-info}/WHEEL +0 -0
- {jarvis_ai_assistant-0.1.89.dist-info → jarvis_ai_assistant-0.1.91.dist-info}/top_level.txt +0 -0
jarvis/__init__.py
CHANGED
jarvis/agent.py
CHANGED
|
@@ -132,7 +132,7 @@ class Agent:
|
|
|
132
132
|
embedding = self.embedding_model.encode([text],
|
|
133
133
|
convert_to_tensor=True,
|
|
134
134
|
normalize_embeddings=True)
|
|
135
|
-
vector = np.array(embedding, dtype=np.float32)
|
|
135
|
+
vector = np.array(embedding.cpu().numpy(), dtype=np.float32)
|
|
136
136
|
return vector[0] # 返回第一个向量,因为我们只编码了一个文本
|
|
137
137
|
except Exception as e:
|
|
138
138
|
PrettyOutput.print(f"创建方法论嵌入向量失败: {str(e)}", OutputType.ERROR)
|
|
@@ -337,7 +337,7 @@ class Agent:
|
|
|
337
337
|
|
|
338
338
|
self.model.set_system_message(f"""你是 {self.name},一个问题处理能力强大的 AI 助手。
|
|
339
339
|
|
|
340
|
-
|
|
340
|
+
如果用户需要执行任务,你会严格按照以下步骤处理问题:
|
|
341
341
|
1. 问题重述:确认理解问题
|
|
342
342
|
2. 根因分析(如果是问题分析类需要,其他不需要)
|
|
343
343
|
3. 设定目标:需要可达成,可检验的一个或多个目标
|
|
@@ -388,7 +388,7 @@ arguments:
|
|
|
388
388
|
-------------------------------------------------------------
|
|
389
389
|
|
|
390
390
|
""")
|
|
391
|
-
self.prompt = f"
|
|
391
|
+
self.prompt = f"{user_input}"
|
|
392
392
|
|
|
393
393
|
while True:
|
|
394
394
|
try:
|
jarvis/jarvis_codebase/main.py
CHANGED
|
@@ -20,6 +20,7 @@ class CodeBase:
|
|
|
20
20
|
os.chdir(self.root_dir)
|
|
21
21
|
self.thread_count = get_thread_count()
|
|
22
22
|
self.max_context_length = get_max_context_length()
|
|
23
|
+
self.index = None
|
|
23
24
|
|
|
24
25
|
# 初始化数据目录
|
|
25
26
|
self.data_dir = os.path.join(self.root_dir, ".jarvis-codebase")
|
|
@@ -473,10 +474,10 @@ class CodeBase:
|
|
|
473
474
|
def search_similar(self, query: str, top_k: int = 30) -> List[Tuple[str, float, str]]:
|
|
474
475
|
"""搜索关联文件"""
|
|
475
476
|
try:
|
|
477
|
+
if self.index is None:
|
|
478
|
+
return []
|
|
476
479
|
# 生成多个查询变体以提高召回率
|
|
477
480
|
model = PlatformRegistry.get_global_platform_registry().get_normal_platform()
|
|
478
|
-
model.set_suppress_output(True)
|
|
479
|
-
|
|
480
481
|
prompt = f"""请根据以下查询,生成3个不同的表述,每个表述都要完整表达原始查询的意思。这些表述将用于代码搜索,要保持专业性和准确性。
|
|
481
482
|
原始查询: {query}
|
|
482
483
|
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
from jarvis.models.registry import PlatformRegistry
|
|
2
|
+
from jarvis.utils import PrettyOutput, OutputType, load_env_from_file, get_multiline_input
|
|
3
|
+
|
|
4
|
+
def list_platforms():
|
|
5
|
+
"""列出所有支持的平台和模型"""
|
|
6
|
+
registry = PlatformRegistry.get_global_platform_registry()
|
|
7
|
+
platforms = registry.get_available_platforms()
|
|
8
|
+
|
|
9
|
+
PrettyOutput.section("支持的平台和模型", OutputType.SUCCESS)
|
|
10
|
+
|
|
11
|
+
for platform_name in platforms:
|
|
12
|
+
# 创建平台实例
|
|
13
|
+
platform = registry.create_platform(platform_name)
|
|
14
|
+
if not platform:
|
|
15
|
+
continue
|
|
16
|
+
|
|
17
|
+
# 获取平台支持的模型列表
|
|
18
|
+
try:
|
|
19
|
+
models = platform.get_model_list()
|
|
20
|
+
|
|
21
|
+
# 打印平台名称
|
|
22
|
+
PrettyOutput.section(f"{platform_name}", OutputType.SUCCESS)
|
|
23
|
+
|
|
24
|
+
# 打印模型列表
|
|
25
|
+
if models:
|
|
26
|
+
for model_name, description in models:
|
|
27
|
+
if description:
|
|
28
|
+
PrettyOutput.print(f" • {model_name} - {description}", OutputType.SUCCESS)
|
|
29
|
+
else:
|
|
30
|
+
PrettyOutput.print(f" • {model_name}", OutputType.SUCCESS)
|
|
31
|
+
else:
|
|
32
|
+
PrettyOutput.print(" 没有可用的模型信息", OutputType.WARNING)
|
|
33
|
+
|
|
34
|
+
except Exception as e:
|
|
35
|
+
PrettyOutput.print(f"获取 {platform_name} 平台模型列表失败: {str(e)}", OutputType.ERROR)
|
|
36
|
+
|
|
37
|
+
def chat_with_model(platform_name: str, model_name: str):
|
|
38
|
+
"""与指定平台和模型进行对话"""
|
|
39
|
+
registry = PlatformRegistry.get_global_platform_registry()
|
|
40
|
+
|
|
41
|
+
# 创建平台实例
|
|
42
|
+
platform = registry.create_platform(platform_name)
|
|
43
|
+
if not platform:
|
|
44
|
+
PrettyOutput.print(f"创建平台 {platform_name} 失败", OutputType.ERROR)
|
|
45
|
+
return
|
|
46
|
+
|
|
47
|
+
try:
|
|
48
|
+
# 设置模型
|
|
49
|
+
platform.set_model_name(model_name)
|
|
50
|
+
PrettyOutput.print(f"已连接到 {platform_name} 平台的 {model_name} 模型", OutputType.SUCCESS)
|
|
51
|
+
|
|
52
|
+
# 开始对话循环
|
|
53
|
+
while True:
|
|
54
|
+
# 获取用户输入
|
|
55
|
+
user_input = get_multiline_input("")
|
|
56
|
+
|
|
57
|
+
# 检查是否取消输入
|
|
58
|
+
if user_input == "__interrupt__":
|
|
59
|
+
break
|
|
60
|
+
|
|
61
|
+
# 检查是否为空输入
|
|
62
|
+
if not user_input.strip():
|
|
63
|
+
continue
|
|
64
|
+
|
|
65
|
+
try:
|
|
66
|
+
# 发送到模型并获取回复
|
|
67
|
+
response = platform.chat(user_input)
|
|
68
|
+
if not response:
|
|
69
|
+
PrettyOutput.print("未获得有效回复", OutputType.WARNING)
|
|
70
|
+
|
|
71
|
+
except Exception as e:
|
|
72
|
+
PrettyOutput.print(f"对话失败: {str(e)}", OutputType.ERROR)
|
|
73
|
+
|
|
74
|
+
except Exception as e:
|
|
75
|
+
PrettyOutput.print(f"初始化对话失败: {str(e)}", OutputType.ERROR)
|
|
76
|
+
finally:
|
|
77
|
+
# 清理资源
|
|
78
|
+
try:
|
|
79
|
+
platform.delete_chat()
|
|
80
|
+
except:
|
|
81
|
+
pass
|
|
82
|
+
|
|
83
|
+
def info_command(args):
|
|
84
|
+
"""处理 info 子命令"""
|
|
85
|
+
list_platforms()
|
|
86
|
+
|
|
87
|
+
def chat_command(args):
|
|
88
|
+
"""处理 chat 子命令"""
|
|
89
|
+
if not args.platform or not args.model:
|
|
90
|
+
PrettyOutput.print("请指定平台和模型。使用 'jarvis info' 查看可用的平台和模型。", OutputType.ERROR)
|
|
91
|
+
return
|
|
92
|
+
chat_with_model(args.platform, args.model)
|
|
93
|
+
|
|
94
|
+
def main():
|
|
95
|
+
"""主函数"""
|
|
96
|
+
import argparse
|
|
97
|
+
|
|
98
|
+
load_env_from_file()
|
|
99
|
+
|
|
100
|
+
parser = argparse.ArgumentParser(description='Jarvis AI Platform')
|
|
101
|
+
subparsers = parser.add_subparsers(dest='command', help='可用的子命令')
|
|
102
|
+
|
|
103
|
+
# info 子命令
|
|
104
|
+
info_parser = subparsers.add_parser('info', help='显示支持的平台和模型信息')
|
|
105
|
+
|
|
106
|
+
# chat 子命令
|
|
107
|
+
chat_parser = subparsers.add_parser('chat', help='与指定的平台和模型进行对话')
|
|
108
|
+
chat_parser.add_argument('--platform', '-p', help='指定要使用的平台')
|
|
109
|
+
chat_parser.add_argument('--model', '-m', help='指定要使用的模型')
|
|
110
|
+
|
|
111
|
+
args = parser.parse_args()
|
|
112
|
+
|
|
113
|
+
if args.command == 'info':
|
|
114
|
+
info_command(args)
|
|
115
|
+
elif args.command == 'chat':
|
|
116
|
+
chat_command(args)
|
|
117
|
+
else:
|
|
118
|
+
parser.print_help()
|
|
119
|
+
|
|
120
|
+
if __name__ == "__main__":
|
|
121
|
+
main()
|
jarvis/jarvis_rag/main.py
CHANGED
|
@@ -144,6 +144,7 @@ class RAGTool:
|
|
|
144
144
|
self.min_paragraph_length = int(os.environ.get("JARVIS_MIN_PARAGRAPH_LENGTH", "50")) # 最小段落长度
|
|
145
145
|
self.max_paragraph_length = int(os.environ.get("JARVIS_MAX_PARAGRAPH_LENGTH", "1000")) # 最大段落长度
|
|
146
146
|
self.context_window = int(os.environ.get("JARVIS_CONTEXT_WINDOW", "5")) # 上下文窗口大小,默认前后各5个片段
|
|
147
|
+
self.max_context_length = int(get_max_context_length() * 0.8)
|
|
147
148
|
|
|
148
149
|
# 初始化数据目录
|
|
149
150
|
self.data_dir = os.path.join(self.root_dir, ".jarvis-rag")
|
|
@@ -163,7 +164,6 @@ class RAGTool:
|
|
|
163
164
|
self.cache_path = os.path.join(self.data_dir, "cache.pkl")
|
|
164
165
|
self.documents: List[Document] = []
|
|
165
166
|
self.index = None
|
|
166
|
-
self.max_context_length = get_max_context_length()
|
|
167
167
|
|
|
168
168
|
# 加载缓存
|
|
169
169
|
self._load_cache()
|
|
@@ -400,7 +400,7 @@ class RAGTool:
|
|
|
400
400
|
PrettyOutput.print(f"成功索引了 {len(self.documents)} 个文档片段",
|
|
401
401
|
output_type=OutputType.SUCCESS)
|
|
402
402
|
|
|
403
|
-
def search(self, query: str, top_k: int =
|
|
403
|
+
def search(self, query: str, top_k: int = 30) -> List[Tuple[Document, float]]:
|
|
404
404
|
"""优化搜索策略"""
|
|
405
405
|
if not self.index:
|
|
406
406
|
PrettyOutput.print("索引未构建,正在构建...", output_type=OutputType.INFO)
|
|
@@ -557,27 +557,55 @@ class RAGTool:
|
|
|
557
557
|
results = self.query(question)
|
|
558
558
|
if not results:
|
|
559
559
|
return None
|
|
560
|
+
|
|
561
|
+
# 显示找到的文档片段
|
|
562
|
+
for doc in results:
|
|
563
|
+
PrettyOutput.print(f"文件: {doc.metadata['file_path']}", output_type=OutputType.INFO)
|
|
564
|
+
PrettyOutput.print(f"片段 {doc.metadata['chunk_index'] + 1}/{doc.metadata['total_chunks']}",
|
|
565
|
+
output_type=OutputType.INFO)
|
|
566
|
+
PrettyOutput.print("\n内容:", output_type=OutputType.INFO)
|
|
567
|
+
content = doc.content.encode('utf-8', errors='replace').decode('utf-8')
|
|
568
|
+
PrettyOutput.print(content, output_type=OutputType.INFO)
|
|
569
|
+
|
|
570
|
+
# 构建基础提示词
|
|
571
|
+
base_prompt = f"""请基于以下文档片段回答用户的问题。如果文档内容不足以完整回答问题,请明确指出。
|
|
572
|
+
|
|
573
|
+
用户问题: {question}
|
|
560
574
|
|
|
561
|
-
|
|
575
|
+
相关文档片段:
|
|
576
|
+
"""
|
|
577
|
+
end_prompt = "\n请提供准确、简洁的回答,如果文档内容不足以完整回答问题,请明确指出。"
|
|
578
|
+
|
|
579
|
+
# 计算可用于文档内容的最大长度
|
|
580
|
+
# 预留一些空间给模型回答
|
|
581
|
+
available_length = self.max_context_length - len(base_prompt) - len(end_prompt) - 500
|
|
582
|
+
|
|
583
|
+
# 构建上下文,同时控制总长度
|
|
562
584
|
context = []
|
|
585
|
+
current_length = 0
|
|
586
|
+
|
|
563
587
|
for doc in results:
|
|
564
|
-
|
|
588
|
+
# 计算这个文档片段的内容长度
|
|
589
|
+
doc_content = f"""
|
|
565
590
|
来源文件: {doc.metadata['file_path']}
|
|
566
591
|
内容:
|
|
567
592
|
{doc.content}
|
|
568
593
|
---
|
|
569
|
-
"""
|
|
594
|
+
"""
|
|
595
|
+
content_length = len(doc_content)
|
|
596
|
+
|
|
597
|
+
# 如果添加这个片段会超出限制,就停止添加
|
|
598
|
+
if current_length + content_length > available_length:
|
|
599
|
+
PrettyOutput.print("由于上下文长度限制,部分相关文档片段被省略",
|
|
600
|
+
output_type=OutputType.WARNING)
|
|
601
|
+
break
|
|
602
|
+
|
|
603
|
+
context.append(doc_content)
|
|
604
|
+
current_length += content_length
|
|
570
605
|
|
|
571
|
-
#
|
|
572
|
-
prompt =
|
|
606
|
+
# 构建完整的提示词
|
|
607
|
+
prompt = base_prompt + ''.join(context) + end_prompt
|
|
573
608
|
|
|
574
|
-
用户问题: {question}
|
|
575
|
-
|
|
576
|
-
相关文档片段:
|
|
577
|
-
{''.join(context)}
|
|
578
|
-
|
|
579
|
-
请提供准确、简洁的回答,并在适当时引用具体的文档来源。
|
|
580
|
-
"""
|
|
581
609
|
# 获取模型实例并生成回答
|
|
582
610
|
model = PlatformRegistry.get_global_platform_registry().get_normal_platform()
|
|
583
611
|
response = model.chat(prompt)
|
|
@@ -55,12 +55,6 @@ def process_request(request: str) -> Optional[str]:
|
|
|
55
55
|
4. 不要添加任何换行或额外空格
|
|
56
56
|
5. 如果需要多个命令,使用 && 连接
|
|
57
57
|
|
|
58
|
-
安全要求:
|
|
59
|
-
- 生成的命令必须是安全的,不能包含危险操作
|
|
60
|
-
- 如果需要sudo权限,要明确提示用户
|
|
61
|
-
- 对于复杂操作,优先使用管道而不是临时文件
|
|
62
|
-
- 确保命令的可移植性,优先使用通用的POSIX命令
|
|
63
|
-
|
|
64
58
|
示例输入:
|
|
65
59
|
"查找当前目录下的所有Python文件"
|
|
66
60
|
|
jarvis/models/ai8.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import os
|
|
2
|
-
from typing import Dict, List
|
|
2
|
+
from typing import Dict, List, Tuple
|
|
3
3
|
from jarvis.models.base import BasePlatform
|
|
4
4
|
from jarvis.utils import PrettyOutput, OutputType
|
|
5
5
|
import requests
|
|
@@ -12,7 +12,9 @@ class AI8Model(BasePlatform):
|
|
|
12
12
|
platform_name = "ai8"
|
|
13
13
|
BASE_URL = "https://ai8.rcouyi.com"
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
def get_model_list(self) -> List[Tuple[str, str]]:
|
|
16
|
+
"""获取模型列表"""
|
|
17
|
+
return [(name,info['desc']) for name,info in self.models.items()]
|
|
16
18
|
|
|
17
19
|
def __init__(self):
|
|
18
20
|
"""Initialize model"""
|
|
@@ -22,53 +24,13 @@ class AI8Model(BasePlatform):
|
|
|
22
24
|
self.files = []
|
|
23
25
|
self.models = {} # 存储模型信息
|
|
24
26
|
|
|
25
|
-
# 获取可用模型列表
|
|
26
|
-
available_models = self.get_available_models()
|
|
27
|
-
|
|
28
|
-
if AI8Model.first_time:
|
|
29
|
-
AI8Model.first_time = False
|
|
30
|
-
if available_models:
|
|
31
|
-
PrettyOutput.section("支持的模型", OutputType.SUCCESS)
|
|
32
|
-
for model in self.models.values():
|
|
33
|
-
# 格式化显示模型信息
|
|
34
|
-
model_str = f"{model['value']:<30}"
|
|
35
|
-
|
|
36
|
-
# 添加标签
|
|
37
|
-
model_str += f"{model['label']}"
|
|
38
|
-
|
|
39
|
-
# 添加标签和积分信息
|
|
40
|
-
attrs = []
|
|
41
|
-
if model['attr'].get('tag'):
|
|
42
|
-
attrs.append(model['attr']['tag'])
|
|
43
|
-
if model['attr'].get('integral'):
|
|
44
|
-
attrs.append(model['attr']['integral'])
|
|
45
|
-
|
|
46
|
-
# 添加特性标记
|
|
47
|
-
features = []
|
|
48
|
-
if model['attr'].get('multimodal'):
|
|
49
|
-
features.append("多模态")
|
|
50
|
-
if model['attr'].get('plugin'):
|
|
51
|
-
features.append("插件支持")
|
|
52
|
-
if model['attr'].get('onlyImg'):
|
|
53
|
-
features.append("图像支持")
|
|
54
|
-
if features:
|
|
55
|
-
model_str += f" [{'|'.join(features)}]"
|
|
56
|
-
|
|
57
|
-
# 添加备注
|
|
58
|
-
if model['attr'].get('note'):
|
|
59
|
-
model_str += f" - {model['attr']['note']}"
|
|
60
|
-
|
|
61
|
-
PrettyOutput.print(model_str, OutputType.INFO)
|
|
62
|
-
else:
|
|
63
|
-
PrettyOutput.print("获取模型列表失败", OutputType.WARNING)
|
|
64
|
-
|
|
65
27
|
self.token = os.getenv("AI8_API_KEY")
|
|
66
28
|
if not self.token:
|
|
67
|
-
|
|
29
|
+
PrettyOutput.print("AI8_API_KEY未设置", OutputType.WARNING)
|
|
68
30
|
|
|
69
31
|
|
|
70
32
|
self.model_name = os.getenv("JARVIS_MODEL") or "deepseek-chat"
|
|
71
|
-
if self.model_name not in self.
|
|
33
|
+
if self.model_name not in self.get_available_models():
|
|
72
34
|
PrettyOutput.print(f"警告: 当前选择的模型 {self.model_name} 不在可用列表中", OutputType.WARNING)
|
|
73
35
|
|
|
74
36
|
|
|
@@ -283,6 +245,9 @@ class AI8Model(BasePlatform):
|
|
|
283
245
|
List[str]: 可用模型名称列表
|
|
284
246
|
"""
|
|
285
247
|
try:
|
|
248
|
+
if self.models:
|
|
249
|
+
return list(self.models.keys())
|
|
250
|
+
|
|
286
251
|
headers = {
|
|
287
252
|
'Content-Type': 'application/json',
|
|
288
253
|
'Accept': 'application/json, text/plain, */*',
|
|
@@ -311,6 +276,33 @@ class AI8Model(BasePlatform):
|
|
|
311
276
|
model['value']: model
|
|
312
277
|
for model in data['data']['models']
|
|
313
278
|
}
|
|
279
|
+
|
|
280
|
+
for model in self.models.values():
|
|
281
|
+
# 添加标签
|
|
282
|
+
model_str = f"{model['label']}"
|
|
283
|
+
|
|
284
|
+
# 添加标签和积分信息
|
|
285
|
+
attrs = []
|
|
286
|
+
if model['attr'].get('tag'):
|
|
287
|
+
attrs.append(model['attr']['tag'])
|
|
288
|
+
if model['attr'].get('integral'):
|
|
289
|
+
attrs.append(model['attr']['integral'])
|
|
290
|
+
|
|
291
|
+
# 添加特性标记
|
|
292
|
+
features = []
|
|
293
|
+
if model['attr'].get('multimodal'):
|
|
294
|
+
features.append("多模态")
|
|
295
|
+
if model['attr'].get('plugin'):
|
|
296
|
+
features.append("插件支持")
|
|
297
|
+
if model['attr'].get('onlyImg'):
|
|
298
|
+
features.append("图像支持")
|
|
299
|
+
if features:
|
|
300
|
+
model_str += f" [{'|'.join(features)}]"
|
|
301
|
+
|
|
302
|
+
# 添加备注
|
|
303
|
+
if model['attr'].get('note'):
|
|
304
|
+
model_str += f" - {model['attr']['note']}"
|
|
305
|
+
model['desc'] = model_str
|
|
314
306
|
|
|
315
307
|
return list(self.models.keys())
|
|
316
308
|
|
jarvis/models/base.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
from abc import ABC, abstractmethod
|
|
2
|
-
from typing import Dict, List
|
|
2
|
+
from typing import Dict, List, Tuple
|
|
3
3
|
|
|
4
4
|
|
|
5
5
|
class BasePlatform(ABC):
|
|
@@ -14,6 +14,7 @@ class BasePlatform(ABC):
|
|
|
14
14
|
"""销毁模型"""
|
|
15
15
|
self.delete_chat()
|
|
16
16
|
|
|
17
|
+
@abstractmethod
|
|
17
18
|
def set_model_name(self, model_name: str):
|
|
18
19
|
"""设置模型名称"""
|
|
19
20
|
raise NotImplementedError("set_model_name is not implemented")
|
|
@@ -23,10 +24,12 @@ class BasePlatform(ABC):
|
|
|
23
24
|
"""执行对话"""
|
|
24
25
|
raise NotImplementedError("chat is not implemented")
|
|
25
26
|
|
|
27
|
+
@abstractmethod
|
|
26
28
|
def upload_files(self, file_list: List[str]) -> List[Dict]:
|
|
27
29
|
"""上传文件"""
|
|
28
30
|
raise NotImplementedError("upload_files is not implemented")
|
|
29
31
|
|
|
32
|
+
@abstractmethod
|
|
30
33
|
def reset(self):
|
|
31
34
|
"""重置模型"""
|
|
32
35
|
raise NotImplementedError("reset is not implemented")
|
|
@@ -41,9 +44,15 @@ class BasePlatform(ABC):
|
|
|
41
44
|
"""删除对话"""
|
|
42
45
|
raise NotImplementedError("delete_chat is not implemented")
|
|
43
46
|
|
|
47
|
+
@abstractmethod
|
|
44
48
|
def set_system_message(self, message: str):
|
|
45
49
|
"""设置系统消息"""
|
|
46
50
|
raise NotImplementedError("set_system_message is not implemented")
|
|
51
|
+
|
|
52
|
+
@abstractmethod
|
|
53
|
+
def get_model_list(self) -> List[Tuple[str, str]]:
|
|
54
|
+
"""获取模型列表"""
|
|
55
|
+
raise NotImplementedError("get_model_list is not implemented")
|
|
47
56
|
|
|
48
57
|
def set_suppress_output(self, suppress: bool):
|
|
49
58
|
"""设置是否屏蔽输出"""
|
jarvis/models/kimi.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from typing import Dict, List
|
|
1
|
+
from typing import Dict, List, Tuple
|
|
2
2
|
import requests
|
|
3
3
|
import json
|
|
4
4
|
import os
|
|
@@ -12,12 +12,17 @@ class KimiModel(BasePlatform):
|
|
|
12
12
|
"""Kimi模型实现"""
|
|
13
13
|
|
|
14
14
|
platform_name = "kimi"
|
|
15
|
+
|
|
16
|
+
def get_model_list(self) -> List[Tuple[str, str]]:
|
|
17
|
+
"""获取模型列表"""
|
|
18
|
+
return [("kimi", "基于网页Kimi的封装,免费接口")]
|
|
15
19
|
|
|
16
20
|
def __init__(self):
|
|
17
21
|
"""
|
|
18
22
|
初始化Kimi模型
|
|
19
23
|
"""
|
|
20
24
|
super().__init__()
|
|
25
|
+
self.chat_id = ""
|
|
21
26
|
self.api_key = os.getenv("KIMI_API_KEY")
|
|
22
27
|
if not self.api_key:
|
|
23
28
|
PrettyOutput.print("\n需要设置 KIMI_API_KEY 才能使用 Jarvis。请按以下步骤操作:", OutputType.INFO)
|
|
@@ -35,7 +40,7 @@ class KimiModel(BasePlatform):
|
|
|
35
40
|
PrettyOutput.print("\n 方法 2: 直接设置环境变量:", OutputType.INFO)
|
|
36
41
|
PrettyOutput.print(" export KIMI_API_KEY=your_key_here", OutputType.INFO)
|
|
37
42
|
PrettyOutput.print("\n设置完成后重新运行 Jarvis。", OutputType.INFO)
|
|
38
|
-
|
|
43
|
+
PrettyOutput.print("KIMI_API_KEY未设置", OutputType.WARNING)
|
|
39
44
|
self.auth_header = f"Bearer {self.api_key}"
|
|
40
45
|
self.chat_id = ""
|
|
41
46
|
self.uploaded_files = [] # 存储已上传文件的信息
|
jarvis/models/ollama.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import requests
|
|
2
|
-
from typing import List, Dict
|
|
2
|
+
from typing import List, Dict, Tuple
|
|
3
3
|
from jarvis.models.base import BasePlatform
|
|
4
4
|
from jarvis.utils import OutputType, PrettyOutput
|
|
5
5
|
import os
|
|
@@ -20,7 +20,6 @@ class OllamaPlatform(BasePlatform):
|
|
|
20
20
|
|
|
21
21
|
# 检查 Ollama 服务是否可用
|
|
22
22
|
try:
|
|
23
|
-
PrettyOutput.print(f"正在连接 Ollama 服务 ({self.api_base})...", OutputType.INFO)
|
|
24
23
|
response = requests.get(f"{self.api_base}/api/tags")
|
|
25
24
|
response.raise_for_status()
|
|
26
25
|
available_models = [model["name"] for model in response.json().get("models", [])]
|
|
@@ -32,16 +31,6 @@ class OllamaPlatform(BasePlatform):
|
|
|
32
31
|
PrettyOutput.print(f" ollama pull {self.model_name}", OutputType.INFO)
|
|
33
32
|
raise Exception("No available models found")
|
|
34
33
|
|
|
35
|
-
PrettyOutput.print(f"可用模型: {', '.join(available_models)}", OutputType.INFO)
|
|
36
|
-
|
|
37
|
-
if self.model_name not in available_models:
|
|
38
|
-
PrettyOutput.print(f"\n警告:模型 {self.model_name} 未下载", OutputType.WARNING)
|
|
39
|
-
PrettyOutput.print("\n请使用以下命令下载模型:", OutputType.INFO)
|
|
40
|
-
PrettyOutput.print(f"ollama pull {self.model_name}", OutputType.INFO)
|
|
41
|
-
raise Exception(f"Model {self.model_name} is not available")
|
|
42
|
-
|
|
43
|
-
PrettyOutput.print(f"使用模型: {self.model_name}", OutputType.SUCCESS)
|
|
44
|
-
|
|
45
34
|
except requests.exceptions.ConnectionError:
|
|
46
35
|
PrettyOutput.print("\nOllama 服务未启动或无法连接", OutputType.ERROR)
|
|
47
36
|
PrettyOutput.print("请确保已经:", OutputType.INFO)
|
|
@@ -53,6 +42,12 @@ class OllamaPlatform(BasePlatform):
|
|
|
53
42
|
self.messages = []
|
|
54
43
|
self.system_message = ""
|
|
55
44
|
|
|
45
|
+
def get_model_list(self) -> List[Tuple[str, str]]:
|
|
46
|
+
"""获取模型列表"""
|
|
47
|
+
response = requests.get(f"{self.api_base}/api/tags")
|
|
48
|
+
response.raise_for_status()
|
|
49
|
+
return [(model["name"], "") for model in response.json().get("models", [])]
|
|
50
|
+
|
|
56
51
|
def set_model_name(self, model_name: str):
|
|
57
52
|
"""设置模型名称"""
|
|
58
53
|
self.model_name = model_name
|
jarvis/models/openai.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from typing import Dict, List
|
|
1
|
+
from typing import Dict, List, Tuple
|
|
2
2
|
import os
|
|
3
3
|
from openai import OpenAI
|
|
4
4
|
from jarvis.models.base import BasePlatform
|
|
@@ -8,17 +8,22 @@ class OpenAIModel(BasePlatform):
|
|
|
8
8
|
"""DeepSeek模型实现"""
|
|
9
9
|
|
|
10
10
|
platform_name = "openai"
|
|
11
|
+
|
|
12
|
+
def upload_files(self, file_list: List[str]):
|
|
13
|
+
"""上传文件"""
|
|
14
|
+
PrettyOutput.print("OpenAI 不支持上传文件", OutputType.WARNING)
|
|
11
15
|
|
|
12
16
|
def __init__(self):
|
|
13
17
|
"""
|
|
14
18
|
初始化DeepSeek模型
|
|
15
19
|
"""
|
|
16
20
|
super().__init__()
|
|
21
|
+
self.system_message = ""
|
|
17
22
|
self.api_key = os.getenv("OPENAI_API_KEY")
|
|
18
23
|
if not self.api_key:
|
|
19
24
|
PrettyOutput.print("\n需要设置以下环境变量才能使用 OpenAI 模型:", OutputType.INFO)
|
|
20
25
|
PrettyOutput.print(" • OPENAI_API_KEY: API 密钥", OutputType.INFO)
|
|
21
|
-
PrettyOutput.print(" • OPENAI_API_BASE: (可选) API 基础地址,默认使用 https://api.
|
|
26
|
+
PrettyOutput.print(" • OPENAI_API_BASE: (可选) API 基础地址,默认使用 https://api.openai.com/v1", OutputType.INFO)
|
|
22
27
|
PrettyOutput.print("\n可以通过以下方式设置:", OutputType.INFO)
|
|
23
28
|
PrettyOutput.print("1. 创建或编辑 ~/.jarvis_env 文件:", OutputType.INFO)
|
|
24
29
|
PrettyOutput.print(" OPENAI_API_KEY=your_api_key", OutputType.INFO)
|
|
@@ -28,7 +33,7 @@ class OpenAIModel(BasePlatform):
|
|
|
28
33
|
PrettyOutput.print(" export OPENAI_API_KEY=your_api_key", OutputType.INFO)
|
|
29
34
|
PrettyOutput.print(" export OPENAI_API_BASE=your_api_base", OutputType.INFO)
|
|
30
35
|
PrettyOutput.print(" export OPENAI_MODEL_NAME=your_model_name", OutputType.INFO)
|
|
31
|
-
|
|
36
|
+
PrettyOutput.print("OPENAI_API_KEY未设置", OutputType.WARNING)
|
|
32
37
|
|
|
33
38
|
self.base_url = os.getenv("OPENAI_API_BASE", "https://api.openai.com/v1")
|
|
34
39
|
self.model_name = os.getenv("JARVIS_MODEL") or "gpt-4o"
|
|
@@ -41,6 +46,10 @@ class OpenAIModel(BasePlatform):
|
|
|
41
46
|
self.messages: List[Dict[str, str]] = []
|
|
42
47
|
self.system_message = ""
|
|
43
48
|
|
|
49
|
+
def get_model_list(self) -> List[Tuple[str, str]]:
|
|
50
|
+
"""获取模型列表"""
|
|
51
|
+
return []
|
|
52
|
+
|
|
44
53
|
def set_model_name(self, model_name: str):
|
|
45
54
|
"""设置模型名称"""
|
|
46
55
|
|
jarvis/models/oyi.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import mimetypes
|
|
2
2
|
import os
|
|
3
|
-
from typing import Dict, List
|
|
3
|
+
from typing import Dict, List, Tuple
|
|
4
4
|
from jarvis.models.base import BasePlatform
|
|
5
5
|
from jarvis.utils import PrettyOutput, OutputType, get_max_context_length
|
|
6
6
|
import requests
|
|
@@ -12,36 +12,26 @@ class OyiModel(BasePlatform):
|
|
|
12
12
|
platform_name = "oyi"
|
|
13
13
|
BASE_URL = "https://api-10086.rcouyi.com"
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
def get_model_list(self) -> List[Tuple[str, str]]:
|
|
16
|
+
"""获取模型列表"""
|
|
17
|
+
return [(name,info['desc']) for name,info in self.models.items()]
|
|
16
18
|
|
|
17
19
|
def __init__(self):
|
|
18
20
|
"""Initialize model"""
|
|
19
21
|
super().__init__()
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
# 获取可用模型列表
|
|
23
|
-
available_models = self.get_available_models()
|
|
24
|
-
|
|
25
|
-
if OyiModel.first_time:
|
|
26
|
-
OyiModel.first_time = False
|
|
27
|
-
if available_models:
|
|
28
|
-
for model in available_models:
|
|
29
|
-
PrettyOutput.print(model, OutputType.INFO)
|
|
30
|
-
else:
|
|
31
|
-
PrettyOutput.print("获取模型列表失败", OutputType.WARNING)
|
|
32
|
-
|
|
22
|
+
self.models = {}
|
|
33
23
|
self.messages = []
|
|
34
24
|
self.system_message = ""
|
|
35
25
|
self.conversation = None
|
|
36
|
-
self.upload_files = []
|
|
26
|
+
self.upload_files = []
|
|
37
27
|
self.first_chat = True
|
|
38
28
|
|
|
39
29
|
self.token = os.getenv("OYI_API_KEY")
|
|
40
30
|
if not self.token:
|
|
41
|
-
|
|
31
|
+
PrettyOutput.print("OYI_API_KEY未设置", OutputType.WARNING)
|
|
42
32
|
|
|
43
33
|
self.model_name = os.getenv("JARVIS_MODEL") or "deepseek-chat"
|
|
44
|
-
if self.model_name not in [m.split()[0] for m in
|
|
34
|
+
if self.model_name not in [m.split()[0] for m in self.get_available_models()]:
|
|
45
35
|
PrettyOutput.print(f"警告: 当前选择的模型 {self.model_name} 不在可用列表中", OutputType.WARNING)
|
|
46
36
|
|
|
47
37
|
|
|
@@ -313,6 +303,9 @@ class OyiModel(BasePlatform):
|
|
|
313
303
|
List[str]: 可用模型名称列表
|
|
314
304
|
"""
|
|
315
305
|
try:
|
|
306
|
+
if self.models:
|
|
307
|
+
return list(self.models.keys())
|
|
308
|
+
|
|
316
309
|
headers = {
|
|
317
310
|
'Content-Type': 'application/json',
|
|
318
311
|
'Accept': 'application/json, text/plain, */*',
|
|
@@ -343,7 +336,8 @@ class OyiModel(BasePlatform):
|
|
|
343
336
|
models = []
|
|
344
337
|
for model in self.models.values():
|
|
345
338
|
# 基本信息
|
|
346
|
-
|
|
339
|
+
model_name = model['value']
|
|
340
|
+
model_str = model['label']
|
|
347
341
|
|
|
348
342
|
# 添加后缀标签
|
|
349
343
|
suffix = model.get('suffix', [])
|
|
@@ -364,8 +358,8 @@ class OyiModel(BasePlatform):
|
|
|
364
358
|
# 添加文件上传支持标记
|
|
365
359
|
if model.get('uploadFile'):
|
|
366
360
|
model_str += " [支持文件上传]"
|
|
367
|
-
|
|
368
|
-
models.append(
|
|
361
|
+
model['desc'] = model_str
|
|
362
|
+
models.append(model_name)
|
|
369
363
|
|
|
370
364
|
return sorted(models)
|
|
371
365
|
|