jarvis-ai-assistant 0.1.111__py3-none-any.whl → 0.1.112__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 +40 -34
- jarvis/jarvis_code_agent/code_agent.py +23 -5
- jarvis/jarvis_code_agent/file_select.py +16 -16
- jarvis/jarvis_code_agent/patch.py +17 -11
- jarvis/jarvis_code_agent/relevant_files.py +33 -40
- jarvis/jarvis_codebase/main.py +57 -48
- jarvis/jarvis_lsp/cpp.py +1 -1
- jarvis/jarvis_lsp/go.py +1 -1
- jarvis/jarvis_lsp/python.py +0 -2
- jarvis/jarvis_lsp/registry.py +13 -13
- jarvis/jarvis_lsp/rust.py +1 -1
- jarvis/jarvis_platform/ai8.py +14 -14
- jarvis/jarvis_platform/base.py +1 -1
- jarvis/jarvis_platform/kimi.py +17 -17
- jarvis/jarvis_platform/ollama.py +14 -14
- jarvis/jarvis_platform/openai.py +8 -8
- jarvis/jarvis_platform/oyi.py +19 -19
- jarvis/jarvis_platform/registry.py +6 -6
- jarvis/jarvis_platform_manager/main.py +17 -17
- jarvis/jarvis_rag/main.py +25 -25
- jarvis/jarvis_smart_shell/main.py +6 -6
- jarvis/jarvis_tools/ask_codebase.py +3 -3
- jarvis/jarvis_tools/ask_user.py +2 -2
- jarvis/jarvis_tools/create_code_agent.py +8 -8
- jarvis/jarvis_tools/create_sub_agent.py +2 -2
- jarvis/jarvis_tools/execute_shell.py +2 -2
- jarvis/jarvis_tools/file_operation.py +1 -1
- jarvis/jarvis_tools/git_commiter.py +4 -4
- jarvis/jarvis_tools/methodology.py +3 -3
- jarvis/jarvis_tools/rag.py +3 -3
- jarvis/jarvis_tools/read_code.py +1 -1
- jarvis/jarvis_tools/read_webpage.py +19 -6
- jarvis/jarvis_tools/registry.py +11 -11
- jarvis/jarvis_tools/search.py +88 -27
- jarvis/jarvis_tools/select_code_files.py +1 -1
- jarvis/jarvis_tools/tool_generator.py +182 -0
- jarvis/utils.py +18 -20
- jarvis_ai_assistant-0.1.112.dist-info/METADATA +460 -0
- jarvis_ai_assistant-0.1.112.dist-info/RECORD +64 -0
- jarvis_ai_assistant-0.1.111.dist-info/METADATA +0 -461
- jarvis_ai_assistant-0.1.111.dist-info/RECORD +0 -63
- {jarvis_ai_assistant-0.1.111.dist-info → jarvis_ai_assistant-0.1.112.dist-info}/LICENSE +0 -0
- {jarvis_ai_assistant-0.1.111.dist-info → jarvis_ai_assistant-0.1.112.dist-info}/WHEEL +0 -0
- {jarvis_ai_assistant-0.1.111.dist-info → jarvis_ai_assistant-0.1.112.dist-info}/entry_points.txt +0 -0
- {jarvis_ai_assistant-0.1.111.dist-info → jarvis_ai_assistant-0.1.112.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Tool Generator Tool - Automatically creates new tools using LLM
|
|
3
|
+
"""
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
import re
|
|
6
|
+
from typing import Dict, Any
|
|
7
|
+
from jarvis.jarvis_platform.registry import PlatformRegistry
|
|
8
|
+
|
|
9
|
+
class ToolGenerator:
|
|
10
|
+
name = "tool_generator"
|
|
11
|
+
description = "Generates new tools using LLM that integrate with the system"
|
|
12
|
+
parameters = {
|
|
13
|
+
"type": "object",
|
|
14
|
+
"properties": {
|
|
15
|
+
"tool_name": {
|
|
16
|
+
"type": "string",
|
|
17
|
+
"description": "Name of the new tool"
|
|
18
|
+
},
|
|
19
|
+
"description": {
|
|
20
|
+
"type": "string",
|
|
21
|
+
"description": "Description of the tool's purpose"
|
|
22
|
+
},
|
|
23
|
+
"input_spec": {
|
|
24
|
+
"type": "string",
|
|
25
|
+
"description": "Specification of required inputs and functionality"
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
"required": ["tool_name", "description", "input_spec"]
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
def execute(self, arguments: Dict[str, Any]) -> Dict[str, Any]:
|
|
32
|
+
"""Generate and save a new tool using LLM"""
|
|
33
|
+
# Get fresh model instance for each execution
|
|
34
|
+
model = PlatformRegistry.get_global_platform_registry().get_codegen_platform()
|
|
35
|
+
|
|
36
|
+
try:
|
|
37
|
+
tool_name = arguments["tool_name"]
|
|
38
|
+
description = arguments["description"]
|
|
39
|
+
input_spec = arguments["input_spec"]
|
|
40
|
+
|
|
41
|
+
# Generate tool implementation using LLM
|
|
42
|
+
prompt = self._create_prompt(tool_name, description, input_spec)
|
|
43
|
+
llm_response = model.chat_until_success(prompt)
|
|
44
|
+
|
|
45
|
+
# Extract implementation with more flexible parsing
|
|
46
|
+
implementation = self._extract_code(llm_response)
|
|
47
|
+
if not implementation:
|
|
48
|
+
return {
|
|
49
|
+
"success": False,
|
|
50
|
+
"stdout": "",
|
|
51
|
+
"stderr": "Could not extract valid Python code from LLM response"
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
# Validate return value format
|
|
55
|
+
if not self._validate_return_value_format(implementation):
|
|
56
|
+
return {
|
|
57
|
+
"success": False,
|
|
58
|
+
"stdout": "",
|
|
59
|
+
"stderr": "Generated tool does not follow required return value format"
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
# Save the new tool
|
|
63
|
+
tools_dir = Path.home() / ".jarvis" / "tools"
|
|
64
|
+
tools_dir.mkdir(parents=True, exist_ok=True)
|
|
65
|
+
tool_file = tools_dir / f"{tool_name}.py"
|
|
66
|
+
|
|
67
|
+
with open(tool_file, "w") as f:
|
|
68
|
+
f.write(implementation)
|
|
69
|
+
|
|
70
|
+
return {
|
|
71
|
+
"success": True,
|
|
72
|
+
"stdout": f"Tool successfully generated at: {tool_file}",
|
|
73
|
+
"stderr": ""
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
except Exception as e:
|
|
77
|
+
return {
|
|
78
|
+
"success": False,
|
|
79
|
+
"stdout": "",
|
|
80
|
+
"stderr": f"Tool generation failed: {str(e)}"
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
def _create_prompt(self, tool_name: str, description: str, input_spec: str) -> str:
|
|
84
|
+
"""Create the LLM prompt for tool generation"""
|
|
85
|
+
example_code = '''
|
|
86
|
+
<TOOL>
|
|
87
|
+
from typing import Dict, Any
|
|
88
|
+
from jarvis.utils import OutputType, PrettyOutput
|
|
89
|
+
from jarvis.jarvis_platform.registry import PlatformRegistry
|
|
90
|
+
|
|
91
|
+
class CustomTool:
|
|
92
|
+
name = "Tool name" # Tool name used when calling
|
|
93
|
+
description = "Tool description" # Tool purpose
|
|
94
|
+
parameters = { # Parameters JSON Schema
|
|
95
|
+
"type": "object",
|
|
96
|
+
"properties": {
|
|
97
|
+
"param1": {
|
|
98
|
+
"type": "string",
|
|
99
|
+
"description": "Parameter description"
|
|
100
|
+
}
|
|
101
|
+
},
|
|
102
|
+
"required": ["param1"]
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
def execute(self, args: Dict[str, Any]) -> Dict[str, Any]:
|
|
106
|
+
"""Execute the tool functionality
|
|
107
|
+
|
|
108
|
+
Args:
|
|
109
|
+
args: Parameters passed to the tool
|
|
110
|
+
|
|
111
|
+
Returns:
|
|
112
|
+
{
|
|
113
|
+
"success": bool,
|
|
114
|
+
"stdout": str,
|
|
115
|
+
"stderr": str,
|
|
116
|
+
}
|
|
117
|
+
"""
|
|
118
|
+
try:
|
|
119
|
+
# Implement the tool logic here
|
|
120
|
+
# Use LLM
|
|
121
|
+
# model = PlatformRegistry.get_global_platform_registry().get_codegen_platform()
|
|
122
|
+
# result = model.chat_until_success(prompt)
|
|
123
|
+
|
|
124
|
+
result = "Tool result"
|
|
125
|
+
return {
|
|
126
|
+
"success": True,
|
|
127
|
+
"stdout": result,
|
|
128
|
+
"stderr": ""
|
|
129
|
+
}
|
|
130
|
+
except Exception as e:
|
|
131
|
+
return {
|
|
132
|
+
"success": False,
|
|
133
|
+
"stdout": "",
|
|
134
|
+
"stderr": str(e)
|
|
135
|
+
}
|
|
136
|
+
</TOOL>
|
|
137
|
+
'''
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
return f'''Create a Python tool class that integrates with the Jarvis system. Follow these requirements:
|
|
141
|
+
1. Class name: {tool_name.capitalize()}Tool
|
|
142
|
+
2. Description: {description}
|
|
143
|
+
3. Input specification: {input_spec}
|
|
144
|
+
4. Must include these class attributes:
|
|
145
|
+
- name: str (tool identifier)
|
|
146
|
+
- description: str (tool purpose)
|
|
147
|
+
- parameters: dict (JSON schema for inputs)
|
|
148
|
+
5. Must implement execute(self, args: Dict) -> Dict method
|
|
149
|
+
6. The execute method MUST return a dictionary with these exact fields:
|
|
150
|
+
- success: bool (indicating operation success)
|
|
151
|
+
- stdout: str (primary output/result)
|
|
152
|
+
- stderr: str (error message if any)
|
|
153
|
+
7. Must handle errors gracefully
|
|
154
|
+
8. Return ONLY the Python implementation code
|
|
155
|
+
9. The code should be complete and ready to use.
|
|
156
|
+
10. Output the code in the following format:
|
|
157
|
+
<TOOL>
|
|
158
|
+
{example_code}
|
|
159
|
+
</TOOL>
|
|
160
|
+
|
|
161
|
+
Example:
|
|
162
|
+
{example_code}
|
|
163
|
+
'''
|
|
164
|
+
|
|
165
|
+
def _extract_code(self, response: str) -> str:
|
|
166
|
+
"""Flexibly extract Python code from LLM response"""
|
|
167
|
+
# Find the first occurrence of <TOOL> and </TOOL>
|
|
168
|
+
sm = re.search(r'<TOOL>(.*?)</TOOL>', response, re.DOTALL)
|
|
169
|
+
if sm:
|
|
170
|
+
return sm.group(1)
|
|
171
|
+
return ""
|
|
172
|
+
|
|
173
|
+
def _validate_return_value_format(self, code: str) -> bool:
|
|
174
|
+
"""Validate that execute method returns correct format"""
|
|
175
|
+
required_fields = ["success", "stdout", "stderr"]
|
|
176
|
+
# Look for execute method
|
|
177
|
+
if "def execute(self, args: Dict) -> Dict:" not in code and \
|
|
178
|
+
"def execute(self, args: Dict) -> Dict[str, Any]:" not in code:
|
|
179
|
+
return False
|
|
180
|
+
|
|
181
|
+
# Check for required fields in return statement
|
|
182
|
+
return all(field in code for field in required_fields)
|
jarvis/utils.py
CHANGED
|
@@ -322,7 +322,7 @@ def get_multiline_input(tip: str) -> str:
|
|
|
322
322
|
lines.append(line)
|
|
323
323
|
|
|
324
324
|
except KeyboardInterrupt:
|
|
325
|
-
PrettyOutput.print("
|
|
325
|
+
PrettyOutput.print("输入已取消", OutputType.INFO)
|
|
326
326
|
return ""
|
|
327
327
|
|
|
328
328
|
return "\n".join(lines)
|
|
@@ -348,7 +348,7 @@ def init_env():
|
|
|
348
348
|
except ValueError:
|
|
349
349
|
continue
|
|
350
350
|
except Exception as e:
|
|
351
|
-
PrettyOutput.print(f"
|
|
351
|
+
PrettyOutput.print(f"警告: 读取 {env_file} 失败: {e}", OutputType.WARNING)
|
|
352
352
|
|
|
353
353
|
|
|
354
354
|
def while_success(func, sleep_time: float = 0.1):
|
|
@@ -356,7 +356,7 @@ def while_success(func, sleep_time: float = 0.1):
|
|
|
356
356
|
try:
|
|
357
357
|
return func()
|
|
358
358
|
except Exception as e:
|
|
359
|
-
PrettyOutput.print(f"
|
|
359
|
+
PrettyOutput.print(f"执行失败: {str(e)}, 等待 {sleep_time}s...", OutputType.ERROR)
|
|
360
360
|
time.sleep(sleep_time)
|
|
361
361
|
continue
|
|
362
362
|
|
|
@@ -366,7 +366,7 @@ def while_true(func, sleep_time: float = 0.1):
|
|
|
366
366
|
ret = func()
|
|
367
367
|
if ret:
|
|
368
368
|
break
|
|
369
|
-
PrettyOutput.print(f"
|
|
369
|
+
PrettyOutput.print(f"执行失败, 等待 {sleep_time}s...", OutputType.WARNING)
|
|
370
370
|
time.sleep(sleep_time)
|
|
371
371
|
return ret
|
|
372
372
|
|
|
@@ -433,7 +433,7 @@ def load_rerank_model():
|
|
|
433
433
|
model_name = "BAAI/bge-reranker-v2-m3"
|
|
434
434
|
cache_dir = os.path.expanduser("~/.cache/huggingface/hub")
|
|
435
435
|
|
|
436
|
-
PrettyOutput.print(f"
|
|
436
|
+
PrettyOutput.print(f"加载重排序模型: {model_name}...", OutputType.INFO)
|
|
437
437
|
|
|
438
438
|
try:
|
|
439
439
|
# Load model and tokenizer
|
|
@@ -484,7 +484,7 @@ def is_long_context(files: list) -> bool:
|
|
|
484
484
|
if total_tokens > threshold:
|
|
485
485
|
return True
|
|
486
486
|
except Exception as e:
|
|
487
|
-
PrettyOutput.print(f"
|
|
487
|
+
PrettyOutput.print(f"读取文件 {file_path} 失败: {e}", OutputType.WARNING)
|
|
488
488
|
continue
|
|
489
489
|
|
|
490
490
|
return total_tokens > threshold
|
|
@@ -511,13 +511,13 @@ def _create_methodology_embedding(embedding_model: Any, methodology_text: str) -
|
|
|
511
511
|
vector = np.array(embedding.cpu().numpy(), dtype=np.float32)
|
|
512
512
|
return vector[0] # Return first vector, because we only encoded one text
|
|
513
513
|
except Exception as e:
|
|
514
|
-
PrettyOutput.print(f"
|
|
514
|
+
PrettyOutput.print(f"创建方法论嵌入向量失败: {str(e)}", OutputType.ERROR)
|
|
515
515
|
return np.zeros(1536, dtype=np.float32)
|
|
516
516
|
|
|
517
517
|
|
|
518
518
|
def load_methodology(user_input: str) -> str:
|
|
519
519
|
"""Load methodology and build vector index"""
|
|
520
|
-
PrettyOutput.print("
|
|
520
|
+
PrettyOutput.print("加载方法论...", OutputType.PROGRESS)
|
|
521
521
|
user_jarvis_methodology = os.path.expanduser("~/.jarvis/methodology")
|
|
522
522
|
if not os.path.exists(user_jarvis_methodology):
|
|
523
523
|
return ""
|
|
@@ -565,7 +565,7 @@ def load_methodology(user_input: str) -> str:
|
|
|
565
565
|
methodology_index.add_with_ids(vectors_array, np.array(ids)) # type: ignore
|
|
566
566
|
query_embedding = _create_methodology_embedding(embedding_model, user_input)
|
|
567
567
|
k = min(3, len(methodology_data))
|
|
568
|
-
PrettyOutput.print(f"
|
|
568
|
+
PrettyOutput.print(f"检索方法论...", OutputType.INFO)
|
|
569
569
|
distances, indices = methodology_index.search(
|
|
570
570
|
query_embedding.reshape(1, -1), k
|
|
571
571
|
) # type: ignore
|
|
@@ -590,9 +590,7 @@ def load_methodology(user_input: str) -> str:
|
|
|
590
590
|
return make_methodology_prompt(data)
|
|
591
591
|
|
|
592
592
|
except Exception as e:
|
|
593
|
-
PrettyOutput.print(f"
|
|
594
|
-
import traceback
|
|
595
|
-
PrettyOutput.print(f"Error trace: {traceback.format_exc()}", OutputType.INFO)
|
|
593
|
+
PrettyOutput.print(f"加载方法论失败: {str(e)}", OutputType.ERROR)
|
|
596
594
|
return ""
|
|
597
595
|
|
|
598
596
|
|
|
@@ -648,15 +646,15 @@ def init_gpu_config() -> Dict:
|
|
|
648
646
|
torch.cuda.empty_cache()
|
|
649
647
|
|
|
650
648
|
PrettyOutput.print(
|
|
651
|
-
f"GPU
|
|
652
|
-
f"
|
|
653
|
-
f"
|
|
649
|
+
f"GPU已初始化: {torch.cuda.get_device_name(0)}\n"
|
|
650
|
+
f"设备内存: {gpu_mem / 1024**3:.1f}GB\n"
|
|
651
|
+
f"共享内存: {config['shared_memory'] / 1024**3:.1f}GB",
|
|
654
652
|
output_type=OutputType.SUCCESS
|
|
655
653
|
)
|
|
656
654
|
else:
|
|
657
|
-
PrettyOutput.print("
|
|
655
|
+
PrettyOutput.print("没有GPU可用, 使用CPU模式", output_type=OutputType.WARNING)
|
|
658
656
|
except Exception as e:
|
|
659
|
-
PrettyOutput.print(f"GPU
|
|
657
|
+
PrettyOutput.print(f"GPU初始化失败: {str(e)}", output_type=OutputType.WARNING)
|
|
660
658
|
|
|
661
659
|
return config
|
|
662
660
|
|
|
@@ -677,7 +675,7 @@ def get_embedding_batch(embedding_model: Any, texts: List[str]) -> np.ndarray:
|
|
|
677
675
|
all_vectors.extend(vectors)
|
|
678
676
|
return np.vstack(all_vectors)
|
|
679
677
|
except Exception as e:
|
|
680
|
-
PrettyOutput.print(f"
|
|
678
|
+
PrettyOutput.print(f"批量嵌入失败: {str(e)}", OutputType.ERROR)
|
|
681
679
|
return np.zeros((0, embedding_model.get_sentence_embedding_dimension()), dtype=np.float32)
|
|
682
680
|
|
|
683
681
|
|
|
@@ -782,11 +780,11 @@ def get_context_token_count(text: str) -> int:
|
|
|
782
780
|
try:
|
|
783
781
|
# Use a fast tokenizer that's good at general text
|
|
784
782
|
tokenizer = load_tokenizer()
|
|
785
|
-
chunks = split_text_into_chunks(text,
|
|
783
|
+
chunks = split_text_into_chunks(text, 512)
|
|
786
784
|
return sum([len(tokenizer.encode(chunk)) for chunk in chunks])
|
|
787
785
|
|
|
788
786
|
except Exception as e:
|
|
789
|
-
PrettyOutput.print(f"
|
|
787
|
+
PrettyOutput.print(f"计算token失败: {str(e)}", OutputType.WARNING)
|
|
790
788
|
# Fallback to rough character-based estimate
|
|
791
789
|
return len(text) // 4 # Rough estimate of 4 chars per token
|
|
792
790
|
|