janito 1.4.0__py3-none-any.whl → 1.5.0__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.
- janito/__init__.py +1 -1
- janito/agent/__init__.py +0 -1
- janito/agent/agent.py +7 -25
- janito/agent/config.py +4 -6
- janito/agent/config_defaults.py +2 -2
- janito/agent/content_handler.py +0 -0
- janito/agent/conversation.py +63 -37
- janito/agent/message_handler.py +18 -0
- janito/agent/openai_schema_generator.py +116 -0
- janito/agent/queued_message_handler.py +32 -0
- janito/agent/rich_tool_handler.py +43 -0
- janito/agent/runtime_config.py +1 -1
- janito/agent/templates/system_instructions.j2 +10 -4
- janito/agent/tool_registry.py +92 -0
- janito/agent/tools/append_text_to_file.py +41 -0
- janito/agent/tools/ask_user.py +16 -3
- janito/agent/tools/create_directory.py +31 -0
- janito/agent/tools/create_file.py +52 -0
- janito/agent/tools/fetch_url.py +23 -8
- janito/agent/tools/find_files.py +40 -21
- janito/agent/tools/get_file_outline.py +26 -8
- janito/agent/tools/get_lines.py +53 -19
- janito/agent/tools/move_file.py +50 -0
- janito/agent/tools/py_compile.py +27 -11
- janito/agent/tools/python_exec.py +43 -14
- janito/agent/tools/remove_directory.py +23 -7
- janito/agent/tools/remove_file.py +38 -0
- janito/agent/tools/replace_text_in_file.py +40 -17
- janito/agent/tools/run_bash_command.py +107 -80
- janito/agent/tools/search_files.py +38 -19
- janito/agent/tools/tool_base.py +30 -3
- janito/agent/tools/tools_utils.py +11 -0
- janito/agent/tools/utils.py +0 -1
- janito/cli/_print_config.py +1 -1
- janito/cli/arg_parser.py +2 -1
- janito/cli/config_commands.py +3 -6
- janito/cli/main.py +2 -2
- janito/cli/runner.py +18 -14
- janito/cli_chat_shell/chat_loop.py +10 -15
- janito/cli_chat_shell/commands.py +8 -3
- janito/cli_chat_shell/config_shell.py +0 -3
- janito/cli_chat_shell/session_manager.py +11 -0
- janito/cli_chat_shell/ui.py +12 -113
- janito/render_prompt.py +0 -1
- janito/rich_utils.py +30 -0
- janito/web/app.py +10 -12
- janito-1.5.0.dist-info/METADATA +176 -0
- janito-1.5.0.dist-info/RECORD +64 -0
- janito/agent/queued_tool_handler.py +0 -16
- janito/agent/tool_handler.py +0 -196
- janito/agent/tools/file_ops.py +0 -114
- janito/agent/tools/rich_utils.py +0 -31
- janito-1.4.0.dist-info/METADATA +0 -142
- janito-1.4.0.dist-info/RECORD +0 -55
- {janito-1.4.0.dist-info → janito-1.5.0.dist-info}/WHEEL +0 -0
- {janito-1.4.0.dist-info → janito-1.5.0.dist-info}/entry_points.txt +0 -0
- {janito-1.4.0.dist-info → janito-1.5.0.dist-info}/licenses/LICENSE +0 -0
- {janito-1.4.0.dist-info → janito-1.5.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,41 @@
|
|
1
|
+
from janito.agent.tools.tool_base import ToolBase
|
2
|
+
from janito.agent.tool_registry import register_tool
|
3
|
+
|
4
|
+
@register_tool(name="append_text_to_file")
|
5
|
+
class AppendTextToFileTool(ToolBase):
|
6
|
+
"""
|
7
|
+
Append the given text to the end of a file.
|
8
|
+
"""
|
9
|
+
def call(self, file_path: str, text_to_append: str) -> str:
|
10
|
+
"""
|
11
|
+
Append the given text to the end of a file.
|
12
|
+
|
13
|
+
Append the given text to the end of a file.
|
14
|
+
|
15
|
+
Args:
|
16
|
+
file_path (str): Path to the file where text will be appended.
|
17
|
+
text_to_append (str): The text content to append to the file.
|
18
|
+
Returns:
|
19
|
+
str: Status message. Example formats:
|
20
|
+
- "Appended 3 lines to /path/to/file.txt"
|
21
|
+
- "Warning: No text provided to append. Operation skipped."
|
22
|
+
- "Error appending text: <error message>"
|
23
|
+
"""
|
24
|
+
if not text_to_append:
|
25
|
+
self.report_warning("⚠️ Warning: No text provided to append. Operation skipped.")
|
26
|
+
return "Warning: No text provided to append. Operation skipped."
|
27
|
+
disp_path = display_path(file_path)
|
28
|
+
self.report_info(f"📝 Appending to {disp_path} ({len(text_to_append)} chars)")
|
29
|
+
try:
|
30
|
+
with open(file_path, 'a', encoding='utf-8') as f:
|
31
|
+
f.write(text_to_append)
|
32
|
+
|
33
|
+
num_lines = text_to_append.count('\n') + (1 if text_to_append else 0)
|
34
|
+
self.report_success(f"✅ {num_lines} {pluralize('line', num_lines)} appended")
|
35
|
+
return f"Appended {num_lines} {pluralize('line', num_lines)} to {file_path}"
|
36
|
+
except Exception as e:
|
37
|
+
self.report_error(f"❌ Error: {e}")
|
38
|
+
return f"Error appending text: {e}"
|
39
|
+
# Use display_path for consistent path reporting
|
40
|
+
from janito.agent.tools.tools_utils import display_path
|
41
|
+
from janito.agent.tools.tools_utils import pluralize
|
janito/agent/tools/ask_user.py
CHANGED
@@ -1,9 +1,23 @@
|
|
1
1
|
from janito.agent.tools.tool_base import ToolBase
|
2
|
-
from janito.agent.
|
2
|
+
from janito.agent.tool_registry import register_tool
|
3
3
|
|
4
|
+
@register_tool(name="ask_user")
|
4
5
|
class AskUserTool(ToolBase):
|
5
6
|
"""Ask the user a question and return their response."""
|
6
7
|
def call(self, question: str) -> str:
|
8
|
+
"""
|
9
|
+
Ask the user a question and return their response.
|
10
|
+
|
11
|
+
Args:
|
12
|
+
question (str): The question to ask the user.
|
13
|
+
|
14
|
+
Returns:
|
15
|
+
str: The user's response as a string. Example:
|
16
|
+
- "Yes"
|
17
|
+
- "No"
|
18
|
+
- "Some detailed answer..."
|
19
|
+
"""
|
20
|
+
|
7
21
|
from rich import print as rich_print
|
8
22
|
from rich.panel import Panel
|
9
23
|
from prompt_toolkit import PromptSession
|
@@ -57,5 +71,4 @@ class AskUserTool(ToolBase):
|
|
57
71
|
return response
|
58
72
|
|
59
73
|
|
60
|
-
from janito.agent.
|
61
|
-
ToolHandler.register_tool(AskUserTool, name="ask_user")
|
74
|
+
from janito.agent.tool_registry import register_tool
|
@@ -0,0 +1,31 @@
|
|
1
|
+
from janito.agent.tool_registry import register_tool
|
2
|
+
from janito.agent.tools.utils import expand_path, display_path
|
3
|
+
from janito.agent.tools.tool_base import ToolBase
|
4
|
+
|
5
|
+
@register_tool(name="create_directory")
|
6
|
+
class CreateDirectoryTool(ToolBase):
|
7
|
+
"""
|
8
|
+
Create a new directory at the specified path.
|
9
|
+
"""
|
10
|
+
def call(self, path: str, overwrite: bool = False) -> str:
|
11
|
+
"""
|
12
|
+
Create a new directory at the specified path.
|
13
|
+
|
14
|
+
Args:
|
15
|
+
path (str): Path for the new directory.
|
16
|
+
overwrite (bool, optional): Whether to overwrite if the directory exists. Defaults to False.
|
17
|
+
|
18
|
+
Returns:
|
19
|
+
str: Status message indicating the result. Example:
|
20
|
+
- "✅ Successfully created the directory at ..."
|
21
|
+
- "❗ Cannot create directory: ..."
|
22
|
+
"""
|
23
|
+
original_path = path
|
24
|
+
path = expand_path(path)
|
25
|
+
disp_path = display_path(original_path, path)
|
26
|
+
if os.path.exists(path):
|
27
|
+
if not os.path.isdir(path):
|
28
|
+
self.report_error(f"❌ Path '{disp_path}' exists and is not a directory.")
|
29
|
+
return f"❌ Path '{disp_path}' exists and is not a directory."
|
30
|
+
# Directory creation logic would go here
|
31
|
+
return f"✅ Successfully created the directory at '{disp_path}'."
|
@@ -0,0 +1,52 @@
|
|
1
|
+
import os
|
2
|
+
from janito.agent.tool_registry import register_tool
|
3
|
+
from janito.agent.tools.utils import expand_path, display_path
|
4
|
+
from janito.agent.tools.tool_base import ToolBase
|
5
|
+
from janito.agent.tools.tools_utils import pluralize
|
6
|
+
|
7
|
+
@register_tool(name="create_file")
|
8
|
+
class CreateFileTool(ToolBase):
|
9
|
+
"""
|
10
|
+
Create a new file or update an existing file with the given content.
|
11
|
+
"""
|
12
|
+
def call(self, path: str, content: str, overwrite: bool = False) -> str:
|
13
|
+
"""
|
14
|
+
Create or update a file with the given content.
|
15
|
+
|
16
|
+
Args:
|
17
|
+
path (str): Path to the file to create or update.
|
18
|
+
content (str): Content to write to the file.
|
19
|
+
overwrite (bool, optional): Whether to overwrite if the file exists. Defaults to False.
|
20
|
+
|
21
|
+
Returns:
|
22
|
+
str: Status message indicating the result. Example:
|
23
|
+
- "✅ Successfully created the file at ..."
|
24
|
+
- "❗ Cannot create file: ..."
|
25
|
+
"""
|
26
|
+
original_path = path
|
27
|
+
path = expand_path(path)
|
28
|
+
updating = os.path.exists(path) and not os.path.isdir(path)
|
29
|
+
disp_path = display_path(original_path, path)
|
30
|
+
if os.path.exists(path):
|
31
|
+
if os.path.isdir(path):
|
32
|
+
self.report_error("❌ Error: is a directory")
|
33
|
+
return f"❌ Cannot create file: '{disp_path}' is an existing directory."
|
34
|
+
if not overwrite:
|
35
|
+
self.report_error(f"❗ Error: file '{disp_path}' exists and overwrite is False")
|
36
|
+
return f"❗ Cannot create file: '{disp_path}' already exists and overwrite is False."
|
37
|
+
if updating and overwrite:
|
38
|
+
self.report_info(f"📝 Updating file: '{disp_path}' ... ")
|
39
|
+
else:
|
40
|
+
self.report_info(f"📝 Creating file: '{disp_path}' ... ")
|
41
|
+
old_lines = None
|
42
|
+
if updating and overwrite:
|
43
|
+
with open(path, "r", encoding="utf-8") as f:
|
44
|
+
old_lines = sum(1 for _ in f)
|
45
|
+
with open(path, "w", encoding="utf-8") as f:
|
46
|
+
f.write(content)
|
47
|
+
new_lines = content.count('\n') + 1 if content else 0
|
48
|
+
if old_lines is not None:
|
49
|
+
self.report_success(f"✅ {new_lines} {pluralize('line', new_lines)}")
|
50
|
+
return f"✅ Successfully updated the file at '{disp_path}' ({old_lines} > {new_lines} lines)."
|
51
|
+
self.report_success(f"✅ {new_lines} {pluralize('line', new_lines)}")
|
52
|
+
return f"✅ Successfully created the file at '{disp_path}' ({new_lines} lines)."
|
janito/agent/tools/fetch_url.py
CHANGED
@@ -1,17 +1,33 @@
|
|
1
1
|
import requests
|
2
|
-
from typing import Optional
|
3
2
|
from bs4 import BeautifulSoup
|
4
|
-
from janito.agent.
|
5
|
-
|
3
|
+
from janito.agent.tool_registry import register_tool
|
4
|
+
|
6
5
|
from janito.agent.tools.tool_base import ToolBase
|
7
6
|
|
7
|
+
@register_tool(name="fetch_url")
|
8
8
|
class FetchUrlTool(ToolBase):
|
9
9
|
"""Fetch the content of a web page and extract its text."""
|
10
10
|
def call(self, url: str, search_strings: list[str] = None) -> str:
|
11
|
-
|
11
|
+
"""
|
12
|
+
Fetch the content of a web page and extract its text.
|
13
|
+
|
14
|
+
Args:
|
15
|
+
url (str): The URL of the web page to fetch.
|
16
|
+
search_strings (list[str], optional): Strings to search for in the page content.
|
17
|
+
|
18
|
+
Returns:
|
19
|
+
str: Extracted text content from the web page, or a warning message. Example:
|
20
|
+
- "<main text content...>"
|
21
|
+
- "No lines found for the provided search strings."
|
22
|
+
- "Warning: Empty URL provided. Operation skipped."
|
23
|
+
"""
|
24
|
+
if not url.strip():
|
25
|
+
self.report_warning("⚠️ Warning: Empty URL provided. Operation skipped.")
|
26
|
+
return "Warning: Empty URL provided. Operation skipped."
|
27
|
+
self.report_info(f"🌐 Fetching URL: {url} ... ")
|
12
28
|
response = requests.get(url, timeout=10)
|
13
29
|
response.raise_for_status()
|
14
|
-
self.update_progress(f"Fetched URL with status {response.status_code}")
|
30
|
+
self.update_progress({'event': 'progress', 'message': f"Fetched URL with status {response.status_code}"})
|
15
31
|
soup = BeautifulSoup(response.text, 'html.parser')
|
16
32
|
text = soup.get_text(separator='\n')
|
17
33
|
|
@@ -27,9 +43,8 @@ class FetchUrlTool(ToolBase):
|
|
27
43
|
if filtered:
|
28
44
|
text = '\n...\n'.join(filtered)
|
29
45
|
else:
|
30
|
-
text = "No
|
46
|
+
text = "No lines found for the provided search strings."
|
31
47
|
|
32
|
-
|
48
|
+
self.report_success("✅ Result")
|
33
49
|
return text
|
34
50
|
|
35
|
-
ToolHandler.register_tool(FetchUrlTool, name="fetch_url")
|
janito/agent/tools/find_files.py
CHANGED
@@ -1,31 +1,50 @@
|
|
1
1
|
from janito.agent.tools.tool_base import ToolBase
|
2
|
-
from janito.agent.
|
3
|
-
|
4
|
-
|
2
|
+
from janito.agent.tool_registry import register_tool
|
3
|
+
|
4
|
+
|
5
5
|
import fnmatch
|
6
|
+
from janito.agent.tools.gitignore_utils import filter_ignored
|
6
7
|
|
8
|
+
@register_tool(name="find_files")
|
7
9
|
class FindFilesTool(ToolBase):
|
8
|
-
|
9
|
-
def call(self,
|
10
|
+
|
11
|
+
def call(self, directories: list[str], pattern: str, recursive: bool=False, max_results: int=100) -> str:
|
12
|
+
"""
|
13
|
+
Find files in one or more directories matching a pattern. Respects .gitignore.
|
14
|
+
|
15
|
+
Args:
|
16
|
+
directories: List of directories to search in.
|
17
|
+
pattern: File pattern to match. Uses Unix shell-style wildcards (fnmatch), e.g. '*.py', 'data_??.csv', '[a-z]*.txt'.
|
18
|
+
recursive: Whether to search recursively in subdirectories. Defaults to False.
|
19
|
+
max_results: Maximum number of results to return. Defaults to 100.
|
20
|
+
Returns:
|
21
|
+
Newline-separated list of matching file paths. Example:
|
22
|
+
"/path/to/file1.py\n/path/to/file2.py"
|
23
|
+
"Warning: Empty file pattern provided. Operation skipped."
|
24
|
+
"""
|
10
25
|
import os
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
return os.path.relpath(path)
|
16
|
-
disp_path = _display_path(directory)
|
17
|
-
rec = "recursively" if recursive else "non-recursively"
|
18
|
-
print_info(f"\U0001F50D Searching '{disp_path}' for pattern '{pattern}' ({rec}, max {max_results})")
|
19
|
-
self.update_progress(f"Searching for files in {directory} matching {pattern}")
|
26
|
+
if not pattern:
|
27
|
+
self.report_warning("⚠️ Warning: Empty file pattern provided. Operation skipped.")
|
28
|
+
return "Warning: Empty file pattern provided. Operation skipped."
|
29
|
+
from janito.agent.tools.tools_utils import display_path
|
20
30
|
matches = []
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
31
|
+
rec = "recursively" if recursive else "non-recursively"
|
32
|
+
for directory in directories:
|
33
|
+
disp_path = display_path(directory)
|
34
|
+
self.report_info(f"🔍 Searching for files '{pattern}' in '{disp_path}'")
|
35
|
+
for root, dirs, files in os.walk(directory):
|
36
|
+
dirs, files = filter_ignored(root, dirs, files)
|
37
|
+
for filename in fnmatch.filter(files, pattern):
|
38
|
+
matches.append(os.path.join(root, filename))
|
39
|
+
if len(matches) >= max_results:
|
40
|
+
break
|
41
|
+
if not recursive:
|
25
42
|
break
|
26
|
-
if
|
43
|
+
if len(matches) >= max_results:
|
27
44
|
break
|
28
|
-
|
45
|
+
|
46
|
+
self.report_success(f" ✅ {len(matches)} {pluralize('file', len(matches))}")
|
29
47
|
return "\n".join(matches)
|
30
48
|
|
31
|
-
|
49
|
+
|
50
|
+
from janito.agent.tools.tools_utils import pluralize
|
@@ -1,21 +1,39 @@
|
|
1
1
|
from janito.agent.tools.tool_base import ToolBase
|
2
|
-
from janito.agent.
|
2
|
+
from janito.agent.tool_registry import register_tool
|
3
3
|
|
4
|
-
from janito.agent.tools.rich_utils import print_info, print_success, print_error
|
5
4
|
|
5
|
+
|
6
|
+
|
7
|
+
@register_tool(name="get_file_outline")
|
6
8
|
class GetFileOutlineTool(ToolBase):
|
7
9
|
"""Get an outline of a file's structure."""
|
8
10
|
def call(self, file_path: str) -> str:
|
9
|
-
|
10
|
-
|
11
|
+
"""
|
12
|
+
Get an outline of a file's structure.
|
13
|
+
|
14
|
+
Args:
|
15
|
+
file_path (str): Path to the file.
|
16
|
+
|
17
|
+
Returns:
|
18
|
+
str: Outline of the file's structure, starting with a summary line. Example:
|
19
|
+
- "Outline: 5 items\nclass MyClass:\ndef my_function():\n..."
|
20
|
+
- "Error reading file: <error message>"
|
21
|
+
"""
|
22
|
+
from janito.agent.tools.tools_utils import display_path
|
23
|
+
disp_path = display_path(file_path)
|
24
|
+
self.report_info(f"📄 Getting outline for: {disp_path}")
|
25
|
+
|
11
26
|
try:
|
12
27
|
with open(file_path, 'r', encoding='utf-8') as f:
|
13
28
|
lines = f.readlines()
|
14
29
|
outline = [line.strip() for line in lines if line.strip()]
|
15
|
-
|
16
|
-
|
30
|
+
num_items = len(outline)
|
31
|
+
|
32
|
+
self.report_success(f" ✅ {num_items} {pluralize('item', num_items)}")
|
33
|
+
return f"Outline: {num_items} items\n" + '\n'.join(outline)
|
17
34
|
except Exception as e:
|
18
|
-
|
35
|
+
self.report_error(f" ❌ Error reading file: {e}")
|
19
36
|
return f"Error reading file: {e}"
|
20
37
|
|
21
|
-
|
38
|
+
|
39
|
+
from janito.agent.tools.tools_utils import pluralize
|
janito/agent/tools/get_lines.py
CHANGED
@@ -1,34 +1,68 @@
|
|
1
1
|
from janito.agent.tools.tool_base import ToolBase
|
2
|
-
from janito.agent.
|
3
|
-
from janito.agent.tools.rich_utils import print_info, print_success, print_error
|
2
|
+
from janito.agent.tool_registry import register_tool
|
4
3
|
|
4
|
+
|
5
|
+
|
6
|
+
@register_tool(name="get_lines")
|
5
7
|
class GetLinesTool(ToolBase):
|
6
|
-
"""
|
8
|
+
"""Read lines from a file. Returns specific lines if a range is provided, or the entire file if no range is given."""
|
7
9
|
def call(self, file_path: str, from_line: int=None, to_line: int=None) -> str:
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
10
|
+
"""
|
11
|
+
Get specific lines from a file.
|
12
|
+
|
13
|
+
Args:
|
14
|
+
file_path (str): Path to the file to read lines from.
|
15
|
+
from_line (int, optional): Starting line number (1-based). If None, starts from the first line.
|
16
|
+
to_line (int, optional): Ending line number (1-based). If None, reads to the end of the file. If both are None, the entire file is returned.
|
17
|
+
|
18
|
+
Returns:
|
19
|
+
str: File content with a header indicating the file name and line range. Example:
|
20
|
+
- "---\nFile: /path/to/file.py | Lines: 1-10 (of 100)\n---\n<lines...>"
|
21
|
+
- "---\nFile: /path/to/file.py | All lines (total: 100)\n---\n<all lines...>"
|
22
|
+
- "Error reading file: <error message>"
|
23
|
+
- "❗ not found"
|
24
|
+
"""
|
25
|
+
from janito.agent.tools.tools_utils import display_path
|
26
|
+
disp_path = display_path(file_path)
|
15
27
|
if from_line and to_line:
|
16
|
-
|
17
|
-
print_info(f"📄 Reading {disp_path}:{from_line} ({count} lines)", end="")
|
28
|
+
self.report_info(f"📄 Reading {disp_path} lines {from_line}-{to_line}")
|
18
29
|
else:
|
19
|
-
|
20
|
-
|
30
|
+
self.report_info(f"📄 Reading {disp_path} (all lines)")
|
31
|
+
|
21
32
|
try:
|
22
33
|
with open(file_path, 'r', encoding='utf-8') as f:
|
23
34
|
lines = f.readlines()
|
24
35
|
selected = lines[(from_line-1 if from_line else 0):(to_line if to_line else None)]
|
36
|
+
selected_len = len(selected)
|
37
|
+
total_lines = len(lines)
|
38
|
+
if from_line and to_line:
|
39
|
+
requested = to_line - from_line + 1
|
40
|
+
if selected_len < requested:
|
41
|
+
|
42
|
+
self.report_success(f" ✅ {selected_len} {pluralize('line', selected_len)} (end)")
|
43
|
+
elif to_line < total_lines:
|
44
|
+
|
45
|
+
self.report_success(f" ✅ {selected_len} {pluralize('line', selected_len)} ({total_lines - to_line} lines to end)")
|
46
|
+
else:
|
47
|
+
|
48
|
+
self.report_success(f" ✅ {selected_len} {pluralize('line', selected_len)} (end)")
|
49
|
+
else:
|
50
|
+
|
51
|
+
self.report_success(f" ✅ {selected_len} {pluralize('line', selected_len)} (full file)")
|
52
|
+
# Prepare header
|
25
53
|
if from_line and to_line:
|
26
|
-
|
54
|
+
header = f"---\nFile: {disp_path} | Lines: {from_line}-{to_line} (of {total_lines})\n---\n"
|
55
|
+
elif from_line:
|
56
|
+
header = f"---\nFile: {disp_path} | Lines: {from_line}-END (of {total_lines})\n---\n"
|
27
57
|
else:
|
28
|
-
|
29
|
-
return ''.join(selected)
|
58
|
+
header = f"---\nFile: {disp_path} | All lines (total: {total_lines})\n---\n"
|
59
|
+
return header + ''.join(selected)
|
30
60
|
except Exception as e:
|
31
|
-
|
61
|
+
if isinstance(e, FileNotFoundError):
|
62
|
+
self.report_error("❗ not found")
|
63
|
+
return "❗ not found"
|
64
|
+
self.report_error(f" ❌ Error: {e}")
|
32
65
|
return f"Error reading file: {e}"
|
33
66
|
|
34
|
-
|
67
|
+
|
68
|
+
from janito.agent.tools.tools_utils import pluralize
|
@@ -0,0 +1,50 @@
|
|
1
|
+
import os
|
2
|
+
import shutil
|
3
|
+
from janito.agent.tool_registry import register_tool
|
4
|
+
from janito.agent.tools.utils import expand_path, display_path
|
5
|
+
from janito.agent.tools.tool_base import ToolBase
|
6
|
+
|
7
|
+
@register_tool(name="move_file")
|
8
|
+
class MoveFileTool(ToolBase):
|
9
|
+
"""
|
10
|
+
Move a file from src_path to dest_path.
|
11
|
+
"""
|
12
|
+
def call(self, src_path: str, dest_path: str, overwrite: bool = False) -> str:
|
13
|
+
"""
|
14
|
+
Move a file from src_path to dest_path.
|
15
|
+
|
16
|
+
Args:
|
17
|
+
src_path (str): Source file path.
|
18
|
+
dest_path (str): Destination file path.
|
19
|
+
overwrite (bool, optional): Whether to overwrite if the destination exists. Defaults to False.
|
20
|
+
|
21
|
+
Returns:
|
22
|
+
str: Status message indicating the result.
|
23
|
+
"""
|
24
|
+
original_src = src_path
|
25
|
+
original_dest = dest_path
|
26
|
+
src = expand_path(src_path)
|
27
|
+
dest = expand_path(dest_path)
|
28
|
+
disp_src = display_path(original_src, src)
|
29
|
+
disp_dest = display_path(original_dest, dest)
|
30
|
+
|
31
|
+
if not os.path.exists(src):
|
32
|
+
self.report_error(f"\u274c Source file '{disp_src}' does not exist.")
|
33
|
+
return f"\u274c Source file '{disp_src}' does not exist."
|
34
|
+
if not os.path.isfile(src):
|
35
|
+
self.report_error(f"\u274c Source path '{disp_src}' is not a file.")
|
36
|
+
return f"\u274c Source path '{disp_src}' is not a file."
|
37
|
+
if os.path.exists(dest):
|
38
|
+
if not overwrite:
|
39
|
+
self.report_error(f"\u2757 Destination '{disp_dest}' exists and overwrite is False.")
|
40
|
+
return f"\u2757 Destination '{disp_dest}' already exists and overwrite is False."
|
41
|
+
if os.path.isdir(dest):
|
42
|
+
self.report_error(f"\u274c Destination '{disp_dest}' is a directory.")
|
43
|
+
return f"\u274c Destination '{disp_dest}' is a directory."
|
44
|
+
try:
|
45
|
+
shutil.move(src, dest)
|
46
|
+
self.report_success(f"\u2705 File moved from '{disp_src}' to '{disp_dest}'")
|
47
|
+
return f"\u2705 Successfully moved the file from '{disp_src}' to '{disp_dest}'."
|
48
|
+
except Exception as e:
|
49
|
+
self.report_error(f"\u274c Error moving file: {e}")
|
50
|
+
return f"\u274c Error moving file: {e}"
|
janito/agent/tools/py_compile.py
CHANGED
@@ -1,23 +1,39 @@
|
|
1
1
|
from janito.agent.tools.tool_base import ToolBase
|
2
|
-
from janito.agent.
|
3
|
-
|
2
|
+
from janito.agent.tool_registry import register_tool
|
3
|
+
|
4
|
+
|
4
5
|
from typing import Optional
|
5
6
|
import py_compile
|
6
7
|
|
8
|
+
@register_tool(name="py_compile")
|
7
9
|
class PyCompileTool(ToolBase):
|
8
|
-
"""
|
10
|
+
"""
|
11
|
+
Validate a Python file by compiling it with py_compile.
|
12
|
+
Useful to validate python files after changing them, specially after import changes.
|
13
|
+
"""
|
9
14
|
def call(self, file_path: str, doraise: Optional[bool] = True) -> str:
|
10
|
-
|
11
|
-
|
15
|
+
"""
|
16
|
+
Compile a Python file to check for syntax errors.
|
17
|
+
|
18
|
+
Args:
|
19
|
+
file_path (str): Path to the Python file to compile.
|
20
|
+
doraise (bool, optional): Whether to raise exceptions on compilation errors. Defaults to True.
|
21
|
+
|
22
|
+
Returns:
|
23
|
+
str: Compilation status message. Example:
|
24
|
+
- "✅ Compiled"
|
25
|
+
- "Compile error: <error message>"
|
26
|
+
- "Error: <error message>"
|
27
|
+
"""
|
28
|
+
self.report_info(f"🛠️ Compiling Python file: {file_path}")
|
29
|
+
|
12
30
|
try:
|
13
31
|
py_compile.compile(file_path, doraise=doraise)
|
14
|
-
|
15
|
-
return
|
32
|
+
self.report_success("✅ Compiled")
|
33
|
+
return "✅ Compiled"
|
16
34
|
except py_compile.PyCompileError as e:
|
17
|
-
|
35
|
+
self.report_error(f" [py_compile] Compile error: {e}")
|
18
36
|
return f"Compile error: {e}"
|
19
37
|
except Exception as e:
|
20
|
-
|
38
|
+
self.report_error(f" [py_compile] Error: {e}")
|
21
39
|
return f"Error: {e}"
|
22
|
-
|
23
|
-
ToolHandler.register_tool(PyCompileTool, name="py_compile_file")
|
@@ -1,14 +1,12 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
import sys
|
1
|
+
|
2
|
+
|
4
3
|
import multiprocessing
|
5
4
|
import io
|
6
|
-
from typing import Optional
|
7
5
|
from janito.agent.tools.tool_base import ToolBase
|
6
|
+
from janito.agent.tool_registry import register_tool
|
8
7
|
|
9
8
|
|
10
9
|
def _run_python_code(code: str, result_queue):
|
11
|
-
import traceback
|
12
10
|
import contextlib
|
13
11
|
stdout = io.StringIO()
|
14
12
|
stderr = io.StringIO()
|
@@ -22,18 +20,49 @@ def _run_python_code(code: str, result_queue):
|
|
22
20
|
|
23
21
|
|
24
22
|
# Converted python_exec function into PythonExecTool subclass
|
23
|
+
@register_tool(name="python_exec")
|
25
24
|
class PythonExecTool(ToolBase):
|
26
25
|
"""
|
27
26
|
Execute Python code in a separate process and capture output.
|
27
|
+
Useful for exact calculations, retrieving the current date and time, or performing any Python-supported operation.
|
28
28
|
Args:
|
29
29
|
code (str): The Python code to execute.
|
30
30
|
Returns:
|
31
31
|
str: Formatted stdout, stderr, and return code.
|
32
32
|
"""
|
33
33
|
def call(self, code: str) -> str:
|
34
|
-
|
35
|
-
|
36
|
-
|
34
|
+
"""
|
35
|
+
Execute arbitrary Python code, including exact calculations, getting the current date, time, and more.
|
36
|
+
|
37
|
+
Args:
|
38
|
+
code (str): The Python code to execute.
|
39
|
+
|
40
|
+
Returns:
|
41
|
+
str: Formatted output including stdout, stderr, and return code. Example:
|
42
|
+
- "stdout:\n<output>\nstderr:\n<errors>\nreturncode: 0"
|
43
|
+
- "stdout:\n\nstderr:\nNo result returned from process.\nreturncode: -1"
|
44
|
+
"""
|
45
|
+
"""
|
46
|
+
Execute arbitrary Python code, including exact calculations, getting the current date, time, and more.
|
47
|
+
|
48
|
+
Args:
|
49
|
+
code (str): The Python code to execute.
|
50
|
+
|
51
|
+
Returns:
|
52
|
+
str: Formatted stdout, stderr, and return code.
|
53
|
+
"""
|
54
|
+
"""
|
55
|
+
Execute arbitrary Python code, including exact calculations, getting the current date, time, and more.
|
56
|
+
|
57
|
+
Args:
|
58
|
+
code (str): The Python code to execute.
|
59
|
+
|
60
|
+
Returns:
|
61
|
+
str: Formatted stdout, stderr, and return code.
|
62
|
+
"""
|
63
|
+
self.report_info("🐍 Executing Python code ...")
|
64
|
+
self.report_info(code)
|
65
|
+
|
37
66
|
result_queue = multiprocessing.Queue()
|
38
67
|
process = multiprocessing.Process(target=_run_python_code, args=(code, result_queue))
|
39
68
|
process.start()
|
@@ -42,13 +71,13 @@ class PythonExecTool(ToolBase):
|
|
42
71
|
result = result_queue.get()
|
43
72
|
else:
|
44
73
|
result = {'stdout': '', 'stderr': 'No result returned from process.', 'returncode': -1}
|
45
|
-
|
74
|
+
|
46
75
|
if result['returncode'] == 0:
|
47
|
-
|
48
|
-
|
76
|
+
|
77
|
+
self.report_success("✅ Python code executed")
|
49
78
|
else:
|
50
|
-
|
51
|
-
|
79
|
+
|
80
|
+
self.report_error(f"\u274c Python code execution failed with return code {result['returncode']}")
|
52
81
|
return f"stdout:\n{result['stdout']}\nstderr:\n{result['stderr']}\nreturncode: {result['returncode']}"
|
53
82
|
|
54
|
-
|
83
|
+
|