jarvis-ai-assistant 0.1.111__py3-none-any.whl → 0.1.113__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 +72 -41
- 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 +8 -5
- 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 +69 -28
- jarvis_ai_assistant-0.1.113.dist-info/METADATA +460 -0
- jarvis_ai_assistant-0.1.113.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.113.dist-info}/LICENSE +0 -0
- {jarvis_ai_assistant-0.1.111.dist-info → jarvis_ai_assistant-0.1.113.dist-info}/WHEEL +0 -0
- {jarvis_ai_assistant-0.1.111.dist-info → jarvis_ai_assistant-0.1.113.dist-info}/entry_points.txt +0 -0
- {jarvis_ai_assistant-0.1.111.dist-info → jarvis_ai_assistant-0.1.113.dist-info}/top_level.txt +0 -0
jarvis/jarvis_tools/search.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
from typing import Dict, Any, List
|
|
2
2
|
from jarvis.jarvis_platform.registry import PlatformRegistry
|
|
3
|
-
from jarvis.utils import PrettyOutput, OutputType
|
|
3
|
+
from jarvis.utils import PrettyOutput, OutputType, get_context_token_count, get_max_token_count
|
|
4
4
|
from jarvis.jarvis_tools.read_webpage import WebpageTool
|
|
5
5
|
from playwright.sync_api import sync_playwright
|
|
6
6
|
from urllib.parse import quote
|
|
@@ -58,7 +58,7 @@ def bing_search(query):
|
|
|
58
58
|
return summaries
|
|
59
59
|
|
|
60
60
|
except Exception as error:
|
|
61
|
-
PrettyOutput.print(f"
|
|
61
|
+
PrettyOutput.print(f"搜索错误:{str(error)}", OutputType.ERROR)
|
|
62
62
|
return None
|
|
63
63
|
|
|
64
64
|
class SearchTool:
|
|
@@ -106,28 +106,89 @@ class SearchTool:
|
|
|
106
106
|
})
|
|
107
107
|
return formatted_results
|
|
108
108
|
except Exception as e:
|
|
109
|
-
PrettyOutput.print(f"
|
|
109
|
+
PrettyOutput.print(f"搜索请求失败:{str(e)}", OutputType.ERROR)
|
|
110
110
|
return []
|
|
111
111
|
|
|
112
112
|
def _extract_info(self, contents: List[str], question: str) -> str:
|
|
113
113
|
"""Use language model to extract key information from web content"""
|
|
114
|
-
|
|
114
|
+
try:
|
|
115
|
+
# Reserve tokens for prompt and response
|
|
116
|
+
max_tokens = get_max_token_count()
|
|
117
|
+
reserved_tokens = 2000 # Reserve tokens for prompt template and response
|
|
118
|
+
available_tokens = max_tokens - reserved_tokens
|
|
119
|
+
|
|
120
|
+
# Split contents into batches
|
|
121
|
+
batches = []
|
|
122
|
+
current_batch = []
|
|
123
|
+
current_tokens = 0
|
|
124
|
+
|
|
125
|
+
for content in contents:
|
|
126
|
+
content_tokens = get_context_token_count(content)
|
|
127
|
+
|
|
128
|
+
# If adding this content would exceed limit, start new batch
|
|
129
|
+
if current_tokens + content_tokens > available_tokens:
|
|
130
|
+
if current_batch:
|
|
131
|
+
batches.append(current_batch)
|
|
132
|
+
current_batch = [content]
|
|
133
|
+
current_tokens = content_tokens
|
|
134
|
+
else:
|
|
135
|
+
current_batch.append(content)
|
|
136
|
+
current_tokens += content_tokens
|
|
137
|
+
|
|
138
|
+
# Add final batch
|
|
139
|
+
if current_batch:
|
|
140
|
+
batches.append(current_batch)
|
|
115
141
|
|
|
116
|
-
|
|
142
|
+
# Process each batch
|
|
143
|
+
batch_results = []
|
|
144
|
+
for i, batch in enumerate(batches, 1):
|
|
145
|
+
PrettyOutput.print(f"正在处理批次 {i}/{len(batches)}...", OutputType.PROGRESS)
|
|
146
|
+
|
|
147
|
+
prompt = f"""Please analyze these search results to answer the question: {question}
|
|
148
|
+
|
|
149
|
+
Search results content (Batch {i}/{len(batches)}):
|
|
117
150
|
{'-' * 40}
|
|
118
|
-
{''.join(
|
|
151
|
+
{''.join(batch)}
|
|
119
152
|
{'-' * 40}
|
|
120
153
|
|
|
121
|
-
Please
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
154
|
+
Please extract key information related to the question. Focus on:
|
|
155
|
+
1. Relevant facts and details
|
|
156
|
+
2. Maintaining objectivity
|
|
157
|
+
3. Citing sources when appropriate
|
|
158
|
+
4. Noting any uncertainties
|
|
159
|
+
|
|
160
|
+
Format your response as a clear summary of findings from this batch."""
|
|
161
|
+
|
|
162
|
+
response = self.model.chat_until_success(prompt)
|
|
163
|
+
batch_results.append(response)
|
|
164
|
+
|
|
165
|
+
# If only one batch, return its result directly
|
|
166
|
+
if len(batch_results) == 1:
|
|
167
|
+
return batch_results[0]
|
|
168
|
+
|
|
169
|
+
# Synthesize results from all batches
|
|
170
|
+
batch_findings = '\n\n'.join(f'Batch {i+1}:\n{result}' for i, result in enumerate(batch_results))
|
|
171
|
+
separator = '-' * 40
|
|
172
|
+
|
|
173
|
+
synthesis_prompt = f"""Please provide a comprehensive answer to the original question by synthesizing the findings from multiple batches of search results.
|
|
174
|
+
|
|
175
|
+
Original Question: {question}
|
|
176
|
+
|
|
177
|
+
Findings from each batch:
|
|
178
|
+
{separator}
|
|
179
|
+
{batch_findings}
|
|
180
|
+
{separator}
|
|
181
|
+
|
|
182
|
+
Please synthesize a final answer that:
|
|
183
|
+
1. Combines key insights from all batches
|
|
184
|
+
2. Resolves any contradictions between sources
|
|
185
|
+
3. Maintains clear source attribution
|
|
186
|
+
4. Acknowledges any remaining uncertainties
|
|
187
|
+
5. Provides a coherent and complete response to the original question"""
|
|
188
|
+
|
|
189
|
+
final_response = self.model.chat_until_success(synthesis_prompt)
|
|
190
|
+
return final_response
|
|
127
191
|
|
|
128
|
-
try:
|
|
129
|
-
response = self.model.chat_until_success(prompt)
|
|
130
|
-
return response
|
|
131
192
|
except Exception as e:
|
|
132
193
|
return f"Information extraction failed: {str(e)}"
|
|
133
194
|
|
|
@@ -139,8 +200,8 @@ When answering, pay attention to:
|
|
|
139
200
|
max_results = args.get("max_results", 3)
|
|
140
201
|
|
|
141
202
|
# Print search information
|
|
142
|
-
PrettyOutput.print(f"
|
|
143
|
-
PrettyOutput.print(f"
|
|
203
|
+
PrettyOutput.print(f"搜索关键词: {query}", OutputType.INFO)
|
|
204
|
+
PrettyOutput.print(f"相关问题: {question}", OutputType.INFO)
|
|
144
205
|
|
|
145
206
|
# Get search results
|
|
146
207
|
results = self._search(query, max_results)
|
|
@@ -155,13 +216,13 @@ When answering, pay attention to:
|
|
|
155
216
|
contents = []
|
|
156
217
|
for i, result in enumerate(results, 1):
|
|
157
218
|
try:
|
|
158
|
-
PrettyOutput.print(f"
|
|
219
|
+
PrettyOutput.print(f"正在读取结果 {i}/{len(results)}... {result['title']} - {result['href']}", OutputType.PROGRESS)
|
|
159
220
|
webpage_result = self.webpage_tool.execute({"url": result["href"]})
|
|
160
221
|
if webpage_result["success"]:
|
|
161
222
|
contents.append(f"\nSource {i}: {result['href']}\n")
|
|
162
223
|
contents.append(webpage_result["stdout"])
|
|
163
224
|
except Exception as e:
|
|
164
|
-
PrettyOutput.print(f"
|
|
225
|
+
PrettyOutput.print(f"读取结果失败 {i}: {str(e)}", OutputType.WARNING)
|
|
165
226
|
continue
|
|
166
227
|
|
|
167
228
|
if not contents:
|
|
@@ -172,7 +233,7 @@ When answering, pay attention to:
|
|
|
172
233
|
}
|
|
173
234
|
|
|
174
235
|
# Extract information
|
|
175
|
-
PrettyOutput.print("
|
|
236
|
+
PrettyOutput.print("正在分析搜索结果...", OutputType.PROGRESS)
|
|
176
237
|
analysis = self._extract_info(contents, question)
|
|
177
238
|
|
|
178
239
|
return {
|
|
@@ -200,15 +261,15 @@ def main():
|
|
|
200
261
|
args = parser.parse_args()
|
|
201
262
|
|
|
202
263
|
try:
|
|
203
|
-
PrettyOutput.print(f"
|
|
264
|
+
PrettyOutput.print(f"搜索: {args.query}", OutputType.INFO)
|
|
204
265
|
|
|
205
266
|
results = bing_search(args.query)
|
|
206
267
|
|
|
207
268
|
if not results:
|
|
208
|
-
PrettyOutput.print("
|
|
269
|
+
PrettyOutput.print("未找到搜索结果", OutputType.WARNING)
|
|
209
270
|
sys.exit(1)
|
|
210
271
|
|
|
211
|
-
PrettyOutput.print(f"\
|
|
272
|
+
PrettyOutput.print(f"\n找到 {len(results)} 个结果:", OutputType.INFO)
|
|
212
273
|
|
|
213
274
|
for i, result in enumerate(results[:args.max], 1):
|
|
214
275
|
output = []
|
|
@@ -217,16 +278,16 @@ def main():
|
|
|
217
278
|
output.append(f"{i}. {result['href']}")
|
|
218
279
|
else:
|
|
219
280
|
output.append(f"{i}. {result['title']}")
|
|
220
|
-
output.append(f"
|
|
281
|
+
output.append(f"链接: {result['href']}")
|
|
221
282
|
if result['abstract']:
|
|
222
|
-
output.append(f"
|
|
283
|
+
output.append(f"摘要: {result['abstract']}")
|
|
223
284
|
PrettyOutput.print("\n".join(output), OutputType.INFO)
|
|
224
285
|
|
|
225
286
|
except KeyboardInterrupt:
|
|
226
|
-
PrettyOutput.print("
|
|
287
|
+
PrettyOutput.print("搜索已取消", OutputType.WARNING)
|
|
227
288
|
sys.exit(1)
|
|
228
289
|
except Exception as e:
|
|
229
|
-
PrettyOutput.print(f"
|
|
290
|
+
PrettyOutput.print(f"执行错误: {str(e)}", OutputType.ERROR)
|
|
230
291
|
sys.exit(1)
|
|
231
292
|
|
|
232
293
|
if __name__ == "__main__":
|
|
@@ -33,7 +33,7 @@ class CodeFileSelecterTool:
|
|
|
33
33
|
related_files = args.get("related_files", [])
|
|
34
34
|
root_dir = args.get("root_dir", ".").strip()
|
|
35
35
|
|
|
36
|
-
PrettyOutput.print("
|
|
36
|
+
PrettyOutput.print("开始交互式文件选择...", OutputType.INFO)
|
|
37
37
|
|
|
38
38
|
# Use file_select module to handle file selection
|
|
39
39
|
selected_files = select_files(
|
|
@@ -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
|
@@ -165,18 +165,61 @@ class PrettyOutput:
|
|
|
165
165
|
|
|
166
166
|
@staticmethod
|
|
167
167
|
def print(text: str, output_type: OutputType, timestamp: bool = True, lang: Optional[str] = None, traceback: bool = False):
|
|
168
|
-
"""Print formatted output using rich console
|
|
168
|
+
"""Print formatted output using rich console with styling
|
|
169
|
+
|
|
170
|
+
Args:
|
|
171
|
+
text: The text content to print
|
|
172
|
+
output_type: The type of output (affects styling)
|
|
173
|
+
timestamp: Whether to show timestamp
|
|
174
|
+
lang: Language for syntax highlighting
|
|
175
|
+
traceback: Whether to show traceback for errors
|
|
176
|
+
"""
|
|
177
|
+
from rich.style import Style as RichStyle
|
|
178
|
+
|
|
179
|
+
# Define styles for different output types
|
|
180
|
+
styles = {
|
|
181
|
+
OutputType.SYSTEM: RichStyle(color="cyan", bold=True),
|
|
182
|
+
OutputType.CODE: RichStyle(color="green"),
|
|
183
|
+
OutputType.RESULT: RichStyle(color="blue"),
|
|
184
|
+
OutputType.ERROR: RichStyle(color="red", bold=True),
|
|
185
|
+
OutputType.INFO: RichStyle(color="yellow"),
|
|
186
|
+
OutputType.PLANNING: RichStyle(color="magenta"),
|
|
187
|
+
OutputType.PROGRESS: RichStyle(color="white"),
|
|
188
|
+
OutputType.SUCCESS: RichStyle(color="green", bold=True),
|
|
189
|
+
OutputType.WARNING: RichStyle(color="yellow", bold=True),
|
|
190
|
+
OutputType.DEBUG: RichStyle(color="blue", dim=True),
|
|
191
|
+
OutputType.USER: RichStyle(color="green"),
|
|
192
|
+
OutputType.TOOL: RichStyle(color="yellow", italic=True)
|
|
193
|
+
}
|
|
194
|
+
|
|
169
195
|
# Get formatted header
|
|
170
196
|
lang = lang if lang is not None else PrettyOutput._detect_language(text, default_lang='markdown')
|
|
171
197
|
header = PrettyOutput._format("", output_type, timestamp)
|
|
172
|
-
|
|
173
|
-
content = Syntax(text, lang, theme="monokai")
|
|
174
|
-
|
|
175
|
-
# Print panel with appropriate border style
|
|
176
|
-
border_style = "red" if output_type == OutputType.ERROR else output_type.value
|
|
177
|
-
console.print(Panel(content, border_style=border_style, title=header, title_align="left", highlight=True))
|
|
178
198
|
|
|
179
|
-
#
|
|
199
|
+
# Create syntax highlighted content
|
|
200
|
+
content = Syntax(
|
|
201
|
+
text,
|
|
202
|
+
lang,
|
|
203
|
+
theme="monokai",
|
|
204
|
+
word_wrap=True,
|
|
205
|
+
background_color="default"
|
|
206
|
+
)
|
|
207
|
+
|
|
208
|
+
# Create panel with styling
|
|
209
|
+
panel = Panel(
|
|
210
|
+
content,
|
|
211
|
+
style=styles[output_type],
|
|
212
|
+
border_style=styles[output_type],
|
|
213
|
+
title=header,
|
|
214
|
+
title_align="left",
|
|
215
|
+
padding=(1, 2),
|
|
216
|
+
highlight=True
|
|
217
|
+
)
|
|
218
|
+
|
|
219
|
+
# Print panel
|
|
220
|
+
console.print(panel)
|
|
221
|
+
|
|
222
|
+
# Print stack trace for errors if requested
|
|
180
223
|
if traceback or output_type == OutputType.ERROR:
|
|
181
224
|
console.print_exception()
|
|
182
225
|
|
|
@@ -322,7 +365,7 @@ def get_multiline_input(tip: str) -> str:
|
|
|
322
365
|
lines.append(line)
|
|
323
366
|
|
|
324
367
|
except KeyboardInterrupt:
|
|
325
|
-
PrettyOutput.print("
|
|
368
|
+
PrettyOutput.print("输入已取消", OutputType.INFO)
|
|
326
369
|
return ""
|
|
327
370
|
|
|
328
371
|
return "\n".join(lines)
|
|
@@ -348,7 +391,7 @@ def init_env():
|
|
|
348
391
|
except ValueError:
|
|
349
392
|
continue
|
|
350
393
|
except Exception as e:
|
|
351
|
-
PrettyOutput.print(f"
|
|
394
|
+
PrettyOutput.print(f"警告: 读取 {env_file} 失败: {e}", OutputType.WARNING)
|
|
352
395
|
|
|
353
396
|
|
|
354
397
|
def while_success(func, sleep_time: float = 0.1):
|
|
@@ -356,7 +399,7 @@ def while_success(func, sleep_time: float = 0.1):
|
|
|
356
399
|
try:
|
|
357
400
|
return func()
|
|
358
401
|
except Exception as e:
|
|
359
|
-
PrettyOutput.print(f"
|
|
402
|
+
PrettyOutput.print(f"执行失败: {str(e)}, 等待 {sleep_time}s...", OutputType.ERROR)
|
|
360
403
|
time.sleep(sleep_time)
|
|
361
404
|
continue
|
|
362
405
|
|
|
@@ -366,7 +409,7 @@ def while_true(func, sleep_time: float = 0.1):
|
|
|
366
409
|
ret = func()
|
|
367
410
|
if ret:
|
|
368
411
|
break
|
|
369
|
-
PrettyOutput.print(f"
|
|
412
|
+
PrettyOutput.print(f"执行失败, 等待 {sleep_time}s...", OutputType.WARNING)
|
|
370
413
|
time.sleep(sleep_time)
|
|
371
414
|
return ret
|
|
372
415
|
|
|
@@ -433,7 +476,7 @@ def load_rerank_model():
|
|
|
433
476
|
model_name = "BAAI/bge-reranker-v2-m3"
|
|
434
477
|
cache_dir = os.path.expanduser("~/.cache/huggingface/hub")
|
|
435
478
|
|
|
436
|
-
PrettyOutput.print(f"
|
|
479
|
+
PrettyOutput.print(f"加载重排序模型: {model_name}...", OutputType.INFO)
|
|
437
480
|
|
|
438
481
|
try:
|
|
439
482
|
# Load model and tokenizer
|
|
@@ -484,7 +527,7 @@ def is_long_context(files: list) -> bool:
|
|
|
484
527
|
if total_tokens > threshold:
|
|
485
528
|
return True
|
|
486
529
|
except Exception as e:
|
|
487
|
-
PrettyOutput.print(f"
|
|
530
|
+
PrettyOutput.print(f"读取文件 {file_path} 失败: {e}", OutputType.WARNING)
|
|
488
531
|
continue
|
|
489
532
|
|
|
490
533
|
return total_tokens > threshold
|
|
@@ -511,13 +554,13 @@ def _create_methodology_embedding(embedding_model: Any, methodology_text: str) -
|
|
|
511
554
|
vector = np.array(embedding.cpu().numpy(), dtype=np.float32)
|
|
512
555
|
return vector[0] # Return first vector, because we only encoded one text
|
|
513
556
|
except Exception as e:
|
|
514
|
-
PrettyOutput.print(f"
|
|
557
|
+
PrettyOutput.print(f"创建方法论嵌入向量失败: {str(e)}", OutputType.ERROR)
|
|
515
558
|
return np.zeros(1536, dtype=np.float32)
|
|
516
559
|
|
|
517
560
|
|
|
518
561
|
def load_methodology(user_input: str) -> str:
|
|
519
562
|
"""Load methodology and build vector index"""
|
|
520
|
-
PrettyOutput.print("
|
|
563
|
+
PrettyOutput.print("加载方法论...", OutputType.PROGRESS)
|
|
521
564
|
user_jarvis_methodology = os.path.expanduser("~/.jarvis/methodology")
|
|
522
565
|
if not os.path.exists(user_jarvis_methodology):
|
|
523
566
|
return ""
|
|
@@ -565,7 +608,7 @@ def load_methodology(user_input: str) -> str:
|
|
|
565
608
|
methodology_index.add_with_ids(vectors_array, np.array(ids)) # type: ignore
|
|
566
609
|
query_embedding = _create_methodology_embedding(embedding_model, user_input)
|
|
567
610
|
k = min(3, len(methodology_data))
|
|
568
|
-
PrettyOutput.print(f"
|
|
611
|
+
PrettyOutput.print(f"检索方法论...", OutputType.INFO)
|
|
569
612
|
distances, indices = methodology_index.search(
|
|
570
613
|
query_embedding.reshape(1, -1), k
|
|
571
614
|
) # type: ignore
|
|
@@ -590,9 +633,7 @@ def load_methodology(user_input: str) -> str:
|
|
|
590
633
|
return make_methodology_prompt(data)
|
|
591
634
|
|
|
592
635
|
except Exception as e:
|
|
593
|
-
PrettyOutput.print(f"
|
|
594
|
-
import traceback
|
|
595
|
-
PrettyOutput.print(f"Error trace: {traceback.format_exc()}", OutputType.INFO)
|
|
636
|
+
PrettyOutput.print(f"加载方法论失败: {str(e)}", OutputType.ERROR)
|
|
596
637
|
return ""
|
|
597
638
|
|
|
598
639
|
|
|
@@ -648,15 +689,15 @@ def init_gpu_config() -> Dict:
|
|
|
648
689
|
torch.cuda.empty_cache()
|
|
649
690
|
|
|
650
691
|
PrettyOutput.print(
|
|
651
|
-
f"GPU
|
|
652
|
-
f"
|
|
653
|
-
f"
|
|
692
|
+
f"GPU已初始化: {torch.cuda.get_device_name(0)}\n"
|
|
693
|
+
f"设备内存: {gpu_mem / 1024**3:.1f}GB\n"
|
|
694
|
+
f"共享内存: {config['shared_memory'] / 1024**3:.1f}GB",
|
|
654
695
|
output_type=OutputType.SUCCESS
|
|
655
696
|
)
|
|
656
697
|
else:
|
|
657
|
-
PrettyOutput.print("
|
|
698
|
+
PrettyOutput.print("没有GPU可用, 使用CPU模式", output_type=OutputType.WARNING)
|
|
658
699
|
except Exception as e:
|
|
659
|
-
PrettyOutput.print(f"GPU
|
|
700
|
+
PrettyOutput.print(f"GPU初始化失败: {str(e)}", output_type=OutputType.WARNING)
|
|
660
701
|
|
|
661
702
|
return config
|
|
662
703
|
|
|
@@ -677,7 +718,7 @@ def get_embedding_batch(embedding_model: Any, texts: List[str]) -> np.ndarray:
|
|
|
677
718
|
all_vectors.extend(vectors)
|
|
678
719
|
return np.vstack(all_vectors)
|
|
679
720
|
except Exception as e:
|
|
680
|
-
PrettyOutput.print(f"
|
|
721
|
+
PrettyOutput.print(f"批量嵌入失败: {str(e)}", OutputType.ERROR)
|
|
681
722
|
return np.zeros((0, embedding_model.get_sentence_embedding_dimension()), dtype=np.float32)
|
|
682
723
|
|
|
683
724
|
|
|
@@ -782,11 +823,11 @@ def get_context_token_count(text: str) -> int:
|
|
|
782
823
|
try:
|
|
783
824
|
# Use a fast tokenizer that's good at general text
|
|
784
825
|
tokenizer = load_tokenizer()
|
|
785
|
-
chunks = split_text_into_chunks(text,
|
|
826
|
+
chunks = split_text_into_chunks(text, 512)
|
|
786
827
|
return sum([len(tokenizer.encode(chunk)) for chunk in chunks])
|
|
787
828
|
|
|
788
829
|
except Exception as e:
|
|
789
|
-
PrettyOutput.print(f"
|
|
830
|
+
PrettyOutput.print(f"计算token失败: {str(e)}", OutputType.WARNING)
|
|
790
831
|
# Fallback to rough character-based estimate
|
|
791
832
|
return len(text) // 4 # Rough estimate of 4 chars per token
|
|
792
833
|
|