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.
Files changed (58) hide show
  1. janito/__init__.py +1 -1
  2. janito/agent/__init__.py +0 -1
  3. janito/agent/agent.py +7 -25
  4. janito/agent/config.py +4 -6
  5. janito/agent/config_defaults.py +2 -2
  6. janito/agent/content_handler.py +0 -0
  7. janito/agent/conversation.py +63 -37
  8. janito/agent/message_handler.py +18 -0
  9. janito/agent/openai_schema_generator.py +116 -0
  10. janito/agent/queued_message_handler.py +32 -0
  11. janito/agent/rich_tool_handler.py +43 -0
  12. janito/agent/runtime_config.py +1 -1
  13. janito/agent/templates/system_instructions.j2 +10 -4
  14. janito/agent/tool_registry.py +92 -0
  15. janito/agent/tools/append_text_to_file.py +41 -0
  16. janito/agent/tools/ask_user.py +16 -3
  17. janito/agent/tools/create_directory.py +31 -0
  18. janito/agent/tools/create_file.py +52 -0
  19. janito/agent/tools/fetch_url.py +23 -8
  20. janito/agent/tools/find_files.py +40 -21
  21. janito/agent/tools/get_file_outline.py +26 -8
  22. janito/agent/tools/get_lines.py +53 -19
  23. janito/agent/tools/move_file.py +50 -0
  24. janito/agent/tools/py_compile.py +27 -11
  25. janito/agent/tools/python_exec.py +43 -14
  26. janito/agent/tools/remove_directory.py +23 -7
  27. janito/agent/tools/remove_file.py +38 -0
  28. janito/agent/tools/replace_text_in_file.py +40 -17
  29. janito/agent/tools/run_bash_command.py +107 -80
  30. janito/agent/tools/search_files.py +38 -19
  31. janito/agent/tools/tool_base.py +30 -3
  32. janito/agent/tools/tools_utils.py +11 -0
  33. janito/agent/tools/utils.py +0 -1
  34. janito/cli/_print_config.py +1 -1
  35. janito/cli/arg_parser.py +2 -1
  36. janito/cli/config_commands.py +3 -6
  37. janito/cli/main.py +2 -2
  38. janito/cli/runner.py +18 -14
  39. janito/cli_chat_shell/chat_loop.py +10 -15
  40. janito/cli_chat_shell/commands.py +8 -3
  41. janito/cli_chat_shell/config_shell.py +0 -3
  42. janito/cli_chat_shell/session_manager.py +11 -0
  43. janito/cli_chat_shell/ui.py +12 -113
  44. janito/render_prompt.py +0 -1
  45. janito/rich_utils.py +30 -0
  46. janito/web/app.py +10 -12
  47. janito-1.5.0.dist-info/METADATA +176 -0
  48. janito-1.5.0.dist-info/RECORD +64 -0
  49. janito/agent/queued_tool_handler.py +0 -16
  50. janito/agent/tool_handler.py +0 -196
  51. janito/agent/tools/file_ops.py +0 -114
  52. janito/agent/tools/rich_utils.py +0 -31
  53. janito-1.4.0.dist-info/METADATA +0 -142
  54. janito-1.4.0.dist-info/RECORD +0 -55
  55. {janito-1.4.0.dist-info → janito-1.5.0.dist-info}/WHEEL +0 -0
  56. {janito-1.4.0.dist-info → janito-1.5.0.dist-info}/entry_points.txt +0 -0
  57. {janito-1.4.0.dist-info → janito-1.5.0.dist-info}/licenses/LICENSE +0 -0
  58. {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
@@ -1,9 +1,23 @@
1
1
  from janito.agent.tools.tool_base import ToolBase
2
- from janito.agent.tools.rich_utils import print_info, print_success
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.tool_handler import ToolHandler
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)."
@@ -1,17 +1,33 @@
1
1
  import requests
2
- from typing import Optional
3
2
  from bs4 import BeautifulSoup
4
- from janito.agent.tool_handler import ToolHandler
5
- from janito.agent.tools.rich_utils import print_info, print_success, print_error
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
- print_info(f"🌐 Fetching URL: {url} ... ")
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 matches found for the provided search strings."
46
+ text = "No lines found for the provided search strings."
31
47
 
32
- print_success("\u2705 Success")
48
+ self.report_success(" Result")
33
49
  return text
34
50
 
35
- ToolHandler.register_tool(FetchUrlTool, name="fetch_url")
@@ -1,31 +1,50 @@
1
1
  from janito.agent.tools.tool_base import ToolBase
2
- from janito.agent.tool_handler import ToolHandler
3
- from janito.agent.tools.rich_utils import print_info, print_success
4
- import os
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
- """Find files in a directory matching a pattern."""
9
- def call(self, directory: str, pattern: str, recursive: bool=False, max_results: int=100) -> str:
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
- def _display_path(path):
12
- import os
13
- if os.path.isabs(path):
14
- return path
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
- for root, dirs, files in os.walk(directory):
22
- for filename in fnmatch.filter(files, pattern):
23
- matches.append(os.path.join(root, filename))
24
- if len(matches) >= max_results:
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 not recursive:
43
+ if len(matches) >= max_results:
27
44
  break
28
- print_success(f"✅ {len(matches)} found")
45
+
46
+ self.report_success(f" ✅ {len(matches)} {pluralize('file', len(matches))}")
29
47
  return "\n".join(matches)
30
48
 
31
- ToolHandler.register_tool(FindFilesTool, name="find_files")
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.tool_handler import ToolHandler
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
- print_info(f"📄 Getting outline for: {file_path}")
10
- self.update_progress(f"Getting outline for: {file_path}")
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
- print_success(f"\u2705 Outline generated for {file_path}")
16
- return '\n'.join(outline)
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
- print_error(f"\u274c Error reading file: {e}")
35
+ self.report_error(f" Error reading file: {e}")
19
36
  return f"Error reading file: {e}"
20
37
 
21
- ToolHandler.register_tool(GetFileOutlineTool, name="get_file_outline")
38
+
39
+ from janito.agent.tools.tools_utils import pluralize
@@ -1,34 +1,68 @@
1
1
  from janito.agent.tools.tool_base import ToolBase
2
- from janito.agent.tool_handler import ToolHandler
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
- """Get specific lines from a file."""
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
- import os
9
- def _display_path(path):
10
- import os
11
- if os.path.isabs(path):
12
- return path
13
- return os.path.relpath(path)
14
- disp_path = _display_path(file_path)
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
- count = to_line - from_line + 1
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
- print_info(f"📄 Reading {disp_path} (all lines)", end="")
20
- self.update_progress(f"Getting lines {from_line} to {to_line} from {file_path}")
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
- print_success(f" {to_line - from_line + 1} lines read")
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
- print_success(f" {len(lines)} lines read")
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
- print_error(f" ❌ Error: {e}")
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
- ToolHandler.register_tool(GetLinesTool, name="get_lines")
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}"
@@ -1,23 +1,39 @@
1
1
  from janito.agent.tools.tool_base import ToolBase
2
- from janito.agent.tool_handler import ToolHandler
3
- from janito.agent.tools.rich_utils import print_info, print_success, print_error
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
- """Validate a Python file by compiling it with py_compile."""
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
- print_info(f"[py_compile] Compiling Python file: {file_path}")
11
- self.update_progress(f"Compiling Python file: {file_path}")
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
- print_success(f"[py_compile] Compiled successfully: {file_path}")
15
- return f"Compiled successfully: {file_path}"
32
+ self.report_success(" Compiled")
33
+ return "Compiled"
16
34
  except py_compile.PyCompileError as e:
17
- print_error(f"[py_compile] Compile error: {e}")
35
+ self.report_error(f" [py_compile] Compile error: {e}")
18
36
  return f"Compile error: {e}"
19
37
  except Exception as e:
20
- print_error(f"[py_compile] Error: {e}")
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
- from janito.agent.tool_handler import ToolHandler
2
- from janito.agent.tools.rich_utils import print_info
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
- print_info(f"🐍 Executing Python code ...")
35
- print_info(code)
36
- self.update_progress("Starting Python code execution...")
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
- self.update_progress(f"Python code execution completed with return code: {result['returncode']}")
74
+
46
75
  if result['returncode'] == 0:
47
- from janito.agent.tools.rich_utils import print_success
48
- print_success(f"\u2705 Python code executed successfully.")
76
+
77
+ self.report_success(" Python code executed")
49
78
  else:
50
- from janito.agent.tools.rich_utils import print_error
51
- print_error(f"\u274c Python code execution failed with return code {result['returncode']}")
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
- ToolHandler.register_tool(PythonExecTool, name="python_exec")
83
+