jarvis-ai-assistant 0.1.96__py3-none-any.whl → 0.1.97__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 +138 -144
- jarvis/jarvis_codebase/main.py +87 -54
- jarvis/jarvis_coder/git_utils.py +4 -7
- jarvis/jarvis_coder/main.py +17 -22
- jarvis/jarvis_coder/patch_handler.py +141 -441
- jarvis/jarvis_coder/plan_generator.py +64 -36
- jarvis/jarvis_platform/main.py +1 -1
- jarvis/jarvis_rag/main.py +1 -1
- jarvis/jarvis_smart_shell/main.py +15 -15
- jarvis/main.py +24 -24
- jarvis/models/ai8.py +22 -22
- jarvis/models/base.py +17 -13
- jarvis/models/kimi.py +31 -31
- jarvis/models/ollama.py +28 -28
- jarvis/models/openai.py +22 -24
- jarvis/models/oyi.py +25 -25
- jarvis/models/registry.py +33 -34
- jarvis/tools/ask_user.py +5 -5
- jarvis/tools/base.py +2 -2
- jarvis/tools/chdir.py +9 -9
- jarvis/tools/codebase_qa.py +4 -4
- jarvis/tools/coder.py +4 -4
- jarvis/tools/file_ops.py +1 -1
- jarvis/tools/generator.py +23 -23
- jarvis/tools/methodology.py +4 -4
- jarvis/tools/rag.py +4 -4
- jarvis/tools/registry.py +38 -38
- jarvis/tools/search.py +42 -42
- jarvis/tools/shell.py +13 -13
- jarvis/tools/sub_agent.py +16 -16
- jarvis/tools/thinker.py +41 -41
- jarvis/tools/webpage.py +17 -17
- jarvis/utils.py +59 -60
- {jarvis_ai_assistant-0.1.96.dist-info → jarvis_ai_assistant-0.1.97.dist-info}/METADATA +1 -1
- jarvis_ai_assistant-0.1.97.dist-info/RECORD +47 -0
- jarvis_ai_assistant-0.1.96.dist-info/RECORD +0 -47
- {jarvis_ai_assistant-0.1.96.dist-info → jarvis_ai_assistant-0.1.97.dist-info}/LICENSE +0 -0
- {jarvis_ai_assistant-0.1.96.dist-info → jarvis_ai_assistant-0.1.97.dist-info}/WHEEL +0 -0
- {jarvis_ai_assistant-0.1.96.dist-info → jarvis_ai_assistant-0.1.97.dist-info}/entry_points.txt +0 -0
- {jarvis_ai_assistant-0.1.96.dist-info → jarvis_ai_assistant-0.1.97.dist-info}/top_level.txt +0 -0
|
@@ -1,49 +1,54 @@
|
|
|
1
|
-
|
|
1
|
+
import re
|
|
2
|
+
from typing import Dict, List, Tuple
|
|
2
3
|
from jarvis.models.registry import PlatformRegistry
|
|
3
|
-
from jarvis.utils import PrettyOutput, OutputType, get_multiline_input
|
|
4
|
-
from jarvis.models.base import BasePlatform
|
|
4
|
+
from jarvis.utils import PrettyOutput, OutputType, get_multiline_input
|
|
5
5
|
|
|
6
6
|
class PlanGenerator:
|
|
7
7
|
"""修改方案生成器"""
|
|
8
8
|
|
|
9
|
-
def
|
|
10
|
-
"""初始化
|
|
11
|
-
|
|
12
|
-
Args:
|
|
13
|
-
thinking_model: 用于思考的大模型
|
|
14
|
-
"""
|
|
15
|
-
self.thinking_model = PlatformRegistry.get_global_platform_registry().get_thinking_platform()
|
|
16
|
-
|
|
17
|
-
def _build_prompt(self, feature: str, related_files: List[Dict]) -> str:
|
|
9
|
+
def _build_prompt(self, feature: str, related_files: List[Dict], additional_info: str) -> str:
|
|
18
10
|
"""构建提示词
|
|
19
11
|
|
|
20
12
|
Args:
|
|
21
13
|
feature: 功能描述
|
|
22
14
|
related_files: 相关文件列表
|
|
23
|
-
|
|
15
|
+
additional_info: 用户补充信息
|
|
24
16
|
|
|
25
17
|
Returns:
|
|
26
18
|
str: 完整的提示词
|
|
27
19
|
"""
|
|
28
|
-
prompt = "
|
|
20
|
+
prompt = "You are a code modification expert who can generate modification plans based on requirements and relevant code snippets. I need your help to analyze how to implement the following feature:\n\n"
|
|
29
21
|
prompt += f"{feature}\n\n"
|
|
30
22
|
|
|
31
|
-
prompt += "
|
|
23
|
+
prompt += "Here are the relevant code file snippets:\n\n"
|
|
32
24
|
|
|
33
25
|
for file in related_files:
|
|
34
|
-
prompt += f"
|
|
26
|
+
prompt += f"File: {file['file_path']}\n"
|
|
35
27
|
for part in file["parts"]:
|
|
36
28
|
prompt += f"<PART>\n{part}\n</PART>\n"
|
|
37
29
|
|
|
38
|
-
prompt += "\n
|
|
39
|
-
prompt += "1.
|
|
40
|
-
prompt += "2.
|
|
41
|
-
prompt += "3.
|
|
42
|
-
prompt += "4.
|
|
30
|
+
prompt += "\nPlease provide detailed modifications needed to implement this feature. Include:\n"
|
|
31
|
+
prompt += "1. Which files need to be modified\n"
|
|
32
|
+
prompt += "2. How to modify each file, no explanation needed\n"
|
|
33
|
+
prompt += "3. Don't assume other files or code exist, only generate modification plans based on provided file contents and description\n"
|
|
34
|
+
prompt += "4. Don't implement features outside the requirement\n"
|
|
35
|
+
prompt += "5. Output only one modification plan per file (can be multiple lines)\n"
|
|
36
|
+
prompt += "6. Output format as follows:\n"
|
|
37
|
+
prompt += "<PLAN>\n"
|
|
38
|
+
prompt += "> path/to/file1\n"
|
|
39
|
+
prompt += "modification plan\n"
|
|
40
|
+
prompt += "</PLAN>\n"
|
|
41
|
+
prompt += "<PLAN>\n"
|
|
42
|
+
prompt += "> path/to/file2\n"
|
|
43
|
+
prompt += "modification plan\n"
|
|
44
|
+
prompt += "</PLAN>\n"
|
|
45
|
+
if additional_info:
|
|
46
|
+
prompt += f"# Additional information:\n{additional_info}\n"
|
|
43
47
|
|
|
44
48
|
return prompt
|
|
45
49
|
|
|
46
|
-
|
|
50
|
+
|
|
51
|
+
def generate_plan(self, feature: str, related_files: List[Dict]) -> Tuple[str, Dict[str,str]]:
|
|
47
52
|
"""生成修改方案
|
|
48
53
|
|
|
49
54
|
Args:
|
|
@@ -51,25 +56,48 @@ class PlanGenerator:
|
|
|
51
56
|
related_files: 相关文件列表
|
|
52
57
|
|
|
53
58
|
Returns:
|
|
54
|
-
str: 修改方案,如果用户取消则返回 None
|
|
59
|
+
Tuple[str, Dict[str,str]]: 修改方案,如果用户取消则返回 None
|
|
55
60
|
"""
|
|
56
|
-
|
|
61
|
+
additional_info = ""
|
|
57
62
|
while True:
|
|
63
|
+
prompt = self._build_prompt(feature, related_files, additional_info)
|
|
58
64
|
# 构建提示词
|
|
59
65
|
PrettyOutput.print("开始生成修改方案...", OutputType.PROGRESS)
|
|
60
66
|
|
|
61
67
|
# 获取修改方案
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
68
|
+
raw_plan = PlatformRegistry.get_global_platform_registry().get_thinking_platform().chat_until_success(prompt)
|
|
69
|
+
structed_plan = self._extract_code(raw_plan)
|
|
70
|
+
if not structed_plan:
|
|
71
|
+
PrettyOutput.print("修改方案生成失败,请重试", OutputType.ERROR)
|
|
72
|
+
tmp = get_multiline_input("请输入您的补充意见或建议(直接回车取消):")
|
|
73
|
+
if tmp == "__interrupt__" or prompt == "":
|
|
74
|
+
return "", {}
|
|
75
|
+
additional_info += tmp + "\n"
|
|
76
|
+
continue
|
|
77
|
+
user_input = input("\n是否同意这个修改方案?(y/n) [y]: ").strip().lower() or 'y'
|
|
78
|
+
if user_input == 'y' or user_input == '':
|
|
79
|
+
return raw_plan, structed_plan
|
|
67
80
|
elif user_input == 'n':
|
|
68
|
-
return ""
|
|
69
|
-
else: # 'f' - feedback
|
|
70
81
|
# 获取用户反馈
|
|
71
|
-
|
|
72
|
-
if prompt == "__interrupt__":
|
|
73
|
-
return ""
|
|
74
|
-
|
|
75
|
-
continue
|
|
82
|
+
tmp = get_multiline_input("请输入您的补充意见或建议(直接回车取消):")
|
|
83
|
+
if prompt == "__interrupt__" or prompt == "":
|
|
84
|
+
return "", {}
|
|
85
|
+
additional_info += tmp + "\n"
|
|
86
|
+
continue
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def _extract_code(self, response: str) -> Dict[str, str]:
|
|
90
|
+
"""从响应中提取代码
|
|
91
|
+
|
|
92
|
+
Args:
|
|
93
|
+
response: 模型响应内容
|
|
94
|
+
|
|
95
|
+
Returns:
|
|
96
|
+
Dict[str, List[str]]: 代码字典,key为文件路径,value为代码片段列表
|
|
97
|
+
"""
|
|
98
|
+
code_dict = {}
|
|
99
|
+
for match in re.finditer(r'<PLAN>\n> (.+?)\n(.*?)\n</PLAN>', response, re.DOTALL):
|
|
100
|
+
file_path = match.group(1)
|
|
101
|
+
code_part = match.group(2)
|
|
102
|
+
code_dict[file_path] = code_part
|
|
103
|
+
return code_dict
|
jarvis/jarvis_platform/main.py
CHANGED
jarvis/jarvis_rag/main.py
CHANGED
|
@@ -46,34 +46,34 @@ def process_request(request: str) -> Optional[str]:
|
|
|
46
46
|
current_path = os.getcwd()
|
|
47
47
|
|
|
48
48
|
# 设置系统提示
|
|
49
|
-
system_message = f"""
|
|
49
|
+
system_message = f"""You are a shell command generation assistant.
|
|
50
50
|
|
|
51
|
-
|
|
51
|
+
Your only task is to convert user's natural language requirements into corresponding shell commands.
|
|
52
52
|
|
|
53
|
-
|
|
54
|
-
1.
|
|
55
|
-
2.
|
|
56
|
-
3.
|
|
57
|
-
4.
|
|
58
|
-
5.
|
|
53
|
+
Strict requirements:
|
|
54
|
+
1. Only return the shell command itself
|
|
55
|
+
2. Do not add any markers (like ```, /**/, // etc.)
|
|
56
|
+
3. Do not add any explanations or descriptions
|
|
57
|
+
4. Do not add any line breaks or extra spaces
|
|
58
|
+
5. If multiple commands are needed, connect them with &&
|
|
59
59
|
|
|
60
|
-
|
|
61
|
-
"
|
|
60
|
+
Example input:
|
|
61
|
+
"Find all Python files in the current directory"
|
|
62
62
|
|
|
63
|
-
|
|
63
|
+
Example output:
|
|
64
64
|
find . -name "*.py"
|
|
65
65
|
|
|
66
|
-
|
|
66
|
+
Remember: Only return the command itself, without any additional content.
|
|
67
67
|
"""
|
|
68
68
|
model.set_system_message(system_message)
|
|
69
69
|
|
|
70
|
-
prefix = f"
|
|
71
|
-
prefix += f"
|
|
70
|
+
prefix = f"Current path: {current_path}\n"
|
|
71
|
+
prefix += f"Current shell: {shell}\n"
|
|
72
72
|
|
|
73
73
|
# 使用yaspin显示Thinking状态
|
|
74
74
|
with yaspin(Spinners.dots, text="Thinking", color="yellow") as spinner:
|
|
75
75
|
# 处理请求
|
|
76
|
-
result = model.
|
|
76
|
+
result = model.chat_until_success(prefix + request)
|
|
77
77
|
|
|
78
78
|
# 提取命令
|
|
79
79
|
if result and isinstance(result, str):
|
jarvis/main.py
CHANGED
|
@@ -10,7 +10,7 @@ from prompt_toolkit import prompt
|
|
|
10
10
|
|
|
11
11
|
from jarvis.models.registry import PlatformRegistry
|
|
12
12
|
|
|
13
|
-
#
|
|
13
|
+
# Add parent directory to Python path to support imports
|
|
14
14
|
sys.path.insert(0, str(Path(__file__).parent.parent))
|
|
15
15
|
|
|
16
16
|
from jarvis.agent import Agent
|
|
@@ -22,7 +22,7 @@ def load_tasks() -> dict:
|
|
|
22
22
|
"""Load tasks from .jarvis files in user home and current directory."""
|
|
23
23
|
tasks = {}
|
|
24
24
|
|
|
25
|
-
#
|
|
25
|
+
# Check .jarvis in user directory
|
|
26
26
|
user_jarvis = os.path.expanduser("~/.jarvis")
|
|
27
27
|
if os.path.exists(user_jarvis):
|
|
28
28
|
try:
|
|
@@ -30,39 +30,39 @@ def load_tasks() -> dict:
|
|
|
30
30
|
user_tasks = yaml.safe_load(f)
|
|
31
31
|
|
|
32
32
|
if isinstance(user_tasks, dict):
|
|
33
|
-
#
|
|
33
|
+
# Validate and add user directory tasks
|
|
34
34
|
for name, desc in user_tasks.items():
|
|
35
|
-
if desc: #
|
|
35
|
+
if desc: # Ensure description is not empty
|
|
36
36
|
tasks[str(name)] = str(desc)
|
|
37
37
|
else:
|
|
38
38
|
PrettyOutput.print("Warning: ~/.jarvis file should contain a dictionary of task_name: task_description", OutputType.ERROR)
|
|
39
39
|
except Exception as e:
|
|
40
40
|
PrettyOutput.print(f"Error loading ~/.jarvis file: {str(e)}", OutputType.ERROR)
|
|
41
41
|
|
|
42
|
-
#
|
|
42
|
+
# Check .jarvis in current directory
|
|
43
43
|
if os.path.exists(".jarvis"):
|
|
44
44
|
try:
|
|
45
45
|
with open(".jarvis", "r", encoding="utf-8") as f:
|
|
46
46
|
local_tasks = yaml.safe_load(f)
|
|
47
47
|
|
|
48
48
|
if isinstance(local_tasks, dict):
|
|
49
|
-
#
|
|
49
|
+
# Validate and add current directory tasks, overwrite user directory tasks if there is a name conflict
|
|
50
50
|
for name, desc in local_tasks.items():
|
|
51
|
-
if desc: #
|
|
51
|
+
if desc: # Ensure description is not empty
|
|
52
52
|
tasks[str(name)] = str(desc)
|
|
53
53
|
else:
|
|
54
54
|
PrettyOutput.print("Warning: .jarvis file should contain a dictionary of task_name: task_description", OutputType.ERROR)
|
|
55
55
|
except Exception as e:
|
|
56
56
|
PrettyOutput.print(f"Error loading .jarvis file: {str(e)}", OutputType.ERROR)
|
|
57
57
|
|
|
58
|
-
#
|
|
58
|
+
# Read methodology
|
|
59
59
|
method_path = os.path.expanduser("~/.jarvis_methodology")
|
|
60
60
|
if os.path.exists(method_path):
|
|
61
61
|
with open(method_path, "r", encoding="utf-8") as f:
|
|
62
62
|
methodology = yaml.safe_load(f)
|
|
63
63
|
if isinstance(methodology, dict):
|
|
64
64
|
for name, desc in methodology.items():
|
|
65
|
-
tasks[f"
|
|
65
|
+
tasks[f"Run Methodology: {str(name)}\n {str(desc)}" ] = str(desc)
|
|
66
66
|
|
|
67
67
|
return tasks
|
|
68
68
|
|
|
@@ -77,13 +77,13 @@ def select_task(tasks: dict) -> str:
|
|
|
77
77
|
PrettyOutput.print("\nAvailable tasks:", OutputType.INFO)
|
|
78
78
|
for i, name in enumerate(task_names, 1):
|
|
79
79
|
PrettyOutput.print(f"[{i}] {name}", OutputType.INFO)
|
|
80
|
-
PrettyOutput.print("[0]
|
|
80
|
+
PrettyOutput.print("[0] Skip predefined tasks", OutputType.INFO)
|
|
81
81
|
|
|
82
82
|
|
|
83
83
|
while True:
|
|
84
84
|
try:
|
|
85
85
|
choice = prompt(
|
|
86
|
-
"\
|
|
86
|
+
"\nPlease select a task number (0 to skip): ",
|
|
87
87
|
).strip()
|
|
88
88
|
|
|
89
89
|
if not choice:
|
|
@@ -103,16 +103,16 @@ def select_task(tasks: dict) -> str:
|
|
|
103
103
|
except EOFError:
|
|
104
104
|
return "" # Return empty on Ctrl+D
|
|
105
105
|
except Exception as e:
|
|
106
|
-
PrettyOutput.print(f"
|
|
106
|
+
PrettyOutput.print(f"Failed to select task: {str(e)}", OutputType.ERROR)
|
|
107
107
|
continue
|
|
108
108
|
|
|
109
109
|
def main():
|
|
110
|
-
"""Jarvis
|
|
111
|
-
#
|
|
110
|
+
"""Jarvis main entry point"""
|
|
111
|
+
# Add argument parser
|
|
112
112
|
load_env_from_file()
|
|
113
|
-
parser = argparse.ArgumentParser(description='Jarvis AI
|
|
114
|
-
parser.add_argument('-f', '--files', nargs='*', help='
|
|
115
|
-
parser.add_argument('--keep-history', action='store_true', help='
|
|
113
|
+
parser = argparse.ArgumentParser(description='Jarvis AI assistant')
|
|
114
|
+
parser.add_argument('-f', '--files', nargs='*', help='List of files to process')
|
|
115
|
+
parser.add_argument('--keep-history', action='store_true', help='Keep chat history (do not delete session)')
|
|
116
116
|
args = parser.parse_args()
|
|
117
117
|
|
|
118
118
|
try:
|
|
@@ -121,32 +121,32 @@ def main():
|
|
|
121
121
|
|
|
122
122
|
# 如果用户传入了模型参数,则更换当前模型为用户指定的模型
|
|
123
123
|
|
|
124
|
-
#
|
|
125
|
-
PrettyOutput.print(f"Jarvis
|
|
124
|
+
# Welcome information
|
|
125
|
+
PrettyOutput.print(f"Jarvis initialized - With {agent.model.name()}", OutputType.SYSTEM)
|
|
126
126
|
if args.keep_history:
|
|
127
|
-
PrettyOutput.print("
|
|
127
|
+
PrettyOutput.print("History preservation mode enabled", OutputType.INFO)
|
|
128
128
|
|
|
129
129
|
# 加载预定义任务
|
|
130
130
|
tasks = load_tasks()
|
|
131
131
|
if tasks:
|
|
132
132
|
selected_task = select_task(tasks)
|
|
133
133
|
if selected_task:
|
|
134
|
-
PrettyOutput.print(f"\
|
|
134
|
+
PrettyOutput.print(f"\nExecute task: {selected_task}", OutputType.INFO)
|
|
135
135
|
agent.run(selected_task, args.files)
|
|
136
136
|
return 0
|
|
137
137
|
|
|
138
138
|
# 如果没有选择预定义任务,进入交互模式
|
|
139
139
|
while True:
|
|
140
140
|
try:
|
|
141
|
-
user_input = get_multiline_input("
|
|
141
|
+
user_input = get_multiline_input("Please enter your task (input empty line to exit):")
|
|
142
142
|
if not user_input or user_input == "__interrupt__":
|
|
143
143
|
break
|
|
144
144
|
agent.run(user_input, args.files)
|
|
145
145
|
except Exception as e:
|
|
146
|
-
PrettyOutput.print(f"
|
|
146
|
+
PrettyOutput.print(f"Error: {str(e)}", OutputType.ERROR)
|
|
147
147
|
|
|
148
148
|
except Exception as e:
|
|
149
|
-
PrettyOutput.print(f"
|
|
149
|
+
PrettyOutput.print(f"Initialization error: {str(e)}", OutputType.ERROR)
|
|
150
150
|
return 1
|
|
151
151
|
|
|
152
152
|
return 0
|
jarvis/models/ai8.py
CHANGED
|
@@ -26,16 +26,16 @@ class AI8Model(BasePlatform):
|
|
|
26
26
|
|
|
27
27
|
self.token = os.getenv("AI8_API_KEY")
|
|
28
28
|
if not self.token:
|
|
29
|
-
PrettyOutput.print("AI8_API_KEY
|
|
29
|
+
PrettyOutput.print("AI8_API_KEY is not set", OutputType.WARNING)
|
|
30
30
|
|
|
31
31
|
|
|
32
32
|
self.model_name = os.getenv("JARVIS_MODEL") or "deepseek-chat"
|
|
33
33
|
if self.model_name not in self.get_available_models():
|
|
34
|
-
PrettyOutput.print(f"
|
|
34
|
+
PrettyOutput.print(f"Warning: The selected model {self.model_name} is not in the available list", OutputType.WARNING)
|
|
35
35
|
|
|
36
36
|
|
|
37
37
|
def set_model_name(self, model_name: str):
|
|
38
|
-
"""
|
|
38
|
+
"""Set model name"""
|
|
39
39
|
|
|
40
40
|
self.model_name = model_name
|
|
41
41
|
|
|
@@ -59,12 +59,12 @@ class AI8Model(BasePlatform):
|
|
|
59
59
|
)
|
|
60
60
|
|
|
61
61
|
if response.status_code != 200:
|
|
62
|
-
PrettyOutput.print(f"
|
|
62
|
+
PrettyOutput.print(f"Failed to create session: {response.status_code}", OutputType.ERROR)
|
|
63
63
|
return False
|
|
64
64
|
|
|
65
65
|
data = response.json()
|
|
66
66
|
if data['code'] != 0:
|
|
67
|
-
PrettyOutput.print(f"
|
|
67
|
+
PrettyOutput.print(f"Failed to create session: {data.get('msg', 'Unknown error')}", OutputType.ERROR)
|
|
68
68
|
return False
|
|
69
69
|
|
|
70
70
|
self.conversation = data['data']
|
|
@@ -92,14 +92,14 @@ class AI8Model(BasePlatform):
|
|
|
92
92
|
self.conversation = data['data']
|
|
93
93
|
return True
|
|
94
94
|
else:
|
|
95
|
-
PrettyOutput.print(f"
|
|
95
|
+
PrettyOutput.print(f"Failed to update session settings: {data.get('msg', 'Unknown error')}", OutputType.ERROR)
|
|
96
96
|
return False
|
|
97
97
|
else:
|
|
98
|
-
PrettyOutput.print(f"
|
|
98
|
+
PrettyOutput.print(f"Failed to update session settings: {response.status_code}", OutputType.ERROR)
|
|
99
99
|
return False
|
|
100
100
|
|
|
101
101
|
except Exception as e:
|
|
102
|
-
PrettyOutput.print(f"
|
|
102
|
+
PrettyOutput.print(f"Failed to create session: {str(e)}", OutputType.ERROR)
|
|
103
103
|
return False
|
|
104
104
|
|
|
105
105
|
def upload_files(self, file_list: List[str]) -> List[Dict]:
|
|
@@ -119,7 +119,7 @@ class AI8Model(BasePlatform):
|
|
|
119
119
|
self.system_message = message
|
|
120
120
|
|
|
121
121
|
def chat(self, message: str) -> str:
|
|
122
|
-
"""
|
|
122
|
+
"""Execute conversation"""
|
|
123
123
|
try:
|
|
124
124
|
|
|
125
125
|
# 确保有会话ID
|
|
@@ -160,7 +160,7 @@ class AI8Model(BasePlatform):
|
|
|
160
160
|
)
|
|
161
161
|
|
|
162
162
|
if response.status_code != 200:
|
|
163
|
-
error_msg = f"
|
|
163
|
+
error_msg = f"Failed to chat: {response.status_code}"
|
|
164
164
|
PrettyOutput.print(error_msg, OutputType.ERROR)
|
|
165
165
|
raise Exception(error_msg)
|
|
166
166
|
|
|
@@ -188,7 +188,7 @@ class AI8Model(BasePlatform):
|
|
|
188
188
|
return full_response
|
|
189
189
|
|
|
190
190
|
except Exception as e:
|
|
191
|
-
PrettyOutput.print(f"
|
|
191
|
+
PrettyOutput.print(f"Chat exception: {str(e)}", OutputType.ERROR)
|
|
192
192
|
raise e
|
|
193
193
|
|
|
194
194
|
def name(self) -> str:
|
|
@@ -227,23 +227,23 @@ class AI8Model(BasePlatform):
|
|
|
227
227
|
self.reset()
|
|
228
228
|
return True
|
|
229
229
|
else:
|
|
230
|
-
error_msg = f"
|
|
230
|
+
error_msg = f"Failed to delete session: {data.get('msg', 'Unknown error')}"
|
|
231
231
|
PrettyOutput.print(error_msg, OutputType.ERROR)
|
|
232
232
|
return False
|
|
233
233
|
else:
|
|
234
|
-
error_msg = f"
|
|
234
|
+
error_msg = f"Failed to delete session request: {response.status_code}"
|
|
235
235
|
PrettyOutput.print(error_msg, OutputType.ERROR)
|
|
236
236
|
return False
|
|
237
237
|
|
|
238
238
|
except Exception as e:
|
|
239
|
-
PrettyOutput.print(f"
|
|
239
|
+
PrettyOutput.print(f"Failed to delete session: {str(e)}", OutputType.ERROR)
|
|
240
240
|
return False
|
|
241
241
|
|
|
242
242
|
def get_available_models(self) -> List[str]:
|
|
243
|
-
"""
|
|
243
|
+
"""Get available model list
|
|
244
244
|
|
|
245
245
|
Returns:
|
|
246
|
-
List[str]:
|
|
246
|
+
List[str]: Available model name list
|
|
247
247
|
"""
|
|
248
248
|
try:
|
|
249
249
|
if self.models:
|
|
@@ -264,12 +264,12 @@ class AI8Model(BasePlatform):
|
|
|
264
264
|
)
|
|
265
265
|
|
|
266
266
|
if response.status_code != 200:
|
|
267
|
-
PrettyOutput.print(f"
|
|
267
|
+
PrettyOutput.print(f"Failed to get model list: {response.status_code}", OutputType.ERROR)
|
|
268
268
|
return []
|
|
269
269
|
|
|
270
270
|
data = response.json()
|
|
271
271
|
if data['code'] != 0:
|
|
272
|
-
PrettyOutput.print(f"
|
|
272
|
+
PrettyOutput.print(f"Failed to get model list: {data.get('msg', 'Unknown error')}", OutputType.ERROR)
|
|
273
273
|
return []
|
|
274
274
|
|
|
275
275
|
# 保存模型信息
|
|
@@ -292,11 +292,11 @@ class AI8Model(BasePlatform):
|
|
|
292
292
|
# 添加特性标记
|
|
293
293
|
features = []
|
|
294
294
|
if model['attr'].get('multimodal'):
|
|
295
|
-
features.append("
|
|
295
|
+
features.append("Multimodal")
|
|
296
296
|
if model['attr'].get('plugin'):
|
|
297
|
-
features.append("
|
|
297
|
+
features.append("Plugin support")
|
|
298
298
|
if model['attr'].get('onlyImg'):
|
|
299
|
-
features.append("
|
|
299
|
+
features.append("Image support")
|
|
300
300
|
if features:
|
|
301
301
|
model_str += f" [{'|'.join(features)}]"
|
|
302
302
|
|
|
@@ -308,6 +308,6 @@ class AI8Model(BasePlatform):
|
|
|
308
308
|
return list(self.models.keys())
|
|
309
309
|
|
|
310
310
|
except Exception as e:
|
|
311
|
-
PrettyOutput.print(f"
|
|
311
|
+
PrettyOutput.print(f"Failed to get model list: {str(e)}", OutputType.WARNING)
|
|
312
312
|
return []
|
|
313
313
|
|
jarvis/models/base.py
CHANGED
|
@@ -1,59 +1,63 @@
|
|
|
1
1
|
from abc import ABC, abstractmethod
|
|
2
2
|
from typing import Dict, List, Tuple
|
|
3
3
|
|
|
4
|
+
from jarvis.utils import OutputType, PrettyOutput, while_success, while_true
|
|
5
|
+
|
|
4
6
|
|
|
5
7
|
class BasePlatform(ABC):
|
|
6
|
-
"""
|
|
8
|
+
"""Base class for large language models"""
|
|
7
9
|
|
|
8
10
|
def __init__(self):
|
|
9
|
-
"""
|
|
11
|
+
"""Initialize model"""
|
|
10
12
|
self.suppress_output = False # 添加输出控制标志
|
|
11
|
-
pass
|
|
12
13
|
|
|
13
14
|
def __del__(self):
|
|
14
|
-
"""
|
|
15
|
+
"""Destroy model"""
|
|
15
16
|
self.delete_chat()
|
|
16
17
|
|
|
17
18
|
@abstractmethod
|
|
18
19
|
def set_model_name(self, model_name: str):
|
|
19
|
-
"""
|
|
20
|
+
"""Set model name"""
|
|
20
21
|
raise NotImplementedError("set_model_name is not implemented")
|
|
21
22
|
|
|
22
23
|
@abstractmethod
|
|
23
24
|
def chat(self, message: str) -> str:
|
|
24
|
-
"""
|
|
25
|
+
"""Execute conversation"""
|
|
25
26
|
raise NotImplementedError("chat is not implemented")
|
|
26
27
|
|
|
28
|
+
def chat_until_success(self, message: str) -> str:
|
|
29
|
+
return while_true(lambda: while_success(lambda: self.chat(message), 5), 5)
|
|
30
|
+
|
|
27
31
|
@abstractmethod
|
|
28
32
|
def upload_files(self, file_list: List[str]) -> List[Dict]:
|
|
29
|
-
"""
|
|
33
|
+
"""Upload files"""
|
|
30
34
|
raise NotImplementedError("upload_files is not implemented")
|
|
31
35
|
|
|
32
36
|
@abstractmethod
|
|
33
37
|
def reset(self):
|
|
34
|
-
"""
|
|
38
|
+
"""Reset model"""
|
|
35
39
|
raise NotImplementedError("reset is not implemented")
|
|
36
40
|
|
|
37
41
|
@abstractmethod
|
|
38
42
|
def name(self) -> str:
|
|
39
|
-
"""
|
|
43
|
+
"""Model name"""
|
|
40
44
|
raise NotImplementedError("name is not implemented")
|
|
41
45
|
|
|
42
46
|
@abstractmethod
|
|
43
47
|
def delete_chat(self)->bool:
|
|
44
|
-
"""
|
|
48
|
+
"""Delete chat"""
|
|
45
49
|
raise NotImplementedError("delete_chat is not implemented")
|
|
46
50
|
|
|
47
51
|
@abstractmethod
|
|
48
52
|
def set_system_message(self, message: str):
|
|
49
|
-
"""
|
|
53
|
+
"""Set system message"""
|
|
50
54
|
raise NotImplementedError("set_system_message is not implemented")
|
|
51
55
|
|
|
52
56
|
@abstractmethod
|
|
53
57
|
def get_model_list(self) -> List[Tuple[str, str]]:
|
|
54
|
-
"""
|
|
58
|
+
"""Get model list"""
|
|
55
59
|
raise NotImplementedError("get_model_list is not implemented")
|
|
56
60
|
|
|
57
61
|
def set_suppress_output(self, suppress: bool):
|
|
58
|
-
"""
|
|
62
|
+
"""Set whether to suppress output"""
|
|
59
63
|
self.suppress_output = suppress
|