jarvis-ai-assistant 0.1.101__py3-none-any.whl → 0.1.102__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 -24
- jarvis/jarvis_code_agent/main.py +1 -3
- jarvis/jarvis_github/main.py +232 -0
- jarvis/models/ai8.py +2 -3
- jarvis/models/oyi.py +1 -3
- jarvis/tools/registry.py +48 -40
- jarvis/utils.py +9 -124
- {jarvis_ai_assistant-0.1.101.dist-info → jarvis_ai_assistant-0.1.102.dist-info}/METADATA +1 -47
- {jarvis_ai_assistant-0.1.101.dist-info → jarvis_ai_assistant-0.1.102.dist-info}/RECORD +15 -20
- {jarvis_ai_assistant-0.1.101.dist-info → jarvis_ai_assistant-0.1.102.dist-info}/entry_points.txt +0 -3
- jarvis/jarvis_codebase/main.py +0 -875
- jarvis/jarvis_coder/main.py +0 -241
- jarvis/jarvis_coder/plan_generator.py +0 -145
- jarvis/jarvis_rag/__init__.py +0 -0
- jarvis/jarvis_rag/main.py +0 -822
- jarvis/tools/rag.py +0 -138
- /jarvis/{jarvis_codebase → jarvis_github}/__init__.py +0 -0
- {jarvis_ai_assistant-0.1.101.dist-info → jarvis_ai_assistant-0.1.102.dist-info}/LICENSE +0 -0
- {jarvis_ai_assistant-0.1.101.dist-info → jarvis_ai_assistant-0.1.102.dist-info}/WHEEL +0 -0
- {jarvis_ai_assistant-0.1.101.dist-info → jarvis_ai_assistant-0.1.102.dist-info}/top_level.txt +0 -0
jarvis/__init__.py
CHANGED
jarvis/agent.py
CHANGED
|
@@ -8,7 +8,7 @@ import yaml
|
|
|
8
8
|
from jarvis.models.registry import PlatformRegistry
|
|
9
9
|
from jarvis.tools import ToolRegistry
|
|
10
10
|
from jarvis.tools.registry import load_tools
|
|
11
|
-
from jarvis.utils import PrettyOutput, OutputType, get_single_line_input, load_methodology, add_agent, delete_current_agent, get_max_context_length, get_multiline_input,
|
|
11
|
+
from jarvis.utils import PrettyOutput, OutputType, get_single_line_input, load_methodology, add_agent, delete_current_agent, get_max_context_length, get_multiline_input, init_env
|
|
12
12
|
import os
|
|
13
13
|
|
|
14
14
|
class Agent:
|
|
@@ -35,29 +35,9 @@ class Agent:
|
|
|
35
35
|
self.conversation_length = 0 # Use length counter instead
|
|
36
36
|
self.system_prompt = system_prompt
|
|
37
37
|
# Load configuration from environment variables
|
|
38
|
-
|
|
38
|
+
|
|
39
39
|
self.max_context_length = get_max_context_length()
|
|
40
40
|
|
|
41
|
-
# Initialize embedding model
|
|
42
|
-
try:
|
|
43
|
-
self.embedding_model = load_embedding_model()
|
|
44
|
-
|
|
45
|
-
# Warm up model and get correct dimension
|
|
46
|
-
test_text = "This is a test text to ensure the model is fully loaded."
|
|
47
|
-
test_embedding = self.embedding_model.encode(
|
|
48
|
-
test_text,
|
|
49
|
-
convert_to_tensor=True,
|
|
50
|
-
normalize_embeddings=True
|
|
51
|
-
)
|
|
52
|
-
self.embedding_dimension = len(test_embedding)
|
|
53
|
-
PrettyOutput.print("Successfully loaded embedding model", OutputType.SUCCESS)
|
|
54
|
-
|
|
55
|
-
# Initialize HNSW index (use correct dimension)
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
except Exception as e:
|
|
59
|
-
PrettyOutput.print(f"Failed to load embedding model: {str(e)}", OutputType.ERROR)
|
|
60
|
-
raise
|
|
61
41
|
|
|
62
42
|
# Initialize methodology related attributes
|
|
63
43
|
self.methodology_data = []
|
|
@@ -441,7 +421,6 @@ Strict Rules:
|
|
|
441
421
|
- Don't create fake dialogues
|
|
442
422
|
- If current information is insufficient, you may ask the user
|
|
443
423
|
- Not all problem-solving steps are mandatory, skip as appropriate
|
|
444
|
-
- Ask user before executing tools that might damage system or user's codebase
|
|
445
424
|
- Request user guidance when multiple iterations show no progress
|
|
446
425
|
- If yaml string contains colons, wrap the entire string in quotes to avoid yaml parsing errors
|
|
447
426
|
- Use | syntax for multi-line strings in yaml
|
|
@@ -459,7 +438,7 @@ def main():
|
|
|
459
438
|
|
|
460
439
|
try:
|
|
461
440
|
# 获取全局模型实例
|
|
462
|
-
agent = Agent(system_prompt=origin_agent_system_prompt)
|
|
441
|
+
agent = Agent(system_prompt=origin_agent_system_prompt, tool_registry=ToolRegistry())
|
|
463
442
|
|
|
464
443
|
# 加载预定义任务
|
|
465
444
|
tasks = load_tasks()
|
jarvis/jarvis_code_agent/main.py
CHANGED
|
@@ -79,8 +79,8 @@ DEVELOPMENT WORKFLOW:
|
|
|
79
79
|
<TOOL_CALL>
|
|
80
80
|
name: create_code_sub_agent
|
|
81
81
|
arguments:
|
|
82
|
+
name: code_sub_agent
|
|
82
83
|
subtask: Implement new authentication flow
|
|
83
|
-
codebase_dir: .
|
|
84
84
|
</TOOL_CALL>
|
|
85
85
|
|
|
86
86
|
FILE SELECTION WORKFLOW:
|
|
@@ -147,10 +147,8 @@ TOOL USAGE:
|
|
|
147
147
|
- ask_user: Ask user for confirmation and information if needed
|
|
148
148
|
- create_code_sub_agent: Create agent for each small change
|
|
149
149
|
- file_operation: Read files
|
|
150
|
-
- rag: Ask questions based on a document directory, supporting multiple document formats (txt, pdf, docx, etc.)
|
|
151
150
|
- search: Use Bing search engine to search for information, and extract key information based on the question
|
|
152
151
|
- thinker: Deep thinking and logical reasoning
|
|
153
|
-
|
|
154
152
|
2. Planning Tools:
|
|
155
153
|
- thinker: Generate a detailed modification plan based on user requirements and actual code conditions.
|
|
156
154
|
- create_code_sub_agent: Create agent for each small change
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import sys
|
|
3
|
+
import argparse
|
|
4
|
+
from typing import Dict, List, Optional
|
|
5
|
+
import yaml
|
|
6
|
+
|
|
7
|
+
from jarvis.agent import Agent
|
|
8
|
+
from jarvis.utils import PrettyOutput, OutputType, get_single_line_input, init_env
|
|
9
|
+
from jarvis.tools import ToolRegistry
|
|
10
|
+
|
|
11
|
+
# System prompt for the GitHub workflow agent
|
|
12
|
+
github_workflow_prompt = """You are a GitHub Workflow Agent that helps manage the complete development workflow using GitHub CLI (gh). Follow these steps strictly:
|
|
13
|
+
|
|
14
|
+
1. Environment Check:
|
|
15
|
+
- Verify gh CLI installation
|
|
16
|
+
- Check authentication status
|
|
17
|
+
- Set up authentication if needed
|
|
18
|
+
|
|
19
|
+
2. Issue Management:
|
|
20
|
+
- List and display available issues
|
|
21
|
+
- Help user select an issue to work on
|
|
22
|
+
- Analyze the selected issue thoroughly
|
|
23
|
+
|
|
24
|
+
3. Development Planning:
|
|
25
|
+
- Create a development branch for the issue
|
|
26
|
+
- Generate a detailed modification plan
|
|
27
|
+
- Break down the task into smaller steps
|
|
28
|
+
|
|
29
|
+
4. Implementation:
|
|
30
|
+
- Guide through the implementation process
|
|
31
|
+
- Track changes and progress
|
|
32
|
+
- Ensure code quality
|
|
33
|
+
|
|
34
|
+
5. Review and Submit:
|
|
35
|
+
- Review changes before submission
|
|
36
|
+
- Create and submit pull request
|
|
37
|
+
- Handle review feedback
|
|
38
|
+
- Close the issue upon completion
|
|
39
|
+
|
|
40
|
+
Always follow GitHub best practices and provide clear feedback at each step.
|
|
41
|
+
"""
|
|
42
|
+
|
|
43
|
+
def check_gh_installation() -> bool:
|
|
44
|
+
"""Check if GitHub CLI is installed"""
|
|
45
|
+
return os.system("gh --version > /dev/null 2>&1") == 0
|
|
46
|
+
|
|
47
|
+
def check_gh_auth() -> bool:
|
|
48
|
+
"""Check if GitHub CLI is authenticated"""
|
|
49
|
+
return os.system("gh auth status > /dev/null 2>&1") == 0
|
|
50
|
+
|
|
51
|
+
def setup_gh_auth() -> bool:
|
|
52
|
+
"""Guide user through GitHub CLI authentication"""
|
|
53
|
+
PrettyOutput.print("Starting GitHub CLI authentication...", OutputType.INFO)
|
|
54
|
+
return os.system("gh auth login") == 0
|
|
55
|
+
|
|
56
|
+
def list_issues() -> List[Dict]:
|
|
57
|
+
"""List all available issues"""
|
|
58
|
+
try:
|
|
59
|
+
# Get issues in JSON format
|
|
60
|
+
result = os.popen("gh issue list --json number,title,body,url").read()
|
|
61
|
+
issues = yaml.safe_load(result)
|
|
62
|
+
return issues
|
|
63
|
+
except Exception as e:
|
|
64
|
+
PrettyOutput.print(f"Error listing issues: {str(e)}", OutputType.ERROR)
|
|
65
|
+
return []
|
|
66
|
+
|
|
67
|
+
def select_issue(issues: List[Dict]) -> Optional[Dict]:
|
|
68
|
+
"""Display issues and let user select one"""
|
|
69
|
+
if not issues:
|
|
70
|
+
PrettyOutput.print("No issues found.", OutputType.WARNING)
|
|
71
|
+
return None
|
|
72
|
+
|
|
73
|
+
PrettyOutput.print("\nAvailable Issues:", OutputType.INFO)
|
|
74
|
+
for i, issue in enumerate(issues, 1):
|
|
75
|
+
print(f"{i}. #{issue['number']} - {issue['title']}")
|
|
76
|
+
|
|
77
|
+
while True:
|
|
78
|
+
try:
|
|
79
|
+
choice = get_single_line_input("\nSelect an issue number (or 0 to exit): ")
|
|
80
|
+
if not choice or choice == "0":
|
|
81
|
+
return None
|
|
82
|
+
|
|
83
|
+
index = int(choice) - 1
|
|
84
|
+
if 0 <= index < len(issues):
|
|
85
|
+
return issues[index]
|
|
86
|
+
else:
|
|
87
|
+
PrettyOutput.print("Invalid selection. Please try again.", OutputType.WARNING)
|
|
88
|
+
except ValueError:
|
|
89
|
+
PrettyOutput.print("Please enter a valid number.", OutputType.WARNING)
|
|
90
|
+
|
|
91
|
+
def create_development_branch(issue_number: int) -> bool:
|
|
92
|
+
"""Create a development branch for the issue"""
|
|
93
|
+
try:
|
|
94
|
+
result = os.system(f"gh issue develop {issue_number} --checkout")
|
|
95
|
+
return result == 0
|
|
96
|
+
except Exception as e:
|
|
97
|
+
PrettyOutput.print(f"Error creating branch: {str(e)}", OutputType.ERROR)
|
|
98
|
+
return False
|
|
99
|
+
|
|
100
|
+
def create_pull_request(issue_number: int, title: str, body: str) -> bool:
|
|
101
|
+
"""Create a pull request for the changes"""
|
|
102
|
+
try:
|
|
103
|
+
cmd = f'gh pr create --title "{title}" --body "{body}" --issue {issue_number}'
|
|
104
|
+
result = os.system(cmd)
|
|
105
|
+
return result == 0
|
|
106
|
+
except Exception as e:
|
|
107
|
+
PrettyOutput.print(f"Error creating pull request: {str(e)}", OutputType.ERROR)
|
|
108
|
+
return False
|
|
109
|
+
|
|
110
|
+
def close_issue(issue_number: int) -> bool:
|
|
111
|
+
"""Close the issue"""
|
|
112
|
+
try:
|
|
113
|
+
result = os.system(f"gh issue close {issue_number}")
|
|
114
|
+
return result == 0
|
|
115
|
+
except Exception as e:
|
|
116
|
+
PrettyOutput.print(f"Error closing issue: {str(e)}", OutputType.ERROR)
|
|
117
|
+
return False
|
|
118
|
+
|
|
119
|
+
def install_gh_linux() -> bool:
|
|
120
|
+
"""Install GitHub CLI on Linux"""
|
|
121
|
+
PrettyOutput.print("Installing GitHub CLI...", OutputType.INFO)
|
|
122
|
+
|
|
123
|
+
# Detect package manager
|
|
124
|
+
package_managers = {
|
|
125
|
+
"apt": "curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg | sudo dd of=/usr/share/keyrings/githubcli-archive-keyring.gpg && \
|
|
126
|
+
echo \"deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main\" | sudo tee /etc/apt/sources.list.d/github-cli.list > /dev/null && \
|
|
127
|
+
sudo apt update && sudo apt install gh -y",
|
|
128
|
+
"dnf": "sudo dnf install 'dnf-command(config-manager)' -y && \
|
|
129
|
+
sudo dnf config-manager --add-repo https://cli.github.com/packages/rpm/gh-cli.repo && \
|
|
130
|
+
sudo dnf install gh -y",
|
|
131
|
+
"yum": "sudo yum install 'dnf-command(config-manager)' -y && \
|
|
132
|
+
sudo yum config-manager --add-repo https://cli.github.com/packages/rpm/gh-cli.repo && \
|
|
133
|
+
sudo yum install gh -y",
|
|
134
|
+
"pacman": "sudo pacman -S github-cli --noconfirm",
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
# Try to detect the package manager
|
|
138
|
+
for pm, cmd in package_managers.items():
|
|
139
|
+
if os.system(f"which {pm} > /dev/null 2>&1") == 0:
|
|
140
|
+
PrettyOutput.print(f"Detected {pm} package manager", OutputType.INFO)
|
|
141
|
+
if os.system(cmd) == 0:
|
|
142
|
+
PrettyOutput.print("GitHub CLI installed successfully!", OutputType.SUCCESS)
|
|
143
|
+
return True
|
|
144
|
+
else:
|
|
145
|
+
PrettyOutput.print(f"Failed to install using {pm}", OutputType.ERROR)
|
|
146
|
+
return False
|
|
147
|
+
|
|
148
|
+
PrettyOutput.print(
|
|
149
|
+
"Could not detect supported package manager. Please install manually:\n"
|
|
150
|
+
"https://github.com/cli/cli/blob/trunk/docs/install_linux.md",
|
|
151
|
+
OutputType.ERROR
|
|
152
|
+
)
|
|
153
|
+
return False
|
|
154
|
+
|
|
155
|
+
def main():
|
|
156
|
+
"""Main entry point for GitHub workflow"""
|
|
157
|
+
init_env()
|
|
158
|
+
|
|
159
|
+
# Check GitHub CLI installation
|
|
160
|
+
if not check_gh_installation():
|
|
161
|
+
if sys.platform.startswith('linux'):
|
|
162
|
+
if not install_gh_linux():
|
|
163
|
+
return 1
|
|
164
|
+
else:
|
|
165
|
+
PrettyOutput.print(
|
|
166
|
+
"GitHub CLI (gh) is not installed. Please install it first:\n"
|
|
167
|
+
"- Windows: winget install GitHub.cli\n"
|
|
168
|
+
"- macOS: brew install gh\n"
|
|
169
|
+
"- Linux: See https://github.com/cli/cli/blob/trunk/docs/install_linux.md",
|
|
170
|
+
OutputType.ERROR
|
|
171
|
+
)
|
|
172
|
+
return 1
|
|
173
|
+
|
|
174
|
+
# Check authentication
|
|
175
|
+
if not check_gh_auth():
|
|
176
|
+
PrettyOutput.print("GitHub CLI needs authentication.", OutputType.WARNING)
|
|
177
|
+
if not setup_gh_auth():
|
|
178
|
+
PrettyOutput.print("Authentication failed. Please try again.", OutputType.ERROR)
|
|
179
|
+
return 1
|
|
180
|
+
|
|
181
|
+
# List and select issue
|
|
182
|
+
issues = list_issues()
|
|
183
|
+
selected_issue = select_issue(issues)
|
|
184
|
+
if not selected_issue:
|
|
185
|
+
PrettyOutput.print("No issue selected. Exiting.", OutputType.INFO)
|
|
186
|
+
return 0
|
|
187
|
+
|
|
188
|
+
# Create GitHub workflow agent
|
|
189
|
+
tool_registry = ToolRegistry()
|
|
190
|
+
agent = Agent(
|
|
191
|
+
system_prompt=github_workflow_prompt,
|
|
192
|
+
name="GitHub Workflow Agent",
|
|
193
|
+
tool_registry=tool_registry
|
|
194
|
+
)
|
|
195
|
+
|
|
196
|
+
# Start the workflow
|
|
197
|
+
try:
|
|
198
|
+
# Create development branch
|
|
199
|
+
if not create_development_branch(selected_issue['number']):
|
|
200
|
+
return 1
|
|
201
|
+
|
|
202
|
+
# Run the agent with the selected issue
|
|
203
|
+
workflow_request = f"""
|
|
204
|
+
Working on issue #{selected_issue['number']}: {selected_issue['title']}
|
|
205
|
+
|
|
206
|
+
Issue description:
|
|
207
|
+
{selected_issue['body']}
|
|
208
|
+
|
|
209
|
+
Please guide through the development process and help create a pull request.
|
|
210
|
+
"""
|
|
211
|
+
|
|
212
|
+
result = agent.run(workflow_request)
|
|
213
|
+
|
|
214
|
+
# Create pull request and close issue if successful
|
|
215
|
+
if result and "success" in result.lower():
|
|
216
|
+
if create_pull_request(
|
|
217
|
+
selected_issue['number'],
|
|
218
|
+
f"Fix #{selected_issue['number']}: {selected_issue['title']}",
|
|
219
|
+
"Implements the requested changes and fixes the issue."
|
|
220
|
+
):
|
|
221
|
+
close_issue(selected_issue['number'])
|
|
222
|
+
PrettyOutput.print("Workflow completed successfully!", OutputType.SUCCESS)
|
|
223
|
+
return 0
|
|
224
|
+
|
|
225
|
+
except Exception as e:
|
|
226
|
+
PrettyOutput.print(f"Error in workflow: {str(e)}", OutputType.ERROR)
|
|
227
|
+
return 1
|
|
228
|
+
|
|
229
|
+
return 1
|
|
230
|
+
|
|
231
|
+
if __name__ == "__main__":
|
|
232
|
+
sys.exit(main())
|
jarvis/models/ai8.py
CHANGED
|
@@ -14,6 +14,7 @@ class AI8Model(BasePlatform):
|
|
|
14
14
|
|
|
15
15
|
def get_model_list(self) -> List[Tuple[str, str]]:
|
|
16
16
|
"""获取模型列表"""
|
|
17
|
+
self.get_available_models()
|
|
17
18
|
return [(name,info['desc']) for name,info in self.models.items()]
|
|
18
19
|
|
|
19
20
|
def __init__(self):
|
|
@@ -30,9 +31,7 @@ class AI8Model(BasePlatform):
|
|
|
30
31
|
|
|
31
32
|
|
|
32
33
|
self.model_name = os.getenv("JARVIS_MODEL") or "deepseek-chat"
|
|
33
|
-
|
|
34
|
-
PrettyOutput.print(f"Warning: The selected model {self.model_name} is not in the available list", OutputType.WARNING)
|
|
35
|
-
|
|
34
|
+
|
|
36
35
|
|
|
37
36
|
def set_model_name(self, model_name: str):
|
|
38
37
|
"""Set model name"""
|
jarvis/models/oyi.py
CHANGED
|
@@ -14,6 +14,7 @@ class OyiModel(BasePlatform):
|
|
|
14
14
|
|
|
15
15
|
def get_model_list(self) -> List[Tuple[str, str]]:
|
|
16
16
|
"""Get model list"""
|
|
17
|
+
self.get_available_models()
|
|
17
18
|
return [(name,info['desc']) for name,info in self.models.items()]
|
|
18
19
|
|
|
19
20
|
def __init__(self):
|
|
@@ -31,9 +32,6 @@ class OyiModel(BasePlatform):
|
|
|
31
32
|
PrettyOutput.print("OYI_API_KEY is not set", OutputType.WARNING)
|
|
32
33
|
|
|
33
34
|
self.model_name = os.getenv("JARVIS_MODEL") or "deepseek-chat"
|
|
34
|
-
if self.model_name not in [m.split()[0] for m in self.get_available_models()]:
|
|
35
|
-
PrettyOutput.print(f"Warning: The selected model {self.model_name} is not in the available list", OutputType.WARNING)
|
|
36
|
-
|
|
37
35
|
|
|
38
36
|
def set_model_name(self, model_name: str):
|
|
39
37
|
"""Set model name"""
|
jarvis/tools/registry.py
CHANGED
|
@@ -22,7 +22,6 @@ def load_tools() -> str:
|
|
|
22
22
|
tools_prompt += f"- Name: {tool['name']}\n"
|
|
23
23
|
tools_prompt += f" Description: {tool['description']}\n"
|
|
24
24
|
tools_prompt += f" Parameters: {tool['parameters']}\n"
|
|
25
|
-
tools_prompt += f" Usage Format: <TOOL_CALL>\n"
|
|
26
25
|
tools_prompt += """
|
|
27
26
|
Tool Usage Format:
|
|
28
27
|
|
|
@@ -106,48 +105,55 @@ class ToolRegistry:
|
|
|
106
105
|
PrettyOutput.print(f"File does not exist: {p_file_path}", OutputType.ERROR)
|
|
107
106
|
return False
|
|
108
107
|
|
|
109
|
-
#
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
if not spec or not spec.loader:
|
|
113
|
-
PrettyOutput.print(f"Failed to load module: {p_file_path}", OutputType.ERROR)
|
|
114
|
-
return False
|
|
115
|
-
|
|
116
|
-
module = importlib.util.module_from_spec(spec) # type: ignore
|
|
117
|
-
sys.modules[module_name] = module # Add to sys.modules to support relative imports
|
|
118
|
-
spec.loader.exec_module(module)
|
|
108
|
+
# Add the parent directory to sys.path temporarily
|
|
109
|
+
parent_dir = str(p_file_path.parent)
|
|
110
|
+
sys.path.insert(0, parent_dir)
|
|
119
111
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
112
|
+
try:
|
|
113
|
+
# Import the module using standard import mechanism
|
|
114
|
+
module_name = p_file_path.stem
|
|
115
|
+
module = __import__(module_name)
|
|
116
|
+
|
|
117
|
+
# Find the tool class in the module
|
|
118
|
+
tool_found = False
|
|
119
|
+
for item_name in dir(module):
|
|
120
|
+
item = getattr(module, item_name)
|
|
121
|
+
# Check if it is a class and has the necessary attributes
|
|
122
|
+
if (isinstance(item, type) and
|
|
123
|
+
hasattr(item, 'name') and
|
|
124
|
+
hasattr(item, 'description') and
|
|
125
|
+
hasattr(item, 'parameters')):
|
|
126
|
+
|
|
127
|
+
if hasattr(item, "check"):
|
|
128
|
+
if not item.check():
|
|
129
|
+
PrettyOutput.print(f"Tool {item.name} check failed, skipping", OutputType.INFO)
|
|
130
|
+
continue
|
|
131
|
+
|
|
132
|
+
# Instantiate the tool class
|
|
133
|
+
tool_instance = item()
|
|
134
|
+
|
|
135
|
+
# Register the tool
|
|
136
|
+
self.register_tool(
|
|
137
|
+
name=tool_instance.name,
|
|
138
|
+
description=tool_instance.description,
|
|
139
|
+
parameters=tool_instance.parameters,
|
|
140
|
+
func=tool_instance.execute
|
|
141
|
+
)
|
|
142
|
+
tool_found = True
|
|
143
|
+
break
|
|
144
|
+
|
|
145
|
+
if not tool_found:
|
|
146
|
+
PrettyOutput.print(f"No valid tool class found in the file: {p_file_path}", OutputType.INFO)
|
|
147
|
+
return False
|
|
142
148
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
149
|
+
return True
|
|
150
|
+
|
|
151
|
+
finally:
|
|
152
|
+
# Remove the directory from sys.path
|
|
153
|
+
sys.path.remove(parent_dir)
|
|
146
154
|
|
|
147
|
-
return True
|
|
148
|
-
|
|
149
155
|
except Exception as e:
|
|
150
|
-
PrettyOutput.print(f"Failed to load tool from {
|
|
156
|
+
PrettyOutput.print(f"Failed to load tool from {Path(file_path).name}: {str(e)}", OutputType.ERROR)
|
|
151
157
|
return False
|
|
152
158
|
|
|
153
159
|
def register_tool(self, name: str, description: str, parameters: Dict, func: Callable):
|
|
@@ -264,7 +270,9 @@ Please provide a summary:"""
|
|
|
264
270
|
|
|
265
271
|
else:
|
|
266
272
|
PrettyOutput.section("Execution failed", OutputType.WARNING)
|
|
267
|
-
|
|
273
|
+
|
|
274
|
+
if len(tool_calls) > 1:
|
|
275
|
+
output += f"\n\n--- 一次只能执行一个工具, 因此以下工具未执行\n{str(tool_calls[1:])} ---"
|
|
268
276
|
return output
|
|
269
277
|
|
|
270
278
|
except Exception as e:
|
jarvis/utils.py
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
from ast import List, Str
|
|
2
1
|
import hashlib
|
|
3
2
|
from pathlib import Path
|
|
4
3
|
import sys
|
|
@@ -6,18 +5,14 @@ import time
|
|
|
6
5
|
import os
|
|
7
6
|
from enum import Enum
|
|
8
7
|
from datetime import datetime
|
|
9
|
-
from typing import Any
|
|
8
|
+
from typing import Any
|
|
10
9
|
import colorama
|
|
11
10
|
from colorama import Fore, Style as ColoramaStyle
|
|
12
11
|
import numpy as np
|
|
13
12
|
from prompt_toolkit import PromptSession
|
|
14
13
|
from prompt_toolkit.styles import Style as PromptStyle
|
|
15
14
|
from prompt_toolkit.formatted_text import FormattedText
|
|
16
|
-
from sentence_transformers import SentenceTransformer
|
|
17
|
-
from transformers import AutoModelForSequenceClassification, AutoTokenizer
|
|
18
|
-
import torch
|
|
19
15
|
import yaml
|
|
20
|
-
import faiss
|
|
21
16
|
|
|
22
17
|
# 初始化colorama
|
|
23
18
|
colorama.init()
|
|
@@ -244,70 +239,6 @@ def find_git_root(dir="."):
|
|
|
244
239
|
os.chdir(curr_dir)
|
|
245
240
|
return ret
|
|
246
241
|
|
|
247
|
-
def load_embedding_model():
|
|
248
|
-
model_name = "BAAI/bge-m3"
|
|
249
|
-
cache_dir = os.path.expanduser("~/.cache/huggingface/hub")
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
try:
|
|
253
|
-
# Load model
|
|
254
|
-
embedding_model = SentenceTransformer(
|
|
255
|
-
model_name,
|
|
256
|
-
cache_folder=cache_dir,
|
|
257
|
-
local_files_only=True
|
|
258
|
-
)
|
|
259
|
-
except Exception as e:
|
|
260
|
-
PrettyOutput.print(f"Failed to load embedding model: {str(e)}", OutputType.ERROR)
|
|
261
|
-
os.system(f'huggingface-cli download --repo-type model --local-dir {cache_dir} {model_name}')
|
|
262
|
-
# Load model
|
|
263
|
-
embedding_model = SentenceTransformer(
|
|
264
|
-
model_name,
|
|
265
|
-
cache_folder=cache_dir,
|
|
266
|
-
local_files_only=True
|
|
267
|
-
)
|
|
268
|
-
|
|
269
|
-
return embedding_model
|
|
270
|
-
|
|
271
|
-
def load_rerank_model():
|
|
272
|
-
"""Load reranking model"""
|
|
273
|
-
model_name = "BAAI/bge-reranker-v2-m3"
|
|
274
|
-
cache_dir = os.path.expanduser("~/.cache/huggingface/hub")
|
|
275
|
-
|
|
276
|
-
PrettyOutput.print(f"Loading reranking model: {model_name}...", OutputType.INFO)
|
|
277
|
-
|
|
278
|
-
try:
|
|
279
|
-
# Load model and tokenizer
|
|
280
|
-
tokenizer = AutoTokenizer.from_pretrained(
|
|
281
|
-
model_name,
|
|
282
|
-
cache_dir=cache_dir,
|
|
283
|
-
local_files_only=True
|
|
284
|
-
)
|
|
285
|
-
model = AutoModelForSequenceClassification.from_pretrained(
|
|
286
|
-
model_name,
|
|
287
|
-
cache_dir=cache_dir,
|
|
288
|
-
local_files_only=True
|
|
289
|
-
)
|
|
290
|
-
except Exception as e:
|
|
291
|
-
PrettyOutput.print(f"Failed to load reranking model: {str(e)}", OutputType.ERROR)
|
|
292
|
-
os.system(f'huggingface-cli download --repo-type model --local-dir {cache_dir} {model_name}')
|
|
293
|
-
# Load model and tokenizer
|
|
294
|
-
tokenizer = AutoTokenizer.from_pretrained(
|
|
295
|
-
model_name,
|
|
296
|
-
cache_dir=cache_dir,
|
|
297
|
-
local_files_only=True
|
|
298
|
-
)
|
|
299
|
-
model = AutoModelForSequenceClassification.from_pretrained(
|
|
300
|
-
model_name,
|
|
301
|
-
cache_dir=cache_dir,
|
|
302
|
-
local_files_only=True
|
|
303
|
-
)
|
|
304
|
-
|
|
305
|
-
# Use GPU if available
|
|
306
|
-
if torch.cuda.is_available():
|
|
307
|
-
model = model.cuda()
|
|
308
|
-
model.eval()
|
|
309
|
-
|
|
310
|
-
return model, tokenizer
|
|
311
242
|
|
|
312
243
|
def get_max_context_length():
|
|
313
244
|
return int(os.getenv('JARVIS_MAX_CONTEXT_LENGTH', '131072')) # 默认128k
|
|
@@ -359,7 +290,6 @@ def _create_methodology_embedding(embedding_model: Any, methodology_text: str) -
|
|
|
359
290
|
|
|
360
291
|
def load_methodology(user_input: str) -> str:
|
|
361
292
|
"""Load methodology and build vector index"""
|
|
362
|
-
PrettyOutput.print("Loading methodology...", OutputType.PROGRESS)
|
|
363
293
|
user_jarvis_methodology = os.path.expanduser("~/.jarvis/methodology")
|
|
364
294
|
if not os.path.exists(user_jarvis_methodology):
|
|
365
295
|
return ""
|
|
@@ -368,62 +298,17 @@ def load_methodology(user_input: str) -> str:
|
|
|
368
298
|
with open(user_jarvis_methodology, "r", encoding="utf-8") as f:
|
|
369
299
|
data = yaml.safe_load(f)
|
|
370
300
|
|
|
371
|
-
# Reset data structure
|
|
372
|
-
methodology_data = []
|
|
373
|
-
vectors = []
|
|
374
|
-
ids = []
|
|
375
|
-
|
|
376
|
-
# Get embedding model
|
|
377
|
-
embedding_model = load_embedding_model()
|
|
378
301
|
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
# Create embedding vector for each methodology
|
|
384
|
-
for i, (key, value) in enumerate(data.items()):
|
|
385
|
-
PrettyOutput.print(f"Vectorizing methodology: {key} ...", OutputType.INFO)
|
|
386
|
-
methodology_text = f"{key}\n{value}"
|
|
387
|
-
embedding = _create_methodology_embedding(embedding_model, methodology_text)
|
|
388
|
-
vectors.append(embedding)
|
|
389
|
-
ids.append(i)
|
|
390
|
-
methodology_data.append({"key": key, "value": value})
|
|
391
|
-
|
|
392
|
-
if vectors:
|
|
393
|
-
vectors_array = np.vstack(vectors)
|
|
394
|
-
# Use correct dimension from test embedding
|
|
395
|
-
hnsw_index = faiss.IndexHNSWFlat(embedding_dimension, 16)
|
|
396
|
-
hnsw_index.hnsw.efConstruction = 40
|
|
397
|
-
hnsw_index.hnsw.efSearch = 16
|
|
398
|
-
methodology_index = faiss.IndexIDMap(hnsw_index)
|
|
399
|
-
methodology_index.add_with_ids(vectors_array, np.array(ids)) # type: ignore
|
|
400
|
-
query_embedding = _create_methodology_embedding(embedding_model, user_input)
|
|
401
|
-
k = min(5, len(methodology_data))
|
|
402
|
-
PrettyOutput.print(f"Retrieving methodology...", OutputType.INFO)
|
|
403
|
-
distances, indices = methodology_index.search(
|
|
404
|
-
query_embedding.reshape(1, -1), k
|
|
405
|
-
) # type: ignore
|
|
406
|
-
|
|
407
|
-
relevant_methodologies = {}
|
|
408
|
-
for dist, idx in zip(distances[0], indices[0]):
|
|
409
|
-
if idx >= 0:
|
|
410
|
-
similarity = 1.0 / (1.0 + float(dist))
|
|
411
|
-
methodology = methodology_data[idx]
|
|
412
|
-
PrettyOutput.print(
|
|
413
|
-
f"Methodology '{methodology['key']}' similarity: {similarity:.3f}",
|
|
414
|
-
OutputType.INFO
|
|
415
|
-
)
|
|
416
|
-
if similarity >= 0.5:
|
|
417
|
-
relevant_methodologies[methodology["key"]] = methodology["value"]
|
|
418
|
-
|
|
419
|
-
if relevant_methodologies:
|
|
420
|
-
return f"""This is the standard methodology for handling previous problems, if the current task is similar, you can refer to it:
|
|
421
|
-
{relevant_methodologies}
|
|
422
|
-
"""
|
|
423
|
-
return ""
|
|
302
|
+
ret = """This is the standard methodology for handling previous problems, if the current task is similar, you can refer to it:"""
|
|
303
|
+
for k, v in data.items():
|
|
304
|
+
ret += f"\n{k}: {v}\n"
|
|
305
|
+
return ret
|
|
424
306
|
|
|
425
307
|
except Exception as e:
|
|
426
308
|
PrettyOutput.print(f"Error loading methodology: {str(e)}", OutputType.ERROR)
|
|
427
309
|
import traceback
|
|
428
310
|
PrettyOutput.print(f"Error trace: {traceback.format_exc()}", OutputType.INFO)
|
|
429
|
-
return ""
|
|
311
|
+
return ""
|
|
312
|
+
|
|
313
|
+
def no_embedding() -> bool:
|
|
314
|
+
return bool(os.environ.get("JARVIS_NO_EMBEDDING", ""))
|