jarvis-ai-assistant 0.1.110__py3-none-any.whl → 0.1.111__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.110"
3
+ __version__ = "0.1.111"
jarvis/agent.py CHANGED
@@ -8,7 +8,7 @@ import yaml
8
8
  from jarvis.jarvis_platform.base import BasePlatform
9
9
  from jarvis.jarvis_platform.registry import PlatformRegistry
10
10
  from jarvis.jarvis_tools.registry import ToolRegistry, tool_call_help
11
- from jarvis.utils import PrettyOutput, OutputType, get_context_token_count, is_auto_complete, is_need_summary, is_record_methodology, load_methodology, add_agent, delete_current_agent, get_max_token_count, get_multiline_input, init_env, is_use_methodology
11
+ from jarvis.utils import PrettyOutput, OutputType, get_context_token_count, is_auto_complete, is_execute_tool_confirm, is_need_summary, is_record_methodology, load_methodology, add_agent, delete_current_agent, get_max_token_count, get_multiline_input, init_env, is_use_methodology, user_confirm
12
12
  import os
13
13
 
14
14
  class Agent:
@@ -42,7 +42,8 @@ class Agent:
42
42
  use_methodology: Optional[bool] = None,
43
43
  record_methodology: Optional[bool] = None,
44
44
  need_summary: Optional[bool] = None,
45
- max_context_length: Optional[int] = None):
45
+ max_context_length: Optional[int] = None,
46
+ execute_tool_confirm: Optional[bool] = None):
46
47
  """Initialize an Agent instance.
47
48
 
48
49
  Args:
@@ -78,6 +79,8 @@ class Agent:
78
79
  self.output_handler_before_tool = output_handler_before_tool if output_handler_before_tool else []
79
80
  self.output_handler_after_tool = output_handler_after_tool if output_handler_after_tool else []
80
81
 
82
+ self.execute_tool_confirm = execute_tool_confirm if execute_tool_confirm is not None else is_execute_tool_confirm()
83
+
81
84
  self.summary_prompt = summary_prompt if summary_prompt else f"""Please generate a concise summary report of the task execution, including:
82
85
 
83
86
  1. Task Objective: Task restatement
@@ -344,10 +347,11 @@ Please continue the task based on the above information.
344
347
  continue
345
348
 
346
349
  if len(result) > 0:
347
- PrettyOutput.print("Executing tool call...", OutputType.PROGRESS)
348
- tool_result = self.tool_registry.handle_tool_calls(result)
349
- self.prompt += tool_result
350
-
350
+ if not self.execute_tool_confirm or user_confirm(f"Execute tool call: {result[0]['name']}?"):
351
+ PrettyOutput.print("Executing tool call...", OutputType.PROGRESS)
352
+ tool_result = self.tool_registry.handle_tool_calls(result)
353
+ self.prompt += tool_result
354
+
351
355
  for handler in self.output_handler_after_tool:
352
356
  self.prompt += handler(current_response)
353
357
 
@@ -499,6 +503,8 @@ When users need to execute tasks, you will strictly follow these steps to handle
499
503
  10. Auto check the task goal completion status: If the task goal is completed, use the task completion command to end the task
500
504
  11. Task Completion: End the task using task completion command when finished
501
505
 
506
+ Tip: Chat in user's language
507
+
502
508
  Methodology Template:
503
509
  1. Problem Restatement
504
510
  2. Optimal Solution
@@ -516,7 +522,7 @@ def main():
516
522
 
517
523
  try:
518
524
  # 获取全局模型实例
519
- agent = Agent(system_prompt=origin_agent_system_prompt, tool_registry=ToolRegistry())
525
+ agent = Agent(system_prompt=origin_agent_system_prompt)
520
526
 
521
527
  # 加载预定义任务
522
528
  tasks = _load_tasks()
@@ -1,13 +1,13 @@
1
1
  import os
2
- from typing import List
2
+ from typing import Dict, List
3
3
 
4
4
  from jarvis.agent import Agent
5
5
  from jarvis.jarvis_code_agent.patch import apply_patch
6
- from jarvis.jarvis_code_agent.relevant_files import find_relevant_files
6
+ from jarvis.jarvis_code_agent.relevant_files import find_relevant_information
7
7
  from jarvis.jarvis_platform.registry import PlatformRegistry
8
8
  from jarvis.jarvis_tools.git_commiter import GitCommitTool
9
9
  from jarvis.jarvis_tools.registry import ToolRegistry
10
- from jarvis.utils import OutputType, PrettyOutput, get_file_line_count, get_multiline_input, has_uncommitted_changes, init_env, find_git_root
10
+ from jarvis.utils import OutputType, PrettyOutput, get_multiline_input, has_uncommitted_changes, init_env, find_git_root
11
11
 
12
12
 
13
13
  class CodeAgent:
@@ -26,7 +26,13 @@ class CodeAgent:
26
26
  "lsp_find_definition",
27
27
  "lsp_prepare_rename",
28
28
  "lsp_validate_edit"])
29
- code_system_prompt = """You are a code agent responsible for modifying code. You will analyze code and create patches while following these guidelines:
29
+ code_system_prompt = """You are a code agent responsible for modifying code. Your primary task is to understand existing code first and ensure compatibility with the current system.
30
+
31
+ # Critical First Steps
32
+ 1. READ and UNDERSTAND existing code thoroughly
33
+ 2. Identify current patterns and conventions
34
+ 3. Map out affected components and their interactions
35
+ 4. Plan changes that maintain system integrity
30
36
 
31
37
  # Patch Format
32
38
  <PATCH>
@@ -41,40 +47,43 @@ Key Rules:
41
47
  • Same start/end number: insert before that line
42
48
  • Start=0, end=0: create new file with content
43
49
 
44
- # Code Compatibility
50
+ # Code Compatibility Requirements
45
51
  1. System Integration
46
- Maintain existing API contracts
47
- Preserve function signatures
48
- Keep data structures compatible
49
- Honor return types and exceptions
52
+ MUST preserve existing API contracts
53
+ MUST maintain current function signatures
54
+ MUST keep data structure compatibility
55
+ MUST follow error handling patterns
50
56
 
51
57
  2. Style Consistency
52
- • Match naming conventions
53
- • Follow indentation patterns
54
- • Use existing import style
55
- Keep comment format
58
+ • Match existing naming conventions exactly
59
+ • Follow established code organization
60
+ • Use current import style and order
61
+ Maintain comment style and level of detail
56
62
 
57
63
  3. Pattern Alignment
58
- Use similar error handling
59
- • Follow existing design patterns
60
- Match logging approach
61
- • Keep configuration style
64
+ Reuse existing error handling approaches
65
+ • Follow established design patterns
66
+ Use current logging conventions
67
+ • Keep configuration consistency
62
68
 
63
69
  # Development Process
64
- 1. ANALYZE
65
- Break down requirements
66
- Assess task complexity
67
- Determine if subtasks needed
68
-
69
- 2. ASSESS
70
- Map dependencies
71
- Check compatibility impact
72
- Identify integration points
73
-
74
- 3. IMPLEMENT
75
- • Follow existing patterns
76
- Make minimal changes
77
- Verify integrations
70
+ 1. ANALYZE (Current Code)
71
+ Read and understand existing implementations
72
+ Map out current code structure
73
+ Identify established patterns
74
+ • Note key dependencies
75
+
76
+ 2. ASSESS (Changes)
77
+ Evaluate impact on existing code
78
+ Check all dependencies
79
+ • Verify API compatibility
80
+ Consider side effects
81
+
82
+ 3. IMPLEMENT (Carefully)
83
+ Make minimal necessary changes
84
+ • Follow existing patterns exactly
85
+ • Preserve all interfaces
86
+ • Maintain backward compatibility
78
87
 
79
88
  # File Handling
80
89
  Large Files (>200 lines):
@@ -84,18 +93,18 @@ Large Files (>200 lines):
84
93
 
85
94
  # Available Tools
86
95
  Primary:
96
+ • read_code - MUST use to understand existing code
87
97
  • LSP tools for code analysis
88
- • read_code with line ranges
89
98
  • execute_shell for searches
90
- • ask_user when unclear
99
+ • ask_user when uncertain
91
100
 
92
101
  # Quality Requirements
93
- Code Changes Must:
94
- Preserve interfaces
95
- Match existing style
102
+ Every Change Must:
103
+ Be based on thorough code reading
104
+ Preserve all interfaces
105
+ ✓ Match existing style exactly
96
106
  ✓ Handle errors consistently
97
107
  ✓ Maintain documentation
98
- ✓ Keep test coverage
99
108
  ✓ Follow project patterns"""
100
109
  self.agent = Agent(system_prompt=code_system_prompt,
101
110
  name="CodeAgent",
@@ -103,7 +112,7 @@ Code Changes Must:
103
112
  is_sub_agent=False,
104
113
  use_methodology=False,
105
114
  tool_registry=tool_registry,
106
- platform=PlatformRegistry().get_thinking_platform(),
115
+ platform=PlatformRegistry().get_codegen_platform(),
107
116
  record_methodology=False,
108
117
  output_handler_after_tool=[apply_patch],
109
118
  need_summary=False)
@@ -119,17 +128,22 @@ Code Changes Must:
119
128
  git_commiter.execute({})
120
129
 
121
130
 
122
- def make_files_prompt(self, files: List[str]) -> str:
123
- """Make the files prompt.
131
+ def make_files_prompt(self, files: List[Dict[str, str]]) -> str:
132
+ """Make the files prompt with content that fits within token limit.
124
133
 
125
134
  Args:
126
135
  files: The files to be modified
127
136
 
137
+ Returns:
138
+ str: A prompt containing file paths and contents within token limit
128
139
  """
129
- return "\n".join(
130
- f"- {file} ({get_file_line_count(file)} lines)"
131
- for file in files
132
- )
140
+ prompt_parts = []
141
+
142
+ # Then try to add file contents
143
+ for file in files:
144
+ prompt_parts.append(f'''- {file['file']} ({file['reason']})''')
145
+
146
+ return "\n".join(prompt_parts)
133
147
 
134
148
  def run(self, user_input: str) :
135
149
  """Run the code agent with the given user input.
@@ -142,15 +156,15 @@ Code Changes Must:
142
156
  """
143
157
  try:
144
158
  self._init_env()
145
- files = find_relevant_files(user_input, self.root_dir)
146
- self.agent.run(self._build_first_edit_prompt(user_input, self.make_files_prompt(files)))
159
+ files, information = find_relevant_information(user_input, self.root_dir)
160
+ self.agent.run(self._build_first_edit_prompt(user_input, self.make_files_prompt(files), information))
147
161
 
148
162
  except Exception as e:
149
163
  return f"Error during execution: {str(e)}"
150
164
 
151
165
 
152
166
 
153
- def _build_first_edit_prompt(self, user_input: str, files_prompt: str) -> str:
167
+ def _build_first_edit_prompt(self, user_input: str, files_prompt: str, information: str) -> str:
154
168
  """Build the initial prompt for the agent.
155
169
 
156
170
  Args:
@@ -160,13 +174,17 @@ Code Changes Must:
160
174
  Returns:
161
175
  str: The formatted prompt
162
176
  """
177
+
163
178
  return f"""# Code Modification Task
164
179
 
165
180
  ## User Requirement
166
181
  {user_input}
167
182
 
168
- ## Available Files
183
+ ## Maybe Relevant Files
169
184
  {files_prompt}
185
+
186
+ ## Some Information
187
+ {information}
170
188
  """
171
189
  def main():
172
190
  """Jarvis main entry point"""
@@ -2,7 +2,7 @@ import os
2
2
  import re
3
3
  from typing import Dict, List
4
4
  from prompt_toolkit import PromptSession
5
- from prompt_toolkit.completion import WordCompleter, Completer, Completion
5
+ from prompt_toolkit.completion import Completer, Completion
6
6
  from jarvis.utils import OutputType, PrettyOutput, get_single_line_input, user_confirm
7
7
 
8
8
 
@@ -125,7 +125,7 @@ def _fuzzy_match_files(root_dir: str, pattern: str) -> List[str]:
125
125
 
126
126
  return sorted(matches)
127
127
 
128
- def select_files(related_files: List[str], root_dir: str) -> List[str]:
128
+ def select_files(related_files: List[Dict[str, str]], root_dir: str) -> List[Dict[str, str]]:
129
129
  """Let the user select and supplement related files"""
130
130
  PrettyOutput.section("Related files", OutputType.INFO)
131
131
 
@@ -133,7 +133,7 @@ def select_files(related_files: List[str], root_dir: str) -> List[str]:
133
133
  # Display found files
134
134
  selected_files = list(related_files) # Default select all
135
135
  for i, file in enumerate(related_files, 1):
136
- output += f"[{i}] {file}\n"
136
+ output += f"[{i}] {file['file']} ({file['reason']})\n"
137
137
 
138
138
  PrettyOutput.print(output, OutputType.INFO, lang="markdown")
139
139
 
@@ -199,7 +199,7 @@ def select_files(related_files: List[str], root_dir: str) -> List[str]:
199
199
  continue
200
200
 
201
201
  try:
202
- selected_files.append(path)
202
+ selected_files.append({"file": path, "reason": "User Added"})
203
203
  PrettyOutput.print(f"File added: {path}", OutputType.SUCCESS)
204
204
  except Exception as e:
205
205
  PrettyOutput.print(f"Failed to read file: {str(e)}", OutputType.ERROR)
@@ -115,11 +115,14 @@ def handle_commit_workflow()->bool:
115
115
  Returns:
116
116
  tuple[bool, str, str]: (continue_execution, commit_id, commit_message)
117
117
  """
118
+ os.system("git add .")
118
119
  diff = os.popen("git diff HEAD").read()
120
+ os.system("git reset HEAD")
119
121
  PrettyOutput.print(diff, OutputType.CODE, lang="diff")
120
122
  if not user_confirm("Do you want to commit the code?", default=True):
121
123
  os.system("git reset HEAD")
122
124
  os.system("git checkout -- .")
125
+ os.system("git clean -fd")
123
126
  return False
124
127
 
125
128
  git_commiter = GitCommitTool()
@@ -1,27 +1,86 @@
1
1
  import os
2
2
  import re
3
- from typing import List
3
+ from typing import Dict, List, Optional, Tuple
4
4
 
5
- import yaml
6
- from jarvis.agent import Agent
7
5
  from jarvis.jarvis_code_agent.file_select import select_files
8
6
  from jarvis.jarvis_codebase.main import CodeBase
9
7
  from jarvis.jarvis_platform.registry import PlatformRegistry
10
- from jarvis.jarvis_tools.registry import ToolRegistry
11
- from jarvis.utils import OutputType, PrettyOutput, is_disable_codebase
8
+ from jarvis.utils import OutputType, PrettyOutput
12
9
 
10
+ def make_question(requirement: str) -> Optional[str]:
11
+ """Generate structured questions to gather necessary information for the requirement.
12
+
13
+ Args:
14
+ requirement: The user's requirement description
15
+
16
+ Returns:
17
+ str: A formatted string containing relevant questions
18
+ """
19
+ prompt = f"""You are a helpful assistant that generates SPECIFIC questions in English for a code analysis team. The analysis team:
20
+ - Has access to the codebase but NO CONTEXT about the requirement
21
+ - Will search and analyze code based on your questions
22
+ - Needs complete context to understand what to look for
13
23
 
14
- def find_relevant_files(user_input: str, root_dir: str) -> List[str]:
15
- try:
16
- files_from_codebase = []
17
- if not is_disable_codebase():
18
- PrettyOutput.print("Find files from codebase...", OutputType.INFO)
19
- codebase = CodeBase(root_dir)
20
- files_from_codebase = codebase.search_similar(user_input)
24
+ Key Instructions:
25
+ 1. Write questions in clear, professional English
26
+ 2. Include necessary CONTEXT in each question
27
+ 3. Be SPECIFIC about what needs to be found/analyzed
28
+ 4. Provide enough background for someone with no prior knowledge
29
+
30
+ For example:
31
+ BAD: "How is error handling implemented?"
32
+ GOOD: "Given that we need to add retry logic to the file upload feature, how does the current error handling work in upload_handler.py, specifically around network failures and timeout scenarios?"
33
+
34
+ Consider these aspects when forming questions:
35
+
36
+ 1. Implementation Context:
37
+ - "What is the current implementation of [specific feature]?"
38
+ - "Which modules/classes handle [specific functionality]?"
39
+ - "What is the existing workflow for [specific operation]?"
40
+
41
+ 2. Technical Investigation:
42
+ - "How does the system currently handle [specific scenario]?"
43
+ - "What patterns are used for [specific behavior]?"
44
+ - "Where are the configuration settings for [specific feature]?"
45
+
46
+ 3. Integration Details:
47
+ - "Which components call or depend on [specific module]?"
48
+ - "What is the data flow between [component A] and [component B]?"
49
+ - "How are errors propagated from [specific component]?"
21
50
 
51
+ 4. Requirements Context:
52
+ - "Given [specific requirement], what are the current limitations?"
53
+ - "For [specific change], what validation rules apply?"
54
+ - "In the context of [feature], what edge cases exist?"
55
+
56
+ User Requirement:
57
+ {requirement}
58
+
59
+ Output Format:
60
+ <QUESTION>
61
+ [Write 3-5 specific questions in English, ensuring each includes full context for someone with no prior knowledge of the requirement]
62
+ </QUESTION>
63
+ """
64
+ model = PlatformRegistry().get_normal_platform()
65
+ response = model.chat_until_success(prompt)
66
+ response = re.search(r'<QUESTION>(.*?)</QUESTION>', response, re.DOTALL)
67
+ if response is None:
68
+ return None
69
+ return response.group(1)
70
+
71
+
72
+ def find_relevant_information(user_input: str, root_dir: str) -> Tuple[List[Dict[str, str]], str]:
73
+ try:
74
+ PrettyOutput.print("Find files from codebase...", OutputType.INFO)
75
+ codebase = CodeBase(root_dir)
76
+ question = make_question(user_input)
77
+ if question is None:
78
+ return [], ""
79
+ files_from_codebase, infomation = codebase.ask_codebase(question)
22
80
  PrettyOutput.print("Find files by agent...", OutputType.INFO)
23
81
 
24
82
  selected_files = select_files(files_from_codebase, os.getcwd())
25
- return selected_files
26
- except Exception as e:
27
- return []
83
+ return selected_files, infomation
84
+ except Exception:
85
+ PrettyOutput.print("Failed to find relevant files", OutputType.ERROR)
86
+ return [], ""
@@ -524,7 +524,7 @@ Content: {content}
524
524
  score = len(matched_keywords) / len(keywords)
525
525
  return score
526
526
 
527
- def pick_results(self, query: List[str], initial_results: List[str]) -> List[str]:
527
+ def pick_results(self, query: List[str], initial_results: List[str]) -> List[Dict[str,str]]:
528
528
  """Use a large model to pick the search results
529
529
 
530
530
  Args:
@@ -538,14 +538,14 @@ Content: {content}
538
538
  return []
539
539
 
540
540
  try:
541
- PrettyOutput.print(f"Picking results for query: \n" + "\n".join(query), output_type=OutputType.INFO)
541
+ PrettyOutput.print(f"Picking results ...", output_type=OutputType.INFO)
542
542
 
543
543
  # Maximum content length per batch
544
544
  max_batch_length = self.max_token_count - 1000 # Reserve space for prompt
545
545
  max_file_length = max_batch_length // 3 # Limit individual file size
546
546
 
547
547
  # Process files in batches
548
- all_selected_files = set()
548
+ all_selected_files = []
549
549
  current_batch = []
550
550
  current_token_count = 0
551
551
 
@@ -565,7 +565,7 @@ Content: {content}
565
565
  # Process current batch
566
566
  if current_batch:
567
567
  selected = self._process_batch('\n'.join(query), current_batch)
568
- all_selected_files.update(selected)
568
+ all_selected_files.extend(selected)
569
569
  # Start new batch
570
570
  current_batch = [file_info]
571
571
  current_token_count = tokens_count
@@ -580,17 +580,16 @@ Content: {content}
580
580
  # Process final batch
581
581
  if current_batch:
582
582
  selected = self._process_batch('\n'.join(query), current_batch)
583
- all_selected_files.update(selected)
583
+ all_selected_files.extend(selected)
584
584
 
585
585
  # Convert set to list and maintain original order
586
- final_results = [path for path in initial_results if path in all_selected_files]
587
- return final_results
586
+ return all_selected_files
588
587
 
589
588
  except Exception as e:
590
589
  PrettyOutput.print(f"Failed to pick: {str(e)}", OutputType.ERROR)
591
- return initial_results
590
+ return [{"file": f, "reason": "" } for f in initial_results]
592
591
 
593
- def _process_batch(self, query: str, files_info: List[str]) -> List[str]:
592
+ def _process_batch(self, query: str, files_info: List[str]) -> List[Dict[str, str]]:
594
593
  """Process a batch of files"""
595
594
  prompt = f"""As a code analysis expert, please help identify the most relevant files for the given query using chain-of-thought reasoning.
596
595
 
@@ -611,8 +610,10 @@ Think through this step by step:
611
610
 
612
611
  Please output your selection in YAML format:
613
612
  <FILES>
614
- - path/to/most/relevant.py
613
+ - file: path/to/most/relevant.py
614
+ reason: xxxxxxxxxx
615
615
  - path/to/next/relevant.py
616
+ reason: yyyyyyyyyy
616
617
  </FILES>
617
618
 
618
619
  Important:
@@ -724,7 +725,7 @@ Please provide 10 search-optimized expressions in the specified format.
724
725
  return results
725
726
 
726
727
 
727
- def search_similar(self, query: str, top_k: int = 30) -> List[str]:
728
+ def search_similar(self, query: str, top_k: int = 30) -> List[Dict[str, str]]:
728
729
  """Search related files with optimized retrieval"""
729
730
  try:
730
731
  self.generate_codebase()
@@ -764,13 +765,13 @@ Please provide 10 search-optimized expressions in the specified format.
764
765
  all_results.sort(key=lambda x: x[1], reverse=True)
765
766
  results = all_results[:top_k]
766
767
 
767
- # Display results with scores
768
- message = "Found related files:\n"
769
- for path, score, _ in results:
770
- message += f"File: {path} (Score: {score:.3f})\n"
771
- PrettyOutput.print(message.rstrip(), output_type=OutputType.INFO, lang="markdown")
772
-
773
768
  results = self.pick_results(query_variants, [path for path, _, _ in results])
769
+
770
+ output = "Found related files:\n"
771
+ for file in results:
772
+ output += f'''- {file['file']} ({file['reason']})\n'''
773
+ PrettyOutput.print(output, output_type=OutputType.INFO, lang="markdown")
774
+
774
775
 
775
776
  return results
776
777
 
@@ -778,18 +779,13 @@ Please provide 10 search-optimized expressions in the specified format.
778
779
  PrettyOutput.print(f"Failed to search: {str(e)}", output_type=OutputType.ERROR)
779
780
  return []
780
781
 
781
- def ask_codebase(self, query: str, top_k: int=20) -> str:
782
+ def ask_codebase(self, query: str, top_k: int=20) -> Tuple[List[Dict[str, str]], str]:
782
783
  """Query the codebase with enhanced context building"""
783
784
  files_from_codebase = self.search_similar(query, top_k)
784
785
 
785
786
  if not files_from_codebase:
786
787
  PrettyOutput.print("No related files found", output_type=OutputType.WARNING)
787
- return ""
788
-
789
- output = "Found related files:\n"
790
- for path in files_from_codebase:
791
- output += f"- {path}\n"
792
- PrettyOutput.print(output, output_type=OutputType.INFO, lang="markdown")
788
+ return [],""
793
789
 
794
790
  # Build enhanced prompt
795
791
  prompt = f"""Based on the following code files, please provide a comprehensive and accurate answer to the user's question.
@@ -799,6 +795,8 @@ Important guidelines:
799
795
  2. Explain technical concepts clearly
800
796
  3. Include relevant code snippets when helpful
801
797
  4. If the code doesn't fully answer the question, indicate what's missing
798
+ 5. Answer in user's language.
799
+ 6. Answer with professional language.
802
800
 
803
801
  Question: {query}
804
802
 
@@ -810,9 +808,9 @@ Relevant code files (ordered by relevance):
810
808
 
811
809
  for path in files_from_codebase:
812
810
  try:
813
- content = open(path, "r", encoding="utf-8").read()
811
+ content = open(path["file"], "r", encoding="utf-8").read()
814
812
  file_content = f"""
815
- File: {path}
813
+ File: {path["file"]}
816
814
  Content:
817
815
  {content}
818
816
  ----------------------------------------
@@ -832,21 +830,8 @@ Content:
832
830
  output_type=OutputType.ERROR)
833
831
  continue
834
832
 
835
- prompt += """
836
- Please structure your answer as follows:
837
- 1. Direct answer to the question
838
- 2. Relevant code explanations
839
- 3. Implementation details
840
- 4. Any missing information or limitations
841
- 5. Add reference files and code snippets at the end of the answer.
842
-
843
- Answer in Chinese using professional language.
844
- """
845
-
846
- model = PlatformRegistry.get_global_platform_registry().get_normal_platform()
847
- response = model.chat_until_success(prompt)
848
-
849
- return response
833
+ model = PlatformRegistry.get_global_platform_registry().get_thinking_platform()
834
+ return files_from_codebase, model.chat_until_success(prompt)
850
835
 
851
836
  def is_index_generated(self) -> bool:
852
837
  """Check if the index has been generated"""
@@ -948,7 +933,7 @@ def main():
948
933
 
949
934
  elif args.command == 'ask':
950
935
  response = codebase.ask_codebase(args.question, args.top_k)
951
- output = f"""Answer:\n{response}"""
936
+ output = f"""{response}"""
952
937
  PrettyOutput.print(output, output_type=OutputType.INFO)
953
938
 
954
939
  else:
@@ -52,7 +52,7 @@ class AskCodebaseTool:
52
52
  codebase = CodeBase(git_root)
53
53
 
54
54
  # Use ask_codebase method
55
- response = codebase.ask_codebase(question, top_k)
55
+ _, response = codebase.ask_codebase(question, top_k)
56
56
 
57
57
  return {
58
58
  "success": True,
@@ -3,9 +3,7 @@ import re
3
3
  from typing import Dict, Any
4
4
 
5
5
  import yaml
6
- from jarvis.agent import Agent
7
6
  from jarvis.jarvis_platform.registry import PlatformRegistry
8
- from jarvis.jarvis_tools.registry import ToolRegistry
9
7
  from jarvis.utils import OutputType, PrettyOutput, init_env
10
8
  import sys
11
9
 
@@ -100,11 +100,12 @@ class ReadCodeTool:
100
100
 
101
101
  content = "".join(formatted_lines)
102
102
 
103
- PrettyOutput.print(f"File: {filepath}\nLines: [{start_line}, {end_line})\n{content}", OutputType.CODE)
103
+ output = f"File: {filepath}\nLines: [{start_line}, {end_line})\n{content}";
104
+ PrettyOutput.print(output, OutputType.CODE)
104
105
 
105
106
  return {
106
107
  "success": True,
107
- "stdout": content,
108
+ "stdout": output,
108
109
  "stderr": ""
109
110
  }
110
111
 
@@ -0,0 +1,19 @@
1
+ from datetime import datetime
2
+
3
+ class DateValidator:
4
+ @staticmethod
5
+ def validate_iso_date(date_str: str) -> bool:
6
+ try:
7
+ datetime.fromisoformat(date_str)
8
+ return True
9
+ except ValueError:
10
+ return False
11
+
12
+ @staticmethod
13
+ def validate_date_range(start: str, end: str) -> bool:
14
+ try:
15
+ start_dt = datetime.fromisoformat(start)
16
+ end_dt = datetime.fromisoformat(end)
17
+ return start_dt <= end_dt
18
+ except ValueError:
19
+ return False
jarvis/utils.py CHANGED
@@ -213,6 +213,8 @@ class FileCompleter(Completer):
213
213
  """Custom completer for file paths with fuzzy matching."""
214
214
  def __init__(self):
215
215
  self.path_completer = PathCompleter()
216
+ self.max_suggestions = 10 # 增加显示数量
217
+ self.min_score = 10 # 降低相似度阈值
216
218
 
217
219
  def get_completions(self, document: Document, complete_event):
218
220
  text = document.text_before_cursor
@@ -251,21 +253,26 @@ class FileCompleter(Completer):
251
253
  # If no input after @, show all files
252
254
  # Otherwise use fuzzy matching
253
255
  if not file_path:
254
- scored_files = [(path, 100) for path in all_files]
256
+ scored_files = [(path, 100) for path in all_files[:self.max_suggestions]]
255
257
  else:
256
258
  scored_files = [
257
259
  (path, fuzz.ratio(file_path.lower(), path.lower()))
258
260
  for path in all_files
259
261
  ]
262
+ # Sort by score and take top results
260
263
  scored_files.sort(key=lambda x: x[1], reverse=True)
264
+ scored_files = scored_files[:self.max_suggestions]
261
265
 
262
266
  # Return completions for files
263
267
  for path, score in scored_files:
264
- if not file_path or score > 30: # Show all if no input, otherwise filter by score
268
+ if not file_path or score > self.min_score:
269
+ display_text = path
270
+ if file_path and score < 100:
271
+ display_text = f"{path} ({score}%)"
265
272
  completion = Completion(
266
273
  text=path,
267
274
  start_position=-len(file_path),
268
- display=f"{path}" if not file_path else f"{path} ({score}%)",
275
+ display=display_text,
269
276
  display_meta="File"
270
277
  )
271
278
  yield completion
@@ -376,6 +383,7 @@ def has_uncommitted_changes():
376
383
  working_changes = os.popen("git diff --exit-code").read().strip() != ""
377
384
  # Check staged changes
378
385
  staged_changes = os.popen("git diff --cached --exit-code").read().strip() != ""
386
+ os.system("git reset HEAD")
379
387
  return working_changes or staged_changes
380
388
 
381
389
  def load_embedding_model():
@@ -685,9 +693,6 @@ def dont_use_local_model():
685
693
 
686
694
  def is_auto_complete() -> bool:
687
695
  return os.getenv('JARVIS_AUTO_COMPLETE', 'false') == 'true'
688
-
689
- def is_disable_codebase() -> bool:
690
- return os.getenv('JARVIS_DISABLE_CODEBASE', 'false') == 'true'
691
696
 
692
697
  def is_use_methodology() -> bool:
693
698
  return os.getenv('JARVIS_USE_METHODOLOGY', 'true') == 'true'
@@ -731,6 +736,9 @@ def get_cheap_platform_name() -> str:
731
736
  def get_cheap_model_name() -> str:
732
737
  return os.getenv('JARVIS_CHEAP_MODEL', os.getenv('JARVIS_MODEL', 'kimi'))
733
738
 
739
+ def is_execute_tool_confirm() -> bool:
740
+ return os.getenv('JARVIS_EXECUTE_TOOL_CONFIRM', 'false') == 'true'
741
+
734
742
  def split_text_into_chunks(text: str, max_length: int = 512) -> List[str]:
735
743
  """Split text into chunks with overlapping windows"""
736
744
  chunks = []
@@ -774,7 +782,7 @@ def get_context_token_count(text: str) -> int:
774
782
  try:
775
783
  # Use a fast tokenizer that's good at general text
776
784
  tokenizer = load_tokenizer()
777
- chunks = split_text_into_chunks(text, 512)
785
+ chunks = split_text_into_chunks(text, 1024)
778
786
  return sum([len(tokenizer.encode(chunk)) for chunk in chunks])
779
787
 
780
788
  except Exception as e:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: jarvis-ai-assistant
3
- Version: 0.1.110
3
+ Version: 0.1.111
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
@@ -149,7 +149,6 @@ Jarvis supports configuration through environment variables that can be set in t
149
149
  | JARVIS_MAX_PARAGRAPH_LENGTH | Maximum paragraph length | 1000 | No |
150
150
  | JARVIS_CONTEXT_WINDOW | Context window size | 5 | No |
151
151
  | JARVIS_AUTO_COMPLETE | Enable auto completion | false | No |
152
- | JARVIS_DISABLE_CODEBASE | Disable codebase features | false | No |
153
152
  | JARVIS_USE_METHODOLOGY | Enable methodology | true | No |
154
153
  | JARVIS_RECORD_METHODOLOGY | Record methodology | true | No |
155
154
  | JARVIS_NEED_SUMMARY | Generate summaries | true | No |
@@ -1,13 +1,13 @@
1
- jarvis/__init__.py,sha256=0rw1RD8VrgfjPxXtqbnGEx3Aj9K4mgrYWuVSzACO3Ic,51
2
- jarvis/agent.py,sha256=eV2Bgm5Q6gnQb2QeEo9bHCDaLY0v3RSV8Ylm_gS2_Yc,22678
3
- jarvis/utils.py,sha256=7v9hs9Tlyi9XMLYkPUzbzzMXGuAlJAx5999O2d7kP9A,26945
1
+ jarvis/__init__.py,sha256=EwzKMyQQYv9-9h1YUWEh1i7Ne5vKRvUB3mF27llLwo4,51
2
+ jarvis/agent.py,sha256=r-wTNio1BwbySStjJH2fF-yCVftDJzWtX9GJxMrkD3Y,23062
3
+ jarvis/utils.py,sha256=iwmiKVg5ATyKzOGzDaunZHl2ThosQRMyqjQzj1Hu6Wc,27289
4
4
  jarvis/jarvis_code_agent/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
- jarvis/jarvis_code_agent/code_agent.py,sha256=nigsmCK6D2z0dFU_1HFNYEvXr3lWdl0rm6p4VgiOk6o,5980
6
- jarvis/jarvis_code_agent/file_select.py,sha256=1kOVRLPS1GZcDyGpCW9hOPbfCEwF8f0-qVPaRZPHzoM,8154
7
- jarvis/jarvis_code_agent/patch.py,sha256=bOhegGKs4JEmJJOZfUlmwzGI6kakMyi2Q62HADJ7Npk,4594
8
- jarvis/jarvis_code_agent/relevant_files.py,sha256=Q4nI45zuyWt5aKuc4OR7-a6UbOXOym3oEzQJvqxkF8Q,946
5
+ jarvis/jarvis_code_agent/code_agent.py,sha256=2EptFqsX7xgbTIrsr1lMxo7j6g1t0jrbuuS1J3QrrI0,6935
6
+ jarvis/jarvis_code_agent/file_select.py,sha256=w4s-aUqGRVC2zP45_QgMAlWuSq8ocnT1s1XRioejVNc,8222
7
+ jarvis/jarvis_code_agent/patch.py,sha256=0_T_P1NOGqYe91VaQ6oofNI1Q8XVcfxMH69aT-txd_c,4688
8
+ jarvis/jarvis_code_agent/relevant_files.py,sha256=BPc8HXIS86HdLS76j9seL9Sw78Eh3G_Oyj--lwdoZZY,3475
9
9
  jarvis/jarvis_codebase/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
- jarvis/jarvis_codebase/main.py,sha256=40ySTIrQld5uW60vW9pawZopjDNVvtmlW27oNVaJXH0,39683
10
+ jarvis/jarvis_codebase/main.py,sha256=Yjy9P9xh1s7GIDskpajPObgE8zDfwj9-oi8eipEwWbw,39186
11
11
  jarvis/jarvis_lsp/base.py,sha256=_7pdbMKjdtYBW0DsRbjIodDHM3J7df-YgXHejN_WIrU,4490
12
12
  jarvis/jarvis_lsp/cpp.py,sha256=F7Zo3BErkvtWS1_H9zQO83pX_FUmnijux-2SjhWzKCE,4985
13
13
  jarvis/jarvis_lsp/go.py,sha256=p8LULiFdq4qjDYQzXFlzH0-FQZ3IyfiwN_sbO9i0L_A,5310
@@ -29,7 +29,7 @@ jarvis/jarvis_rag/main.py,sha256=Lr3b2eTB9TXZGZGdG4Sl9bdtE5NFRbv_bRysxeWNCEo,313
29
29
  jarvis/jarvis_smart_shell/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
30
30
  jarvis/jarvis_smart_shell/main.py,sha256=VdUR-x932OccEwU0pcQM_pb_I4yfrAutE3hfm6jf5es,3955
31
31
  jarvis/jarvis_tools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
32
- jarvis/jarvis_tools/ask_codebase.py,sha256=1mLe9CHDU-NFJHmu3mxrWuA4IiHqQyum2ga31P3hLzU,2991
32
+ jarvis/jarvis_tools/ask_codebase.py,sha256=KLECEUGcAwXPAJF4sYV4CS2PYIc7RPBjHfUHZXCbMNQ,2994
33
33
  jarvis/jarvis_tools/ask_user.py,sha256=9ZhIffoPGSfxJIwiIsL03V95yM8uYJsRg7j1C8ltNGc,1830
34
34
  jarvis/jarvis_tools/base.py,sha256=c0DMoDDPxmsqUYJR989zgUs7nIYRY6GWBrAdusIZKjc,656
35
35
  jarvis/jarvis_tools/chdir.py,sha256=A53BNXFB9tvwoV_cxW_LpF_DfxANgAEJ8rjikTaTa-I,1813
@@ -40,7 +40,7 @@ jarvis/jarvis_tools/deep_thinking.py,sha256=ltsMUfmE8XNyYTkgpWMD1Qow-6_x0dcD8WL7
40
40
  jarvis/jarvis_tools/deep_thinking_agent.py,sha256=UBBWq8kp6SDEhwYXjO-tMMHP7Wblx5OA-gpQ8h_1tdk,4378
41
41
  jarvis/jarvis_tools/execute_shell.py,sha256=bawfof8bUg3f9bjyCSifLa9bU-hkNoNOuos22uZffdg,2564
42
42
  jarvis/jarvis_tools/file_operation.py,sha256=-1U_J5SEuBjRylzEl7wvCfjspNv6aA49UvFHLNQ3bJU,4098
43
- jarvis/jarvis_tools/git_commiter.py,sha256=4dcFMTN3qmfuTfMkF6an4K95PTR1_0qBoplr20lH2jQ,2565
43
+ jarvis/jarvis_tools/git_commiter.py,sha256=bGGqSHraR5E1YMrGDudKh6uZyMEhjlLiCYJUp9wvFhU,2480
44
44
  jarvis/jarvis_tools/lsp_find_definition.py,sha256=xV8YeN1RJfwd2F3gE6OnDeTwl-AnCmrxueHocbXkQOc,4800
45
45
  jarvis/jarvis_tools/lsp_find_references.py,sha256=FohlJeLfTxcMUASfbjOT93hQGtI2WeyTpMGwRwShW_I,4043
46
46
  jarvis/jarvis_tools/lsp_get_diagnostics.py,sha256=bEvbDk8TnKg9TTFFxMrYOJm5TBDgz5gO04WJFQUwQQE,4490
@@ -49,14 +49,15 @@ jarvis/jarvis_tools/lsp_prepare_rename.py,sha256=RxUyIef4awtp-jgupcD1LcPlno9P3mO
49
49
  jarvis/jarvis_tools/lsp_validate_edit.py,sha256=M0iglK2QbnIEFv0RYK6o2iAYnv259jB6EU7To-rc51E,5247
50
50
  jarvis/jarvis_tools/methodology.py,sha256=RFqcVjKuj8ESGmNYcQz_HyphsitDvF3XtqgGaqhafDQ,5770
51
51
  jarvis/jarvis_tools/rag.py,sha256=2fQHqc4bw8JM-OxGTsHobLIOTo8Mip3rdtJCmAoY8XU,4952
52
- jarvis/jarvis_tools/read_code.py,sha256=5DGmeXTgumAiG0RP1xB4sF4NdmBm5BEGjRRlIBzjGnQ,4002
52
+ jarvis/jarvis_tools/read_code.py,sha256=Pr7e06qHpVn7_cuXCZY1HuJ28MCHMPzCQltn9GSza8o,4030
53
53
  jarvis/jarvis_tools/read_webpage.py,sha256=JCReSXhkDHDkQ606sZYIKG1Itlprjpmu1sSbF-Ed-jI,2478
54
54
  jarvis/jarvis_tools/registry.py,sha256=OR-BxSVfI3ER_1rAPMZfLf45E2YpheeS01j8MJ8RGso,11841
55
55
  jarvis/jarvis_tools/search.py,sha256=PLSSNETyajpqDoStCTfkoy-D41IMNudTuVzonMlT6Aw,9225
56
56
  jarvis/jarvis_tools/select_code_files.py,sha256=bjJGwCNw0Ue_8jW60K1gcy1rUgKqoHihicu5SS58WNk,1890
57
- jarvis_ai_assistant-0.1.110.dist-info/LICENSE,sha256=AGgVgQmTqFvaztRtCAXsAMryUymB18gZif7_l2e1XOg,1063
58
- jarvis_ai_assistant-0.1.110.dist-info/METADATA,sha256=0Ogu7gge_EAhRT6p7HXEqeGNb4uAFMa6MedSJQsp0yY,14392
59
- jarvis_ai_assistant-0.1.110.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
60
- jarvis_ai_assistant-0.1.110.dist-info/entry_points.txt,sha256=UYj4FYvOH8jJ0GgCJTA_TAmJ3wvikos-hUVbCwt_KOc,480
61
- jarvis_ai_assistant-0.1.110.dist-info/top_level.txt,sha256=1BOxyWfzOP_ZXj8rVTDnNCJ92bBGB0rwq8N1PCpoMIs,7
62
- jarvis_ai_assistant-0.1.110.dist-info/RECORD,,
57
+ jarvis/utils/date_utils.py,sha256=DZCQ91FBC4dnQRKCqNnACTg3HuqzQ7R1DRYqH59tON4,537
58
+ jarvis_ai_assistant-0.1.111.dist-info/LICENSE,sha256=AGgVgQmTqFvaztRtCAXsAMryUymB18gZif7_l2e1XOg,1063
59
+ jarvis_ai_assistant-0.1.111.dist-info/METADATA,sha256=FEftsBlMHWqbUroz5ZdWcDD6JaOZlKlzQ3PJshAC-bQ,14323
60
+ jarvis_ai_assistant-0.1.111.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
61
+ jarvis_ai_assistant-0.1.111.dist-info/entry_points.txt,sha256=UYj4FYvOH8jJ0GgCJTA_TAmJ3wvikos-hUVbCwt_KOc,480
62
+ jarvis_ai_assistant-0.1.111.dist-info/top_level.txt,sha256=1BOxyWfzOP_ZXj8rVTDnNCJ92bBGB0rwq8N1PCpoMIs,7
63
+ jarvis_ai_assistant-0.1.111.dist-info/RECORD,,