janito 1.14.3__py3-none-any.whl → 2.0.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 (282) hide show
  1. janito/__init__.py +6 -1
  2. janito/__main__.py +1 -1
  3. janito/agent/setup_agent.py +139 -0
  4. janito/agent/templates/profiles/{system_prompt_template_base.txt.j2 → system_prompt_template_main.txt.j2} +1 -1
  5. janito/cli/__init__.py +9 -0
  6. janito/cli/chat_mode/bindings.py +37 -0
  7. janito/cli/chat_mode/chat_entry.py +23 -0
  8. janito/cli/chat_mode/prompt_style.py +19 -0
  9. janito/cli/chat_mode/session.py +272 -0
  10. janito/{shell/prompt/completer.py → cli/chat_mode/shell/autocomplete.py} +7 -6
  11. janito/cli/chat_mode/shell/commands/__init__.py +55 -0
  12. janito/cli/chat_mode/shell/commands/base.py +9 -0
  13. janito/cli/chat_mode/shell/commands/clear.py +12 -0
  14. janito/{shell → cli/chat_mode/shell}/commands/conversation_restart.py +34 -30
  15. janito/cli/chat_mode/shell/commands/edit.py +25 -0
  16. janito/cli/chat_mode/shell/commands/help.py +16 -0
  17. janito/cli/chat_mode/shell/commands/history_view.py +93 -0
  18. janito/cli/chat_mode/shell/commands/lang.py +25 -0
  19. janito/cli/chat_mode/shell/commands/last.py +137 -0
  20. janito/cli/chat_mode/shell/commands/livelogs.py +49 -0
  21. janito/cli/chat_mode/shell/commands/multi.py +51 -0
  22. janito/cli/chat_mode/shell/commands/prompt.py +64 -0
  23. janito/cli/chat_mode/shell/commands/role.py +36 -0
  24. janito/cli/chat_mode/shell/commands/session.py +40 -0
  25. janito/{shell → cli/chat_mode/shell}/commands/session_control.py +2 -2
  26. janito/cli/chat_mode/shell/commands/termweb_log.py +92 -0
  27. janito/cli/chat_mode/shell/commands/tools.py +32 -0
  28. janito/{shell → cli/chat_mode/shell}/commands/utility.py +4 -7
  29. janito/{shell → cli/chat_mode/shell}/commands/verbose.py +5 -5
  30. janito/cli/chat_mode/shell/session/__init__.py +1 -0
  31. janito/{shell → cli/chat_mode/shell}/session/manager.py +9 -1
  32. janito/cli/chat_mode/toolbar.py +90 -0
  33. janito/cli/cli_commands/list_models.py +35 -0
  34. janito/cli/cli_commands/list_providers.py +9 -0
  35. janito/cli/cli_commands/list_tools.py +53 -0
  36. janito/cli/cli_commands/model_selection.py +50 -0
  37. janito/cli/cli_commands/model_utils.py +84 -0
  38. janito/cli/cli_commands/set_api_key.py +19 -0
  39. janito/cli/cli_commands/show_config.py +51 -0
  40. janito/cli/cli_commands/show_system_prompt.py +62 -0
  41. janito/cli/config.py +28 -0
  42. janito/cli/console.py +3 -0
  43. janito/cli/core/__init__.py +4 -0
  44. janito/cli/core/event_logger.py +59 -0
  45. janito/cli/core/getters.py +31 -0
  46. janito/cli/core/runner.py +141 -0
  47. janito/cli/core/setters.py +174 -0
  48. janito/cli/core/unsetters.py +54 -0
  49. janito/cli/main.py +8 -196
  50. janito/cli/main_cli.py +312 -0
  51. janito/cli/prompt_core.py +230 -0
  52. janito/cli/prompt_handler.py +6 -0
  53. janito/cli/rich_terminal_reporter.py +101 -0
  54. janito/cli/single_shot_mode/__init__.py +6 -0
  55. janito/cli/single_shot_mode/handler.py +137 -0
  56. janito/cli/termweb_starter.py +73 -24
  57. janito/cli/utils.py +25 -0
  58. janito/cli/verbose_output.py +196 -0
  59. janito/config.py +5 -0
  60. janito/config_manager.py +110 -0
  61. janito/conversation_history.py +30 -0
  62. janito/{agent/tools_utils/dir_walk_utils.py → dir_walk_utils.py} +3 -2
  63. janito/driver_events.py +98 -0
  64. janito/drivers/anthropic/driver.py +113 -0
  65. janito/drivers/azure_openai/driver.py +36 -0
  66. janito/drivers/driver_registry.py +33 -0
  67. janito/drivers/google_genai/driver.py +54 -0
  68. janito/drivers/google_genai/schema_generator.py +67 -0
  69. janito/drivers/mistralai/driver.py +41 -0
  70. janito/drivers/openai/driver.py +334 -0
  71. janito/event_bus/__init__.py +2 -0
  72. janito/event_bus/bus.py +68 -0
  73. janito/event_bus/event.py +15 -0
  74. janito/event_bus/handler.py +31 -0
  75. janito/event_bus/queue_bus.py +57 -0
  76. janito/exceptions.py +23 -0
  77. janito/formatting_token.py +54 -0
  78. janito/i18n/pt.py +1 -0
  79. janito/llm/__init__.py +5 -0
  80. janito/llm/agent.py +443 -0
  81. janito/llm/auth.py +62 -0
  82. janito/llm/driver.py +239 -0
  83. janito/llm/driver_config.py +34 -0
  84. janito/llm/driver_config_builder.py +34 -0
  85. janito/llm/driver_input.py +12 -0
  86. janito/llm/message_parts.py +60 -0
  87. janito/llm/model.py +38 -0
  88. janito/llm/provider.py +187 -0
  89. janito/perf_singleton.py +3 -0
  90. janito/performance_collector.py +167 -0
  91. janito/provider_config.py +98 -0
  92. janito/provider_registry.py +152 -0
  93. janito/providers/__init__.py +7 -0
  94. janito/providers/anthropic/model_info.py +22 -0
  95. janito/providers/anthropic/provider.py +65 -0
  96. janito/providers/azure_openai/model_info.py +15 -0
  97. janito/providers/azure_openai/provider.py +72 -0
  98. janito/providers/deepseek/__init__.py +1 -0
  99. janito/providers/deepseek/model_info.py +16 -0
  100. janito/providers/deepseek/provider.py +91 -0
  101. janito/providers/google/__init__.py +1 -0
  102. janito/providers/google/model_info.py +40 -0
  103. janito/providers/google/provider.py +69 -0
  104. janito/providers/mistralai/model_info.py +37 -0
  105. janito/providers/mistralai/provider.py +69 -0
  106. janito/providers/openai/__init__.py +1 -0
  107. janito/providers/openai/model_info.py +137 -0
  108. janito/providers/openai/provider.py +107 -0
  109. janito/providers/openai/schema_generator.py +63 -0
  110. janito/providers/provider_static_info.py +21 -0
  111. janito/providers/registry.py +26 -0
  112. janito/report_events.py +38 -0
  113. janito/termweb/app.py +1 -1
  114. janito/tools/__init__.py +16 -0
  115. janito/tools/adapters/__init__.py +1 -0
  116. janito/tools/adapters/local/__init__.py +54 -0
  117. janito/tools/adapters/local/adapter.py +92 -0
  118. janito/{agent/tools → tools/adapters/local}/ask_user.py +30 -13
  119. janito/tools/adapters/local/copy_file.py +84 -0
  120. janito/{agent/tools → tools/adapters/local}/create_directory.py +11 -10
  121. janito/tools/adapters/local/create_file.py +82 -0
  122. janito/tools/adapters/local/delete_text_in_file.py +136 -0
  123. janito/{agent/tools → tools/adapters/local}/fetch_url.py +18 -19
  124. janito/tools/adapters/local/find_files.py +140 -0
  125. janito/tools/adapters/local/get_file_outline/core.py +151 -0
  126. janito/{agent/tools → tools/adapters/local}/get_file_outline/python_outline.py +125 -0
  127. janito/tools/adapters/local/get_file_outline/python_outline_v2.py +156 -0
  128. janito/{agent/tools → tools/adapters/local}/get_file_outline/search_outline.py +12 -7
  129. janito/{agent/tools → tools/adapters/local}/move_file.py +13 -9
  130. janito/{agent/tools → tools/adapters/local}/open_url.py +7 -5
  131. janito/tools/adapters/local/python_code_run.py +165 -0
  132. janito/tools/adapters/local/python_command_run.py +163 -0
  133. janito/tools/adapters/local/python_file_run.py +162 -0
  134. janito/{agent/tools → tools/adapters/local}/remove_directory.py +15 -9
  135. janito/{agent/tools → tools/adapters/local}/remove_file.py +17 -14
  136. janito/{agent/tools → tools/adapters/local}/replace_text_in_file.py +27 -22
  137. janito/tools/adapters/local/run_bash_command.py +176 -0
  138. janito/tools/adapters/local/run_powershell_command.py +219 -0
  139. janito/{agent/tools → tools/adapters/local}/search_text/core.py +32 -12
  140. janito/{agent/tools → tools/adapters/local}/search_text/match_lines.py +13 -4
  141. janito/{agent/tools → tools/adapters/local}/search_text/pattern_utils.py +12 -4
  142. janito/{agent/tools → tools/adapters/local}/search_text/traverse_directory.py +15 -2
  143. janito/{agent/tools → tools/adapters/local}/validate_file_syntax/core.py +12 -11
  144. janito/{agent/tools → tools/adapters/local}/validate_file_syntax/css_validator.py +1 -1
  145. janito/{agent/tools → tools/adapters/local}/validate_file_syntax/html_validator.py +1 -1
  146. janito/{agent/tools → tools/adapters/local}/validate_file_syntax/js_validator.py +1 -1
  147. janito/{agent/tools → tools/adapters/local}/validate_file_syntax/json_validator.py +1 -1
  148. janito/{agent/tools → tools/adapters/local}/validate_file_syntax/markdown_validator.py +1 -1
  149. janito/{agent/tools → tools/adapters/local}/validate_file_syntax/ps1_validator.py +1 -1
  150. janito/{agent/tools → tools/adapters/local}/validate_file_syntax/python_validator.py +1 -1
  151. janito/{agent/tools → tools/adapters/local}/validate_file_syntax/xml_validator.py +1 -1
  152. janito/{agent/tools → tools/adapters/local}/validate_file_syntax/yaml_validator.py +1 -1
  153. janito/{agent/tools/get_lines.py → tools/adapters/local/view_file.py} +45 -27
  154. janito/tools/inspect_registry.py +17 -0
  155. janito/tools/tool_base.py +105 -0
  156. janito/tools/tool_events.py +58 -0
  157. janito/tools/tool_run_exception.py +12 -0
  158. janito/{agent → tools}/tool_use_tracker.py +2 -4
  159. janito/{agent/tools_utils/utils.py → tools/tool_utils.py} +18 -9
  160. janito/tools/tools_adapter.py +207 -0
  161. janito/tools/tools_schema.py +104 -0
  162. janito/utils.py +11 -0
  163. janito/version.py +4 -0
  164. janito-2.0.0.dist-info/METADATA +232 -0
  165. janito-2.0.0.dist-info/RECORD +180 -0
  166. janito/agent/__init__.py +0 -0
  167. janito/agent/api_exceptions.py +0 -4
  168. janito/agent/config.py +0 -147
  169. janito/agent/config_defaults.py +0 -12
  170. janito/agent/config_utils.py +0 -0
  171. janito/agent/content_handler.py +0 -0
  172. janito/agent/conversation.py +0 -238
  173. janito/agent/conversation_api.py +0 -306
  174. janito/agent/conversation_exceptions.py +0 -18
  175. janito/agent/conversation_tool_calls.py +0 -39
  176. janito/agent/conversation_ui.py +0 -17
  177. janito/agent/event.py +0 -24
  178. janito/agent/event_dispatcher.py +0 -24
  179. janito/agent/event_handler_protocol.py +0 -5
  180. janito/agent/event_system.py +0 -15
  181. janito/agent/llm_conversation_history.py +0 -82
  182. janito/agent/message_handler.py +0 -20
  183. janito/agent/message_handler_protocol.py +0 -5
  184. janito/agent/openai_client.py +0 -149
  185. janito/agent/openai_schema_generator.py +0 -187
  186. janito/agent/profile_manager.py +0 -96
  187. janito/agent/queued_message_handler.py +0 -50
  188. janito/agent/rich_live.py +0 -32
  189. janito/agent/rich_message_handler.py +0 -115
  190. janito/agent/runtime_config.py +0 -36
  191. janito/agent/test_handler_protocols.py +0 -47
  192. janito/agent/test_openai_schema_generator.py +0 -93
  193. janito/agent/tests/__init__.py +0 -1
  194. janito/agent/tool_base.py +0 -63
  195. janito/agent/tool_executor.py +0 -122
  196. janito/agent/tool_registry.py +0 -49
  197. janito/agent/tools/__init__.py +0 -47
  198. janito/agent/tools/create_file.py +0 -59
  199. janito/agent/tools/delete_text_in_file.py +0 -97
  200. janito/agent/tools/find_files.py +0 -106
  201. janito/agent/tools/get_file_outline/core.py +0 -81
  202. janito/agent/tools/present_choices.py +0 -64
  203. janito/agent/tools/python_command_runner.py +0 -201
  204. janito/agent/tools/python_file_runner.py +0 -199
  205. janito/agent/tools/python_stdin_runner.py +0 -208
  206. janito/agent/tools/replace_file.py +0 -72
  207. janito/agent/tools/run_bash_command.py +0 -218
  208. janito/agent/tools/run_powershell_command.py +0 -251
  209. janito/agent/tools_utils/__init__.py +0 -1
  210. janito/agent/tools_utils/action_type.py +0 -7
  211. janito/agent/tools_utils/test_gitignore_utils.py +0 -46
  212. janito/cli/_livereload_log_utils.py +0 -13
  213. janito/cli/_print_config.py +0 -96
  214. janito/cli/_termweb_log_utils.py +0 -17
  215. janito/cli/_utils.py +0 -9
  216. janito/cli/arg_parser.py +0 -272
  217. janito/cli/cli_main.py +0 -281
  218. janito/cli/config_commands.py +0 -211
  219. janito/cli/config_runner.py +0 -35
  220. janito/cli/formatting_runner.py +0 -12
  221. janito/cli/livereload_starter.py +0 -60
  222. janito/cli/logging_setup.py +0 -38
  223. janito/cli/one_shot.py +0 -80
  224. janito/livereload/app.py +0 -25
  225. janito/rich_utils.py +0 -59
  226. janito/shell/__init__.py +0 -0
  227. janito/shell/commands/__init__.py +0 -61
  228. janito/shell/commands/config.py +0 -22
  229. janito/shell/commands/edit.py +0 -24
  230. janito/shell/commands/history_view.py +0 -18
  231. janito/shell/commands/lang.py +0 -19
  232. janito/shell/commands/livelogs.py +0 -42
  233. janito/shell/commands/prompt.py +0 -62
  234. janito/shell/commands/termweb_log.py +0 -94
  235. janito/shell/commands/tools.py +0 -26
  236. janito/shell/commands/track.py +0 -36
  237. janito/shell/main.py +0 -326
  238. janito/shell/prompt/load_prompt.py +0 -57
  239. janito/shell/prompt/session_setup.py +0 -57
  240. janito/shell/session/config.py +0 -109
  241. janito/shell/session/history.py +0 -0
  242. janito/shell/ui/interactive.py +0 -226
  243. janito/termweb/static/editor.css +0 -158
  244. janito/termweb/static/editor.css.bak +0 -145
  245. janito/termweb/static/editor.html +0 -46
  246. janito/termweb/static/editor.html.bak +0 -46
  247. janito/termweb/static/editor.js +0 -265
  248. janito/termweb/static/editor.js.bak +0 -259
  249. janito/termweb/static/explorer.html.bak +0 -59
  250. janito/termweb/static/favicon.ico +0 -0
  251. janito/termweb/static/favicon.ico.bak +0 -0
  252. janito/termweb/static/index.html +0 -53
  253. janito/termweb/static/index.html.bak +0 -54
  254. janito/termweb/static/index.html.bak.bak +0 -175
  255. janito/termweb/static/landing.html.bak +0 -36
  256. janito/termweb/static/termicon.svg +0 -1
  257. janito/termweb/static/termweb.css +0 -214
  258. janito/termweb/static/termweb.css.bak +0 -237
  259. janito/termweb/static/termweb.js +0 -162
  260. janito/termweb/static/termweb.js.bak +0 -168
  261. janito/termweb/static/termweb.js.bak.bak +0 -157
  262. janito/termweb/static/termweb_quickopen.js +0 -135
  263. janito/termweb/static/termweb_quickopen.js.bak +0 -125
  264. janito/tests/test_rich_utils.py +0 -44
  265. janito/web/__init__.py +0 -0
  266. janito/web/__main__.py +0 -25
  267. janito/web/app.py +0 -145
  268. janito-1.14.3.dist-info/METADATA +0 -313
  269. janito-1.14.3.dist-info/RECORD +0 -162
  270. janito-1.14.3.dist-info/licenses/LICENSE +0 -21
  271. /janito/{shell → cli/chat_mode/shell}/input_history.py +0 -0
  272. /janito/{shell/commands/session.py → cli/chat_mode/shell/session/history.py} +0 -0
  273. /janito/{agent/tools_utils/formatting.py → formatting.py} +0 -0
  274. /janito/{agent/tools_utils/gitignore_utils.py → gitignore_utils.py} +0 -0
  275. /janito/{agent/platform_discovery.py → platform_discovery.py} +0 -0
  276. /janito/{agent/tools → tools/adapters/local}/get_file_outline/__init__.py +0 -0
  277. /janito/{agent/tools → tools/adapters/local}/get_file_outline/markdown_outline.py +0 -0
  278. /janito/{agent/tools → tools/adapters/local}/search_text/__init__.py +0 -0
  279. /janito/{agent/tools → tools/adapters/local}/validate_file_syntax/__init__.py +0 -0
  280. {janito-1.14.3.dist-info → janito-2.0.0.dist-info}/WHEEL +0 -0
  281. {janito-1.14.3.dist-info → janito-2.0.0.dist-info}/entry_points.txt +0 -0
  282. {janito-1.14.3.dist-info → janito-2.0.0.dist-info}/top_level.txt +0 -0
@@ -1,115 +0,0 @@
1
- from rich.console import Console
2
- from janito.agent.runtime_config import runtime_config, unified_config
3
- from janito.agent.message_handler_protocol import MessageHandlerProtocol
4
-
5
- console = Console()
6
-
7
-
8
- def _print_metadata_if_verbose(msg):
9
- if unified_config.get("verbose_messages", False):
10
- # Exclude 'message' field from metadata
11
- meta = {k: v for k, v in msg.items() if k != "message"}
12
- if meta:
13
- console.print("[bold][cyan]Message metadata:[/cyan][/bold]")
14
- for k, v in meta.items():
15
- console.print(f" [green]{k}[/green]: [magenta]{v}[/magenta]")
16
-
17
-
18
- class RichMessageHandler(MessageHandlerProtocol):
19
- """
20
- Unified message handler for all output (tool, agent, system) using Rich for styled output.
21
- """
22
-
23
- def __init__(self):
24
- self.console = console
25
-
26
- def handle_message(self, msg, msg_type=None):
27
- """
28
- Handles a dict with 'type' and 'message'.
29
- All messages must be dicts. Raises if not.
30
- """
31
- trust = runtime_config.get("trust")
32
- if trust is None:
33
- trust = unified_config.get("trust", False)
34
-
35
- if not isinstance(msg, dict):
36
- raise TypeError(
37
- f"RichMessageHandler.handle_message expects a dict with 'type' and 'message', got {type(msg)}: {msg!r}"
38
- )
39
-
40
- msg_type = msg.get("type", "info")
41
- message = msg.get("message", "")
42
-
43
- if trust and msg_type != "content":
44
- return # Suppress all except content
45
-
46
- handler_map = {
47
- "content": self._handle_content,
48
- "info": self._handle_info,
49
- "success": self._handle_success,
50
- "error": self._handle_error,
51
- "progress": self._handle_progress,
52
- "warning": self._handle_warning,
53
- "stdout": self._handle_stdout,
54
- "stderr": self._handle_stderr,
55
- }
56
-
57
- handler = handler_map.get(msg_type)
58
- if handler:
59
- handler(msg, message)
60
- # Ignore unsupported message types silently
61
-
62
- def _handle_content(self, msg, message):
63
- from rich.markdown import Markdown
64
-
65
- _print_metadata_if_verbose(msg)
66
- self.console.print(Markdown(message))
67
-
68
- def _handle_info(self, msg, message):
69
- action_type = msg.get("action_type", None)
70
- style = "cyan" # default
71
- action_type_name = action_type.name if action_type else None
72
- if action_type_name == "READ":
73
- style = "cyan"
74
- elif action_type_name == "WRITE":
75
- style = "bright_magenta"
76
- elif action_type_name == "EXECUTE":
77
- style = "yellow"
78
- _print_metadata_if_verbose(msg)
79
- self.console.print(f" {message}", style=style, end="")
80
-
81
- def _handle_success(self, msg, message):
82
- _print_metadata_if_verbose(msg)
83
- self.console.print(message, style="bold green", end="\n")
84
-
85
- def _handle_error(self, msg, message):
86
- _print_metadata_if_verbose(msg)
87
- self.console.print(message, style="bold red", end="\n")
88
-
89
- def _handle_progress(self, msg, message=None):
90
- _print_metadata_if_verbose(msg)
91
- # Existing logic for progress messages (if any)
92
- # Placeholder: implement as needed
93
- pass
94
-
95
- def _handle_warning(self, msg, message):
96
- _print_metadata_if_verbose(msg)
97
- self.console.print(message, style="bold yellow", end="\n")
98
-
99
- def _handle_stdout(self, msg, message):
100
- from rich.text import Text
101
-
102
- _print_metadata_if_verbose(msg)
103
- self.console.print(
104
- Text(message, style="on #003300", no_wrap=True, overflow=None),
105
- end="",
106
- )
107
-
108
- def _handle_stderr(self, msg, message):
109
- from rich.text import Text
110
-
111
- _print_metadata_if_verbose(msg)
112
- self.console.print(
113
- Text(message, style="on #330000", no_wrap=True, overflow=None),
114
- end="",
115
- )
@@ -1,36 +0,0 @@
1
- from .config import BaseConfig, effective_config
2
-
3
-
4
- class RuntimeConfig(BaseConfig):
5
- """In-memory only config, reset on restart"""
6
-
7
- pass
8
-
9
-
10
- runtime_config = RuntimeConfig()
11
-
12
-
13
- class UnifiedConfig:
14
- """
15
- Config lookup order:
16
- 1. runtime_config (in-memory, highest priority)
17
- 2. effective_config (local/global, read-only)
18
- """
19
-
20
- def __init__(self, runtime_cfg, effective_cfg):
21
- self.runtime_cfg = runtime_cfg
22
- self.effective_cfg = effective_cfg
23
-
24
- def get(self, key, default=None):
25
- val = self.runtime_cfg.get(key)
26
- if val is not None:
27
- return val
28
- return self.effective_cfg.get(key, default)
29
-
30
- def all(self):
31
- merged = dict(self.effective_cfg.all())
32
- merged.update(self.runtime_cfg.all())
33
- return merged
34
-
35
-
36
- unified_config = UnifiedConfig(runtime_config, effective_config)
@@ -1,47 +0,0 @@
1
- from janito.agent.message_handler_protocol import MessageHandlerProtocol
2
- from janito.agent.event_handler_protocol import EventHandlerProtocol
3
- from janito.agent.event_dispatcher import EventDispatcher
4
-
5
-
6
- class DummyMessageHandler(MessageHandlerProtocol):
7
- def __init__(self):
8
- self.last_message = None
9
-
10
- def handle_message(self, msg, msg_type=None):
11
- self.last_message = (msg, msg_type)
12
-
13
-
14
- class DummyEvent:
15
- def __init__(self, type_, payload):
16
- self.type = type_
17
- self.payload = payload
18
-
19
-
20
- class DummyEventHandler(EventHandlerProtocol):
21
- def __init__(self):
22
- self.last_event = None
23
-
24
- def handle_event(self, event):
25
- self.last_event = event
26
-
27
-
28
- def test_message_handler():
29
- handler = DummyMessageHandler()
30
- handler.handle_message({"type": "info", "message": "hello"}, "info")
31
- assert handler.last_message[0]["message"] == "hello"
32
- print("MessageHandlerProtocol test passed")
33
-
34
-
35
- def test_event_dispatcher():
36
- dispatcher = EventDispatcher()
37
- handler = DummyEventHandler()
38
- dispatcher.register("test", handler)
39
- event = DummyEvent("test", {"foo": "bar"})
40
- dispatcher.dispatch(event)
41
- assert handler.last_event.payload["foo"] == "bar"
42
- print("EventHandlerProtocol/EventDispatcher test passed")
43
-
44
-
45
- if __name__ == "__main__":
46
- test_message_handler()
47
- test_event_dispatcher()
@@ -1,93 +0,0 @@
1
- """
2
- Tests for OpenAISchemaGenerator class-based API.
3
- """
4
-
5
- import pytest
6
- from janito.agent.openai_schema_generator import OpenAISchemaGenerator
7
-
8
-
9
- class DummyTool:
10
- """
11
- Dummy tool for testing.
12
-
13
- Args:
14
- foo (str): Foo parameter.
15
- bar (int): Bar parameter.
16
- Returns:
17
- The result as a string.
18
- """
19
-
20
- name = "dummy_tool"
21
-
22
- def run(self, foo: str, bar: int) -> str:
23
- """Run the dummy tool."""
24
- return f"{foo}-{bar}"
25
-
26
-
27
- # Simulate decorator metadata for tests
28
- DummyTool._tool_run_method = DummyTool().run
29
- DummyTool._tool_name = DummyTool.name
30
-
31
-
32
- def test_generate_schema_success():
33
- generator = OpenAISchemaGenerator()
34
- tool = DummyTool
35
- schema = generator.generate_schema(tool)
36
- assert schema["name"] == tool.name
37
- assert "foo" in schema["parameters"]["properties"]
38
- assert "bar" in schema["parameters"]["properties"]
39
- assert schema["parameters"]["properties"]["foo"]["type"] == "string"
40
- assert schema["parameters"]["properties"]["bar"]["type"] == "integer"
41
- assert schema["description"].startswith("Dummy tool for testing.")
42
-
43
-
44
- def test_generate_schema_missing_type():
45
- class BadTool:
46
- """
47
- Args:
48
- foo (str): Foo parameter.
49
- Returns:
50
- String result.
51
- """
52
-
53
- name = "bad_tool"
54
-
55
- def run(self, foo):
56
- return str(foo)
57
-
58
- BadTool._tool_run_method = BadTool().run
59
- BadTool._tool_name = BadTool.name
60
- generator = OpenAISchemaGenerator()
61
- with pytest.raises(ValueError):
62
- generator.generate_schema(BadTool)
63
-
64
-
65
- def test_generate_schema_missing_doc():
66
- class BadTool2:
67
- """
68
- Args:
69
- foo (str): Foo parameter.
70
- Returns:
71
- String result.
72
- """
73
-
74
- name = "bad_tool2"
75
-
76
- def run(self, foo: str, bar: int) -> str:
77
- return str(foo)
78
-
79
- BadTool2._tool_run_method = BadTool2().run
80
- BadTool2._tool_name = BadTool2.name
81
- generator = OpenAISchemaGenerator()
82
- with pytest.raises(ValueError):
83
- generator.generate_schema(BadTool2)
84
-
85
-
86
- def test_generate_schema_requires_metadata():
87
- class NotRegisteredTool:
88
- def run(self, foo: str) -> str:
89
- return foo
90
-
91
- generator = OpenAISchemaGenerator()
92
- with pytest.raises(ValueError):
93
- generator.generate_schema(NotRegisteredTool)
@@ -1 +0,0 @@
1
- # Makes this directory a package for test discovery
janito/agent/tool_base.py DELETED
@@ -1,63 +0,0 @@
1
- from abc import ABC, abstractmethod
2
-
3
-
4
- class ToolBase(ABC):
5
- """
6
- Base class for all tools. Inherit from this class to implement a new tool.
7
- """
8
-
9
- def __init__(self):
10
- self.progress_messages = []
11
- self._progress_callback = None # Will be set by ToolHandler if available
12
-
13
- def report_stdout(self, message: str):
14
- self.update_progress({"type": "stdout", "message": message})
15
-
16
- def report_stderr(self, message: str):
17
- self.update_progress({"type": "stderr", "message": message})
18
-
19
- @abstractmethod
20
- def run(self, **kwargs):
21
- """
22
- Abstract call method for tool execution. Should be overridden by subclasses.
23
-
24
- Args:
25
- **kwargs: Arbitrary keyword arguments for the tool.
26
-
27
- Returns:
28
- Any: The result of the tool execution.
29
- """
30
- pass
31
-
32
- def update_progress(self, progress: dict):
33
- """
34
- Report progress. Subclasses can override this to customize progress reporting.
35
- """
36
- self.progress_messages.append(progress)
37
- if hasattr(self, "_progress_callback") and self._progress_callback:
38
- self._progress_callback(progress)
39
-
40
- def report_info(self, action_type, message: str):
41
- self.update_progress(
42
- {
43
- "type": "info",
44
- "tool": self.__class__.__name__,
45
- "action_type": action_type,
46
- "message": message,
47
- }
48
- )
49
-
50
- def report_success(self, message: str):
51
- self.update_progress(
52
- {"type": "success", "tool": self.__class__.__name__, "message": message}
53
- )
54
-
55
- def report_error(self, message: str):
56
- self.update_progress(
57
- {"type": "error", "tool": self.__class__.__name__, "message": message}
58
- )
59
-
60
- def report_warning(self, message: str):
61
- self.update_progress(
62
- {"type": "warning", "tool": self.__class__.__name__, "message": message}
63
- )
@@ -1,122 +0,0 @@
1
- # janito/agent/tool_executor.py
2
- """
3
- ToolExecutor: Responsible for executing tools, validating arguments, handling errors, and reporting progress.
4
- """
5
-
6
- from janito.i18n import tr
7
- import inspect
8
- from janito.agent.tool_base import ToolBase
9
- from janito.agent.runtime_config import runtime_config
10
-
11
-
12
- class ToolExecutor:
13
- def __init__(self, message_handler=None):
14
- self.message_handler = message_handler
15
-
16
- def execute(self, tool_entry, tool_call, arguments):
17
- import uuid
18
-
19
- call_id = getattr(tool_call, "id", None)
20
- if call_id is None:
21
- raise ValueError("Tool call is missing required 'id' from server.")
22
- func = tool_entry["function"]
23
- args = arguments
24
- if runtime_config.get("no_tools_tracking", False):
25
- tool_call_reason = None
26
- else:
27
- tool_call_reason = args.pop(
28
- "tool_call_reason", None
29
- ) # Extract and remove 'tool_call_reason' if present
30
-
31
- self._maybe_log_tool_call(tool_call, args, tool_call_reason)
32
- instance = self._maybe_set_progress_callback(func)
33
- self._emit_tool_call_event(tool_call, call_id, args, tool_call_reason)
34
- self._validate_arguments(func, args, tool_call, call_id, tool_call_reason)
35
- try:
36
- result = func(**args)
37
- self._emit_tool_result_event(tool_call, call_id, result, tool_call_reason)
38
- self._record_tool_usage(tool_call, args, result)
39
- return result
40
- except Exception as e:
41
- self._emit_tool_error_event(tool_call, call_id, str(e), tool_call_reason)
42
- raise
43
-
44
- def _maybe_log_tool_call(self, tool_call, args, tool_call_reason):
45
- verbose = runtime_config.get("verbose", False)
46
- if verbose:
47
- print(
48
- tr(
49
- "[ToolExecutor] {tool_name} called with arguments: {args}",
50
- tool_name=tool_call.function.name,
51
- args=args,
52
- )
53
- )
54
- if runtime_config.get("verbose_reason", False) and tool_call_reason:
55
- print(
56
- tr(
57
- "[ToolExecutor] Reason for call: {tool_call_reason}",
58
- tool_call_reason=tool_call_reason,
59
- )
60
- )
61
-
62
- def _maybe_set_progress_callback(self, func):
63
- instance = None
64
- if hasattr(func, "__self__") and isinstance(func.__self__, ToolBase):
65
- instance = func.__self__
66
- if self.message_handler:
67
- instance._progress_callback = self.message_handler.handle_message
68
- return instance
69
-
70
- def _emit_tool_call_event(self, tool_call, call_id, args, tool_call_reason):
71
- if self.message_handler:
72
- event = {
73
- "type": "tool_call",
74
- "tool": tool_call.function.name,
75
- "call_id": call_id,
76
- "arguments": args,
77
- }
78
- if tool_call_reason and not runtime_config.get("no_tools_tracking", False):
79
- event["tool_call_reason"] = tool_call_reason
80
- self.message_handler.handle_message(event)
81
-
82
- def _validate_arguments(self, func, args, tool_call, call_id, tool_call_reason):
83
- sig = inspect.signature(func)
84
- try:
85
- sig.bind(**args)
86
- except TypeError as e:
87
- error_msg = f"Argument validation error for tool '{tool_call.function.name}': {str(e)}"
88
- self._emit_tool_error_event(tool_call, call_id, error_msg, tool_call_reason)
89
- raise TypeError(error_msg)
90
-
91
- def _emit_tool_result_event(self, tool_call, call_id, result, tool_call_reason):
92
- if self.message_handler:
93
- result_event = {
94
- "type": "tool_result",
95
- "tool": tool_call.function.name,
96
- "call_id": call_id,
97
- "result": result,
98
- }
99
- if tool_call_reason and not runtime_config.get("no_tools_tracking", False):
100
- result_event["tool_call_reason"] = tool_call_reason
101
- self.message_handler.handle_message(result_event)
102
-
103
- def _emit_tool_error_event(self, tool_call, call_id, error, tool_call_reason):
104
- if self.message_handler:
105
- error_event = {
106
- "type": "tool_error",
107
- "tool": tool_call.function.name,
108
- "call_id": call_id,
109
- "error": error,
110
- }
111
- if tool_call_reason and not runtime_config.get("no_tools_tracking", False):
112
- error_event["tool_call_reason"] = tool_call_reason
113
- self.message_handler.handle_message(error_event)
114
-
115
- def _record_tool_usage(self, tool_call, args, result):
116
- try:
117
- from janito.agent.tool_use_tracker import ToolUseTracker
118
-
119
- ToolUseTracker().record(tool_call.function.name, dict(args), result)
120
- except Exception as e:
121
- if runtime_config.get("verbose", False):
122
- print(f"[ToolExecutor] ToolUseTracker record failed: {e}")
@@ -1,49 +0,0 @@
1
- # janito/agent/tool_registry.py
2
- from janito.agent.tool_base import ToolBase
3
- from janito.agent.openai_schema_generator import OpenAISchemaGenerator
4
-
5
- _tool_registry = {}
6
-
7
-
8
- def register_tool(tool=None, *, name: str = None):
9
- if tool is None:
10
- return lambda t: register_tool(t, name=name)
11
- override_name = name
12
- if not (isinstance(tool, type) and issubclass(tool, ToolBase)):
13
- raise TypeError("Tool must be a class derived from ToolBase.")
14
- instance = tool()
15
- if not hasattr(instance, "run") or not callable(instance.run):
16
- raise TypeError(
17
- f"Tool '{tool.__name__}' must implement a callable 'call' method."
18
- )
19
- tool_name = override_name or instance.name
20
- # Add metadata for schema generation
21
- tool._tool_run_method = instance.run
22
- tool._tool_name = tool_name
23
- if tool_name in _tool_registry:
24
- raise ValueError(f"Tool '{tool_name}' is already registered.")
25
- schema = OpenAISchemaGenerator().generate_schema(tool)
26
- _tool_registry[tool_name] = {
27
- "function": instance.run,
28
- "description": schema["description"],
29
- "parameters": schema["parameters"],
30
- "class": tool,
31
- "instance": instance,
32
- }
33
- return tool
34
-
35
-
36
- def get_tool_schemas():
37
- schemas = []
38
- for name, entry in _tool_registry.items():
39
- schemas.append(
40
- {
41
- "type": "function",
42
- "function": {
43
- "name": name,
44
- "description": entry["description"],
45
- "parameters": entry["parameters"],
46
- },
47
- }
48
- )
49
- return schemas
@@ -1,47 +0,0 @@
1
- from . import ask_user
2
- from . import create_directory
3
- from . import create_file
4
- from . import replace_file
5
- from . import fetch_url
6
- from . import open_url
7
- from . import find_files
8
- from . import get_lines
9
- from .get_file_outline import core # noqa: F401,F811
10
- from . import move_file
11
- from .validate_file_syntax import core # noqa: F401,F811
12
- from . import remove_directory
13
- from . import remove_file
14
- from . import replace_text_in_file
15
- from . import delete_text_in_file
16
- from . import run_bash_command
17
- from . import run_powershell_command
18
- from . import present_choices
19
- from . import search_text
20
- from . import python_command_runner
21
- from . import python_file_runner
22
- from . import python_stdin_runner
23
-
24
- __all__ = [
25
- "ask_user",
26
- "create_directory",
27
- "create_file",
28
- "fetch_url",
29
- "open_url",
30
- "find_files",
31
- "GetFileOutlineTool",
32
- "get_lines",
33
- "move_file",
34
- "validate_file_syntax",
35
- "remove_directory",
36
- "remove_file",
37
- "replace_file",
38
- "replace_text_in_file",
39
- "delete_text_in_file",
40
- "run_bash_command",
41
- "run_powershell_command",
42
- "present_choices",
43
- "search_text",
44
- "python_command_runner",
45
- "python_file_runner",
46
- "python_stdin_runner",
47
- ]
@@ -1,59 +0,0 @@
1
- import os
2
- from janito.agent.tool_registry import register_tool
3
-
4
- # from janito.agent.tools_utils.expand_path import expand_path
5
- from janito.agent.tools_utils.utils import display_path
6
- from janito.agent.tool_base import ToolBase
7
- from janito.agent.tools_utils.action_type import ActionType
8
- from janito.i18n import tr
9
-
10
-
11
- from janito.agent.tools.validate_file_syntax.core import validate_file_syntax
12
-
13
-
14
- @register_tool(name="create_file")
15
- class CreateFileTool(ToolBase):
16
- """
17
- Create a new file with the given content.
18
- Args:
19
- file_path (str): Path to the file to create.
20
- content (str): Content to write to the file.
21
- Returns:
22
- str: Status message indicating the result. Example:
23
- - "✅ Successfully created the file at ..."
24
-
25
- Note: Syntax validation is automatically performed after this operation.
26
- """
27
-
28
- def run(self, file_path: str, content: str) -> str:
29
- expanded_file_path = file_path # Using file_path as is
30
- disp_path = display_path(expanded_file_path)
31
- file_path = expanded_file_path
32
- if os.path.exists(file_path):
33
- try:
34
- with open(file_path, "r", encoding="utf-8", errors="replace") as f:
35
- existing_content = f.read()
36
- except Exception as e:
37
- existing_content = f"[Error reading file: {e}]"
38
- return tr(
39
- "❗ Cannot create file: file already exists at '{disp_path}'.\n--- Current file content ---\n{existing_content}",
40
- disp_path=disp_path,
41
- existing_content=existing_content,
42
- )
43
- dir_name = os.path.dirname(file_path)
44
- if dir_name:
45
- os.makedirs(dir_name, exist_ok=True)
46
- self.report_info(
47
- ActionType.WRITE,
48
- tr("📝 Create file '{disp_path}' ...", disp_path=disp_path),
49
- )
50
- with open(file_path, "w", encoding="utf-8", errors="replace") as f:
51
- f.write(content)
52
- new_lines = content.count("\n") + 1 if content else 0
53
- self.report_success(tr("✅ {new_lines} lines", new_lines=new_lines))
54
- # Perform syntax validation and append result
55
- validation_result = validate_file_syntax(file_path)
56
- return (
57
- tr("✅ Created file {new_lines} lines.", new_lines=new_lines)
58
- + f"\n{validation_result}"
59
- )