jarvis-ai-assistant 0.1.3__py3-none-any.whl → 0.1.4__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 CHANGED
@@ -1,3 +1,3 @@
1
1
  """Jarvis AI Assistant"""
2
2
 
3
- __version__ = "0.1.3"
3
+ __version__ = "0.1.4"
jarvis/agent.py CHANGED
@@ -3,21 +3,32 @@ import subprocess
3
3
  from typing import Dict, Any, List, Optional
4
4
  from .tools import ToolRegistry
5
5
  from .utils import Spinner, PrettyOutput, OutputType, get_multiline_input
6
- from .models import BaseModel, OllamaModel
6
+ from .models import BaseModel
7
7
  import re
8
8
  import os
9
9
  from datetime import datetime
10
10
 
11
11
  class Agent:
12
- def __init__(self, model: BaseModel, tool_registry: ToolRegistry):
12
+ def __init__(self, model: BaseModel, tool_registry: Optional[ToolRegistry] = None, name: str = "Jarvis"):
13
+ """Initialize Agent with a model, optional tool registry and name"""
13
14
  self.model = model
14
- self.tool_registry = tool_registry
15
+ self.tool_registry = tool_registry or ToolRegistry(model)
16
+ self.name = name
15
17
  # 编译正则表达式
16
18
  self.tool_call_pattern = re.compile(r'<tool_call>\s*({[^}]+})\s*</tool_call>')
17
19
  self.messages = [
18
20
  {
19
21
  "role": "system",
20
- "content": """You are a rigorous AI assistant, all data must be obtained through tools, and no fabrication or speculation is allowed. """ + "\n" + self.tool_registry.tool_help_text()
22
+ "content": f"""You are {name}, a rigorous AI assistant that executes tasks step by step.
23
+
24
+ Key Principles:
25
+ 1. Execute ONE step at a time
26
+ 2. Wait for each step's result before planning the next
27
+ 3. Use tools to obtain all data, no fabrication
28
+ 4. Create sub-agents for independent subtasks
29
+ 5. Think carefully before each action
30
+
31
+ """ + self.tool_registry.tool_help_text()
21
32
  }
22
33
  ]
23
34
  self.spinner = Spinner()
@@ -28,44 +39,38 @@ class Agent:
28
39
  try:
29
40
  return self.model.chat(
30
41
  messages=messages,
31
- tools=self.tool_registry.get_all_tools() if use_tools else None
42
+ tools=self.tool_registry.get_all_tools() if use_tools else []
32
43
  )
33
44
  except Exception as e:
34
- raise Exception(f"模型调用失败: {str(e)}")
45
+ raise Exception(f"{self.name}: 模型调用失败: {str(e)}")
35
46
  finally:
36
47
  self.spinner.stop()
37
48
 
38
-
39
- def run(self, user_input: str) :
40
- """处理用户输入并返回响应"""
41
- # 检查是否是结束命令
49
+ def run(self, user_input: str) -> str:
50
+ """处理用户输入并返回响应,返回任务总结报告"""
42
51
  self.clear_history()
43
52
  self.messages.append({
44
53
  "role": "user",
45
54
  "content": user_input
46
55
  })
56
+
47
57
  while True:
48
58
  try:
49
- # 获取初始响应
50
59
  response = self._call_model(self.messages)
51
60
  current_response = response
52
61
 
53
- # 将工具执行结果添加到对话
54
62
  self.messages.append({
55
63
  "role": "assistant",
56
64
  "content": response["message"].get("content", ""),
57
65
  "tool_calls": current_response["message"]["tool_calls"]
58
66
  })
59
67
 
60
- # 处理可能的多轮工具调用
61
68
  if len(current_response["message"]["tool_calls"]) > 0:
62
- # 添加当前助手响应到输出(如果有内容)
63
69
  if current_response["message"].get("content"):
64
- PrettyOutput.print(current_response["message"]["content"], OutputType.SYSTEM)
70
+ PrettyOutput.print(f"{self.name}: {current_response['message']['content']}", OutputType.SYSTEM)
65
71
 
66
- # 使用 ToolRegistry 的 handle_tool_calls 方法处理工具调用
67
72
  tool_result = self.tool_registry.handle_tool_calls(current_response["message"]["tool_calls"])
68
- PrettyOutput.print(tool_result, OutputType.RESULT)
73
+ PrettyOutput.print(f"{self.name} Tool Result: {tool_result}", OutputType.RESULT)
69
74
 
70
75
  self.messages.append({
71
76
  "role": "tool",
@@ -73,18 +78,46 @@ class Agent:
73
78
  })
74
79
  continue
75
80
 
76
- # 添加最终响应到对话历史和输出
77
81
  final_content = current_response["message"].get("content", "")
78
-
79
82
  if final_content:
80
- PrettyOutput.print(final_content, OutputType.SYSTEM)
81
-
83
+ PrettyOutput.print(f"{self.name}: {final_content}", OutputType.SYSTEM)
82
84
 
83
- # 如果没有工具调用且响应很短,可能需要继续对话
84
- user_input = get_multiline_input("您可以继续输入,或输入空行结束当前任务")
85
- if not user_input:
86
- PrettyOutput.print("===============任务结束===============", OutputType.INFO)
87
- break
85
+ user_input = get_multiline_input(f"{self.name}: 您可以继续输入,或输入空行结束当前任务")
86
+ if not user_input:
87
+ PrettyOutput.print(f"{self.name}: 正在生成任务总结...", OutputType.INFO)
88
+
89
+ # 请求模型生成任务总结
90
+ summary_prompt = {
91
+ "role": "user",
92
+ "content": """Please provide a concise task summary focusing on:
93
+
94
+ 1. Key Information:
95
+ - Important data points
96
+ - Critical findings
97
+ - Significant tool outputs
98
+
99
+ 2. Task Results:
100
+ - Final outcomes
101
+ - Task completion status
102
+ - Any important conclusions
103
+
104
+ Keep the summary focused and brief, highlighting only the most essential information."""
105
+ }
106
+
107
+ try:
108
+ summary_response = self._call_model(self.messages + [summary_prompt], use_tools=False)
109
+ summary = summary_response["message"].get("content", "")
110
+
111
+ PrettyOutput.print(f"==============={self.name} 任务总结===============", OutputType.INFO)
112
+ PrettyOutput.print(summary, OutputType.SYSTEM)
113
+ PrettyOutput.print("=" * (len(self.name) + 16), OutputType.INFO)
114
+
115
+ return summary
116
+
117
+ except Exception as e:
118
+ error_msg = f"{self.name}: 生成任务总结时出错: {str(e)}"
119
+ PrettyOutput.print(error_msg, OutputType.ERROR)
120
+ return "Task completed but summary generation failed."
88
121
 
89
122
  self.messages.append({
90
123
  "role": "user",
@@ -92,8 +125,9 @@ class Agent:
92
125
  })
93
126
 
94
127
  except Exception as e:
95
- error_msg = f"处理响应时出错: {str(e)}"
128
+ error_msg = f"{self.name}: 处理响应时出错: {str(e)}"
96
129
  PrettyOutput.print(error_msg, OutputType.ERROR)
130
+ return f"Task failed with error: {str(e)}"
97
131
 
98
132
  def clear_history(self):
99
133
  """清除对话历史,只保留系统提示"""
jarvis/main.py CHANGED
@@ -13,7 +13,8 @@ sys.path.insert(0, str(Path(__file__).parent.parent))
13
13
  from jarvis.agent import Agent
14
14
  from jarvis.tools import ToolRegistry
15
15
  from jarvis.models import DDGSModel, OllamaModel
16
- from jarvis.utils import PrettyOutput, OutputType, get_multiline_input
16
+ from jarvis.utils import PrettyOutput, OutputType, get_multiline_input, load_env_from_file
17
+ from jarvis.zte_llm import create_zte_llm
17
18
 
18
19
  # 定义支持的平台和模型
19
20
  SUPPORTED_PLATFORMS = {
@@ -24,6 +25,10 @@ SUPPORTED_PLATFORMS = {
24
25
  "ddgs": {
25
26
  "models": ["gpt-4o-mini", "claude-3-haiku", "llama-3.1-70b", "mixtral-8x7b"],
26
27
  "default": "gpt-4o-mini"
28
+ },
29
+ "zte": {
30
+ "models": ["NebulaBiz", "nebulacoder", "NTele-72B"],
31
+ "default": "NebulaBiz"
27
32
  }
28
33
  }
29
34
 
@@ -97,6 +102,8 @@ def main():
97
102
  )
98
103
 
99
104
  args = parser.parse_args()
105
+
106
+ load_env_from_file()
100
107
 
101
108
  # 验证并设置默认模型
102
109
  if args.model:
@@ -119,9 +126,12 @@ def main():
119
126
  api_base=args.api_base
120
127
  )
121
128
  platform_name = f"Ollama ({args.model})"
122
- else: # ddgs
129
+ elif args.platform == "ddgs": # ddgs
123
130
  model = DDGSModel(model_name=args.model)
124
131
  platform_name = f"DuckDuckGo Search ({args.model})"
132
+ elif args.platform == "zte": # zte
133
+ model = create_zte_llm(model_name=args.model)
134
+ platform_name = f"ZTE ({args.model})"
125
135
 
126
136
  tool_registry = ToolRegistry()
127
137
  agent = Agent(model, tool_registry)
jarvis/tools/__init__.py CHANGED
@@ -5,7 +5,6 @@ from .search import SearchTool
5
5
  from .shell import ShellTool
6
6
  from .user_interaction import UserInteractionTool
7
7
  from .user_confirmation import UserConfirmationTool
8
- from .rag import RAGTool
9
8
  from .webpage import WebpageTool
10
9
 
11
10
  __all__ = [
jarvis/tools/base.py CHANGED
@@ -1,6 +1,7 @@
1
1
  from typing import Dict, Any, List, Optional, Callable
2
2
  import json
3
3
  from ..utils import PrettyOutput, OutputType
4
+ from ..models import BaseModel
4
5
 
5
6
  class Tool:
6
7
  def __init__(self, name: str, description: str, parameters: Dict, func: Callable):
@@ -25,8 +26,9 @@ class Tool:
25
26
  return self.func(arguments)
26
27
 
27
28
  class ToolRegistry:
28
- def __init__(self):
29
+ def __init__(self, model: Optional[BaseModel] = None):
29
30
  self.tools: Dict[str, Tool] = {}
31
+ self.model = model
30
32
  self._register_default_tools()
31
33
 
32
34
  def _register_default_tools(self):
@@ -37,8 +39,8 @@ class ToolRegistry:
37
39
  from .user_confirmation import UserConfirmationTool
38
40
  from .python_script import PythonScriptTool
39
41
  from .file_ops import FileOperationTool
40
- from .rag import RAGTool
41
42
  from .webpage import WebpageTool
43
+ from .sub_agent import SubAgentTool
42
44
 
43
45
  tools = [
44
46
  SearchTool(),
@@ -47,7 +49,6 @@ class ToolRegistry:
47
49
  UserConfirmationTool(),
48
50
  PythonScriptTool(),
49
51
  FileOperationTool(),
50
- RAGTool(),
51
52
  WebpageTool(),
52
53
  ]
53
54
 
@@ -59,6 +60,14 @@ class ToolRegistry:
59
60
  func=tool.execute
60
61
  )
61
62
 
63
+ sub_agent_tool = SubAgentTool(self.model)
64
+ self.register_tool(
65
+ name=sub_agent_tool.name,
66
+ description=sub_agent_tool.description,
67
+ parameters=sub_agent_tool.parameters,
68
+ func=sub_agent_tool.execute
69
+ )
70
+
62
71
  def register_tool(self, name: str, description: str, parameters: Dict, func: Callable):
63
72
  """注册新工具"""
64
73
  self.tools[name] = Tool(name, description, parameters, func)
@@ -125,36 +134,30 @@ class ToolRegistry:
125
134
  5. ask_user: Get input from user with options support
126
135
  6. ask_user_confirmation: Get yes/no confirmation from user
127
136
  7. file_operation: Read/write files in workspace directory
128
- 8. rag_query: Query documents using RAG
129
-
130
- Output Guidelines:
131
- 1. Focus on Essential Information
132
- - Filter out irrelevant or redundant information
133
- - Only output the most relevant search results
134
- - Summarize long content when possible
135
-
136
- 2. Context Management
137
- - Avoid repeating information that's already known
138
- - Don't echo back the entire content of files
139
- - Use snippets instead of full documents
140
-
141
- 3. Error Handling
142
- - Only show relevant parts of error messages
143
- - Include troubleshooting steps when appropriate
144
- - Omit stack traces unless specifically requested
145
-
146
- 4. Search Results
147
- - Focus on the most relevant 2-3 results
148
- - Extract key information instead of full articles
149
- - Skip duplicate or similar content
150
-
151
- 5. RAG Queries
152
- - Return only the most relevant passages
153
- - Combine similar information
154
- - Skip redundant context
155
-
156
- Tool Call Format Requirements:
157
- 1. MUST use exact format:
137
+ 8. create_sub_agent: Create a sub-agent for independent tasks (RECOMMENDED for subtasks)
138
+
139
+ Core Rules:
140
+ 1. ONE Step at a Time
141
+ - Execute only one action per response
142
+ - Explain what you're going to do before doing it
143
+ - Wait for the result before planning next step
144
+ - No multiple actions or parallel execution
145
+
146
+ 2. Sub-Agent Usage
147
+ - Use create_sub_agent for independent subtasks
148
+ - Let sub-agents handle complex task sequences
149
+ - Examples of good sub-agent tasks:
150
+ * Code analysis and documentation
151
+ * File system operations
152
+ * Data processing
153
+ * Research tasks
154
+
155
+ 3. Output Guidelines
156
+ - Focus on essential information
157
+ - Clear step-by-step explanations
158
+ - Summarize results concisely
159
+
160
+ Tool Call Format:
158
161
  <tool_call>
159
162
  {
160
163
  "name": "tool_name",
@@ -164,48 +167,26 @@ Tool Call Format Requirements:
164
167
  }
165
168
  </tool_call>
166
169
 
167
- 2. Format Rules:
168
- - NO comments or explanations inside tool_call blocks
169
- - NO additional text or formatting within JSON
170
- - NO markdown or other markup inside tool_call
171
- - ONLY valid JSON allowed in arguments
172
- - ALL arguments must match tool parameters exactly
173
-
174
- 3. Invalid Examples (DO NOT USE):
175
- <tool_call>
176
- {
177
- "name": "search", # Search tool
178
- "arguments": {
179
- "query": "Python GIL" // Search query
180
- }
181
- }
182
- </tool_call>
183
-
184
- ❌ <tool_call>
185
- ```json
186
- {
187
- "name": "search",
188
- "arguments": {
189
- "query": "Python GIL"
190
- }
191
- }
192
- ```
193
- </tool_call>
194
-
195
- 4. Valid Example:
196
- ✅ <tool_call>
197
- {
198
- "name": "search",
199
- "arguments": {
200
- "query": "Python GIL",
201
- "max_results": 2
202
- }
203
- }
204
- </tool_call>
170
+ Format Rules:
171
+ 1. ONE tool call per response
172
+ 2. Valid JSON only in arguments
173
+ 3. No comments or extra formatting
174
+ 4. Match parameters exactly
175
+
176
+ Example (Correct - Single Step):
177
+ Assistant: I'll start by searching for the documentation.
178
+ <tool_call>
179
+ {
180
+ "name": "search",
181
+ "arguments": {
182
+ "query": "Python GIL documentation",
183
+ "max_results": 2
184
+ }
185
+ }
186
+ </tool_call>
205
187
 
206
188
  Remember:
207
- 1. Quality over quantity
208
- 2. Relevance over completeness
209
- 3. Conciseness over verbosity
210
- 4. Context efficiency is crucial
211
- 5. STRICT adherence to tool call format"""
189
+ 1. ONE step at a time
190
+ 2. Explain before acting
191
+ 3. Wait for results
192
+ 4. Use sub-agents for complex tasks"""
@@ -0,0 +1,79 @@
1
+ from typing import Dict, Any
2
+ from ..agent import Agent
3
+ from ..models import BaseModel
4
+ from ..utils import PrettyOutput, OutputType
5
+ from .base import ToolRegistry
6
+
7
+ class SubAgentTool:
8
+ name = "create_sub_agent"
9
+ description = """Create a sub-agent to handle independent tasks.
10
+
11
+ Use this tool when:
12
+ 1. A subtask can be executed independently
13
+ 2. The task requires separate context management
14
+ 3. To optimize token usage of the main agent
15
+ 4. For parallel task processing
16
+
17
+ The sub-agent will:
18
+ 1. Inherit all tools from the parent agent
19
+ 2. Maintain its own conversation history
20
+ 3. Return a comprehensive task summary
21
+ """
22
+ parameters = {
23
+ "type": "object",
24
+ "properties": {
25
+ "name": {
26
+ "type": "string",
27
+ "description": "Name for the sub-agent (e.g., 'FileAnalyzer', 'CodeReviewer')"
28
+ },
29
+ "task": {
30
+ "type": "string",
31
+ "description": "Task description with complete context"
32
+ },
33
+ "context": {
34
+ "type": "string",
35
+ "description": "Additional context or background information",
36
+ "default": ""
37
+ }
38
+ },
39
+ "required": ["name", "task"]
40
+ }
41
+
42
+ def __init__(self, model: BaseModel):
43
+ """Initialize with the same model as parent agent"""
44
+ self.model = model
45
+
46
+ def execute(self, args: Dict) -> Dict[str, Any]:
47
+ """Create and run a sub-agent for the specified task"""
48
+ try:
49
+ name = args["name"]
50
+ task = args["task"]
51
+ context = args.get("context", "")
52
+
53
+ PrettyOutput.print(f"Creating sub-agent '{name}'...", OutputType.INFO)
54
+
55
+ # Create a new tool registry for the sub-agent
56
+ tool_registry = ToolRegistry()
57
+
58
+ # Create the sub-agent with the specified name
59
+ sub_agent = Agent(self.model, tool_registry, name=name)
60
+
61
+ # Prepare the task with context if provided
62
+ full_task = f"{context}\n\nTask: {task}" if context else task
63
+
64
+ PrettyOutput.print(f"Sub-agent '{name}' executing task...", OutputType.INFO)
65
+
66
+ # Execute the task and get the summary
67
+ summary = sub_agent.run(full_task)
68
+
69
+ return {
70
+ "success": True,
71
+ "stdout": f"Sub-agent '{name}' completed the task.\n\nSummary:\n{summary}",
72
+ "stderr": ""
73
+ }
74
+
75
+ except Exception as e:
76
+ return {
77
+ "success": False,
78
+ "error": f"Sub-agent execution failed: {str(e)}"
79
+ }
jarvis/utils.py CHANGED
@@ -1,11 +1,13 @@
1
+ from pathlib import Path
1
2
  import sys
2
3
  import time
3
4
  import threading
4
- from typing import Optional
5
+ from typing import Dict, Optional
5
6
  from enum import Enum
6
7
  from datetime import datetime
7
8
  import colorama
8
9
  from colorama import Fore, Style
10
+ import os
9
11
 
10
12
  # 初始化colorama
11
13
  colorama.init()
@@ -102,4 +104,25 @@ def get_multiline_input(tip: str) -> str:
102
104
  PrettyOutput.print("\n输入已取消", OutputType.ERROR)
103
105
  return ""
104
106
 
105
- return "\n".join(lines).strip()
107
+ return "\n".join(lines).strip()
108
+
109
+
110
+ def load_env_from_file():
111
+ """Load environment variables from ~/.jarvis_env file"""
112
+ env_file = Path.home() / ".jarvis_env"
113
+
114
+ if env_file.exists():
115
+ try:
116
+ with open(env_file, "r", encoding="utf-8") as f:
117
+ for line in f:
118
+ line = line.strip()
119
+ if line and not line.startswith("#"):
120
+ try:
121
+ key, value = line.split("=", 1)
122
+ os.environ[key.strip()] = value.strip().strip("'").strip('"')
123
+ except ValueError:
124
+ continue
125
+ except Exception as e:
126
+ print(f"Warning: Failed to read ~/.jarvis_env: {e}")
127
+
128
+
jarvis/zte_llm.py ADDED
@@ -0,0 +1,132 @@
1
+ import requests
2
+ import json
3
+ import os
4
+ from pathlib import Path
5
+ from typing import Dict, Any, List, Optional
6
+ from .models import BaseModel
7
+
8
+ class ZteLLM(BaseModel):
9
+ """ZTE Nebula LLM implementation"""
10
+
11
+ def __init__(self,
12
+ app_id: str,
13
+ app_key: str,
14
+ emp_no: str,
15
+ auth_value: str,
16
+ model: str = "nebulacoder",
17
+ ):
18
+ """Initialize ZTE LLM with required credentials"""
19
+ self.app_id = str(app_id)
20
+ self.app_key = str(app_key)
21
+ self.emp_no = str(emp_no)
22
+ self.auth_value = str(auth_value)
23
+ self.model = model
24
+ self.base_url = "https://studio.zte.com.cn/zte-studio-ai-platform/openapi/v1"
25
+
26
+ def _make_request(self, endpoint: str, data: Dict[str, Any]) -> Dict[str, Any]:
27
+ """Make request to ZTE API"""
28
+ headers = {
29
+ 'Content-Type': 'application/json',
30
+ 'Authorization': f'Bearer {self.app_id}-{self.app_key}',
31
+ 'X-Emp-No': self.emp_no,
32
+ 'X-Auth-Value': self.auth_value
33
+ }
34
+
35
+ response = requests.post(
36
+ f"{self.base_url}/{endpoint}",
37
+ headers=headers,
38
+ json=data
39
+ )
40
+
41
+ response.raise_for_status()
42
+ result = response.json()
43
+
44
+ if result["code"]["code"] != "0000":
45
+ raise Exception(f"API Error: {result['code']['msg']}")
46
+
47
+ return result["bo"]
48
+
49
+ def chat(self, messages: List[Dict[str, Any]], tools: Optional[List[Dict]] = None) -> Dict[str, Any]:
50
+ """Chat with ZTE LLM"""
51
+ # Convert messages to prompt
52
+ prompt = self._convert_messages_to_prompt(messages)
53
+
54
+ # Prepare data for API call
55
+ data = {
56
+ "chatUuid": "",
57
+ "chatName": "",
58
+ "stream": False,
59
+ "keep": False,
60
+ "text": prompt,
61
+ "model": self.model
62
+ }
63
+
64
+ # If tools are provided, add them to the prompt
65
+ if tools:
66
+ tools_desc = "Available tools:\n\n" + json.dumps(tools, indent=2, ensure_ascii=False)
67
+ data["text"] = tools_desc + "\n\n" + data["text"]
68
+
69
+ try:
70
+ result = self._make_request("chat", data)
71
+
72
+ # Parse the response to extract potential tool calls
73
+ response_text = result["result"]
74
+ tool_calls = BaseModel.extract_tool_calls(response_text)
75
+
76
+ return {
77
+ "message": {
78
+ "content": response_text,
79
+ "tool_calls": tool_calls
80
+ }
81
+ }
82
+
83
+ except Exception as e:
84
+ raise Exception(f"ZTE LLM chat failed: {str(e)}")
85
+
86
+ def _convert_messages_to_prompt(self, messages: List[Dict[str, Any]]) -> str:
87
+ """Convert message list to a single prompt string"""
88
+ prompt_parts = []
89
+
90
+ for message in messages:
91
+ role = message["role"]
92
+ content = message.get("content", "")
93
+
94
+ if role == "system":
95
+ prompt_parts.append(f"System: {content}")
96
+ elif role == "user":
97
+ prompt_parts.append(f"User: {content}")
98
+ elif role == "assistant":
99
+ prompt_parts.append(f"Assistant: {content}")
100
+ elif role == "tool":
101
+ prompt_parts.append(f"Tool Result: {content}")
102
+
103
+ return "\n\n".join(prompt_parts)
104
+
105
+
106
+ def create_zte_llm(model_name: str = "NebulaBiz") -> ZteLLM:
107
+ """Create ZTE LLM instance with provided parameters"""
108
+ # Load environment variables from file
109
+
110
+ # Get credentials from parameters, env file, or system environment variables
111
+ app_id = os.getenv('ZTE_APP_ID')
112
+ app_key = os.getenv('ZTE_APP_KEY')
113
+ emp_no = os.getenv('ZTE_EMP_NO')
114
+ auth_value = os.getenv('ZTE_AUTH_VALUE')
115
+
116
+ # Validate required credentials
117
+ if not all([app_id, app_key, emp_no, auth_value]):
118
+ raise ValueError(
119
+ "Missing required credentials. Please provide through either:\n"
120
+ "1. Function parameters\n"
121
+ "2. ~/.jarvis_env file\n"
122
+ "3. System environment variables\n\n"
123
+ "Required variables: ZTE_APP_ID, ZTE_APP_KEY, ZTE_EMP_NO, ZTE_AUTH_VALUE"
124
+ )
125
+
126
+ return ZteLLM(
127
+ app_id=app_id,
128
+ app_key=app_key,
129
+ emp_no=emp_no,
130
+ auth_value=auth_value,
131
+ model=model_name
132
+ )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: jarvis-ai-assistant
3
- Version: 0.1.3
3
+ Version: 0.1.4
4
4
  Summary: Jarvis: An AI assistant that uses tools to interact with the system
5
5
  Home-page: https://github.com/skyfireitdiy/Jarvis
6
6
  Author: skyfire
@@ -21,8 +21,7 @@ Requires-Dist: beautifulsoup4>=4.9.3
21
21
  Requires-Dist: duckduckgo-search>=3.0.0
22
22
  Requires-Dist: pyyaml>=5.1
23
23
  Requires-Dist: ollama>=0.1.6
24
- Requires-Dist: sentence-transformers>=2.5.1
25
- Requires-Dist: chromadb>=0.4.24
24
+ Requires-Dist: colorama>=0.4.6
26
25
  Provides-Extra: dev
27
26
  Requires-Dist: pytest; extra == "dev"
28
27
  Requires-Dist: black; extra == "dev"
@@ -1,21 +1,22 @@
1
1
  jarvis/.jarvis,sha256=S4ZMmLqlVLHAPmnwHFQ3vYt0gnDFp-KQRH4okBh8Hpw,209
2
- jarvis/__init__.py,sha256=rLUKzHgrbk82WAMvg4u4GlFDRal1Yw3XpMg_741mQXw,49
3
- jarvis/agent.py,sha256=hEatrIothG6C_RuOsygZz4ez1aF1EycnUPwY2Krwiwk,4092
4
- jarvis/main.py,sha256=wAeeGoRq8fFiTWUCQ31MYVeU9o6SZ48ORUow0jlMJJE,5259
2
+ jarvis/__init__.py,sha256=cIUjE04zhBHLfqRFdRHAeTF1YjKzNHQ6iFpTwPWsrA0,49
3
+ jarvis/agent.py,sha256=2KUmbWDqcyCEC5QgN1YXCliJvO6HGxfQBzLz1IPgG9Y,5538
4
+ jarvis/main.py,sha256=LFymNA5xFzB48IlSzWYSkrhNrUq0fXAukVfu0cV-aQQ,5635
5
5
  jarvis/models.py,sha256=ZQAyc39e_UsNmHkME6ATp6053safQ7Gog-oWwETsrMM,4415
6
- jarvis/utils.py,sha256=-fOPAgiKVPC6DevHM7A7IJvvIzvvFlGM0pHBKyBXDcA,3044
6
+ jarvis/utils.py,sha256=-5tugkGABZdi2QXVStVtCQfag9CRTfCsGUvReitmHUM,3816
7
+ jarvis/zte_llm.py,sha256=ygUeGd8WGm0qrnkt5TrCLnaf5ZFZ0tdfwrA7nrByt4g,4504
7
8
  jarvis/__pycache__/__init__.cpython-313.pyc,sha256=u64PLu4KyOPrirhIqNXZSkj9B6yB1L_ohFYhPX8p334,208
8
9
  jarvis/__pycache__/agent.cpython-313.pyc,sha256=I1JYUKC-zedURwuxQQjd9saj_yQKUhpdbb1KNWlY6Ss,4940
9
10
  jarvis/__pycache__/models.cpython-313.pyc,sha256=fot2VOV0lgODg4b_WuD-kkq-g7uZGLuZlYXiljlW13U,6350
10
11
  jarvis/__pycache__/tools.cpython-313.pyc,sha256=lAD4LrnnWzNZQmHXGfZ_2l7oskOpr2_2OC-gdFhxQY8,33933
11
12
  jarvis/__pycache__/utils.cpython-313.pyc,sha256=o8Fubq8Dcrvv3ATzQ9BnZoZjQmU3_FNPrfTt0ANdKdI,5804
12
- jarvis/tools/__init__.py,sha256=FNF5X32UzKvlT08-hPz7-7vcIyqqMngQYj36LXyNbV0,553
13
- jarvis/tools/base.py,sha256=Y1XInc2skUVZYZzLRPlCpH3JyY3pk1XNZgys6MSCudQ,6586
13
+ jarvis/tools/__init__.py,sha256=FPXTV4QQy5KrUFyhoMW1H9lBlIhTHAAyT0P-QxTBnuY,528
14
+ jarvis/tools/base.py,sha256=iiA2M6IeAG6edVSTvXDBevlQAq6yMrkUINujNW6lg2s,6274
14
15
  jarvis/tools/file_ops.py,sha256=05Vc4u-NGMjLD15WI52eL_nt_RyYt-Z_cXJnnBepf-c,3781
15
16
  jarvis/tools/python_script.py,sha256=_eK8LqNs-Mz50zdcgwbjdd8-qAeOl6kJ_qRDvWawGMw,5006
16
- jarvis/tools/rag.py,sha256=eBrn1fdqy-nd2nR0b4oH1EQpthTYVen-sYDhC5Ypl38,5647
17
17
  jarvis/tools/search.py,sha256=dyJmeP_s2tWv5KQejOl-TuVF12B6SXViiXEyTemD1Wo,1412
18
18
  jarvis/tools/shell.py,sha256=VhEqEdoUGIRW6CT6F0YNMAlzJxpHcsKS9hSS0VC3ezw,2644
19
+ jarvis/tools/sub_agent.py,sha256=I_OmSq1EIcQO0u8zCDqj4hMtsSx4NHiBNTcu9UOxbvQ,2673
19
20
  jarvis/tools/user_confirmation.py,sha256=p0JverifHJfnALeIhtKtaVBBVEGkgSpUzT-PSycG4NY,1850
20
21
  jarvis/tools/user_interaction.py,sha256=xOaoYsCKTa9-K3mg5tsu1zegn1HKcO40k2aJ3VygZqM,3073
21
22
  jarvis/tools/webpage.py,sha256=UTEomu5j7jbOw8c5az2jsjv5E7LeokWKj1QahvZO7xc,3077
@@ -29,8 +30,8 @@ jarvis/tools/__pycache__/shell.cpython-313.pyc,sha256=Xtqt-LzMRfsCXeCE7QxFY8TR8X
29
30
  jarvis/tools/__pycache__/user_confirmation.cpython-313.pyc,sha256=wK3Ev10lHSUSRvoYmi7A0GzxYkzU-C4Wfhs5qW_HBqs,2271
30
31
  jarvis/tools/__pycache__/user_interaction.cpython-313.pyc,sha256=RuVZ-pmiPBDywY3efgXSfohMAciC1avMGPmBK5qlnew,3305
31
32
  jarvis/tools/__pycache__/webpage.cpython-313.pyc,sha256=VcpkaV8IyOOtebedXjn8ybjr8AglU-3-Cs80yzE4FYo,3628
32
- jarvis_ai_assistant-0.1.3.dist-info/METADATA,sha256=eVKO4jecgKkHOIRUXZGvfvi5KiR9CGaZuQLhDR9W3NY,3675
33
- jarvis_ai_assistant-0.1.3.dist-info/WHEEL,sha256=A3WOREP4zgxI0fKrHUG8DC8013e3dK3n7a6HDbcEIwE,91
34
- jarvis_ai_assistant-0.1.3.dist-info/entry_points.txt,sha256=iKu7OMfew9dtfGhW71gIMTg4wvafuPqKb4wyQOnMAGU,44
35
- jarvis_ai_assistant-0.1.3.dist-info/top_level.txt,sha256=1BOxyWfzOP_ZXj8rVTDnNCJ92bBGB0rwq8N1PCpoMIs,7
36
- jarvis_ai_assistant-0.1.3.dist-info/RECORD,,
33
+ jarvis_ai_assistant-0.1.4.dist-info/METADATA,sha256=hiXlGsoga_MEvAZtKDEz0LmrWMUlV-ye8OBbHYQqwUc,3630
34
+ jarvis_ai_assistant-0.1.4.dist-info/WHEEL,sha256=A3WOREP4zgxI0fKrHUG8DC8013e3dK3n7a6HDbcEIwE,91
35
+ jarvis_ai_assistant-0.1.4.dist-info/entry_points.txt,sha256=iKu7OMfew9dtfGhW71gIMTg4wvafuPqKb4wyQOnMAGU,44
36
+ jarvis_ai_assistant-0.1.4.dist-info/top_level.txt,sha256=1BOxyWfzOP_ZXj8rVTDnNCJ92bBGB0rwq8N1PCpoMIs,7
37
+ jarvis_ai_assistant-0.1.4.dist-info/RECORD,,
jarvis/tools/rag.py DELETED
@@ -1,154 +0,0 @@
1
- from typing import Dict, Any, List
2
- import chromadb
3
- from chromadb.utils import embedding_functions
4
- import hashlib
5
- import re
6
- from ..utils import PrettyOutput, OutputType
7
-
8
- class RAGTool:
9
- name = "rag_query"
10
- description = """Execute RAG queries on documents.
11
- Features:
12
- 1. Auto-creates document embeddings
13
- 2. Returns relevant passages
14
- 3. Maintains embedding database
15
- """
16
- parameters = {
17
- "type": "object",
18
- "properties": {
19
- "files": {
20
- "type": "array",
21
- "items": {"type": "string"},
22
- "description": "Files to process"
23
- },
24
- "query": {
25
- "type": "string",
26
- "description": "Query text"
27
- },
28
- "num_passages": {
29
- "type": "integer",
30
- "description": "Number of passages",
31
- "default": 3
32
- },
33
- "chunk_size": {
34
- "type": "integer",
35
- "description": "Chunk size",
36
- "default": 500
37
- }
38
- },
39
- "required": ["files", "query"]
40
- }
41
-
42
- def _get_document_hash(self, content: str) -> str:
43
- return hashlib.md5(content.encode()).hexdigest()
44
-
45
- def _chunk_text(self, text: str, chunk_size: int = 500) -> List[str]:
46
- """将文本分割成适当大小的块"""
47
- # 按句子分割
48
- sentences = re.split(r'(?<=[.!?])\s+', text)
49
- chunks = []
50
- current_chunk = []
51
- current_length = 0
52
-
53
- for sentence in sentences:
54
- sentence_length = len(sentence)
55
- if current_length + sentence_length > chunk_size and current_chunk:
56
- chunks.append(" ".join(current_chunk))
57
- current_chunk = []
58
- current_length = 0
59
- current_chunk.append(sentence)
60
- current_length += sentence_length
61
-
62
- if current_chunk:
63
- chunks.append(" ".join(current_chunk))
64
-
65
- return chunks
66
-
67
- def execute(self, args: Dict) -> Dict[str, Any]:
68
- """执行RAG查询"""
69
- try:
70
- files = args["files"]
71
- query = args["query"]
72
- num_passages = args.get("num_passages", 3)
73
- chunk_size = args.get("chunk_size", 500)
74
-
75
- # 初始化ChromaDB
76
- chroma_client = chromadb.PersistentClient(path="./data/chromadb")
77
-
78
- # 使用sentence-transformers作为嵌入模型
79
- embedding_function = embedding_functions.SentenceTransformerEmbeddingFunction(
80
- model_name="all-MiniLM-L6-v2"
81
- )
82
-
83
- # 获取或创建集合
84
- collection = chroma_client.get_or_create_collection(
85
- name="document_store",
86
- embedding_function=embedding_function
87
- )
88
-
89
- # 处理每个文件
90
- for file_path in files:
91
- try:
92
- with open(file_path, 'r', encoding='utf-8') as f:
93
- content = f.read()
94
-
95
- # 计算文档哈希值
96
- doc_hash = self._get_document_hash(content)
97
-
98
- # 检查文档是否已经存在且未更改
99
- existing_ids = collection.get(
100
- where={"doc_hash": doc_hash}
101
- )
102
-
103
- if not existing_ids["ids"]:
104
- # 分块处理文档
105
- chunks = self._chunk_text(content, chunk_size)
106
-
107
- # 为每个块生成唯一ID
108
- chunk_ids = [f"{doc_hash}_{i}" for i in range(len(chunks))]
109
-
110
- # 添加到数据库
111
- collection.add(
112
- documents=chunks,
113
- ids=chunk_ids,
114
- metadatas=[{
115
- "file_path": file_path,
116
- "doc_hash": doc_hash,
117
- "chunk_index": i
118
- } for i in range(len(chunks))]
119
- )
120
-
121
- PrettyOutput.print(f"已添加文档: {file_path}", OutputType.INFO)
122
- else:
123
- PrettyOutput.print(f"文档已存在且未更改: {file_path}", OutputType.INFO)
124
-
125
- except Exception as e:
126
- PrettyOutput.print(f"处理文件 {file_path} 时出错: {str(e)}", OutputType.ERROR)
127
-
128
- # 执行查询
129
- results = collection.query(
130
- query_texts=[query],
131
- n_results=num_passages
132
- )
133
-
134
- # 格式化输出
135
- output = [f"查询: {query}\n"]
136
- output.append(f"找到 {len(results['documents'][0])} 个相关段落:\n")
137
-
138
- for i, (doc, metadata) in enumerate(zip(results['documents'][0], results['metadatas'][0]), 1):
139
- output.append(f"\n段落 {i}:")
140
- output.append(f"来源: {metadata['file_path']}")
141
- output.append(f"相关内容:\n{doc}\n")
142
- output.append("-" * 50)
143
-
144
- return {
145
- "success": True,
146
- "stdout": "\n".join(output),
147
- "stderr": ""
148
- }
149
-
150
- except Exception as e:
151
- return {
152
- "success": False,
153
- "error": f"RAG查询失败: {str(e)}"
154
- }