janito 1.14.3__py3-none-any.whl → 2.0.1__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 (283) 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/tools/adapters/local/open_html_in_browser.py +34 -0
  131. janito/{agent/tools → tools/adapters/local}/open_url.py +7 -5
  132. janito/tools/adapters/local/python_code_run.py +165 -0
  133. janito/tools/adapters/local/python_command_run.py +163 -0
  134. janito/tools/adapters/local/python_file_run.py +162 -0
  135. janito/{agent/tools → tools/adapters/local}/remove_directory.py +15 -9
  136. janito/{agent/tools → tools/adapters/local}/remove_file.py +17 -14
  137. janito/{agent/tools → tools/adapters/local}/replace_text_in_file.py +27 -22
  138. janito/tools/adapters/local/run_bash_command.py +176 -0
  139. janito/tools/adapters/local/run_powershell_command.py +219 -0
  140. janito/{agent/tools → tools/adapters/local}/search_text/core.py +32 -12
  141. janito/{agent/tools → tools/adapters/local}/search_text/match_lines.py +13 -4
  142. janito/{agent/tools → tools/adapters/local}/search_text/pattern_utils.py +12 -4
  143. janito/{agent/tools → tools/adapters/local}/search_text/traverse_directory.py +15 -2
  144. janito/{agent/tools → tools/adapters/local}/validate_file_syntax/core.py +12 -11
  145. janito/{agent/tools → tools/adapters/local}/validate_file_syntax/css_validator.py +1 -1
  146. janito/{agent/tools → tools/adapters/local}/validate_file_syntax/html_validator.py +1 -1
  147. janito/{agent/tools → tools/adapters/local}/validate_file_syntax/js_validator.py +1 -1
  148. janito/{agent/tools → tools/adapters/local}/validate_file_syntax/json_validator.py +1 -1
  149. janito/{agent/tools → tools/adapters/local}/validate_file_syntax/markdown_validator.py +1 -1
  150. janito/{agent/tools → tools/adapters/local}/validate_file_syntax/ps1_validator.py +1 -1
  151. janito/{agent/tools → tools/adapters/local}/validate_file_syntax/python_validator.py +1 -1
  152. janito/{agent/tools → tools/adapters/local}/validate_file_syntax/xml_validator.py +1 -1
  153. janito/{agent/tools → tools/adapters/local}/validate_file_syntax/yaml_validator.py +1 -1
  154. janito/{agent/tools/get_lines.py → tools/adapters/local/view_file.py} +45 -27
  155. janito/tools/inspect_registry.py +17 -0
  156. janito/tools/tool_base.py +105 -0
  157. janito/tools/tool_events.py +58 -0
  158. janito/tools/tool_run_exception.py +12 -0
  159. janito/{agent → tools}/tool_use_tracker.py +2 -4
  160. janito/{agent/tools_utils/utils.py → tools/tool_utils.py} +18 -9
  161. janito/tools/tools_adapter.py +207 -0
  162. janito/tools/tools_schema.py +104 -0
  163. janito/utils.py +11 -0
  164. janito/version.py +4 -0
  165. janito-2.0.1.dist-info/METADATA +232 -0
  166. janito-2.0.1.dist-info/RECORD +181 -0
  167. janito/agent/__init__.py +0 -0
  168. janito/agent/api_exceptions.py +0 -4
  169. janito/agent/config.py +0 -147
  170. janito/agent/config_defaults.py +0 -12
  171. janito/agent/config_utils.py +0 -0
  172. janito/agent/content_handler.py +0 -0
  173. janito/agent/conversation.py +0 -238
  174. janito/agent/conversation_api.py +0 -306
  175. janito/agent/conversation_exceptions.py +0 -18
  176. janito/agent/conversation_tool_calls.py +0 -39
  177. janito/agent/conversation_ui.py +0 -17
  178. janito/agent/event.py +0 -24
  179. janito/agent/event_dispatcher.py +0 -24
  180. janito/agent/event_handler_protocol.py +0 -5
  181. janito/agent/event_system.py +0 -15
  182. janito/agent/llm_conversation_history.py +0 -82
  183. janito/agent/message_handler.py +0 -20
  184. janito/agent/message_handler_protocol.py +0 -5
  185. janito/agent/openai_client.py +0 -149
  186. janito/agent/openai_schema_generator.py +0 -187
  187. janito/agent/profile_manager.py +0 -96
  188. janito/agent/queued_message_handler.py +0 -50
  189. janito/agent/rich_live.py +0 -32
  190. janito/agent/rich_message_handler.py +0 -115
  191. janito/agent/runtime_config.py +0 -36
  192. janito/agent/test_handler_protocols.py +0 -47
  193. janito/agent/test_openai_schema_generator.py +0 -93
  194. janito/agent/tests/__init__.py +0 -1
  195. janito/agent/tool_base.py +0 -63
  196. janito/agent/tool_executor.py +0 -122
  197. janito/agent/tool_registry.py +0 -49
  198. janito/agent/tools/__init__.py +0 -47
  199. janito/agent/tools/create_file.py +0 -59
  200. janito/agent/tools/delete_text_in_file.py +0 -97
  201. janito/agent/tools/find_files.py +0 -106
  202. janito/agent/tools/get_file_outline/core.py +0 -81
  203. janito/agent/tools/present_choices.py +0 -64
  204. janito/agent/tools/python_command_runner.py +0 -201
  205. janito/agent/tools/python_file_runner.py +0 -199
  206. janito/agent/tools/python_stdin_runner.py +0 -208
  207. janito/agent/tools/replace_file.py +0 -72
  208. janito/agent/tools/run_bash_command.py +0 -218
  209. janito/agent/tools/run_powershell_command.py +0 -251
  210. janito/agent/tools_utils/__init__.py +0 -1
  211. janito/agent/tools_utils/action_type.py +0 -7
  212. janito/agent/tools_utils/test_gitignore_utils.py +0 -46
  213. janito/cli/_livereload_log_utils.py +0 -13
  214. janito/cli/_print_config.py +0 -96
  215. janito/cli/_termweb_log_utils.py +0 -17
  216. janito/cli/_utils.py +0 -9
  217. janito/cli/arg_parser.py +0 -272
  218. janito/cli/cli_main.py +0 -281
  219. janito/cli/config_commands.py +0 -211
  220. janito/cli/config_runner.py +0 -35
  221. janito/cli/formatting_runner.py +0 -12
  222. janito/cli/livereload_starter.py +0 -60
  223. janito/cli/logging_setup.py +0 -38
  224. janito/cli/one_shot.py +0 -80
  225. janito/livereload/app.py +0 -25
  226. janito/rich_utils.py +0 -59
  227. janito/shell/__init__.py +0 -0
  228. janito/shell/commands/__init__.py +0 -61
  229. janito/shell/commands/config.py +0 -22
  230. janito/shell/commands/edit.py +0 -24
  231. janito/shell/commands/history_view.py +0 -18
  232. janito/shell/commands/lang.py +0 -19
  233. janito/shell/commands/livelogs.py +0 -42
  234. janito/shell/commands/prompt.py +0 -62
  235. janito/shell/commands/termweb_log.py +0 -94
  236. janito/shell/commands/tools.py +0 -26
  237. janito/shell/commands/track.py +0 -36
  238. janito/shell/main.py +0 -326
  239. janito/shell/prompt/load_prompt.py +0 -57
  240. janito/shell/prompt/session_setup.py +0 -57
  241. janito/shell/session/config.py +0 -109
  242. janito/shell/session/history.py +0 -0
  243. janito/shell/ui/interactive.py +0 -226
  244. janito/termweb/static/editor.css +0 -158
  245. janito/termweb/static/editor.css.bak +0 -145
  246. janito/termweb/static/editor.html +0 -46
  247. janito/termweb/static/editor.html.bak +0 -46
  248. janito/termweb/static/editor.js +0 -265
  249. janito/termweb/static/editor.js.bak +0 -259
  250. janito/termweb/static/explorer.html.bak +0 -59
  251. janito/termweb/static/favicon.ico +0 -0
  252. janito/termweb/static/favicon.ico.bak +0 -0
  253. janito/termweb/static/index.html +0 -53
  254. janito/termweb/static/index.html.bak +0 -54
  255. janito/termweb/static/index.html.bak.bak +0 -175
  256. janito/termweb/static/landing.html.bak +0 -36
  257. janito/termweb/static/termicon.svg +0 -1
  258. janito/termweb/static/termweb.css +0 -214
  259. janito/termweb/static/termweb.css.bak +0 -237
  260. janito/termweb/static/termweb.js +0 -162
  261. janito/termweb/static/termweb.js.bak +0 -168
  262. janito/termweb/static/termweb.js.bak.bak +0 -157
  263. janito/termweb/static/termweb_quickopen.js +0 -135
  264. janito/termweb/static/termweb_quickopen.js.bak +0 -125
  265. janito/tests/test_rich_utils.py +0 -44
  266. janito/web/__init__.py +0 -0
  267. janito/web/__main__.py +0 -25
  268. janito/web/app.py +0 -145
  269. janito-1.14.3.dist-info/METADATA +0 -313
  270. janito-1.14.3.dist-info/RECORD +0 -162
  271. janito-1.14.3.dist-info/licenses/LICENSE +0 -21
  272. /janito/{shell → cli/chat_mode/shell}/input_history.py +0 -0
  273. /janito/{shell/commands/session.py → cli/chat_mode/shell/session/history.py} +0 -0
  274. /janito/{agent/tools_utils/formatting.py → formatting.py} +0 -0
  275. /janito/{agent/tools_utils/gitignore_utils.py → gitignore_utils.py} +0 -0
  276. /janito/{agent/platform_discovery.py → platform_discovery.py} +0 -0
  277. /janito/{agent/tools → tools/adapters/local}/get_file_outline/__init__.py +0 -0
  278. /janito/{agent/tools → tools/adapters/local}/get_file_outline/markdown_outline.py +0 -0
  279. /janito/{agent/tools → tools/adapters/local}/search_text/__init__.py +0 -0
  280. /janito/{agent/tools → tools/adapters/local}/validate_file_syntax/__init__.py +0 -0
  281. {janito-1.14.3.dist-info → janito-2.0.1.dist-info}/WHEEL +0 -0
  282. {janito-1.14.3.dist-info → janito-2.0.1.dist-info}/entry_points.txt +0 -0
  283. {janito-1.14.3.dist-info → janito-2.0.1.dist-info}/top_level.txt +0 -0
janito/__init__.py CHANGED
@@ -1 +1,6 @@
1
- __version__ = "1.14.3"
1
+ """
2
+ janito: A Python package for managing and interacting with Large Language Model (LLM) providers and their APIs.
3
+ Provides a CLI for credential management and an extensible driver system for LLMs.
4
+ """
5
+
6
+ __version__ = "2.0.1"
janito/__main__.py CHANGED
@@ -1,4 +1,4 @@
1
- from janito.cli.main import main
1
+ from .cli.main import main
2
2
 
3
3
  if __name__ == "__main__":
4
4
  main()
@@ -0,0 +1,139 @@
1
+ from pathlib import Path
2
+ from jinja2 import Template
3
+ import importlib.resources
4
+ import sys
5
+ import warnings
6
+ from janito.tools import get_local_tools_adapter
7
+ from janito.llm.agent import LLMAgent
8
+ from janito.drivers.driver_registry import get_driver_class
9
+ from queue import Queue
10
+ from janito.platform_discovery import PlatformDiscovery
11
+
12
+
13
+ def setup_agent(
14
+ provider_instance,
15
+ llm_driver_config,
16
+ role=None,
17
+ templates_dir=None,
18
+ zero_mode=False,
19
+ input_queue=None,
20
+ output_queue=None,
21
+ verbose_tools=False,
22
+ verbose_agent=False,
23
+ ):
24
+ """
25
+ Creates an agent using a rendered system prompt template, passing an explicit role.
26
+ """
27
+ tools_provider = get_local_tools_adapter()
28
+ tools_provider.set_verbose_tools(verbose_tools)
29
+
30
+ if zero_mode:
31
+ # Pass provider to agent, let agent create driver
32
+ agent = LLMAgent(
33
+ provider_instance,
34
+ tools_provider,
35
+ agent_name=role or "software developer",
36
+ system_prompt=None,
37
+ verbose_agent=verbose_agent,
38
+ )
39
+ return agent
40
+ # Normal flow
41
+ if templates_dir is None:
42
+ # Set default template directory
43
+ templates_dir = Path(__file__).parent / "templates" / "profiles"
44
+ template_path = templates_dir / "system_prompt_template_main.txt.j2"
45
+
46
+ template_content = None
47
+ if template_path.exists():
48
+ with open(template_path, "r", encoding="utf-8") as file:
49
+ template_content = file.read()
50
+ else:
51
+ # Try package import fallback: janito.agent.templates.profiles.system_prompt_template_main.txt.j2
52
+ try:
53
+ with importlib.resources.files("janito.agent.templates.profiles").joinpath(
54
+ "system_prompt_template_main.txt.j2"
55
+ ).open("r", encoding="utf-8") as file:
56
+ template_content = file.read()
57
+ except (FileNotFoundError, ModuleNotFoundError, AttributeError):
58
+ warnings.warn(
59
+ f"[janito] Could not find system_prompt_template_main.txt.j2 in {template_path} nor in janito.agent.templates.profiles package."
60
+ )
61
+ raise FileNotFoundError(
62
+ f"Template file not found in either {template_path} or package resource."
63
+ )
64
+
65
+ template = Template(template_content)
66
+ # Prepare context for Jinja2 rendering from llm_driver_config
67
+ # Compose context for Jinja2 rendering without using to_dict or temperature
68
+ context = {}
69
+ context["role"] = role or "software developer"
70
+ # Inject current platform environment information
71
+ pd = PlatformDiscovery()
72
+ context["platform"] = pd.get_platform_name()
73
+ context["python_version"] = pd.get_python_version()
74
+ context["shell_info"] = pd.detect_shell()
75
+ rendered_prompt = template.render(**context)
76
+ # Create the agent as before, now passing the explicit role
77
+ # Do NOT pass temperature; do not depend on to_dict
78
+ agent = LLMAgent(
79
+ provider_instance,
80
+ tools_provider,
81
+ agent_name=role or "software developer",
82
+ system_prompt=rendered_prompt,
83
+ input_queue=input_queue,
84
+ output_queue=output_queue,
85
+ verbose_agent=verbose_agent,
86
+ )
87
+ agent.template_vars["role"] = context["role"]
88
+ return agent
89
+
90
+
91
+ def create_configured_agent(
92
+ *,
93
+ provider_instance=None,
94
+ llm_driver_config=None,
95
+ role=None,
96
+ verbose_tools=False,
97
+ verbose_agent=False,
98
+ templates_dir=None,
99
+ zero_mode=False,
100
+ ):
101
+ """
102
+ Normalizes agent setup for all CLI modes.
103
+
104
+ Args:
105
+ provider_instance: Provider instance for the agent
106
+ llm_driver_config: LLM driver configuration
107
+ role: Optional role string
108
+ verbose_tools: Optional, default False
109
+ verbose_agent: Optional, default False
110
+ templates_dir: Optional
111
+ zero_mode: Optional, default False
112
+
113
+ Returns:
114
+ Configured agent instance
115
+ """
116
+ # If provider_instance has create_driver, wire queues (single-shot mode)
117
+ input_queue = None
118
+ output_queue = None
119
+ driver = None
120
+ if hasattr(provider_instance, "create_driver"):
121
+ driver = provider_instance.create_driver()
122
+ driver.start() # Ensure the driver background thread is started
123
+ input_queue = getattr(driver, "input_queue", None)
124
+ output_queue = getattr(driver, "output_queue", None)
125
+
126
+ agent = setup_agent(
127
+ provider_instance=provider_instance,
128
+ llm_driver_config=llm_driver_config,
129
+ role=role,
130
+ templates_dir=templates_dir,
131
+ zero_mode=zero_mode,
132
+ input_queue=input_queue,
133
+ output_queue=output_queue,
134
+ verbose_tools=verbose_tools,
135
+ verbose_agent=verbose_agent,
136
+ )
137
+ if driver is not None:
138
+ agent.driver = driver # Attach driver to agent for thread management
139
+ return agent
@@ -3,7 +3,7 @@
3
3
  #}
4
4
  You are: {{ role }}
5
5
 
6
- You will answer following a pattern of: discovery, description, implementation (when a change was request) and validation.
6
+ You will answer following a pattern of: discovery, description, implementation (when a change is requested) and validation.
7
7
 
8
8
  {# Improves tool selection and platform specific constrains, eg, path format, C:\ vs /path #}
9
9
  You will be using the following environment:
janito/cli/__init__.py CHANGED
@@ -0,0 +1,9 @@
1
+ # janito.cli package
2
+ from .utils import format_tokens, format_generation_time
3
+
4
+ __all__ = [
5
+ "setup_provider",
6
+ "setup_agent",
7
+ "format_tokens",
8
+ "format_generation_time",
9
+ ]
@@ -0,0 +1,37 @@
1
+ """
2
+ Key bindings for Janito Chat CLI.
3
+ """
4
+
5
+ from prompt_toolkit.key_binding import KeyBindings
6
+
7
+
8
+ class KeyBindingsFactory:
9
+ @staticmethod
10
+ def create():
11
+ bindings = KeyBindings()
12
+
13
+ @bindings.add("c-y")
14
+ def _(event):
15
+ buf = event.app.current_buffer
16
+ buf.text = "Yes"
17
+ buf.validate_and_handle()
18
+
19
+ @bindings.add("c-n")
20
+ def _(event):
21
+ buf = event.app.current_buffer
22
+ buf.text = "No"
23
+ buf.validate_and_handle()
24
+
25
+ @bindings.add("f1")
26
+ def _(event):
27
+ buf = event.app.current_buffer
28
+ buf.text = "/restart"
29
+ buf.validate_and_handle()
30
+
31
+ @bindings.add("f12")
32
+ def _(event):
33
+ buf = event.app.current_buffer
34
+ buf.text = "Do It"
35
+ buf.validate_and_handle()
36
+
37
+ return bindings
@@ -0,0 +1,23 @@
1
+ """
2
+ Main entry point for the Janito Chat CLI.
3
+ Handles the interactive chat loop and session startup.
4
+ """
5
+
6
+ from rich.console import Console
7
+ from prompt_toolkit.formatted_text import HTML
8
+ from janito.cli.chat_mode.session import ChatSession
9
+
10
+
11
+ def main(args=None):
12
+ console = Console()
13
+ from janito.version import __version__
14
+
15
+ console.print(
16
+ f"[bold green]Welcome to the Janito Chat Mode (v{__version__})! Type /exit or press Ctrl+C to quit.[/bold green]"
17
+ )
18
+ session = ChatSession(console, args=args)
19
+ session.run()
20
+
21
+
22
+ if __name__ == "__main__":
23
+ main()
@@ -0,0 +1,19 @@
1
+ from prompt_toolkit.styles import Style
2
+
3
+ chat_shell_style = Style.from_dict(
4
+ {
5
+ "prompt": "bg:#2323af #ffffff bold",
6
+ "": "bg:#005fdd #ffffff", # blue background for input area
7
+ "bottom-toolbar": "fg:#2323af bg:yellow",
8
+ "key-label": "bg:#ff9500 fg:#232323 bold",
9
+ "provider": "fg:#117fbf",
10
+ "model": "fg:#1f5fa9",
11
+ "role": "fg:#e87c32 bold",
12
+ "msg_count": "fg:#5454dd",
13
+ "session_id": "fg:#704ab9",
14
+ "tokens_total": "fg:#a022c7",
15
+ "tokens_in": "fg:#00af5f",
16
+ "tokens_out": "fg:#01814a",
17
+ "max-tokens": "fg:#888888",
18
+ }
19
+ )
@@ -0,0 +1,272 @@
1
+ """
2
+ Session management for Janito Chat CLI.
3
+ Defines ChatSession and ChatShellState classes.
4
+ """
5
+
6
+ import types
7
+ from rich.console import Console
8
+ from rich.rule import Rule
9
+ from prompt_toolkit.history import InMemoryHistory
10
+ from janito.cli.chat_mode.shell.input_history import UserInputHistory
11
+ from prompt_toolkit.formatted_text import HTML
12
+ from prompt_toolkit import PromptSession
13
+ from janito.cli.chat_mode.toolbar import get_toolbar_func
14
+ from prompt_toolkit.enums import EditingMode
15
+ from janito.cli.chat_mode.prompt_style import chat_shell_style
16
+ from janito.cli.chat_mode.bindings import KeyBindingsFactory
17
+ from janito.cli.chat_mode.shell.commands import handle_command
18
+ from janito.cli.chat_mode.shell.autocomplete import ShellCommandCompleter
19
+
20
+
21
+ class ChatShellState:
22
+ def __init__(self, mem_history, conversation_history):
23
+ self.mem_history = mem_history
24
+ self.conversation_history = conversation_history
25
+ self.paste_mode = False
26
+ self.termweb_port = None
27
+ self.termweb_pid = None
28
+ self.termweb_stdout_path = None
29
+ self.termweb_stderr_path = None
30
+ self.livereload_stderr_path = None
31
+ self.termweb_status = "starting" # Tracks the current termweb status (updated by background thread/UI)
32
+ self.termweb_live_status = (
33
+ None # 'online', 'offline', updated by background checker
34
+ )
35
+ self.termweb_live_checked_time = None # datetime.datetime of last status check
36
+ self.last_usage_info = {}
37
+ self.last_elapsed = None
38
+ self.main_agent = {}
39
+ self.mode = None
40
+ self.agent = None
41
+ self.main_agent = None
42
+ self.main_enabled = False
43
+
44
+
45
+ class ChatSession:
46
+ def __init__(
47
+ self,
48
+ console,
49
+ provider_instance=None,
50
+ llm_driver_config=None,
51
+ role=None,
52
+ args=None,
53
+ verbose_tools=False,
54
+ verbose_agent=False,
55
+ ):
56
+ from janito.cli.prompt_core import PromptHandler as GenericPromptHandler
57
+
58
+ self._prompt_handler = GenericPromptHandler(
59
+ args=None,
60
+ conversation_history=(
61
+ None
62
+ if not hasattr(self, "shell_state")
63
+ else self.shell_state.conversation_history
64
+ ),
65
+ provider_instance=provider_instance,
66
+ )
67
+ self._prompt_handler.agent = None # Will be set below if agent exists
68
+ self.console = console
69
+ self.user_input_history = UserInputHistory()
70
+ self.input_dicts = self.user_input_history.load()
71
+ self.mem_history = InMemoryHistory()
72
+ for item in self.input_dicts:
73
+ if isinstance(item, dict) and "input" in item:
74
+ self.mem_history.append_string(item["input"])
75
+ self.provider_instance = provider_instance
76
+ self.llm_driver_config = llm_driver_config
77
+ from janito.agent.setup_agent import create_configured_agent
78
+
79
+ agent = create_configured_agent(
80
+ provider_instance=provider_instance,
81
+ llm_driver_config=llm_driver_config,
82
+ role=role,
83
+ verbose_tools=verbose_tools,
84
+ verbose_agent=verbose_agent,
85
+ )
86
+ from janito.conversation_history import LLMConversationHistory
87
+
88
+ self.shell_state = ChatShellState(self.mem_history, LLMConversationHistory())
89
+ self.shell_state.agent = agent
90
+ self.agent = agent
91
+ from janito.perf_singleton import performance_collector
92
+
93
+ self.performance_collector = performance_collector
94
+ self.key_bindings = KeyBindingsFactory.create()
95
+ # Attach agent to prompt handler now that agent is initialized
96
+ self._prompt_handler.agent = self.agent
97
+ self._prompt_handler.conversation_history = (
98
+ self.shell_state.conversation_history
99
+ )
100
+
101
+ # TERMWEB logic migrated from runner
102
+ self.termweb_support = False
103
+ if args and getattr(args, "web", False):
104
+ self.termweb_support = True
105
+ self.shell_state.termweb_support = self.termweb_support
106
+ from janito.cli.termweb_starter import termweb_start_and_watch
107
+ from janito.cli.config import get_termweb_port
108
+ import threading
109
+ from rich.console import Console
110
+
111
+ Console().print("[yellow]Starting termweb in background...[/yellow]")
112
+ self.termweb_lock = threading.Lock()
113
+ termweb_thread = termweb_start_and_watch(
114
+ self.shell_state, self.termweb_lock, get_termweb_port()
115
+ )
116
+ # Initial status is set to 'starting' by constructor; the watcher will update
117
+ self.termweb_thread = termweb_thread
118
+
119
+ # Start a background timer to update live termweb status (for UI responsiveness)
120
+ import threading, datetime
121
+
122
+ def update_termweb_liveness():
123
+ while True:
124
+ with self.termweb_lock:
125
+ port = getattr(self.shell_state, "termweb_port", None)
126
+ if port:
127
+ try:
128
+ # is_termweb_running is removed; inline health check here:
129
+ try:
130
+ import http.client
131
+
132
+ conn = http.client.HTTPConnection(
133
+ "localhost", port, timeout=0.5
134
+ )
135
+ conn.request("GET", "/")
136
+ resp = conn.getresponse()
137
+ running = resp.status == 200
138
+ except Exception:
139
+ running = False
140
+ self.shell_state.termweb_live_status = (
141
+ "online" if running else "offline"
142
+ )
143
+ except Exception:
144
+ self.shell_state.termweb_live_status = "offline"
145
+ self.shell_state.termweb_live_checked_time = (
146
+ datetime.datetime.now()
147
+ )
148
+ else:
149
+ self.shell_state.termweb_live_status = None
150
+ self.shell_state.termweb_live_checked_time = (
151
+ datetime.datetime.now()
152
+ )
153
+ # sleep outside lock
154
+ threading.Event().wait(1.0)
155
+
156
+ self._termweb_liveness_thread = threading.Thread(
157
+ target=update_termweb_liveness, daemon=True
158
+ )
159
+ self._termweb_liveness_thread.start()
160
+ # No queue or blocking checks; UI (and timer) will observe self.shell_state fields
161
+
162
+ else:
163
+ self.shell_state.termweb_support = False
164
+ self.shell_state.termweb_status = "offline"
165
+
166
+ def run(self):
167
+ session = self._create_prompt_session()
168
+ self.console.print(
169
+ "[bold green]Type /help for commands. Type /exit or press Ctrl+C to quit.[/bold green]"
170
+ )
171
+ self._chat_loop(session)
172
+
173
+ def _chat_loop(self, session):
174
+ self.msg_count = 0
175
+ timer_started = False
176
+ while True:
177
+ if not timer_started:
178
+ timer_started = True
179
+ cmd_input = self._handle_input(session)
180
+ if cmd_input is None:
181
+ break
182
+ if not cmd_input:
183
+ continue
184
+ if self._handle_exit_conditions(cmd_input):
185
+ break
186
+ if cmd_input.startswith("/"):
187
+ handle_command(cmd_input, shell_state=self.shell_state)
188
+ continue
189
+ self.user_input_history.append(cmd_input)
190
+ try:
191
+ final_event = (
192
+ self._prompt_handler.agent.last_event
193
+ if hasattr(self._prompt_handler.agent, "last_event")
194
+ else None
195
+ )
196
+ self._prompt_handler.run_prompt(cmd_input)
197
+ self.msg_count += 1
198
+ # After prompt, print the stat line using the shared core function
199
+ from janito.formatting_token import print_token_message_summary
200
+
201
+ usage = self.performance_collector.get_last_request_usage()
202
+ print_token_message_summary(self.console, self.msg_count, usage)
203
+ # Print exit reason if present in the final event
204
+ if final_event and hasattr(final_event, "metadata"):
205
+ exit_reason = (
206
+ final_event.metadata.get("exit_reason")
207
+ if hasattr(final_event, "metadata")
208
+ else None
209
+ )
210
+ if exit_reason:
211
+ self.console.print(
212
+ f"[bold yellow]Exit reason: {exit_reason}[/bold yellow]"
213
+ )
214
+
215
+ except Exception as exc:
216
+ self.console.print(f"[red]Exception in agent: {exc}[/red]")
217
+ import traceback
218
+
219
+ self.console.print(traceback.format_exc())
220
+
221
+ def _create_prompt_session(self):
222
+ return PromptSession(
223
+ style=chat_shell_style,
224
+ completer=ShellCommandCompleter(),
225
+ history=self.mem_history,
226
+ editing_mode=EditingMode.EMACS,
227
+ key_bindings=self.key_bindings,
228
+ bottom_toolbar=lambda: get_toolbar_func(
229
+ self.performance_collector, 0, self.shell_state
230
+ )(),
231
+ )
232
+
233
+ def _handle_input(self, session):
234
+ injected = getattr(self.shell_state, "injected_input", None)
235
+ if injected is not None:
236
+ cmd_input = injected
237
+ self.shell_state.injected_input = None
238
+ else:
239
+ try:
240
+ cmd_input = session.prompt(HTML("<inputline>💬 </inputline>"))
241
+ except (KeyboardInterrupt, EOFError):
242
+ self._handle_exit()
243
+ return None
244
+ sanitized = cmd_input.strip()
245
+ # Ensure UTF-8 validity and sanitize if needed
246
+ try:
247
+ # This will raise UnicodeEncodeError if not encodable
248
+ sanitized.encode("utf-8")
249
+ except UnicodeEncodeError:
250
+ # Replace invalid characters
251
+ sanitized = sanitized.encode("utf-8", errors="replace").decode("utf-8")
252
+ self.console.print(
253
+ "[yellow]Warning: Some characters in your input were not valid UTF-8 and have been replaced.[/yellow]"
254
+ )
255
+ return sanitized
256
+
257
+ def _handle_exit(self):
258
+ self.console.print("[bold yellow]Exiting chat. Goodbye![/bold yellow]")
259
+ # Ensure driver thread is joined before exit
260
+ if hasattr(self, "agent") and hasattr(self.agent, "join_driver"):
261
+ if (
262
+ hasattr(self.agent, "input_queue")
263
+ and self.agent.input_queue is not None
264
+ ):
265
+ self.agent.input_queue.put(None)
266
+ self.agent.join_driver()
267
+
268
+ def _handle_exit_conditions(self, cmd_input):
269
+ if cmd_input.lower() in ("/exit", ":q", ":quit"):
270
+ self._handle_exit()
271
+ return True
272
+ return False
@@ -1,15 +1,16 @@
1
1
  from prompt_toolkit.completion import Completer, Completion
2
+ from janito.cli.chat_mode.shell.commands import get_shell_command_names
2
3
 
3
4
 
4
5
  class ShellCommandCompleter(Completer):
5
- def __init__(self):
6
- # Import here to avoid circular import at module level
7
- from janito.shell.commands import COMMAND_HANDLERS
6
+ """
7
+ Provides autocomplete suggestions for shell commands starting with '/'.
8
+ Uses the COMMAND_HANDLERS registry for available commands.
9
+ """
8
10
 
11
+ def __init__(self):
9
12
  # Only commands starting with '/'
10
- self.commands = sorted(
11
- [cmd for cmd in COMMAND_HANDLERS.keys() if cmd.startswith("/")]
12
- )
13
+ self.commands = get_shell_command_names()
13
14
 
14
15
  def get_completions(self, document, complete_event):
15
16
  text = document.text_before_cursor
@@ -0,0 +1,55 @@
1
+ from .base import ShellCmdHandler
2
+ from .edit import EditShellHandler
3
+ from .history_view import ViewShellHandler
4
+ from .lang import LangShellHandler
5
+ from .livelogs import LivelogsShellHandler
6
+ from .last import LastShellHandler
7
+ from .prompt import PromptShellHandler, RoleShellHandler, ProfileShellHandler
8
+ from .multi import MultiShellHandler
9
+ from .role import RoleCommandShellHandler
10
+ from .session import HistoryShellHandler
11
+ from .termweb_log import TermwebLogTailShellHandler
12
+ from .tools import ToolsShellHandler
13
+ from .help import HelpShellHandler
14
+ from janito.cli.console import shared_console
15
+
16
+ COMMAND_HANDLERS = {
17
+ "/clear": __import__(
18
+ "janito.cli.chat_mode.shell.commands.clear", fromlist=["ClearShellHandler"]
19
+ ).ClearShellHandler,
20
+ "/restart": __import__(
21
+ "janito.cli.chat_mode.shell.commands.conversation_restart",
22
+ fromlist=["RestartShellHandler"],
23
+ ).RestartShellHandler,
24
+ "/edit": EditShellHandler,
25
+ "/view": ViewShellHandler,
26
+ "/lang": LangShellHandler,
27
+ "/livelogs": LivelogsShellHandler,
28
+ "/last": LastShellHandler,
29
+ "/prompt": PromptShellHandler,
30
+ "/role": RoleShellHandler,
31
+ "/profile": ProfileShellHandler,
32
+ "/history": HistoryShellHandler,
33
+ "/termweb-logs": TermwebLogTailShellHandler,
34
+ "/tools": ToolsShellHandler,
35
+ "/multi": MultiShellHandler,
36
+ "/help": HelpShellHandler,
37
+ }
38
+
39
+
40
+ def get_shell_command_names():
41
+ return sorted(cmd for cmd in COMMAND_HANDLERS.keys() if cmd.startswith("/"))
42
+
43
+
44
+ def handle_command(command, shell_state=None):
45
+ parts = command.strip().split(maxsplit=1)
46
+ cmd = parts[0]
47
+ after_cmd_line = parts[1] if len(parts) > 1 else ""
48
+ handler_cls = COMMAND_HANDLERS.get(cmd)
49
+ if handler_cls:
50
+ handler = handler_cls(after_cmd_line=after_cmd_line, shell_state=shell_state)
51
+ return handler.run()
52
+ shared_console.print(
53
+ f"[bold red]Invalid command: {cmd}. Type /help for a list of commands.[/bold red]"
54
+ )
55
+ return None
@@ -0,0 +1,9 @@
1
+ class ShellCmdHandler:
2
+ help_text = ""
3
+
4
+ def __init__(self, after_cmd_line=None, shell_state=None):
5
+ self.after_cmd_line = after_cmd_line
6
+ self.shell_state = shell_state
7
+
8
+ def run(self):
9
+ raise NotImplementedError("Subclasses must implement run()")
@@ -0,0 +1,12 @@
1
+ from janito.cli.chat_mode.shell.commands.base import ShellCmdHandler
2
+ from janito.cli.console import shared_console
3
+
4
+
5
+ class ClearShellHandler(ShellCmdHandler):
6
+ help_text = "Clear the terminal screen using Rich console."
7
+
8
+ def run(self):
9
+ shared_console.clear()
10
+ # Optionally show a message after clearing
11
+ # shared_console.print("[green]Screen cleared.[/green]")
12
+ return None