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/cli/config_commands.py
DELETED
@@ -1,211 +0,0 @@
|
|
1
|
-
import sys
|
2
|
-
from janito.agent.config import local_config, global_config, CONFIG_OPTIONS
|
3
|
-
from janito.agent.runtime_config import unified_config, runtime_config
|
4
|
-
from janito.agent.config_defaults import CONFIG_DEFAULTS
|
5
|
-
from rich import print
|
6
|
-
from ._utils import home_shorten
|
7
|
-
import os
|
8
|
-
from pathlib import Path
|
9
|
-
|
10
|
-
|
11
|
-
def handle_run_config(args):
|
12
|
-
if args.run_config:
|
13
|
-
for run_item in args.run_config:
|
14
|
-
try:
|
15
|
-
key, val = run_item.split("=", 1)
|
16
|
-
except ValueError:
|
17
|
-
print("Invalid format for --run-config, expected key=val")
|
18
|
-
sys.exit(1)
|
19
|
-
key = key.strip()
|
20
|
-
if key not in CONFIG_OPTIONS:
|
21
|
-
print(
|
22
|
-
f"Invalid config key: '{key}'. Supported keys are: {', '.join(CONFIG_OPTIONS.keys())}"
|
23
|
-
)
|
24
|
-
sys.exit(1)
|
25
|
-
runtime_config.set(key, val.strip())
|
26
|
-
return True
|
27
|
-
return False
|
28
|
-
|
29
|
-
|
30
|
-
def handle_set_local_config(args):
|
31
|
-
if args.set_local_config:
|
32
|
-
try:
|
33
|
-
key, val = args.set_local_config.split("=", 1)
|
34
|
-
except ValueError:
|
35
|
-
print("Invalid format for --set-local-config, expected key=val")
|
36
|
-
sys.exit(1)
|
37
|
-
key = key.strip()
|
38
|
-
if key not in CONFIG_OPTIONS:
|
39
|
-
print(
|
40
|
-
f"Invalid config key: '{key}'. Supported keys are: {', '.join(CONFIG_OPTIONS.keys())}"
|
41
|
-
)
|
42
|
-
sys.exit(1)
|
43
|
-
local_config.set(key, val.strip())
|
44
|
-
local_config.save()
|
45
|
-
runtime_config.set(key, val.strip())
|
46
|
-
print(f"Local config updated: {key} = {val.strip()}")
|
47
|
-
return True
|
48
|
-
return False
|
49
|
-
|
50
|
-
|
51
|
-
def handle_set_global_config(args):
|
52
|
-
if args.set_global_config:
|
53
|
-
try:
|
54
|
-
key, val = args.set_global_config.split("=", 1)
|
55
|
-
except ValueError:
|
56
|
-
print("Invalid format for --set-global-config, expected key=val")
|
57
|
-
sys.exit(1)
|
58
|
-
key = key.strip()
|
59
|
-
if key not in CONFIG_OPTIONS and not key.startswith("template."):
|
60
|
-
print(
|
61
|
-
f"Invalid config key: '{key}'. Supported keys are: {', '.join(CONFIG_OPTIONS.keys())}"
|
62
|
-
)
|
63
|
-
sys.exit(1)
|
64
|
-
if key.startswith("template."):
|
65
|
-
subkey = key[len("template.") :]
|
66
|
-
template_dict = global_config.get("template", {})
|
67
|
-
template_dict[subkey] = val.strip()
|
68
|
-
global_config.set("template", template_dict)
|
69
|
-
global_config.save()
|
70
|
-
# Remove legacy flat key if present
|
71
|
-
if key in global_config._data:
|
72
|
-
del global_config._data[key]
|
73
|
-
runtime_config.set("template", template_dict)
|
74
|
-
print(f"Global config updated: template.{subkey} = {val.strip()}")
|
75
|
-
return True
|
76
|
-
else:
|
77
|
-
global_config.set(key, val.strip())
|
78
|
-
global_config.save()
|
79
|
-
runtime_config.set(key, val.strip())
|
80
|
-
print(f"Global config updated: {key} = {val.strip()}")
|
81
|
-
return True
|
82
|
-
return False
|
83
|
-
|
84
|
-
|
85
|
-
def handle_set_api_key(args):
|
86
|
-
if args.set_api_key:
|
87
|
-
existing = dict(global_config.all())
|
88
|
-
existing["api_key"] = args.set_api_key.strip()
|
89
|
-
global_config._data = existing
|
90
|
-
global_config.save()
|
91
|
-
runtime_config.set("api_key", args.set_api_key.strip())
|
92
|
-
print("Global API key saved.")
|
93
|
-
return True
|
94
|
-
return False
|
95
|
-
|
96
|
-
|
97
|
-
def handle_show_config(args):
|
98
|
-
if args.show_config:
|
99
|
-
local_items = _collect_config_items(local_config, unified_config, True)
|
100
|
-
global_items = _collect_config_items(
|
101
|
-
global_config, unified_config, False, set(local_items.keys())
|
102
|
-
)
|
103
|
-
_mask_api_keys(local_items)
|
104
|
-
_mask_api_keys(global_items)
|
105
|
-
_print_config_items(local_items, global_items)
|
106
|
-
_print_default_items(local_items, global_items)
|
107
|
-
return True
|
108
|
-
return False
|
109
|
-
|
110
|
-
|
111
|
-
def _collect_config_items(config, unified_config, is_local, exclude_keys=None):
|
112
|
-
items = {}
|
113
|
-
keys = set(config.all().keys())
|
114
|
-
if exclude_keys:
|
115
|
-
keys = keys - set(exclude_keys)
|
116
|
-
for key in sorted(keys):
|
117
|
-
if key == "template":
|
118
|
-
template_dict = config.get("template", {})
|
119
|
-
if template_dict:
|
120
|
-
items["template"] = f"({len(template_dict)} keys set)"
|
121
|
-
for tkey, tval in template_dict.items():
|
122
|
-
items[f" template.{tkey}"] = tval
|
123
|
-
continue
|
124
|
-
if key.startswith("template."):
|
125
|
-
continue
|
126
|
-
if key == "api_key":
|
127
|
-
value = config.get("api_key")
|
128
|
-
value = (
|
129
|
-
value[:4] + "..." + value[-4:]
|
130
|
-
if value and len(value) > 8
|
131
|
-
else ("***" if value else None)
|
132
|
-
)
|
133
|
-
else:
|
134
|
-
value = unified_config.get(key)
|
135
|
-
items[key] = value
|
136
|
-
return items
|
137
|
-
|
138
|
-
|
139
|
-
def _mask_api_keys(cfg):
|
140
|
-
if "api_key" in cfg and cfg["api_key"]:
|
141
|
-
val = cfg["api_key"]
|
142
|
-
cfg["api_key"] = val[:4] + "..." + val[-4:] if len(val) > 8 else "***"
|
143
|
-
|
144
|
-
|
145
|
-
def _print_config_items(local_items, global_items):
|
146
|
-
from ._print_config import print_config_items
|
147
|
-
|
148
|
-
print_config_items(local_items, color_label="[cyan]🏠 Local Configuration[/cyan]")
|
149
|
-
print_config_items(
|
150
|
-
global_items, color_label="[yellow]🌐 Global Configuration[/yellow]"
|
151
|
-
)
|
152
|
-
|
153
|
-
|
154
|
-
def _print_default_items(local_items, global_items):
|
155
|
-
shown_keys = set(local_items.keys()) | set(global_items.keys())
|
156
|
-
default_items = {
|
157
|
-
k: v
|
158
|
-
for k, v in CONFIG_DEFAULTS.items()
|
159
|
-
if k not in shown_keys and k != "api_key"
|
160
|
-
}
|
161
|
-
if default_items:
|
162
|
-
print("[green]🟢 Defaults (not set in config files)[/green]")
|
163
|
-
for key, value in default_items.items():
|
164
|
-
if key == "system_prompt" and value is None:
|
165
|
-
template_path = (
|
166
|
-
Path(__file__).parent
|
167
|
-
/ "agent"
|
168
|
-
/ "templates"
|
169
|
-
/ "system_prompt_template_default.j2"
|
170
|
-
)
|
171
|
-
print(
|
172
|
-
f"{key} = (default template path: {home_shorten(str(template_path))})"
|
173
|
-
)
|
174
|
-
else:
|
175
|
-
print(f"{key} = {value}")
|
176
|
-
print()
|
177
|
-
|
178
|
-
|
179
|
-
def handle_config_reset_local(args):
|
180
|
-
if getattr(args, "config_reset_local", False):
|
181
|
-
local_path = Path(".janito/config.json")
|
182
|
-
if local_path.exists():
|
183
|
-
os.remove(local_path)
|
184
|
-
print(f"Removed local config file: {local_path}")
|
185
|
-
else:
|
186
|
-
print(f"Local config file does not exist: {local_path}")
|
187
|
-
sys.exit(0)
|
188
|
-
|
189
|
-
|
190
|
-
def handle_config_reset_global(args):
|
191
|
-
if getattr(args, "config_reset_global", False):
|
192
|
-
global_path = Path.home() / ".janito/config.json"
|
193
|
-
if global_path.exists():
|
194
|
-
os.remove(global_path)
|
195
|
-
print(f"Removed global config file: {global_path}")
|
196
|
-
else:
|
197
|
-
print(f"Global config file does not exist: {global_path}")
|
198
|
-
sys.exit(0)
|
199
|
-
|
200
|
-
|
201
|
-
def handle_config_commands(args):
|
202
|
-
did_something = False
|
203
|
-
did_something |= handle_run_config(args)
|
204
|
-
did_something |= handle_set_local_config(args)
|
205
|
-
did_something |= handle_set_global_config(args)
|
206
|
-
did_something |= handle_set_api_key(args)
|
207
|
-
did_something |= handle_show_config(args)
|
208
|
-
handle_config_reset_local(args)
|
209
|
-
handle_config_reset_global(args)
|
210
|
-
if did_something:
|
211
|
-
sys.exit(0)
|
janito/cli/config_runner.py
DELETED
@@ -1,35 +0,0 @@
|
|
1
|
-
from janito.agent.profile_manager import AgentProfileManager
|
2
|
-
from janito.agent.runtime_config import unified_config, runtime_config
|
3
|
-
from janito.agent.config import get_api_key
|
4
|
-
|
5
|
-
|
6
|
-
def get_system_prompt_template(args, role):
|
7
|
-
system_prompt_template = None
|
8
|
-
if getattr(args, "system_prompt_template_file", None):
|
9
|
-
with open(args.system_prompt_template_file, "r", encoding="utf-8") as f:
|
10
|
-
system_prompt_template = f.read()
|
11
|
-
runtime_config.set(
|
12
|
-
"system_prompt_template_file", args.system_prompt_template_file
|
13
|
-
)
|
14
|
-
else:
|
15
|
-
system_prompt_template = getattr(
|
16
|
-
args, "system_prompt_template", None
|
17
|
-
) or unified_config.get("system_prompt_template")
|
18
|
-
if getattr(args, "system_prompt_template", None):
|
19
|
-
runtime_config.set("system_prompt_template", system_prompt_template)
|
20
|
-
if system_prompt_template is None:
|
21
|
-
profile_manager = AgentProfileManager(
|
22
|
-
api_key=get_api_key(),
|
23
|
-
model=unified_config.get("model"),
|
24
|
-
role=role,
|
25
|
-
profile_name="base",
|
26
|
-
interaction_mode=unified_config.get("interaction_mode", "prompt"),
|
27
|
-
verbose_tools=unified_config.get("verbose_tools", False),
|
28
|
-
base_url=unified_config.get("base_url", None),
|
29
|
-
azure_openai_api_version=unified_config.get(
|
30
|
-
"azure_openai_api_version", "2023-05-15"
|
31
|
-
),
|
32
|
-
use_azure_openai=unified_config.get("use_azure_openai", False),
|
33
|
-
)
|
34
|
-
system_prompt_template = profile_manager.system_prompt_template
|
35
|
-
return system_prompt_template
|
janito/cli/formatting_runner.py
DELETED
janito/cli/livereload_starter.py
DELETED
@@ -1,60 +0,0 @@
|
|
1
|
-
import sys
|
2
|
-
import subprocess
|
3
|
-
import tempfile
|
4
|
-
import time
|
5
|
-
import http.client
|
6
|
-
import os
|
7
|
-
from rich.console import Console
|
8
|
-
|
9
|
-
|
10
|
-
def wait_for_livereload(port, timeout=3.0):
|
11
|
-
deadline = time.time() + timeout
|
12
|
-
while time.time() < deadline:
|
13
|
-
try:
|
14
|
-
conn = http.client.HTTPConnection("localhost", port, timeout=0.5)
|
15
|
-
conn.request("GET", "/")
|
16
|
-
resp = conn.getresponse()
|
17
|
-
if resp.status in (200, 404):
|
18
|
-
return True
|
19
|
-
except Exception:
|
20
|
-
pass
|
21
|
-
time.sleep(0.1)
|
22
|
-
return False
|
23
|
-
|
24
|
-
|
25
|
-
def start_livereload(selected_port):
|
26
|
-
console = Console()
|
27
|
-
with console.status("[cyan]Starting live reload server...", spinner="dots"):
|
28
|
-
app_py_path = os.path.join(
|
29
|
-
os.path.dirname(__file__), "..", "livereload", "app.py"
|
30
|
-
)
|
31
|
-
app_py_path = os.path.abspath(app_py_path)
|
32
|
-
if not os.path.isfile(app_py_path):
|
33
|
-
console.print("[red]Could not find livereload app.py![/red]")
|
34
|
-
return None, False, None, None
|
35
|
-
livereload_stdout = tempfile.NamedTemporaryFile(
|
36
|
-
delete=False, mode="w+", encoding="utf-8"
|
37
|
-
)
|
38
|
-
livereload_stderr = tempfile.NamedTemporaryFile(
|
39
|
-
delete=False, mode="w+", encoding="utf-8"
|
40
|
-
)
|
41
|
-
livereload_proc = subprocess.Popen(
|
42
|
-
[sys.executable, app_py_path, "--port", str(selected_port)],
|
43
|
-
stdout=livereload_stdout,
|
44
|
-
stderr=livereload_stderr,
|
45
|
-
)
|
46
|
-
if wait_for_livereload(selected_port, timeout=3.0):
|
47
|
-
console.print(
|
48
|
-
f"LiveReload started... Available at http://localhost:{selected_port}"
|
49
|
-
)
|
50
|
-
return livereload_proc, True, livereload_stdout.name, livereload_stderr.name
|
51
|
-
else:
|
52
|
-
livereload_proc.terminate()
|
53
|
-
livereload_proc.wait()
|
54
|
-
from janito.cli._livereload_log_utils import print_livereload_logs
|
55
|
-
|
56
|
-
console.print(
|
57
|
-
f"[red]Failed to start LiveReload on port {selected_port}. Check logs for details.[/red]"
|
58
|
-
)
|
59
|
-
print_livereload_logs(livereload_stdout.name, livereload_stderr.name)
|
60
|
-
return None, False, livereload_stdout.name, livereload_stderr.name
|
janito/cli/logging_setup.py
DELETED
@@ -1,38 +0,0 @@
|
|
1
|
-
import os
|
2
|
-
import logging
|
3
|
-
|
4
|
-
|
5
|
-
from janito.agent.runtime_config import runtime_config
|
6
|
-
|
7
|
-
|
8
|
-
def setup_verbose_logging(args):
|
9
|
-
if runtime_config.get("verbose_http", False) or runtime_config.get(
|
10
|
-
"verbose_http_raw", False
|
11
|
-
):
|
12
|
-
httpx_logger = logging.getLogger("httpx")
|
13
|
-
httpx_logger.setLevel(logging.DEBUG)
|
14
|
-
handler = logging.StreamHandler()
|
15
|
-
handler.setFormatter(
|
16
|
-
logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
|
17
|
-
)
|
18
|
-
httpx_logger.addHandler(handler)
|
19
|
-
|
20
|
-
if runtime_config.get("verbose_http_raw", False):
|
21
|
-
os.environ["HTTPX_LOG_LEVEL"] = "trace"
|
22
|
-
|
23
|
-
httpcore_logger = logging.getLogger("httpcore")
|
24
|
-
httpcore_logger.setLevel(logging.DEBUG)
|
25
|
-
handler_core = logging.StreamHandler()
|
26
|
-
handler_core.setFormatter(
|
27
|
-
logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
|
28
|
-
)
|
29
|
-
httpcore_logger.addHandler(handler_core)
|
30
|
-
|
31
|
-
# Re-add handler to httpx logger in case
|
32
|
-
httpx_logger = logging.getLogger("httpx")
|
33
|
-
httpx_logger.setLevel(logging.DEBUG)
|
34
|
-
handler = logging.StreamHandler()
|
35
|
-
handler.setFormatter(
|
36
|
-
logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
|
37
|
-
)
|
38
|
-
httpx_logger.addHandler(handler)
|
janito/cli/one_shot.py
DELETED
@@ -1,80 +0,0 @@
|
|
1
|
-
from janito.agent.conversation_exceptions import (
|
2
|
-
MaxRoundsExceededError,
|
3
|
-
ProviderError,
|
4
|
-
EmptyResponseError,
|
5
|
-
)
|
6
|
-
from janito.agent.api_exceptions import ApiError
|
7
|
-
from janito.agent.llm_conversation_history import LLMConversationHistory
|
8
|
-
|
9
|
-
|
10
|
-
def prepare_messages(args, profile_manager, runtime_config):
|
11
|
-
prompt = getattr(args, "input_arg", None)
|
12
|
-
messages = []
|
13
|
-
system_prompt_override = runtime_config.get("system_prompt_template")
|
14
|
-
if system_prompt_override:
|
15
|
-
if not runtime_config.get("vanilla_mode", False) or getattr(
|
16
|
-
args, "system", None
|
17
|
-
):
|
18
|
-
messages.append({"role": "system", "content": system_prompt_override})
|
19
|
-
elif profile_manager.system_prompt_template and not runtime_config.get(
|
20
|
-
"vanilla_mode", False
|
21
|
-
):
|
22
|
-
messages.append(
|
23
|
-
{"role": "system", "content": profile_manager.system_prompt_template}
|
24
|
-
)
|
25
|
-
messages.append({"role": "user", "content": prompt})
|
26
|
-
return messages
|
27
|
-
|
28
|
-
|
29
|
-
def print_usage_info(args, info_start_time, result, console):
|
30
|
-
if (
|
31
|
-
getattr(args, "info", False)
|
32
|
-
and info_start_time is not None
|
33
|
-
and result is not None
|
34
|
-
):
|
35
|
-
usage_info = result.get("usage")
|
36
|
-
total_tokens = usage_info.get("total_tokens") if usage_info else None
|
37
|
-
prompt_tokens = usage_info.get("prompt_tokens") if usage_info else None
|
38
|
-
completion_tokens = usage_info.get("completion_tokens") if usage_info else None
|
39
|
-
elapsed = time.time() - info_start_time
|
40
|
-
console.print(
|
41
|
-
f"[bold green]Total tokens:[/] [yellow]{total_tokens}[/yellow] [bold green]| Input:[/] [cyan]{prompt_tokens}[/cyan] [bold green]| Output:[/] [magenta]{completion_tokens}[/magenta] [bold green]| Elapsed:[/] [yellow]{elapsed:.2f}s[/yellow]",
|
42
|
-
style="dim",
|
43
|
-
)
|
44
|
-
|
45
|
-
|
46
|
-
def run_oneshot_mode(args, profile_manager, runtime_config):
|
47
|
-
from rich.console import Console
|
48
|
-
from janito.agent.rich_message_handler import RichMessageHandler
|
49
|
-
import time
|
50
|
-
|
51
|
-
console = Console()
|
52
|
-
message_handler = RichMessageHandler()
|
53
|
-
messages = prepare_messages(args, profile_manager, runtime_config)
|
54
|
-
info_start_time = None
|
55
|
-
if getattr(args, "info", False):
|
56
|
-
info_start_time = time.time()
|
57
|
-
try:
|
58
|
-
max_rounds = 100
|
59
|
-
result = profile_manager.agent.chat(
|
60
|
-
LLMConversationHistory(messages),
|
61
|
-
message_handler=message_handler,
|
62
|
-
spinner=True,
|
63
|
-
max_rounds=max_rounds,
|
64
|
-
)
|
65
|
-
print_usage_info(args, info_start_time, result, console)
|
66
|
-
except MaxRoundsExceededError:
|
67
|
-
console.print("[red]Max conversation rounds exceeded.[/red]")
|
68
|
-
except ProviderError as e:
|
69
|
-
console.print(f"[red]Provider error:[/red] {e}")
|
70
|
-
except EmptyResponseError as e:
|
71
|
-
console.print(f"[red]Error:[/red] {e}")
|
72
|
-
except ApiError as e:
|
73
|
-
if "maximum context length" in str(e):
|
74
|
-
console.print(
|
75
|
-
f"[red]Error:[/red] {e}\n[bold yellow]Tip:[/] Try using [green]--max-tokens[/green] with a lower value."
|
76
|
-
)
|
77
|
-
else:
|
78
|
-
console.print(f"[red]API error:[/red] {e}")
|
79
|
-
except Exception:
|
80
|
-
raise
|
janito/livereload/app.py
DELETED
@@ -1,25 +0,0 @@
|
|
1
|
-
import sys
|
2
|
-
import os
|
3
|
-
from livereload import Server
|
4
|
-
|
5
|
-
|
6
|
-
def main():
|
7
|
-
port = 35729 # Default livereload port
|
8
|
-
if "--port" in sys.argv:
|
9
|
-
idx = sys.argv.index("--port")
|
10
|
-
if idx + 1 < len(sys.argv):
|
11
|
-
try:
|
12
|
-
port = int(sys.argv[idx + 1])
|
13
|
-
except ValueError:
|
14
|
-
pass
|
15
|
-
watch_dir = os.path.abspath(os.getcwd())
|
16
|
-
server = Server()
|
17
|
-
server.watch(watch_dir, delay=1)
|
18
|
-
print(
|
19
|
-
f"Starting livereload server on http://localhost:{port}, watching {watch_dir}"
|
20
|
-
)
|
21
|
-
server.serve(root=watch_dir, port=port, open_url_delay=None)
|
22
|
-
|
23
|
-
|
24
|
-
if __name__ == "__main__":
|
25
|
-
main()
|
janito/rich_utils.py
DELETED
@@ -1,59 +0,0 @@
|
|
1
|
-
"""
|
2
|
-
Utilities for working with the Rich library.
|
3
|
-
"""
|
4
|
-
|
5
|
-
from rich.console import Console
|
6
|
-
from typing import Optional
|
7
|
-
|
8
|
-
|
9
|
-
class RichPrinter:
|
10
|
-
"""
|
11
|
-
Utility class for printing styled messages using the Rich library.
|
12
|
-
|
13
|
-
Args:
|
14
|
-
console (Optional[Console]): An optional Rich Console instance. If not provided, a default Console will be created.
|
15
|
-
|
16
|
-
Methods:
|
17
|
-
print_info(message: str)
|
18
|
-
Print an informational message in cyan (no newline at end).
|
19
|
-
|
20
|
-
print_error(message: str)
|
21
|
-
Print an error message in bold red.
|
22
|
-
|
23
|
-
print_warning(message: str)
|
24
|
-
Print a warning message in bold yellow.
|
25
|
-
|
26
|
-
print_magenta(message: str)
|
27
|
-
Print a message in magenta.
|
28
|
-
"""
|
29
|
-
|
30
|
-
def __init__(self, console: Optional[Console] = None):
|
31
|
-
self.console = console or Console()
|
32
|
-
|
33
|
-
def print_info(self, message: str):
|
34
|
-
self.console.print(message, style="cyan", end="")
|
35
|
-
|
36
|
-
def print_error(self, message: str):
|
37
|
-
self.console.print(message, style="bold red", end="\n")
|
38
|
-
|
39
|
-
def print_warning(self, message: str):
|
40
|
-
self.console.print(message, style="bold yellow", end="\n")
|
41
|
-
|
42
|
-
def print_magenta(self, message: str):
|
43
|
-
self.console.print(message, style="magenta", end="\n")
|
44
|
-
|
45
|
-
def print_colored_message(self, message: str, color_index: int = 0):
|
46
|
-
"""
|
47
|
-
Print a message with a cycling background color for verbose-messages.
|
48
|
-
"""
|
49
|
-
bg_colors = [
|
50
|
-
"on blue",
|
51
|
-
"on green",
|
52
|
-
"on magenta",
|
53
|
-
"on cyan",
|
54
|
-
"on yellow",
|
55
|
-
"on red",
|
56
|
-
"on bright_black",
|
57
|
-
]
|
58
|
-
style = f"bold white {bg_colors[color_index % len(bg_colors)]}"
|
59
|
-
self.console.print(message, style=style, end="\n")
|
janito/shell/__init__.py
DELETED
File without changes
|
@@ -1,61 +0,0 @@
|
|
1
|
-
from .session import handle_history
|
2
|
-
from .prompt import handle_prompt, handle_role
|
3
|
-
from .session_control import handle_exit
|
4
|
-
from .conversation_restart import handle_restart
|
5
|
-
from .utility import handle_help, handle_clear, handle_multi
|
6
|
-
from .tools import handle_tools
|
7
|
-
from .termweb_log import handle_termweb_log_tail, handle_termweb_status
|
8
|
-
from .livelogs import handle_livelogs
|
9
|
-
from .edit import handle_edit
|
10
|
-
from .history_view import handle_view
|
11
|
-
from janito.shell.session.config import handle_config_shell
|
12
|
-
from .verbose import handle_verbose
|
13
|
-
from .lang import handle_lang
|
14
|
-
from janito.agent.runtime_config import runtime_config
|
15
|
-
from .track import handle_track
|
16
|
-
|
17
|
-
COMMAND_HANDLERS = {
|
18
|
-
"/termweb-logs": handle_termweb_log_tail,
|
19
|
-
"/livelogs": handle_livelogs,
|
20
|
-
"/termweb-status": handle_termweb_status,
|
21
|
-
"/edit": handle_edit,
|
22
|
-
"/history": handle_history,
|
23
|
-
"/exit": handle_exit,
|
24
|
-
"exit": handle_exit,
|
25
|
-
"/restart": handle_restart,
|
26
|
-
"/start": handle_restart,
|
27
|
-
"/help": handle_help,
|
28
|
-
"/multi": handle_multi,
|
29
|
-
"/prompt": handle_prompt,
|
30
|
-
"/tools": handle_tools,
|
31
|
-
"/verbose": handle_verbose,
|
32
|
-
}
|
33
|
-
|
34
|
-
if not runtime_config.get("vanilla_mode", False):
|
35
|
-
COMMAND_HANDLERS["/role"] = handle_role
|
36
|
-
|
37
|
-
COMMAND_HANDLERS["/lang"] = handle_lang
|
38
|
-
COMMAND_HANDLERS["/track"] = handle_track
|
39
|
-
|
40
|
-
COMMAND_HANDLERS.update(
|
41
|
-
{
|
42
|
-
"/clear": handle_clear,
|
43
|
-
"/restart": handle_restart,
|
44
|
-
"/config": handle_config_shell,
|
45
|
-
"/view": handle_view,
|
46
|
-
}
|
47
|
-
)
|
48
|
-
|
49
|
-
|
50
|
-
def handle_command(command, console, shell_state=None):
|
51
|
-
parts = command.strip().split()
|
52
|
-
cmd = parts[0]
|
53
|
-
args = parts[1:]
|
54
|
-
handler = COMMAND_HANDLERS.get(cmd)
|
55
|
-
if handler:
|
56
|
-
# Pass shell_state and args as keyword arguments for handlers that expect them
|
57
|
-
return handler(console, args=args, shell_state=shell_state)
|
58
|
-
console.print(
|
59
|
-
f"[bold red]Invalid command: {cmd}. Type /help for a list of commands.[/bold red]"
|
60
|
-
)
|
61
|
-
return None
|
janito/shell/commands/config.py
DELETED
@@ -1,22 +0,0 @@
|
|
1
|
-
def handle_reload(console, *args, **kwargs):
|
2
|
-
from janito.shell.prompt.load_prompt import load_prompt
|
3
|
-
|
4
|
-
agent = kwargs.get("agent")
|
5
|
-
state = kwargs.get("state")
|
6
|
-
filename = args[0] if args else None
|
7
|
-
try:
|
8
|
-
prompt_text = load_prompt(filename)
|
9
|
-
if hasattr(agent, "system_prompt_template"):
|
10
|
-
agent.system_prompt_template = prompt_text
|
11
|
-
# Update the first system message in the conversation if present
|
12
|
-
history = state.get("history") if state else None
|
13
|
-
if history:
|
14
|
-
for msg in history:
|
15
|
-
if msg.get("role") == "system":
|
16
|
-
msg["content"] = prompt_text
|
17
|
-
break
|
18
|
-
console.print(
|
19
|
-
f"[bold green]System prompt reloaded from {'default file' if not filename else filename}![/bold green]"
|
20
|
-
)
|
21
|
-
except Exception as e:
|
22
|
-
console.print(f"[bold red]Failed to reload system prompt:[/bold red] {e}")
|
janito/shell/commands/edit.py
DELETED
@@ -1,24 +0,0 @@
|
|
1
|
-
import os
|
2
|
-
import webbrowser
|
3
|
-
from janito.agent.runtime_config import runtime_config
|
4
|
-
|
5
|
-
|
6
|
-
def handle_edit(console, args=None, shell_state=None, **kwargs):
|
7
|
-
if not args or len(args) < 1:
|
8
|
-
console.print("[red]Usage: /edit <filename>[/red]")
|
9
|
-
return
|
10
|
-
filename = args[0]
|
11
|
-
if not os.path.isfile(filename):
|
12
|
-
console.print(f"[red]File not found:[/red] {filename}")
|
13
|
-
return
|
14
|
-
port = getattr(shell_state, "termweb_port", None) or runtime_config.get(
|
15
|
-
"termweb_port", 8080
|
16
|
-
)
|
17
|
-
url = f"http://localhost:{port}/?path={filename}"
|
18
|
-
console.print(
|
19
|
-
f"[green]Opening in browser:[/green] [underline blue]{url}[/underline blue]"
|
20
|
-
)
|
21
|
-
webbrowser.open(url)
|
22
|
-
|
23
|
-
|
24
|
-
handle_edit.help_text = "Open a file in the browser-based editor"
|
@@ -1,18 +0,0 @@
|
|
1
|
-
def handle_view(console, args=None, shell_state=None, **kwargs):
|
2
|
-
messages = shell_state.conversation_history.get_messages()
|
3
|
-
if not messages:
|
4
|
-
console.print("[yellow]Conversation history is empty.[/yellow]")
|
5
|
-
return
|
6
|
-
for i, msg in enumerate(messages, 1):
|
7
|
-
role = msg.get("role", "?")
|
8
|
-
content = msg.get("content", "")
|
9
|
-
tool_calls = msg.get("tool_calls")
|
10
|
-
tool_call_id = msg.get("tool_call_id")
|
11
|
-
console.print(f"[bold]{i}. {role}:[/bold] {content}")
|
12
|
-
if tool_calls:
|
13
|
-
console.print(f" [cyan]tool_calls:[/cyan] {tool_calls}")
|
14
|
-
if tool_call_id:
|
15
|
-
console.print(f" [magenta]tool_call_id:[/magenta] {tool_call_id}")
|
16
|
-
|
17
|
-
|
18
|
-
handle_view.help_text = "Print the current LLM conversation history"
|
janito/shell/commands/lang.py
DELETED
@@ -1,19 +0,0 @@
|
|
1
|
-
from janito.agent.runtime_config import runtime_config
|
2
|
-
import janito.i18n as i18n
|
3
|
-
|
4
|
-
|
5
|
-
def handle_lang(console, args=None, **kwargs):
|
6
|
-
if not args or len(args) == 0:
|
7
|
-
console.print(
|
8
|
-
"[bold yellow]Uso: /lang [código_idioma] (ex: pt, en, es)[/bold yellow]"
|
9
|
-
)
|
10
|
-
return
|
11
|
-
lang_code = args[0]
|
12
|
-
runtime_config.set("lang", lang_code)
|
13
|
-
i18n.set_locale(lang_code)
|
14
|
-
console.print(
|
15
|
-
f"[bold green]Idioma alterado para:[/bold green] [cyan]{lang_code}[/cyan]"
|
16
|
-
)
|
17
|
-
|
18
|
-
|
19
|
-
handle_lang.help_text = "Change the interface language (e.g., /lang en)"
|