janito 2.3.0__py3-none-any.whl → 2.4.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 (150) hide show
  1. janito/__init__.py +6 -6
  2. janito/_version.py +57 -0
  3. janito/agent/setup_agent.py +92 -18
  4. janito/agent/templates/profiles/system_prompt_template_developer.txt.j2 +44 -0
  5. janito/cli/chat_mode/bindings.py +21 -2
  6. janito/cli/chat_mode/chat_entry.py +2 -3
  7. janito/cli/chat_mode/prompt_style.py +5 -0
  8. janito/cli/chat_mode/session.py +80 -94
  9. janito/cli/chat_mode/session_profile_select.py +80 -0
  10. janito/cli/chat_mode/shell/autocomplete.py +21 -21
  11. janito/cli/chat_mode/shell/commands/__init__.py +13 -7
  12. janito/cli/chat_mode/shell/commands/_priv_check.py +5 -0
  13. janito/cli/chat_mode/shell/commands/clear.py +12 -12
  14. janito/cli/chat_mode/shell/commands/conversation_restart.py +30 -0
  15. janito/cli/chat_mode/shell/commands/execute.py +42 -0
  16. janito/cli/chat_mode/shell/commands/help.py +6 -3
  17. janito/cli/chat_mode/shell/commands/model.py +28 -0
  18. janito/cli/chat_mode/shell/commands/multi.py +51 -51
  19. janito/cli/chat_mode/shell/commands/read.py +37 -0
  20. janito/cli/chat_mode/shell/commands/tools.py +45 -18
  21. janito/cli/chat_mode/shell/commands/write.py +37 -0
  22. janito/cli/chat_mode/shell/commands.bak.zip +0 -0
  23. janito/cli/chat_mode/shell/input_history.py +62 -62
  24. janito/cli/chat_mode/shell/session.bak.zip +0 -0
  25. janito/cli/chat_mode/toolbar.py +44 -27
  26. janito/cli/cli_commands/list_models.py +35 -35
  27. janito/cli/cli_commands/list_providers.py +9 -9
  28. janito/cli/cli_commands/list_tools.py +86 -53
  29. janito/cli/cli_commands/model_selection.py +50 -50
  30. janito/cli/cli_commands/set_api_key.py +19 -19
  31. janito/cli/cli_commands/show_config.py +51 -51
  32. janito/cli/cli_commands/show_system_prompt.py +105 -62
  33. janito/cli/config.py +5 -6
  34. janito/cli/core/__init__.py +4 -4
  35. janito/cli/core/event_logger.py +59 -59
  36. janito/cli/core/runner.py +25 -18
  37. janito/cli/core/setters.py +10 -1
  38. janito/cli/core/unsetters.py +54 -54
  39. janito/cli/main_cli.py +28 -5
  40. janito/cli/prompt_core.py +18 -2
  41. janito/cli/prompt_setup.py +56 -0
  42. janito/cli/single_shot_mode/__init__.py +6 -6
  43. janito/cli/single_shot_mode/handler.py +14 -73
  44. janito/cli/verbose_output.py +1 -1
  45. janito/config.py +5 -5
  46. janito/config_manager.py +13 -0
  47. janito/drivers/anthropic/driver.py +113 -113
  48. janito/drivers/dashscope.bak.zip +0 -0
  49. janito/drivers/openai/README.md +20 -0
  50. janito/drivers/openai_responses.bak.zip +0 -0
  51. janito/event_bus/event.py +2 -2
  52. janito/formatting_token.py +54 -54
  53. janito/i18n/__init__.py +35 -35
  54. janito/i18n/messages.py +23 -23
  55. janito/i18n/pt.py +46 -47
  56. janito/llm/README.md +23 -0
  57. janito/llm/__init__.py +5 -5
  58. janito/llm/agent.py +507 -443
  59. janito/llm/driver.py +8 -0
  60. janito/llm/driver_config_builder.py +34 -34
  61. janito/llm/driver_input.py +12 -12
  62. janito/llm/message_parts.py +60 -60
  63. janito/llm/model.py +38 -38
  64. janito/llm/provider.py +196 -196
  65. janito/provider_registry.py +8 -6
  66. janito/providers/anthropic/model_info.py +22 -22
  67. janito/providers/anthropic/provider.py +2 -0
  68. janito/providers/azure_openai/provider.py +3 -0
  69. janito/providers/dashscope.bak.zip +0 -0
  70. janito/providers/deepseek/__init__.py +1 -1
  71. janito/providers/deepseek/model_info.py +16 -16
  72. janito/providers/deepseek/provider.py +94 -91
  73. janito/providers/google/provider.py +3 -0
  74. janito/providers/mistralai/provider.py +3 -0
  75. janito/providers/openai/provider.py +4 -0
  76. janito/providers/registry.py +26 -26
  77. janito/shell.bak.zip +0 -0
  78. janito/tools/DOCSTRING_STANDARD.txt +33 -0
  79. janito/tools/README.md +3 -0
  80. janito/tools/__init__.py +20 -6
  81. janito/tools/adapters/__init__.py +1 -1
  82. janito/tools/adapters/local/__init__.py +65 -62
  83. janito/tools/adapters/local/adapter.py +18 -35
  84. janito/tools/adapters/local/ask_user.py +101 -102
  85. janito/tools/adapters/local/copy_file.py +84 -84
  86. janito/tools/adapters/local/create_directory.py +69 -69
  87. janito/tools/adapters/local/create_file.py +82 -82
  88. janito/tools/adapters/local/delete_text_in_file.py +2 -2
  89. janito/tools/adapters/local/fetch_url.py +97 -97
  90. janito/tools/adapters/local/find_files.py +139 -138
  91. janito/tools/adapters/local/get_file_outline/__init__.py +1 -1
  92. janito/tools/adapters/local/get_file_outline/core.py +117 -117
  93. janito/tools/adapters/local/get_file_outline/java_outline.py +40 -40
  94. janito/tools/adapters/local/get_file_outline/markdown_outline.py +14 -14
  95. janito/tools/adapters/local/get_file_outline/python_outline.py +303 -303
  96. janito/tools/adapters/local/get_file_outline/python_outline_v2.py +156 -156
  97. janito/tools/adapters/local/get_file_outline/search_outline.py +33 -33
  98. janito/tools/adapters/local/move_file.py +2 -2
  99. janito/tools/adapters/local/open_html_in_browser.py +2 -1
  100. janito/tools/adapters/local/open_url.py +2 -2
  101. janito/tools/adapters/local/python_code_run.py +166 -166
  102. janito/tools/adapters/local/python_command_run.py +164 -164
  103. janito/tools/adapters/local/python_file_run.py +163 -163
  104. janito/tools/adapters/local/remove_directory.py +2 -2
  105. janito/tools/adapters/local/remove_file.py +2 -2
  106. janito/tools/adapters/local/replace_text_in_file.py +2 -2
  107. janito/tools/adapters/local/run_bash_command.py +176 -176
  108. janito/tools/adapters/local/run_powershell_command.py +219 -219
  109. janito/tools/adapters/local/search_text/__init__.py +1 -1
  110. janito/tools/adapters/local/search_text/core.py +201 -201
  111. janito/tools/adapters/local/search_text/pattern_utils.py +73 -73
  112. janito/tools/adapters/local/search_text/traverse_directory.py +145 -145
  113. janito/tools/adapters/local/validate_file_syntax/__init__.py +1 -1
  114. janito/tools/adapters/local/validate_file_syntax/core.py +106 -106
  115. janito/tools/adapters/local/validate_file_syntax/css_validator.py +35 -35
  116. janito/tools/adapters/local/validate_file_syntax/html_validator.py +93 -93
  117. janito/tools/adapters/local/validate_file_syntax/js_validator.py +27 -27
  118. janito/tools/adapters/local/validate_file_syntax/json_validator.py +6 -6
  119. janito/tools/adapters/local/validate_file_syntax/markdown_validator.py +109 -109
  120. janito/tools/adapters/local/validate_file_syntax/ps1_validator.py +32 -32
  121. janito/tools/adapters/local/validate_file_syntax/python_validator.py +5 -5
  122. janito/tools/adapters/local/validate_file_syntax/xml_validator.py +11 -11
  123. janito/tools/adapters/local/validate_file_syntax/yaml_validator.py +6 -6
  124. janito/tools/adapters/local/view_file.py +168 -167
  125. janito/tools/inspect_registry.py +17 -17
  126. janito/tools/outline_file.bak.zip +0 -0
  127. janito/tools/permissions.py +45 -0
  128. janito/tools/permissions_parse.py +12 -0
  129. janito/tools/tool_base.py +118 -105
  130. janito/tools/tool_events.py +58 -58
  131. janito/tools/tool_run_exception.py +12 -12
  132. janito/tools/tool_use_tracker.py +81 -81
  133. janito/tools/tool_utils.py +43 -45
  134. janito/tools/tools_adapter.py +25 -20
  135. janito/tools/tools_schema.py +104 -104
  136. {janito-2.3.0.dist-info → janito-2.4.0.dist-info}/METADATA +425 -388
  137. janito-2.4.0.dist-info/RECORD +195 -0
  138. janito/agent/templates/profiles/system_prompt_template_base_pt.txt.j2 +0 -13
  139. janito/agent/templates/profiles/system_prompt_template_main.txt.j2 +0 -37
  140. janito/cli/chat_mode/shell/commands/edit.py +0 -25
  141. janito/cli/chat_mode/shell/commands/exec.py +0 -27
  142. janito/cli/chat_mode/shell/commands/termweb_log.py +0 -92
  143. janito/cli/termweb_starter.py +0 -122
  144. janito/termweb/app.py +0 -95
  145. janito/version.py +0 -4
  146. janito-2.3.0.dist-info/RECORD +0 -181
  147. {janito-2.3.0.dist-info → janito-2.4.0.dist-info}/WHEEL +0 -0
  148. {janito-2.3.0.dist-info → janito-2.4.0.dist-info}/entry_points.txt +0 -0
  149. {janito-2.3.0.dist-info → janito-2.4.0.dist-info}/licenses/LICENSE +0 -0
  150. {janito-2.3.0.dist-info → janito-2.4.0.dist-info}/top_level.txt +0 -0
@@ -1,8 +1,7 @@
1
1
  from janito.tools.tool_base import ToolBase
2
2
  from janito.tools.tool_events import ToolCallStarted, ToolCallFinished, ToolCallError
3
3
  from janito.exceptions import ToolCallException
4
- from typing import Optional
5
-
4
+ from janito.tools.tool_base import ToolPermissions
6
5
 
7
6
  class ToolsAdapterBase:
8
7
  """
@@ -13,11 +12,10 @@ class ToolsAdapterBase:
13
12
  """
14
13
 
15
14
  def __init__(
16
- self, tools=None, event_bus=None, enabled_tools: Optional[list] = None
15
+ self, tools=None, event_bus=None
17
16
  ):
18
17
  self._tools = tools or []
19
18
  self._event_bus = event_bus # event bus can be set on all adapters
20
- self._enabled_tools = set(enabled_tools) if enabled_tools is not None else None
21
19
  self.verbose_tools = False
22
20
 
23
21
  def set_verbose_tools(self, value: bool):
@@ -31,11 +29,28 @@ class ToolsAdapterBase:
31
29
  def event_bus(self, bus):
32
30
  self._event_bus = bus
33
31
 
32
+ def is_tool_allowed(self, tool):
33
+ """Check if a tool is allowed based on current global AllowedPermissionsState."""
34
+ from janito.tools.permissions import get_global_allowed_permissions
35
+ allowed_permissions = get_global_allowed_permissions()
36
+ perms = tool.permissions # permissions are mandatory and type-checked
37
+ # If all permissions are False, block all tools
38
+ if not (allowed_permissions.read or allowed_permissions.write or allowed_permissions.execute):
39
+ return False
40
+ for perm in ['read', 'write', 'execute']:
41
+ if getattr(perms, perm) and not getattr(allowed_permissions, perm):
42
+ return False
43
+ return True
44
+
34
45
  def get_tools(self):
35
- """Return the list of enabled tools managed by this provider."""
36
- if self._enabled_tools is None:
37
- return self._tools
38
- return [tool for tool in self._tools if getattr(tool, 'tool_name', None) in self._enabled_tools]
46
+ """Return the list of enabled tools managed by this provider, filtered by allowed permissions."""
47
+ tools = [tool for tool in self._tools if self.is_tool_allowed(tool)]
48
+ return tools
49
+
50
+ def set_allowed_permissions(self, allowed_permissions):
51
+ """Set the allowed permissions at runtime. This now updates the global AllowedPermissionsState only."""
52
+ from janito.tools.permissions import set_global_allowed_permissions
53
+ set_global_allowed_permissions(allowed_permissions)
39
54
 
40
55
  def add_tool(self, tool):
41
56
  self._tools.append(tool)
@@ -231,18 +246,8 @@ class ToolsAdapterBase:
231
246
  )
232
247
 
233
248
  def _check_tool_permissions(self, tool_name, request_id, arguments):
234
- if self._enabled_tools is not None and tool_name not in self._enabled_tools:
235
- error_msg = f"Tool '{tool_name}' is not enabled in this adapter."
236
- if self._event_bus:
237
- self._event_bus.publish(
238
- ToolCallError(
239
- tool_name=tool_name,
240
- request_id=request_id,
241
- error=error_msg,
242
- arguments=arguments,
243
- )
244
- )
245
- raise ToolCallException(tool_name, error_msg, arguments=arguments)
249
+ # No enabled_tools check anymore; permission checks are handled by is_tool_allowed
250
+ pass
246
251
 
247
252
  def _ensure_tool_exists(self, tool, tool_name, request_id, arguments):
248
253
  if tool is None:
@@ -1,104 +1,104 @@
1
- import inspect
2
- import typing
3
- import re
4
-
5
-
6
- class ToolSchemaBase:
7
- def parse_param_section(self, lines, param_section_headers):
8
- param_descs = {}
9
- in_params = False
10
- for line in lines:
11
- stripped_line = line.strip()
12
- if any(
13
- stripped_line.lower().startswith(h + ":") or stripped_line.lower() == h
14
- for h in param_section_headers
15
- ):
16
- in_params = True
17
- continue
18
- if in_params:
19
- m = re.match(
20
- r"([a-zA-Z_][a-zA-Z0-9_]*)\s*(?:\(([^)]+)\))?\s*[:\-]?\s*(.+)",
21
- stripped_line,
22
- )
23
- if m:
24
- param, _, desc = m.groups()
25
- param_descs[param] = desc.strip()
26
- elif stripped_line and stripped_line[0] != "-":
27
- if param_descs:
28
- last = list(param_descs)[-1]
29
- param_descs[last] += " " + stripped_line
30
- if (
31
- stripped_line.lower().startswith("returns:")
32
- or stripped_line.lower() == "returns"
33
- ):
34
- break
35
- return param_descs
36
-
37
- def parse_return_section(self, lines):
38
- in_returns = False
39
- return_desc = ""
40
- for line in lines:
41
- stripped_line = line.strip()
42
- if (
43
- stripped_line.lower().startswith("returns:")
44
- or stripped_line.lower() == "returns"
45
- ):
46
- in_returns = True
47
- continue
48
- if in_returns:
49
- if stripped_line:
50
- return_desc += (" " if return_desc else "") + stripped_line
51
- return return_desc
52
-
53
- def parse_docstring(self, docstring: str):
54
- if not docstring:
55
- return "", {}, ""
56
- lines = docstring.strip().split("\n")
57
- summary = lines[0].strip()
58
- param_section_headers = ("args", "arguments", "params", "parameters")
59
- param_descs = self.parse_param_section(lines[1:], param_section_headers)
60
- return_desc = self.parse_return_section(lines[1:])
61
- return summary, param_descs, return_desc
62
-
63
- def validate_tool_class(self, tool_class):
64
- if not hasattr(tool_class, "tool_name") or not isinstance(
65
- tool_class.tool_name, str
66
- ):
67
- raise ValueError(
68
- "Tool class must have a class-level 'tool_name' attribute (str) for registry and schema generation."
69
- )
70
- if not hasattr(tool_class, "run") or not callable(getattr(tool_class, "run")):
71
- raise ValueError("Tool class must have a callable 'run' method.")
72
- func = tool_class.run
73
- tool_name = tool_class.tool_name
74
- sig = inspect.signature(func)
75
- if sig.return_annotation is inspect._empty or sig.return_annotation is not str:
76
- raise ValueError(
77
- f"Tool '{tool_name}' must have an explicit return type of 'str'. Found: {sig.return_annotation}"
78
- )
79
- missing_type_hints = [
80
- name
81
- for name, param in sig.parameters.items()
82
- if name != "self" and param.annotation is inspect._empty
83
- ]
84
- if missing_type_hints:
85
- raise ValueError(
86
- f"Tool '{tool_name}' is missing type hints for parameter(s): {', '.join(missing_type_hints)}.\nAll parameters must have explicit type hints for schema generation."
87
- )
88
- class_doc = (
89
- tool_class.__doc__.strip() if tool_class and tool_class.__doc__ else ""
90
- )
91
- summary, param_descs, return_desc = self.parse_docstring(class_doc)
92
- description = summary
93
- if return_desc:
94
- description += f"\n\nReturns: {return_desc}"
95
- undocumented = [
96
- name
97
- for name, param in sig.parameters.items()
98
- if name != "self" and name not in param_descs
99
- ]
100
- if undocumented:
101
- raise ValueError(
102
- f"Tool '{tool_name}' is missing docstring documentation for parameter(s): {', '.join(undocumented)}.\nParameter documentation must be provided in the Tool class docstring, not the method docstring."
103
- )
104
- return func, tool_name, sig, summary, param_descs, return_desc, description
1
+ import inspect
2
+ import typing
3
+ import re
4
+
5
+
6
+ class ToolSchemaBase:
7
+ def parse_param_section(self, lines, param_section_headers):
8
+ param_descs = {}
9
+ in_params = False
10
+ for line in lines:
11
+ stripped_line = line.strip()
12
+ if any(
13
+ stripped_line.lower().startswith(h + ":") or stripped_line.lower() == h
14
+ for h in param_section_headers
15
+ ):
16
+ in_params = True
17
+ continue
18
+ if in_params:
19
+ m = re.match(
20
+ r"([a-zA-Z_][a-zA-Z0-9_]*)\s*(?:\(([^)]+)\))?\s*[:\-]?\s*(.+)",
21
+ stripped_line,
22
+ )
23
+ if m:
24
+ param, _, desc = m.groups()
25
+ param_descs[param] = desc.strip()
26
+ elif stripped_line and stripped_line[0] != "-":
27
+ if param_descs:
28
+ last = list(param_descs)[-1]
29
+ param_descs[last] += " " + stripped_line
30
+ if (
31
+ stripped_line.lower().startswith("returns:")
32
+ or stripped_line.lower() == "returns"
33
+ ):
34
+ break
35
+ return param_descs
36
+
37
+ def parse_return_section(self, lines):
38
+ in_returns = False
39
+ return_desc = ""
40
+ for line in lines:
41
+ stripped_line = line.strip()
42
+ if (
43
+ stripped_line.lower().startswith("returns:")
44
+ or stripped_line.lower() == "returns"
45
+ ):
46
+ in_returns = True
47
+ continue
48
+ if in_returns:
49
+ if stripped_line:
50
+ return_desc += (" " if return_desc else "") + stripped_line
51
+ return return_desc
52
+
53
+ def parse_docstring(self, docstring: str):
54
+ if not docstring:
55
+ return "", {}, ""
56
+ lines = docstring.strip().split("\n")
57
+ summary = lines[0].strip()
58
+ param_section_headers = ("args", "arguments", "params", "parameters")
59
+ param_descs = self.parse_param_section(lines[1:], param_section_headers)
60
+ return_desc = self.parse_return_section(lines[1:])
61
+ return summary, param_descs, return_desc
62
+
63
+ def validate_tool_class(self, tool_class):
64
+ if not hasattr(tool_class, "tool_name") or not isinstance(
65
+ tool_class.tool_name, str
66
+ ):
67
+ raise ValueError(
68
+ "Tool class must have a class-level 'tool_name' attribute (str) for registry and schema generation."
69
+ )
70
+ if not hasattr(tool_class, "run") or not callable(getattr(tool_class, "run")):
71
+ raise ValueError("Tool class must have a callable 'run' method.")
72
+ func = tool_class.run
73
+ tool_name = tool_class.tool_name
74
+ sig = inspect.signature(func)
75
+ if sig.return_annotation is inspect._empty or sig.return_annotation is not str:
76
+ raise ValueError(
77
+ f"Tool '{tool_name}' must have an explicit return type of 'str'. Found: {sig.return_annotation}"
78
+ )
79
+ missing_type_hints = [
80
+ name
81
+ for name, param in sig.parameters.items()
82
+ if name != "self" and param.annotation is inspect._empty
83
+ ]
84
+ if missing_type_hints:
85
+ raise ValueError(
86
+ f"Tool '{tool_name}' is missing type hints for parameter(s): {', '.join(missing_type_hints)}.\nAll parameters must have explicit type hints for schema generation."
87
+ )
88
+ class_doc = (
89
+ tool_class.__doc__.strip() if tool_class and tool_class.__doc__ else ""
90
+ )
91
+ summary, param_descs, return_desc = self.parse_docstring(class_doc)
92
+ description = summary
93
+ if return_desc:
94
+ description += f"\n\nReturns: {return_desc}"
95
+ undocumented = [
96
+ name
97
+ for name, param in sig.parameters.items()
98
+ if name != "self" and name not in param_descs
99
+ ]
100
+ if undocumented:
101
+ raise ValueError(
102
+ f"Tool '{tool_name}' is missing docstring documentation for parameter(s): {', '.join(undocumented)}.\nParameter documentation must be provided in the Tool class docstring, not the method docstring."
103
+ )
104
+ return func, tool_name, sig, summary, param_descs, return_desc, description