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.
- janito/__init__.py +6 -1
- janito/__main__.py +1 -1
- janito/agent/setup_agent.py +139 -0
- janito/agent/templates/profiles/{system_prompt_template_base.txt.j2 → system_prompt_template_main.txt.j2} +1 -1
- janito/cli/__init__.py +9 -0
- janito/cli/chat_mode/bindings.py +37 -0
- janito/cli/chat_mode/chat_entry.py +23 -0
- janito/cli/chat_mode/prompt_style.py +19 -0
- janito/cli/chat_mode/session.py +272 -0
- janito/{shell/prompt/completer.py → cli/chat_mode/shell/autocomplete.py} +7 -6
- janito/cli/chat_mode/shell/commands/__init__.py +55 -0
- janito/cli/chat_mode/shell/commands/base.py +9 -0
- janito/cli/chat_mode/shell/commands/clear.py +12 -0
- janito/{shell → cli/chat_mode/shell}/commands/conversation_restart.py +34 -30
- janito/cli/chat_mode/shell/commands/edit.py +25 -0
- janito/cli/chat_mode/shell/commands/help.py +16 -0
- janito/cli/chat_mode/shell/commands/history_view.py +93 -0
- janito/cli/chat_mode/shell/commands/lang.py +25 -0
- janito/cli/chat_mode/shell/commands/last.py +137 -0
- janito/cli/chat_mode/shell/commands/livelogs.py +49 -0
- janito/cli/chat_mode/shell/commands/multi.py +51 -0
- janito/cli/chat_mode/shell/commands/prompt.py +64 -0
- janito/cli/chat_mode/shell/commands/role.py +36 -0
- janito/cli/chat_mode/shell/commands/session.py +40 -0
- janito/{shell → cli/chat_mode/shell}/commands/session_control.py +2 -2
- janito/cli/chat_mode/shell/commands/termweb_log.py +92 -0
- janito/cli/chat_mode/shell/commands/tools.py +32 -0
- janito/{shell → cli/chat_mode/shell}/commands/utility.py +4 -7
- janito/{shell → cli/chat_mode/shell}/commands/verbose.py +5 -5
- janito/cli/chat_mode/shell/session/__init__.py +1 -0
- janito/{shell → cli/chat_mode/shell}/session/manager.py +9 -1
- janito/cli/chat_mode/toolbar.py +90 -0
- janito/cli/cli_commands/list_models.py +35 -0
- janito/cli/cli_commands/list_providers.py +9 -0
- janito/cli/cli_commands/list_tools.py +53 -0
- janito/cli/cli_commands/model_selection.py +50 -0
- janito/cli/cli_commands/model_utils.py +84 -0
- janito/cli/cli_commands/set_api_key.py +19 -0
- janito/cli/cli_commands/show_config.py +51 -0
- janito/cli/cli_commands/show_system_prompt.py +62 -0
- janito/cli/config.py +28 -0
- janito/cli/console.py +3 -0
- janito/cli/core/__init__.py +4 -0
- janito/cli/core/event_logger.py +59 -0
- janito/cli/core/getters.py +31 -0
- janito/cli/core/runner.py +141 -0
- janito/cli/core/setters.py +174 -0
- janito/cli/core/unsetters.py +54 -0
- janito/cli/main.py +8 -196
- janito/cli/main_cli.py +312 -0
- janito/cli/prompt_core.py +230 -0
- janito/cli/prompt_handler.py +6 -0
- janito/cli/rich_terminal_reporter.py +101 -0
- janito/cli/single_shot_mode/__init__.py +6 -0
- janito/cli/single_shot_mode/handler.py +137 -0
- janito/cli/termweb_starter.py +73 -24
- janito/cli/utils.py +25 -0
- janito/cli/verbose_output.py +196 -0
- janito/config.py +5 -0
- janito/config_manager.py +110 -0
- janito/conversation_history.py +30 -0
- janito/{agent/tools_utils/dir_walk_utils.py → dir_walk_utils.py} +3 -2
- janito/driver_events.py +98 -0
- janito/drivers/anthropic/driver.py +113 -0
- janito/drivers/azure_openai/driver.py +36 -0
- janito/drivers/driver_registry.py +33 -0
- janito/drivers/google_genai/driver.py +54 -0
- janito/drivers/google_genai/schema_generator.py +67 -0
- janito/drivers/mistralai/driver.py +41 -0
- janito/drivers/openai/driver.py +334 -0
- janito/event_bus/__init__.py +2 -0
- janito/event_bus/bus.py +68 -0
- janito/event_bus/event.py +15 -0
- janito/event_bus/handler.py +31 -0
- janito/event_bus/queue_bus.py +57 -0
- janito/exceptions.py +23 -0
- janito/formatting_token.py +54 -0
- janito/i18n/pt.py +1 -0
- janito/llm/__init__.py +5 -0
- janito/llm/agent.py +443 -0
- janito/llm/auth.py +62 -0
- janito/llm/driver.py +239 -0
- janito/llm/driver_config.py +34 -0
- janito/llm/driver_config_builder.py +34 -0
- janito/llm/driver_input.py +12 -0
- janito/llm/message_parts.py +60 -0
- janito/llm/model.py +38 -0
- janito/llm/provider.py +187 -0
- janito/perf_singleton.py +3 -0
- janito/performance_collector.py +167 -0
- janito/provider_config.py +98 -0
- janito/provider_registry.py +152 -0
- janito/providers/__init__.py +7 -0
- janito/providers/anthropic/model_info.py +22 -0
- janito/providers/anthropic/provider.py +65 -0
- janito/providers/azure_openai/model_info.py +15 -0
- janito/providers/azure_openai/provider.py +72 -0
- janito/providers/deepseek/__init__.py +1 -0
- janito/providers/deepseek/model_info.py +16 -0
- janito/providers/deepseek/provider.py +91 -0
- janito/providers/google/__init__.py +1 -0
- janito/providers/google/model_info.py +40 -0
- janito/providers/google/provider.py +69 -0
- janito/providers/mistralai/model_info.py +37 -0
- janito/providers/mistralai/provider.py +69 -0
- janito/providers/openai/__init__.py +1 -0
- janito/providers/openai/model_info.py +137 -0
- janito/providers/openai/provider.py +107 -0
- janito/providers/openai/schema_generator.py +63 -0
- janito/providers/provider_static_info.py +21 -0
- janito/providers/registry.py +26 -0
- janito/report_events.py +38 -0
- janito/termweb/app.py +1 -1
- janito/tools/__init__.py +16 -0
- janito/tools/adapters/__init__.py +1 -0
- janito/tools/adapters/local/__init__.py +54 -0
- janito/tools/adapters/local/adapter.py +92 -0
- janito/{agent/tools → tools/adapters/local}/ask_user.py +30 -13
- janito/tools/adapters/local/copy_file.py +84 -0
- janito/{agent/tools → tools/adapters/local}/create_directory.py +11 -10
- janito/tools/adapters/local/create_file.py +82 -0
- janito/tools/adapters/local/delete_text_in_file.py +136 -0
- janito/{agent/tools → tools/adapters/local}/fetch_url.py +18 -19
- janito/tools/adapters/local/find_files.py +140 -0
- janito/tools/adapters/local/get_file_outline/core.py +151 -0
- janito/{agent/tools → tools/adapters/local}/get_file_outline/python_outline.py +125 -0
- janito/tools/adapters/local/get_file_outline/python_outline_v2.py +156 -0
- janito/{agent/tools → tools/adapters/local}/get_file_outline/search_outline.py +12 -7
- janito/{agent/tools → tools/adapters/local}/move_file.py +13 -9
- janito/tools/adapters/local/open_html_in_browser.py +34 -0
- janito/{agent/tools → tools/adapters/local}/open_url.py +7 -5
- janito/tools/adapters/local/python_code_run.py +165 -0
- janito/tools/adapters/local/python_command_run.py +163 -0
- janito/tools/adapters/local/python_file_run.py +162 -0
- janito/{agent/tools → tools/adapters/local}/remove_directory.py +15 -9
- janito/{agent/tools → tools/adapters/local}/remove_file.py +17 -14
- janito/{agent/tools → tools/adapters/local}/replace_text_in_file.py +27 -22
- janito/tools/adapters/local/run_bash_command.py +176 -0
- janito/tools/adapters/local/run_powershell_command.py +219 -0
- janito/{agent/tools → tools/adapters/local}/search_text/core.py +32 -12
- janito/{agent/tools → tools/adapters/local}/search_text/match_lines.py +13 -4
- janito/{agent/tools → tools/adapters/local}/search_text/pattern_utils.py +12 -4
- janito/{agent/tools → tools/adapters/local}/search_text/traverse_directory.py +15 -2
- janito/{agent/tools → tools/adapters/local}/validate_file_syntax/core.py +12 -11
- janito/{agent/tools → tools/adapters/local}/validate_file_syntax/css_validator.py +1 -1
- janito/{agent/tools → tools/adapters/local}/validate_file_syntax/html_validator.py +1 -1
- janito/{agent/tools → tools/adapters/local}/validate_file_syntax/js_validator.py +1 -1
- janito/{agent/tools → tools/adapters/local}/validate_file_syntax/json_validator.py +1 -1
- janito/{agent/tools → tools/adapters/local}/validate_file_syntax/markdown_validator.py +1 -1
- janito/{agent/tools → tools/adapters/local}/validate_file_syntax/ps1_validator.py +1 -1
- janito/{agent/tools → tools/adapters/local}/validate_file_syntax/python_validator.py +1 -1
- janito/{agent/tools → tools/adapters/local}/validate_file_syntax/xml_validator.py +1 -1
- janito/{agent/tools → tools/adapters/local}/validate_file_syntax/yaml_validator.py +1 -1
- janito/{agent/tools/get_lines.py → tools/adapters/local/view_file.py} +45 -27
- janito/tools/inspect_registry.py +17 -0
- janito/tools/tool_base.py +105 -0
- janito/tools/tool_events.py +58 -0
- janito/tools/tool_run_exception.py +12 -0
- janito/{agent → tools}/tool_use_tracker.py +2 -4
- janito/{agent/tools_utils/utils.py → tools/tool_utils.py} +18 -9
- janito/tools/tools_adapter.py +207 -0
- janito/tools/tools_schema.py +104 -0
- janito/utils.py +11 -0
- janito/version.py +4 -0
- janito-2.0.1.dist-info/METADATA +232 -0
- janito-2.0.1.dist-info/RECORD +181 -0
- janito/agent/__init__.py +0 -0
- janito/agent/api_exceptions.py +0 -4
- janito/agent/config.py +0 -147
- janito/agent/config_defaults.py +0 -12
- janito/agent/config_utils.py +0 -0
- janito/agent/content_handler.py +0 -0
- janito/agent/conversation.py +0 -238
- janito/agent/conversation_api.py +0 -306
- janito/agent/conversation_exceptions.py +0 -18
- janito/agent/conversation_tool_calls.py +0 -39
- janito/agent/conversation_ui.py +0 -17
- janito/agent/event.py +0 -24
- janito/agent/event_dispatcher.py +0 -24
- janito/agent/event_handler_protocol.py +0 -5
- janito/agent/event_system.py +0 -15
- janito/agent/llm_conversation_history.py +0 -82
- janito/agent/message_handler.py +0 -20
- janito/agent/message_handler_protocol.py +0 -5
- janito/agent/openai_client.py +0 -149
- janito/agent/openai_schema_generator.py +0 -187
- janito/agent/profile_manager.py +0 -96
- janito/agent/queued_message_handler.py +0 -50
- janito/agent/rich_live.py +0 -32
- janito/agent/rich_message_handler.py +0 -115
- janito/agent/runtime_config.py +0 -36
- janito/agent/test_handler_protocols.py +0 -47
- janito/agent/test_openai_schema_generator.py +0 -93
- janito/agent/tests/__init__.py +0 -1
- janito/agent/tool_base.py +0 -63
- janito/agent/tool_executor.py +0 -122
- janito/agent/tool_registry.py +0 -49
- janito/agent/tools/__init__.py +0 -47
- janito/agent/tools/create_file.py +0 -59
- janito/agent/tools/delete_text_in_file.py +0 -97
- janito/agent/tools/find_files.py +0 -106
- janito/agent/tools/get_file_outline/core.py +0 -81
- janito/agent/tools/present_choices.py +0 -64
- janito/agent/tools/python_command_runner.py +0 -201
- janito/agent/tools/python_file_runner.py +0 -199
- janito/agent/tools/python_stdin_runner.py +0 -208
- janito/agent/tools/replace_file.py +0 -72
- janito/agent/tools/run_bash_command.py +0 -218
- janito/agent/tools/run_powershell_command.py +0 -251
- janito/agent/tools_utils/__init__.py +0 -1
- janito/agent/tools_utils/action_type.py +0 -7
- janito/agent/tools_utils/test_gitignore_utils.py +0 -46
- janito/cli/_livereload_log_utils.py +0 -13
- janito/cli/_print_config.py +0 -96
- janito/cli/_termweb_log_utils.py +0 -17
- janito/cli/_utils.py +0 -9
- janito/cli/arg_parser.py +0 -272
- janito/cli/cli_main.py +0 -281
- janito/cli/config_commands.py +0 -211
- janito/cli/config_runner.py +0 -35
- janito/cli/formatting_runner.py +0 -12
- janito/cli/livereload_starter.py +0 -60
- janito/cli/logging_setup.py +0 -38
- janito/cli/one_shot.py +0 -80
- janito/livereload/app.py +0 -25
- janito/rich_utils.py +0 -59
- janito/shell/__init__.py +0 -0
- janito/shell/commands/__init__.py +0 -61
- janito/shell/commands/config.py +0 -22
- janito/shell/commands/edit.py +0 -24
- janito/shell/commands/history_view.py +0 -18
- janito/shell/commands/lang.py +0 -19
- janito/shell/commands/livelogs.py +0 -42
- janito/shell/commands/prompt.py +0 -62
- janito/shell/commands/termweb_log.py +0 -94
- janito/shell/commands/tools.py +0 -26
- janito/shell/commands/track.py +0 -36
- janito/shell/main.py +0 -326
- janito/shell/prompt/load_prompt.py +0 -57
- janito/shell/prompt/session_setup.py +0 -57
- janito/shell/session/config.py +0 -109
- janito/shell/session/history.py +0 -0
- janito/shell/ui/interactive.py +0 -226
- janito/termweb/static/editor.css +0 -158
- janito/termweb/static/editor.css.bak +0 -145
- janito/termweb/static/editor.html +0 -46
- janito/termweb/static/editor.html.bak +0 -46
- janito/termweb/static/editor.js +0 -265
- janito/termweb/static/editor.js.bak +0 -259
- janito/termweb/static/explorer.html.bak +0 -59
- janito/termweb/static/favicon.ico +0 -0
- janito/termweb/static/favicon.ico.bak +0 -0
- janito/termweb/static/index.html +0 -53
- janito/termweb/static/index.html.bak +0 -54
- janito/termweb/static/index.html.bak.bak +0 -175
- janito/termweb/static/landing.html.bak +0 -36
- janito/termweb/static/termicon.svg +0 -1
- janito/termweb/static/termweb.css +0 -214
- janito/termweb/static/termweb.css.bak +0 -237
- janito/termweb/static/termweb.js +0 -162
- janito/termweb/static/termweb.js.bak +0 -168
- janito/termweb/static/termweb.js.bak.bak +0 -157
- janito/termweb/static/termweb_quickopen.js +0 -135
- janito/termweb/static/termweb_quickopen.js.bak +0 -125
- janito/tests/test_rich_utils.py +0 -44
- janito/web/__init__.py +0 -0
- janito/web/__main__.py +0 -25
- janito/web/app.py +0 -145
- janito-1.14.3.dist-info/METADATA +0 -313
- janito-1.14.3.dist-info/RECORD +0 -162
- janito-1.14.3.dist-info/licenses/LICENSE +0 -21
- /janito/{shell → cli/chat_mode/shell}/input_history.py +0 -0
- /janito/{shell/commands/session.py → cli/chat_mode/shell/session/history.py} +0 -0
- /janito/{agent/tools_utils/formatting.py → formatting.py} +0 -0
- /janito/{agent/tools_utils/gitignore_utils.py → gitignore_utils.py} +0 -0
- /janito/{agent/platform_discovery.py → platform_discovery.py} +0 -0
- /janito/{agent/tools → tools/adapters/local}/get_file_outline/__init__.py +0 -0
- /janito/{agent/tools → tools/adapters/local}/get_file_outline/markdown_outline.py +0 -0
- /janito/{agent/tools → tools/adapters/local}/search_text/__init__.py +0 -0
- /janito/{agent/tools → tools/adapters/local}/validate_file_syntax/__init__.py +0 -0
- {janito-1.14.3.dist-info → janito-2.0.1.dist-info}/WHEEL +0 -0
- {janito-1.14.3.dist-info → janito-2.0.1.dist-info}/entry_points.txt +0 -0
- {janito-1.14.3.dist-info → janito-2.0.1.dist-info}/top_level.txt +0 -0
janito/__init__.py
CHANGED
janito/__main__.py
CHANGED
@@ -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
|
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,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
|
-
|
6
|
-
|
7
|
-
|
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 =
|
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,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
|