janito 2.27.1__py3-none-any.whl → 2.29.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- janito/README.md +9 -9
- janito/agent/setup_agent.py +29 -16
- janito/cli/chat_mode/script_runner.py +1 -1
- janito/cli/chat_mode/session.py +160 -56
- janito/cli/chat_mode/session_profile_select.py +8 -2
- janito/cli/chat_mode/shell/commands/execute.py +4 -2
- janito/cli/chat_mode/shell/commands/help.py +2 -0
- janito/cli/chat_mode/shell/commands/privileges.py +6 -2
- janito/cli/chat_mode/shell/commands/provider.py +7 -4
- janito/cli/chat_mode/shell/commands/read.py +4 -2
- janito/cli/chat_mode/shell/commands/security/__init__.py +1 -1
- janito/cli/chat_mode/shell/commands/security/allowed_sites.py +16 -13
- janito/cli/chat_mode/shell/commands/security_command.py +14 -10
- janito/cli/chat_mode/shell/commands/tools.py +4 -2
- janito/cli/chat_mode/shell/commands/unrestricted.py +17 -12
- janito/cli/chat_mode/shell/commands/write.py +4 -2
- janito/cli/chat_mode/toolbar.py +4 -4
- janito/cli/cli_commands/enable_disable_plugin.py +48 -25
- janito/cli/cli_commands/list_models.py +2 -2
- janito/cli/cli_commands/list_plugins.py +18 -18
- janito/cli/cli_commands/list_profiles.py +6 -6
- janito/cli/cli_commands/list_providers.py +1 -1
- janito/cli/cli_commands/model_utils.py +45 -20
- janito/cli/cli_commands/ping_providers.py +10 -10
- janito/cli/cli_commands/set_api_key.py +5 -3
- janito/cli/cli_commands/show_config.py +13 -7
- janito/cli/cli_commands/show_system_prompt.py +13 -6
- janito/cli/core/getters.py +1 -0
- janito/cli/core/model_guesser.py +18 -15
- janito/cli/core/runner.py +15 -7
- janito/cli/core/setters.py +9 -6
- janito/cli/main_cli.py +15 -12
- janito/cli/prompt_setup.py +4 -4
- janito/cli/rich_terminal_reporter.py +2 -1
- janito/config_manager.py +2 -0
- janito/docs/GETTING_STARTED.md +9 -9
- janito/drivers/cerebras/__init__.py +1 -1
- janito/exceptions.py +6 -4
- janito/plugins/__init__.py +2 -2
- janito/plugins/base.py +48 -40
- janito/plugins/builtin.py +13 -9
- janito/plugins/config.py +16 -19
- janito/plugins/discovery.py +73 -66
- janito/plugins/manager.py +62 -60
- janito/provider_registry.py +10 -10
- janito/providers/__init__.py +1 -1
- janito/providers/alibaba/model_info.py +3 -5
- janito/providers/alibaba/provider.py +3 -1
- janito/providers/cerebras/__init__.py +1 -1
- janito/providers/cerebras/model_info.py +12 -27
- janito/providers/cerebras/provider.py +11 -9
- janito/providers/mistral/__init__.py +1 -1
- janito/providers/mistral/model_info.py +1 -1
- janito/providers/mistral/provider.py +1 -1
- janito/providers/moonshot/__init__.py +1 -0
- janito/providers/{moonshotai → moonshot}/model_info.py +3 -3
- janito/providers/{moonshotai → moonshot}/provider.py +8 -8
- janito/providers/openai/provider.py +3 -1
- janito/report_events.py +0 -1
- janito/tools/adapters/local/create_file.py +1 -1
- janito/tools/adapters/local/fetch_url.py +45 -29
- janito/tools/adapters/local/python_command_run.py +2 -1
- janito/tools/adapters/local/python_file_run.py +1 -0
- janito/tools/adapters/local/run_powershell_command.py +1 -1
- janito/tools/adapters/local/validate_file_syntax/jinja2_validator.py +14 -11
- janito/tools/base.py +4 -3
- janito/tools/loop_protection.py +24 -22
- janito/tools/path_utils.py +7 -7
- janito/tools/tool_base.py +0 -2
- janito/tools/tools_adapter.py +15 -5
- janito/tools/url_whitelist.py +27 -26
- {janito-2.27.1.dist-info → janito-2.29.0.dist-info}/METADATA +1 -1
- {janito-2.27.1.dist-info → janito-2.29.0.dist-info}/RECORD +77 -77
- janito/providers/moonshotai/__init__.py +0 -1
- {janito-2.27.1.dist-info → janito-2.29.0.dist-info}/WHEEL +0 -0
- {janito-2.27.1.dist-info → janito-2.29.0.dist-info}/entry_points.txt +0 -0
- {janito-2.27.1.dist-info → janito-2.29.0.dist-info}/licenses/LICENSE +0 -0
- {janito-2.27.1.dist-info → janito-2.29.0.dist-info}/top_level.txt +0 -0
@@ -1,18 +1,20 @@
|
|
1
1
|
"""Security command group for chat mode."""
|
2
2
|
|
3
3
|
from janito.cli.chat_mode.shell.commands.base import ShellCmdHandler as BaseCommand
|
4
|
-
from janito.cli.chat_mode.shell.commands.security.allowed_sites import
|
4
|
+
from janito.cli.chat_mode.shell.commands.security.allowed_sites import (
|
5
|
+
SecurityAllowedSitesCommand,
|
6
|
+
)
|
5
7
|
|
6
8
|
|
7
9
|
class SecurityCommand(BaseCommand):
|
8
10
|
"""Security management command group."""
|
9
|
-
|
11
|
+
|
10
12
|
def get_name(self) -> str:
|
11
13
|
return "security"
|
12
|
-
|
14
|
+
|
13
15
|
def get_description(self) -> str:
|
14
16
|
return "Security management commands"
|
15
|
-
|
17
|
+
|
16
18
|
def get_usage(self):
|
17
19
|
return """
|
18
20
|
Usage: /security <subcommand> [args...]
|
@@ -25,21 +27,23 @@ Examples:
|
|
25
27
|
/security allowed-sites add tradingview.com
|
26
28
|
/security allowed-sites remove yahoo.com
|
27
29
|
"""
|
28
|
-
|
30
|
+
|
29
31
|
def __init__(self, after_cmd_line=None, shell_state=None):
|
30
32
|
super().__init__(after_cmd_line=after_cmd_line, shell_state=shell_state)
|
31
33
|
self.subcommands = {
|
32
|
-
"allowed-sites": SecurityAllowedSitesCommand(
|
34
|
+
"allowed-sites": SecurityAllowedSitesCommand(
|
35
|
+
after_cmd_line=after_cmd_line, shell_state=shell_state
|
36
|
+
)
|
33
37
|
}
|
34
|
-
|
38
|
+
|
35
39
|
def run(self):
|
36
40
|
"""Execute the security command."""
|
37
41
|
args = self.after_cmd_line.strip().split()
|
38
|
-
|
42
|
+
|
39
43
|
if not args:
|
40
44
|
print(self.get_usage())
|
41
45
|
return
|
42
|
-
|
46
|
+
|
43
47
|
subcommand = args[0].lower()
|
44
48
|
if subcommand in self.subcommands:
|
45
49
|
# Pass the remaining args to the subcommand
|
@@ -48,4 +52,4 @@ Examples:
|
|
48
52
|
self.subcommands[subcommand].run()
|
49
53
|
else:
|
50
54
|
print(f"Error: Unknown security subcommand '{subcommand}'")
|
51
|
-
print(self.get_usage())
|
55
|
+
print(self.get_usage())
|
@@ -56,8 +56,10 @@ class ToolsShellHandler(ShellCmdHandler):
|
|
56
56
|
|
57
57
|
def run(self):
|
58
58
|
# Check for no_tools_mode in shell_state
|
59
|
-
if self.shell_state and getattr(self.shell_state,
|
60
|
-
shared_console.print(
|
59
|
+
if self.shell_state and getattr(self.shell_state, "no_tools_mode", False):
|
60
|
+
shared_console.print(
|
61
|
+
"[yellow]No tools are available in this mode (no tools, no context).[/yellow]"
|
62
|
+
)
|
61
63
|
return
|
62
64
|
try:
|
63
65
|
import janito.tools # Ensure all tools are registered
|
@@ -6,7 +6,7 @@ from janito.cli.console import shared_console
|
|
6
6
|
|
7
7
|
class UnrestrictedShellHandler(ShellCmdHandler):
|
8
8
|
"""Toggle unrestricted mode (equivalent to -u CLI flag)."""
|
9
|
-
|
9
|
+
|
10
10
|
help_text = "Toggle unrestricted mode (disable path security and URL whitelist)"
|
11
11
|
|
12
12
|
def run(self):
|
@@ -14,27 +14,32 @@ class UnrestrictedShellHandler(ShellCmdHandler):
|
|
14
14
|
if not self.shell_state:
|
15
15
|
shared_console.print("[red]Error: Shell state not available[/red]")
|
16
16
|
return
|
17
|
-
|
17
|
+
|
18
18
|
# Toggle unrestricted mode
|
19
|
-
current_unrestricted = getattr(self.shell_state,
|
19
|
+
current_unrestricted = getattr(self.shell_state, "unrestricted_mode", False)
|
20
20
|
new_unrestricted = not current_unrestricted
|
21
|
-
|
21
|
+
|
22
22
|
# Update shell state
|
23
23
|
self.shell_state.unrestricted_mode = new_unrestricted
|
24
|
-
|
24
|
+
|
25
25
|
# Update tools adapter
|
26
|
-
if hasattr(self.shell_state,
|
27
|
-
setattr(
|
28
|
-
|
26
|
+
if hasattr(self.shell_state, "tools_adapter"):
|
27
|
+
setattr(
|
28
|
+
self.shell_state.tools_adapter, "unrestricted_paths", new_unrestricted
|
29
|
+
)
|
30
|
+
|
29
31
|
# Update URL whitelist manager
|
30
32
|
from janito.tools.url_whitelist import get_url_whitelist_manager
|
33
|
+
|
31
34
|
whitelist_manager = get_url_whitelist_manager()
|
32
35
|
whitelist_manager.set_unrestricted_mode(new_unrestricted)
|
33
|
-
|
36
|
+
|
34
37
|
status = "enabled" if new_unrestricted else "disabled"
|
35
|
-
warning =
|
36
|
-
|
38
|
+
warning = (
|
39
|
+
" (DANGEROUS - no path or URL restrictions)" if new_unrestricted else ""
|
40
|
+
)
|
41
|
+
|
37
42
|
shared_console.print(
|
38
43
|
f"[bold {'red' if new_unrestricted else 'green'}]"
|
39
44
|
f"Unrestricted mode {status}{warning}[/bold {'red' if new_unrestricted else 'green'}]"
|
40
|
-
)
|
45
|
+
)
|
@@ -6,8 +6,10 @@ class WriteShellHandler(ShellCmdHandler):
|
|
6
6
|
help_text = "/write on|off: Enable or disable write permissions for tools. Usage: /write on or /write off."
|
7
7
|
|
8
8
|
def run(self):
|
9
|
-
if self.shell_state and getattr(self.shell_state,
|
10
|
-
shared_console.print(
|
9
|
+
if self.shell_state and getattr(self.shell_state, "no_tools_mode", False):
|
10
|
+
shared_console.print(
|
11
|
+
"[yellow]No tools are available in this mode (no tools, no context).[/yellow]"
|
12
|
+
)
|
11
13
|
return
|
12
14
|
if not self.shell_state:
|
13
15
|
shared_console.print("[red]Shell state unavailable.[/red]")
|
janito/cli/chat_mode/toolbar.py
CHANGED
@@ -20,17 +20,17 @@ def format_tokens(n, tag=None):
|
|
20
20
|
def assemble_first_line(provider_name, model_name, role, agent=None):
|
21
21
|
cwd = os.getcwd()
|
22
22
|
home = os.path.expanduser("~")
|
23
|
-
|
23
|
+
|
24
24
|
# Convert to relative path if under home directory
|
25
25
|
if cwd.startswith(home):
|
26
|
-
cwd_display = "~" + cwd[len(home):]
|
26
|
+
cwd_display = "~" + cwd[len(home) :]
|
27
27
|
else:
|
28
28
|
cwd_display = cwd
|
29
|
-
|
29
|
+
|
30
30
|
# Shorten long paths for display
|
31
31
|
if len(cwd_display) > 50:
|
32
32
|
cwd_display = "..." + cwd_display[-47:]
|
33
|
-
|
33
|
+
|
34
34
|
return f" Janito {VERSION} | Provider: <provider>{provider_name}</provider> | Model: <model>{model_name}</model> | Dir: <model>{cwd_display}</model>"
|
35
35
|
|
36
36
|
|
@@ -6,25 +6,33 @@ import argparse
|
|
6
6
|
import json
|
7
7
|
from pathlib import Path
|
8
8
|
from typing import Dict, Any
|
9
|
-
from janito.plugins.config import
|
9
|
+
from janito.plugins.config import (
|
10
|
+
load_plugins_config,
|
11
|
+
save_plugins_config,
|
12
|
+
get_plugins_config_path,
|
13
|
+
)
|
10
14
|
from janito.plugins.manager import PluginManager
|
11
15
|
|
12
16
|
|
13
17
|
def handle_enable_plugin(args: argparse.Namespace) -> None:
|
14
18
|
"""Enable a plugin by adding it to plugins.json."""
|
15
19
|
config = load_plugins_config()
|
16
|
-
|
17
|
-
if
|
18
|
-
config[
|
19
|
-
if
|
20
|
-
config[
|
21
|
-
|
20
|
+
|
21
|
+
if "plugins" not in config:
|
22
|
+
config["plugins"] = {}
|
23
|
+
if "load" not in config["plugins"]:
|
24
|
+
config["plugins"]["load"] = {}
|
25
|
+
|
22
26
|
# Set the plugin to enabled (True)
|
23
|
-
config[
|
24
|
-
|
27
|
+
config["plugins"]["load"][args.plugin_name] = True
|
28
|
+
|
25
29
|
if save_plugins_config(config):
|
26
|
-
print(
|
27
|
-
|
30
|
+
print(
|
31
|
+
f"Plugin '{args.plugin_name}' has been enabled in {get_plugins_config_path()}"
|
32
|
+
)
|
33
|
+
print(
|
34
|
+
"Note: You may need to reload the plugin in the current session with 'plugin reload'"
|
35
|
+
)
|
28
36
|
else:
|
29
37
|
print(f"Error: Failed to enable plugin '{args.plugin_name}'")
|
30
38
|
|
@@ -32,33 +40,48 @@ def handle_enable_plugin(args: argparse.Namespace) -> None:
|
|
32
40
|
def handle_disable_plugin(args: argparse.Namespace) -> None:
|
33
41
|
"""Disable a plugin by removing it from plugins.json or setting it to False."""
|
34
42
|
config = load_plugins_config()
|
35
|
-
|
36
|
-
if
|
43
|
+
|
44
|
+
if (
|
45
|
+
"plugins" in config
|
46
|
+
and "load" in config["plugins"]
|
47
|
+
and args.plugin_name in config["plugins"]["load"]
|
48
|
+
):
|
37
49
|
# Remove the plugin entry or set it to False
|
38
50
|
if args.remove:
|
39
|
-
del config[
|
51
|
+
del config["plugins"]["load"][args.plugin_name]
|
40
52
|
action = "removed"
|
41
53
|
else:
|
42
|
-
config[
|
54
|
+
config["plugins"]["load"][args.plugin_name] = False
|
43
55
|
action = "disabled"
|
44
|
-
|
56
|
+
|
45
57
|
if save_plugins_config(config):
|
46
|
-
print(
|
47
|
-
|
58
|
+
print(
|
59
|
+
f"Plugin '{args.plugin_name}' has been {action} in {get_plugins_config_path()}"
|
60
|
+
)
|
61
|
+
print(
|
62
|
+
"Note: You may need to unload the plugin in the current session with 'plugin unload'"
|
63
|
+
)
|
48
64
|
else:
|
49
65
|
print(f"Error: Failed to {action} plugin '{args.plugin_name}'")
|
50
66
|
else:
|
51
|
-
print(
|
52
|
-
|
67
|
+
print(
|
68
|
+
f"Plugin '{args.plugin_name}' is not currently configured in plugins.json"
|
69
|
+
)
|
70
|
+
print(
|
71
|
+
"It may still be loaded in the current session, but won't be loaded on restart"
|
72
|
+
)
|
53
73
|
|
54
74
|
|
55
75
|
def add_enable_plugin_args(parser: argparse.ArgumentParser) -> None:
|
56
76
|
"""Add enable-plugin arguments to argument parser."""
|
57
|
-
parser.add_argument(
|
58
|
-
|
77
|
+
parser.add_argument("plugin_name", help="Name of the plugin to enable")
|
78
|
+
|
59
79
|
|
60
80
|
def add_disable_plugin_args(parser: argparse.ArgumentParser) -> None:
|
61
81
|
"""Add disable-plugin arguments to argument parser."""
|
62
|
-
parser.add_argument(
|
63
|
-
parser.add_argument(
|
64
|
-
|
82
|
+
parser.add_argument("plugin_name", help="Name of the plugin to disable")
|
83
|
+
parser.add_argument(
|
84
|
+
"--remove",
|
85
|
+
action="store_true",
|
86
|
+
help="Completely remove the plugin from config instead of setting to False",
|
87
|
+
)
|
@@ -30,10 +30,10 @@ def handle_list_models(args, provider_instance):
|
|
30
30
|
# Fallback for simple string model lists
|
31
31
|
from rich.table import Table
|
32
32
|
from janito.cli.console import shared_console
|
33
|
-
|
33
|
+
|
34
34
|
table = Table(title=f"Supported models for provider '{provider_name}'")
|
35
35
|
table.add_column("Model Name", style="cyan")
|
36
|
-
|
36
|
+
|
37
37
|
for m in models:
|
38
38
|
table.add_row(str(m))
|
39
39
|
if sys.stdout.isatty():
|
@@ -12,21 +12,21 @@ from janito.plugins.builtin import BuiltinPluginRegistry
|
|
12
12
|
|
13
13
|
def handle_list_plugins(args: argparse.Namespace) -> None:
|
14
14
|
"""List plugins command handler."""
|
15
|
-
|
16
|
-
if getattr(args,
|
15
|
+
|
16
|
+
if getattr(args, "list_plugins_available", False):
|
17
17
|
# List available plugins
|
18
18
|
available = list_available_plugins()
|
19
19
|
builtin_plugins = BuiltinPluginRegistry.list_builtin_plugins()
|
20
|
-
|
20
|
+
|
21
21
|
if available or builtin_plugins:
|
22
22
|
print("Available plugins:")
|
23
|
-
|
23
|
+
|
24
24
|
# Show builtin plugins first
|
25
25
|
if builtin_plugins:
|
26
26
|
print(" Builtin plugins:")
|
27
27
|
for plugin in builtin_plugins:
|
28
28
|
print(f" - {plugin} [BUILTIN]")
|
29
|
-
|
29
|
+
|
30
30
|
# Show other available plugins
|
31
31
|
other_plugins = [p for p in available if p not in builtin_plugins]
|
32
32
|
if other_plugins:
|
@@ -38,32 +38,34 @@ def handle_list_plugins(args: argparse.Namespace) -> None:
|
|
38
38
|
print("Search paths:")
|
39
39
|
print(f" - {os.getcwd()}/plugins")
|
40
40
|
print(f" - {os.path.expanduser('~')}/.janito/plugins")
|
41
|
-
elif getattr(args,
|
41
|
+
elif getattr(args, "list_resources", False):
|
42
42
|
# List all resources from loaded plugins
|
43
43
|
manager = PluginManager()
|
44
44
|
all_resources = manager.list_all_resources()
|
45
|
-
|
45
|
+
|
46
46
|
if all_resources:
|
47
47
|
print("Plugin Resources:")
|
48
48
|
for plugin_name, resources in all_resources.items():
|
49
49
|
metadata = manager.get_plugin_metadata(plugin_name)
|
50
|
-
print(
|
51
|
-
|
50
|
+
print(
|
51
|
+
f"\n{plugin_name} v{metadata.version if metadata else 'unknown'}:"
|
52
|
+
)
|
53
|
+
|
52
54
|
# Group resources by type
|
53
|
-
tools = [r for r in resources if r[
|
54
|
-
commands = [r for r in resources if r[
|
55
|
-
configs = [r for r in resources if r[
|
56
|
-
|
55
|
+
tools = [r for r in resources if r["type"] == "tool"]
|
56
|
+
commands = [r for r in resources if r["type"] == "command"]
|
57
|
+
configs = [r for r in resources if r["type"] == "config"]
|
58
|
+
|
57
59
|
if tools:
|
58
60
|
print(" Tools:")
|
59
61
|
for tool in tools:
|
60
62
|
print(f" - {tool['name']}: {tool['description']}")
|
61
|
-
|
63
|
+
|
62
64
|
if commands:
|
63
65
|
print(" Commands:")
|
64
66
|
for cmd in commands:
|
65
67
|
print(f" - {cmd['name']}: {cmd['description']}")
|
66
|
-
|
68
|
+
|
67
69
|
if configs:
|
68
70
|
print(" Configuration:")
|
69
71
|
for config in configs:
|
@@ -74,7 +76,7 @@ def handle_list_plugins(args: argparse.Namespace) -> None:
|
|
74
76
|
# List loaded plugins
|
75
77
|
manager = PluginManager()
|
76
78
|
loaded = manager.list_plugins()
|
77
|
-
|
79
|
+
|
78
80
|
if loaded:
|
79
81
|
print("Loaded plugins:")
|
80
82
|
for plugin_name in loaded:
|
@@ -89,5 +91,3 @@ def handle_list_plugins(args: argparse.Namespace) -> None:
|
|
89
91
|
print()
|
90
92
|
else:
|
91
93
|
print("No plugins loaded")
|
92
|
-
|
93
|
-
|
@@ -19,13 +19,13 @@ def _extract_profile_name(filename: str) -> str:
|
|
19
19
|
filename = filename[len(_PREFIX) :]
|
20
20
|
if filename.endswith(_SUFFIX):
|
21
21
|
filename = filename[: -len(_SUFFIX)]
|
22
|
-
|
22
|
+
|
23
23
|
# Convert to title case for consistent capitalization, but handle common acronyms
|
24
24
|
name = filename.replace("_", " ")
|
25
|
-
|
25
|
+
|
26
26
|
# Convert to proper title case with consistent capitalization
|
27
27
|
name = filename.replace("_", " ")
|
28
|
-
|
28
|
+
|
29
29
|
# Handle special cases and acronyms
|
30
30
|
special_cases = {
|
31
31
|
"python": "Python",
|
@@ -35,9 +35,9 @@ def _extract_profile_name(filename: str) -> str:
|
|
35
35
|
"developer": "Developer",
|
36
36
|
"analyst": "Analyst",
|
37
37
|
"conversation": "Conversation",
|
38
|
-
"without": "Without"
|
38
|
+
"without": "Without",
|
39
39
|
}
|
40
|
-
|
40
|
+
|
41
41
|
words = name.split()
|
42
42
|
capitalized_words = []
|
43
43
|
for word in words:
|
@@ -46,7 +46,7 @@ def _extract_profile_name(filename: str) -> str:
|
|
46
46
|
capitalized_words.append(special_cases[lower_word])
|
47
47
|
else:
|
48
48
|
capitalized_words.append(word.capitalize())
|
49
|
-
|
49
|
+
|
50
50
|
return " ".join(capitalized_words)
|
51
51
|
|
52
52
|
|
@@ -8,7 +8,7 @@ from janito.cli.cli_commands.ping_providers import handle_ping_providers
|
|
8
8
|
|
9
9
|
def handle_list_providers(args=None):
|
10
10
|
# Check if ping flag is set
|
11
|
-
if args and getattr(args,
|
11
|
+
if args and getattr(args, "ping", False):
|
12
12
|
handle_ping_providers(args)
|
13
13
|
else:
|
14
14
|
list_providers()
|
@@ -19,76 +19,101 @@ def _print_models_table(models, provider_name):
|
|
19
19
|
|
20
20
|
# Get default model for this provider
|
21
21
|
from janito.providers.registry import LLMProviderRegistry
|
22
|
+
|
22
23
|
try:
|
23
24
|
provider_class = LLMProviderRegistry.get(provider_name)
|
24
25
|
default_model = getattr(provider_class, "DEFAULT_MODEL", None)
|
25
26
|
except:
|
26
27
|
default_model = None
|
27
|
-
|
28
|
+
|
28
29
|
for m in models:
|
29
30
|
name = str(m.get("name", ""))
|
30
|
-
|
31
|
+
|
31
32
|
# Highlight default model with different color
|
32
33
|
if name == default_model:
|
33
34
|
name = f"[bold green]⭐ {name}[/bold green]"
|
34
|
-
|
35
|
-
vendor =
|
36
|
-
|
35
|
+
|
36
|
+
vendor = (
|
37
|
+
"Open" if m.get("open") is True or m.get("open") == "Open" else "Locked"
|
38
|
+
)
|
39
|
+
|
37
40
|
context = _format_context(m.get("context", ""))
|
38
41
|
max_input = _format_k(m.get("max_input", ""))
|
39
42
|
max_cot = _format_k(m.get("max_cot", ""))
|
40
43
|
max_response = _format_k(m.get("max_response", ""))
|
41
|
-
|
44
|
+
|
42
45
|
# Determine thinking indicators
|
43
|
-
thinking_supported =
|
46
|
+
thinking_supported = (
|
47
|
+
m.get("thinking_supported") is True or m.get("thinking_supported") == "True"
|
48
|
+
)
|
44
49
|
cot_value = m.get("max_cot", "")
|
45
|
-
|
50
|
+
|
46
51
|
thinking_icon = "📖" if thinking_supported and m.get("thinking", False) else ""
|
47
52
|
# Only show CoT value if it's a valid number and thinking is supported
|
48
53
|
cot_display = ""
|
49
54
|
if thinking_supported and cot_value and str(cot_value).lower() != "n/a":
|
50
55
|
cot_display = _format_k(cot_value)
|
51
|
-
|
56
|
+
|
52
57
|
driver = _format_driver(m.get("driver", ""))
|
53
|
-
|
54
|
-
table.add_row(
|
58
|
+
|
59
|
+
table.add_row(
|
60
|
+
name,
|
61
|
+
vendor,
|
62
|
+
context,
|
63
|
+
max_input,
|
64
|
+
cot_display,
|
65
|
+
max_response,
|
66
|
+
thinking_icon,
|
67
|
+
driver,
|
68
|
+
)
|
55
69
|
|
56
70
|
import sys
|
71
|
+
|
57
72
|
if sys.stdout.isatty():
|
58
73
|
shared_console.print(table)
|
59
74
|
else:
|
60
75
|
# ASCII-friendly fallback table when output is redirected
|
61
76
|
print(f"Supported models for provider '{provider_name}'")
|
62
|
-
print(
|
63
|
-
|
77
|
+
print(
|
78
|
+
"Model Name | Vendor | Context | Max Input | CoT | Max Response | Thinking | Driver"
|
79
|
+
)
|
80
|
+
|
64
81
|
# Get default model for fallback
|
65
82
|
from janito.providers.registry import LLMProviderRegistry
|
83
|
+
|
66
84
|
try:
|
67
85
|
provider_class = LLMProviderRegistry.get(provider_name)
|
68
86
|
default_model = getattr(provider_class, "DEFAULT_MODEL", None)
|
69
87
|
except:
|
70
88
|
default_model = None
|
71
|
-
|
89
|
+
|
72
90
|
for m in models:
|
73
91
|
name = str(m.get("name", ""))
|
74
92
|
if name == default_model:
|
75
93
|
name = f"⭐ {name} (default)"
|
76
|
-
|
77
|
-
vendor =
|
94
|
+
|
95
|
+
vendor = (
|
96
|
+
"Open" if m.get("open") is True or m.get("open") == "Open" else "Locked"
|
97
|
+
)
|
78
98
|
context = _format_context(m.get("context", ""))
|
79
99
|
max_input = _format_k(m.get("max_input", ""))
|
80
100
|
max_cot = _format_k(m.get("max_cot", ""))
|
81
101
|
max_response = _format_k(m.get("max_response", ""))
|
82
|
-
thinking_supported =
|
102
|
+
thinking_supported = (
|
103
|
+
m.get("thinking_supported") is True
|
104
|
+
or m.get("thinking_supported") == "True"
|
105
|
+
)
|
83
106
|
cot_value = m.get("max_cot", "")
|
84
|
-
|
107
|
+
|
85
108
|
thinking = "Y" if thinking_supported and m.get("thinking", False) else ""
|
86
109
|
cot_display = ""
|
87
110
|
if thinking_supported and cot_value and str(cot_value).lower() != "n/a":
|
88
111
|
cot_display = _format_k(cot_value)
|
89
|
-
|
112
|
+
|
90
113
|
driver = _format_driver(m.get("driver", ""))
|
91
|
-
print(
|
114
|
+
print(
|
115
|
+
f"{name} | {vendor} | {context} | {max_input} | {cot_display} | {max_response} | {thinking} | {driver}"
|
116
|
+
)
|
92
117
|
|
93
118
|
|
94
119
|
def _format_k(val):
|
@@ -12,14 +12,14 @@ def handle_ping_providers(args):
|
|
12
12
|
try:
|
13
13
|
# Get all providers
|
14
14
|
providers = list_providers()
|
15
|
-
|
15
|
+
|
16
16
|
# Create table for results
|
17
17
|
table = Table(title="Provider Connectivity Test")
|
18
18
|
table.add_column("Provider", style="cyan")
|
19
19
|
table.add_column("Status", style="magenta")
|
20
20
|
table.add_column("Time", style="green")
|
21
21
|
table.add_column("Details", style="yellow")
|
22
|
-
|
22
|
+
|
23
23
|
# Test each provider
|
24
24
|
for provider_name in providers:
|
25
25
|
start_time = time.time()
|
@@ -27,7 +27,7 @@ def handle_ping_providers(args):
|
|
27
27
|
# Get provider class
|
28
28
|
provider_class = LLMProviderRegistry.get(provider_name)
|
29
29
|
provider = provider_class()
|
30
|
-
|
30
|
+
|
31
31
|
# Test the provider (simplified - just check if we can instantiate and get models)
|
32
32
|
models = provider.get_models()
|
33
33
|
if models:
|
@@ -36,20 +36,20 @@ def handle_ping_providers(args):
|
|
36
36
|
else:
|
37
37
|
status = "⚠ No models"
|
38
38
|
details = "Provider reachable but no models returned"
|
39
|
-
|
39
|
+
|
40
40
|
except Exception as e:
|
41
41
|
status = "✗ Failed"
|
42
42
|
details = str(e)
|
43
|
-
|
43
|
+
|
44
44
|
end_time = time.time()
|
45
45
|
elapsed = f"{(end_time - start_time)*1000:.0f}ms"
|
46
|
-
|
46
|
+
|
47
47
|
table.add_row(provider_name, status, elapsed, details)
|
48
|
-
|
48
|
+
|
49
49
|
# Print results
|
50
50
|
shared_console.print(table)
|
51
|
-
|
51
|
+
|
52
52
|
except Exception as e:
|
53
53
|
print(f"Error testing provider connectivity: {e}")
|
54
|
-
|
55
|
-
return
|
54
|
+
|
55
|
+
return
|
@@ -13,13 +13,15 @@ def handle_set_api_key(args):
|
|
13
13
|
if not provider:
|
14
14
|
print("Error: --set-api-key requires -p/--provider to be specified.")
|
15
15
|
return
|
16
|
-
|
16
|
+
|
17
17
|
# Validate provider name
|
18
18
|
if provider not in LLMProviderRegistry.list_providers():
|
19
19
|
valid_providers = LLMProviderRegistry.list_providers()
|
20
|
-
print(
|
20
|
+
print(
|
21
|
+
f"Error: Unknown provider '{provider}'. Valid providers are: {', '.join(valid_providers)}"
|
22
|
+
)
|
21
23
|
return
|
22
|
-
|
24
|
+
|
23
25
|
try:
|
24
26
|
set_api_key(provider, api_key)
|
25
27
|
auth_manager = LLMAuthManager()
|
@@ -42,24 +42,30 @@ def handle_show_config(args):
|
|
42
42
|
console.print(f"[bold yellow]Current provider:[/bold yellow] {provider!r}\n")
|
43
43
|
if model is not None:
|
44
44
|
console.print(f"[bold yellow]Global model:[/bold yellow] {model!r}\n")
|
45
|
-
|
45
|
+
|
46
46
|
# Show all configuration values
|
47
47
|
console.print("[bold green]Configuration values:[/bold green]")
|
48
48
|
all_config = config.all()
|
49
49
|
if all_config:
|
50
50
|
for key, value in sorted(all_config.items()):
|
51
51
|
# Hide sensitive values like API keys
|
52
|
-
if
|
53
|
-
masked_value =
|
52
|
+
if "api_key" in key.lower() and value:
|
53
|
+
masked_value = (
|
54
|
+
value[:8] + "***" + value[-4:] if len(value) > 12 else "***"
|
55
|
+
)
|
54
56
|
console.print(f" {key}: {masked_value!r}")
|
55
|
-
elif key ==
|
57
|
+
elif key == "providers" and isinstance(value, dict):
|
56
58
|
# Handle nested provider configs with API keys
|
57
59
|
masked_providers = {}
|
58
60
|
for provider_name, provider_config in value.items():
|
59
61
|
masked_config = dict(provider_config)
|
60
|
-
if
|
61
|
-
api_key = masked_config[
|
62
|
-
masked_config[
|
62
|
+
if "api_key" in masked_config and masked_config["api_key"]:
|
63
|
+
api_key = masked_config["api_key"]
|
64
|
+
masked_config["api_key"] = (
|
65
|
+
api_key[:8] + "***" + api_key[-4:]
|
66
|
+
if len(api_key) > 12
|
67
|
+
else "***"
|
68
|
+
)
|
63
69
|
masked_providers[provider_name] = masked_config
|
64
70
|
console.print(f" {key}: {masked_providers!r}")
|
65
71
|
else:
|