janito 2.27.1__py3-none-any.whl → 2.28.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 +17 -5
- 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_core.py +2 -0
- janito/cli/prompt_setup.py +4 -4
- janito/cli/single_shot_mode/handler.py +2 -0
- 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/search_text/core.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.28.0.dist-info}/METADATA +1 -1
- {janito-2.27.1.dist-info → janito-2.28.0.dist-info}/RECORD +79 -79
- janito/providers/moonshotai/__init__.py +0 -1
- {janito-2.27.1.dist-info → janito-2.28.0.dist-info}/WHEEL +0 -0
- {janito-2.27.1.dist-info → janito-2.28.0.dist-info}/entry_points.txt +0 -0
- {janito-2.27.1.dist-info → janito-2.28.0.dist-info}/licenses/LICENSE +0 -0
- {janito-2.27.1.dist-info → janito-2.28.0.dist-info}/top_level.txt +0 -0
janito/plugins/discovery.py
CHANGED
@@ -37,36 +37,38 @@ from .builtin import load_builtin_plugin, BuiltinPluginRegistry
|
|
37
37
|
logger = logging.getLogger(__name__)
|
38
38
|
|
39
39
|
|
40
|
-
def discover_plugins(
|
40
|
+
def discover_plugins(
|
41
|
+
plugin_name: str, search_paths: List[Path] = None
|
42
|
+
) -> Optional[Plugin]:
|
41
43
|
"""
|
42
44
|
Discover and load a plugin by name.
|
43
|
-
|
45
|
+
|
44
46
|
Supports multiple plugin formats:
|
45
47
|
- Single .py files
|
46
48
|
- Python package directories
|
47
49
|
- Package-based plugins (e.g., core.filemanager)
|
48
50
|
- Installed Python packages
|
49
51
|
- ZIP files containing packages
|
50
|
-
|
52
|
+
|
51
53
|
Args:
|
52
54
|
plugin_name: Name of the plugin to discover
|
53
55
|
search_paths: List of directories to search for plugins
|
54
|
-
|
56
|
+
|
55
57
|
Returns:
|
56
58
|
Plugin instance if found, None otherwise
|
57
59
|
"""
|
58
60
|
if search_paths is None:
|
59
61
|
search_paths = []
|
60
|
-
|
62
|
+
|
61
63
|
# Add default search paths
|
62
64
|
default_paths = [
|
63
65
|
Path.cwd() / "plugins",
|
64
66
|
Path.home() / ".janito" / "plugins",
|
65
67
|
Path(sys.prefix) / "share" / "janito" / "plugins",
|
66
68
|
]
|
67
|
-
|
69
|
+
|
68
70
|
all_paths = search_paths + default_paths
|
69
|
-
|
71
|
+
|
70
72
|
# Handle package-based plugins (e.g., core.filemanager)
|
71
73
|
if "." in plugin_name:
|
72
74
|
parts = plugin_name.split(".")
|
@@ -76,33 +78,33 @@ def discover_plugins(plugin_name: str, search_paths: List[Path] = None) -> Optio
|
|
76
78
|
package_path = base_path / package_name / submodule_name / "__init__.py"
|
77
79
|
if package_path.exists():
|
78
80
|
return _load_plugin_from_file(package_path, plugin_name=plugin_name)
|
79
|
-
|
81
|
+
|
80
82
|
plugin_path = base_path / package_name / submodule_name / "plugin.py"
|
81
83
|
if plugin_path.exists():
|
82
84
|
return _load_plugin_from_file(plugin_path, plugin_name=plugin_name)
|
83
|
-
|
85
|
+
|
84
86
|
# Try to find plugin in search paths
|
85
87
|
for base_path in all_paths:
|
86
88
|
plugin_path = base_path / plugin_name
|
87
89
|
if plugin_path.exists():
|
88
90
|
return _load_plugin_from_directory(plugin_path)
|
89
|
-
|
91
|
+
|
90
92
|
# Try as Python module
|
91
93
|
module_path = base_path / f"{plugin_name}.py"
|
92
94
|
if module_path.exists():
|
93
95
|
return _load_plugin_from_file(module_path)
|
94
|
-
|
96
|
+
|
95
97
|
# Check for builtin plugins
|
96
98
|
builtin_plugin = load_builtin_plugin(plugin_name)
|
97
99
|
if builtin_plugin:
|
98
100
|
return builtin_plugin
|
99
|
-
|
101
|
+
|
100
102
|
# Try importing as installed package
|
101
103
|
try:
|
102
104
|
return _load_plugin_from_package(plugin_name)
|
103
105
|
except ImportError:
|
104
106
|
pass
|
105
|
-
|
107
|
+
|
106
108
|
return None
|
107
109
|
|
108
110
|
|
@@ -112,74 +114,78 @@ def _load_plugin_from_directory(plugin_path: Path) -> Optional[Plugin]:
|
|
112
114
|
# Look for __init__.py or plugin.py
|
113
115
|
init_file = plugin_path / "__init__.py"
|
114
116
|
plugin_file = plugin_path / "plugin.py"
|
115
|
-
|
117
|
+
|
116
118
|
if init_file.exists():
|
117
119
|
return _load_plugin_from_file(init_file, plugin_name=plugin_path.name)
|
118
120
|
elif plugin_file.exists():
|
119
121
|
return _load_plugin_from_file(plugin_file, plugin_name=plugin_path.name)
|
120
|
-
|
122
|
+
|
121
123
|
except Exception as e:
|
122
124
|
logger.error(f"Failed to load plugin from directory {plugin_path}: {e}")
|
123
|
-
|
125
|
+
|
124
126
|
return None
|
125
127
|
|
126
128
|
|
127
|
-
def _load_plugin_from_file(
|
129
|
+
def _load_plugin_from_file(
|
130
|
+
file_path: Path, plugin_name: str = None
|
131
|
+
) -> Optional[Plugin]:
|
128
132
|
"""Load a plugin from a Python file."""
|
129
133
|
try:
|
130
134
|
if plugin_name is None:
|
131
135
|
plugin_name = file_path.stem
|
132
|
-
|
136
|
+
|
133
137
|
spec = importlib.util.spec_from_file_location(plugin_name, file_path)
|
134
138
|
if spec is None or spec.loader is None:
|
135
139
|
return None
|
136
|
-
|
140
|
+
|
137
141
|
module = importlib.util.module_from_spec(spec)
|
138
142
|
spec.loader.exec_module(module)
|
139
|
-
|
143
|
+
|
140
144
|
# Look for Plugin class
|
141
145
|
for attr_name in dir(module):
|
142
146
|
attr = getattr(module, attr_name)
|
143
|
-
if
|
144
|
-
issubclass(attr, Plugin) and
|
145
|
-
attr != Plugin):
|
147
|
+
if isinstance(attr, type) and issubclass(attr, Plugin) and attr != Plugin:
|
146
148
|
return attr()
|
147
|
-
|
149
|
+
|
148
150
|
# Check for package-based plugin with __plugin_name__ metadata
|
149
|
-
if hasattr(module,
|
151
|
+
if hasattr(module, "__plugin_name__"):
|
150
152
|
from janito.plugins.base import PluginMetadata
|
151
|
-
|
153
|
+
|
152
154
|
# Create a dynamic plugin class
|
153
155
|
class PackagePlugin(Plugin):
|
154
156
|
def __init__(self):
|
155
157
|
super().__init__()
|
156
158
|
self._module = module
|
157
|
-
|
159
|
+
|
158
160
|
def get_metadata(self) -> PluginMetadata:
|
159
161
|
return PluginMetadata(
|
160
|
-
name=getattr(module,
|
161
|
-
version=getattr(module,
|
162
|
-
description=getattr(
|
163
|
-
|
164
|
-
|
162
|
+
name=getattr(module, "__plugin_name__", plugin_name),
|
163
|
+
version=getattr(module, "__plugin_version__", "1.0.0"),
|
164
|
+
description=getattr(
|
165
|
+
module,
|
166
|
+
"__plugin_description__",
|
167
|
+
f"Package plugin: {plugin_name}",
|
168
|
+
),
|
169
|
+
author=getattr(module, "__plugin_author__", "Unknown"),
|
170
|
+
license=getattr(module, "__plugin_license__", "MIT"),
|
165
171
|
)
|
166
|
-
|
172
|
+
|
167
173
|
def get_tools(self):
|
168
|
-
return getattr(module,
|
169
|
-
|
174
|
+
return getattr(module, "__plugin_tools__", [])
|
175
|
+
|
170
176
|
def initialize(self):
|
171
|
-
if hasattr(module,
|
177
|
+
if hasattr(module, "initialize"):
|
172
178
|
module.initialize()
|
173
|
-
|
179
|
+
|
174
180
|
def cleanup(self):
|
175
|
-
if hasattr(module,
|
181
|
+
if hasattr(module, "cleanup"):
|
176
182
|
module.cleanup()
|
177
|
-
|
183
|
+
|
178
184
|
return PackagePlugin()
|
179
|
-
|
185
|
+
|
180
186
|
except Exception as e:
|
181
187
|
logger.error(f"Failed to load plugin from file {file_path}: {e}")
|
182
|
-
|
188
|
+
|
183
189
|
return None
|
184
190
|
|
185
191
|
|
@@ -187,54 +193,52 @@ def _load_plugin_from_package(package_name: str) -> Optional[Plugin]:
|
|
187
193
|
"""Load a plugin from an installed package."""
|
188
194
|
try:
|
189
195
|
module = importlib.import_module(package_name)
|
190
|
-
|
196
|
+
|
191
197
|
# Look for Plugin class
|
192
198
|
for attr_name in dir(module):
|
193
199
|
attr = getattr(module, attr_name)
|
194
|
-
if
|
195
|
-
issubclass(attr, Plugin) and
|
196
|
-
attr != Plugin):
|
200
|
+
if isinstance(attr, type) and issubclass(attr, Plugin) and attr != Plugin:
|
197
201
|
return attr()
|
198
|
-
|
202
|
+
|
199
203
|
except ImportError as e:
|
200
204
|
logger.debug(f"Could not import package {package_name}: {e}")
|
201
|
-
|
205
|
+
|
202
206
|
return None
|
203
207
|
|
204
208
|
|
205
209
|
def list_available_plugins(search_paths: List[Path] = None) -> List[str]:
|
206
210
|
"""
|
207
211
|
List all available plugins in search paths.
|
208
|
-
|
212
|
+
|
209
213
|
Scans for plugins in multiple formats:
|
210
214
|
- .py files (excluding __init__.py)
|
211
215
|
- Directories with __init__.py or plugin.py
|
212
216
|
- Package directories with plugin metadata (__plugin_name__)
|
213
217
|
- Any valid plugin structure in search paths
|
214
|
-
|
218
|
+
|
215
219
|
Args:
|
216
220
|
search_paths: List of directories to search for plugins
|
217
|
-
|
221
|
+
|
218
222
|
Returns:
|
219
223
|
List of plugin names found
|
220
224
|
"""
|
221
225
|
if search_paths is None:
|
222
226
|
search_paths = []
|
223
|
-
|
227
|
+
|
224
228
|
# Add default search paths
|
225
229
|
default_paths = [
|
226
230
|
Path.cwd() / "plugins",
|
227
|
-
Path.home() / ".janito" / "plugins",
|
231
|
+
Path.home() / ".janito" / "plugins",
|
228
232
|
Path(sys.prefix) / "share" / "janito" / "plugins",
|
229
233
|
]
|
230
|
-
|
234
|
+
|
231
235
|
all_paths = search_paths + default_paths
|
232
236
|
plugins = []
|
233
|
-
|
237
|
+
|
234
238
|
for base_path in all_paths:
|
235
239
|
if not base_path.exists():
|
236
240
|
continue
|
237
|
-
|
241
|
+
|
238
242
|
# Look for directories with __init__.py or plugin.py
|
239
243
|
for item in base_path.iterdir():
|
240
244
|
if item.is_dir():
|
@@ -245,30 +249,33 @@ def list_available_plugins(search_paths: List[Path] = None) -> List[str]:
|
|
245
249
|
if subitem.is_dir() and (subitem / "__init__.py").exists():
|
246
250
|
try:
|
247
251
|
import importlib.util
|
252
|
+
|
248
253
|
spec = importlib.util.spec_from_file_location(
|
249
|
-
f"{item.name}.{subitem.name}",
|
250
|
-
subitem / "__init__.py"
|
254
|
+
f"{item.name}.{subitem.name}",
|
255
|
+
subitem / "__init__.py",
|
251
256
|
)
|
252
257
|
if spec and spec.loader:
|
253
258
|
module = importlib.util.module_from_spec(spec)
|
254
259
|
spec.loader.exec_module(module)
|
255
|
-
|
260
|
+
|
256
261
|
# Check for plugin metadata
|
257
|
-
if hasattr(module,
|
258
|
-
plugins.append(
|
262
|
+
if hasattr(module, "__plugin_name__"):
|
263
|
+
plugins.append(
|
264
|
+
getattr(module, "__plugin_name__")
|
265
|
+
)
|
259
266
|
except Exception:
|
260
267
|
pass
|
261
|
-
|
268
|
+
|
262
269
|
# Also check for plugin.py files
|
263
270
|
plugin_file = item / "plugin.py"
|
264
271
|
if plugin_file.exists():
|
265
272
|
plugins.append(item.name)
|
266
|
-
|
267
|
-
elif item.suffix ==
|
273
|
+
|
274
|
+
elif item.suffix == ".py" and item.stem != "__init__":
|
268
275
|
plugins.append(item.stem)
|
269
|
-
|
276
|
+
|
270
277
|
# Add builtin plugins
|
271
278
|
builtin_plugins = BuiltinPluginRegistry.list_builtin_plugins()
|
272
279
|
plugins.extend(builtin_plugins)
|
273
|
-
|
274
|
-
return sorted(set(plugins))
|
280
|
+
|
281
|
+
return sorted(set(plugins))
|
janito/plugins/manager.py
CHANGED
@@ -23,13 +23,13 @@ class PluginManager:
|
|
23
23
|
"""
|
24
24
|
Manages plugin loading, registration, and lifecycle.
|
25
25
|
"""
|
26
|
-
|
26
|
+
|
27
27
|
def __init__(self, tools_adapter: Optional[LocalToolsAdapter] = None):
|
28
28
|
self.tools_adapter = tools_adapter or LocalToolsAdapter()
|
29
29
|
self.plugins: Dict[str, Plugin] = {}
|
30
30
|
self.plugin_configs: Dict[str, Dict[str, Any]] = {}
|
31
31
|
self.plugin_paths: List[Path] = []
|
32
|
-
|
32
|
+
|
33
33
|
def add_plugin_path(self, path: str) -> None:
|
34
34
|
"""Add a directory to search for plugins."""
|
35
35
|
plugin_path = Path(path)
|
@@ -37,15 +37,17 @@ class PluginManager:
|
|
37
37
|
self.plugin_paths.append(plugin_path)
|
38
38
|
if str(plugin_path) not in sys.path:
|
39
39
|
sys.path.insert(0, str(plugin_path))
|
40
|
-
|
41
|
-
def load_plugin(
|
40
|
+
|
41
|
+
def load_plugin(
|
42
|
+
self, plugin_name: str, config: Optional[Dict[str, Any]] = None
|
43
|
+
) -> bool:
|
42
44
|
"""
|
43
45
|
Load a plugin by name.
|
44
|
-
|
46
|
+
|
45
47
|
Args:
|
46
48
|
plugin_name: Name of the plugin to load
|
47
49
|
config: Optional configuration for the plugin
|
48
|
-
|
50
|
+
|
49
51
|
Returns:
|
50
52
|
True if plugin loaded successfully
|
51
53
|
"""
|
@@ -53,47 +55,47 @@ class PluginManager:
|
|
53
55
|
if plugin_name in self.plugins:
|
54
56
|
logger.warning(f"Plugin {plugin_name} already loaded")
|
55
57
|
return True
|
56
|
-
|
58
|
+
|
57
59
|
plugin = discover_plugins(plugin_name, self.plugin_paths)
|
58
60
|
if not plugin:
|
59
61
|
logger.error(f"Plugin {plugin_name} not found")
|
60
62
|
return False
|
61
|
-
|
63
|
+
|
62
64
|
# Store config
|
63
65
|
if config:
|
64
66
|
self.plugin_configs[plugin_name] = config
|
65
|
-
|
67
|
+
|
66
68
|
# Validate config if provided
|
67
|
-
if config and hasattr(plugin,
|
69
|
+
if config and hasattr(plugin, "validate_config"):
|
68
70
|
if not plugin.validate_config(config):
|
69
71
|
logger.error(f"Invalid configuration for plugin {plugin_name}")
|
70
72
|
return False
|
71
|
-
|
73
|
+
|
72
74
|
# Initialize plugin
|
73
75
|
plugin.initialize()
|
74
|
-
|
76
|
+
|
75
77
|
# Register tools
|
76
78
|
tools = plugin.get_tools()
|
77
79
|
for tool_class in tools:
|
78
80
|
self.tools_adapter.register_tool(tool_class)
|
79
|
-
|
81
|
+
|
80
82
|
# Store plugin
|
81
83
|
self.plugins[plugin_name] = plugin
|
82
|
-
|
84
|
+
|
83
85
|
logger.info(f"Successfully loaded plugin: {plugin_name}")
|
84
86
|
return True
|
85
|
-
|
87
|
+
|
86
88
|
except Exception as e:
|
87
89
|
logger.error(f"Failed to load plugin {plugin_name}: {e}")
|
88
90
|
return False
|
89
|
-
|
91
|
+
|
90
92
|
def unload_plugin(self, plugin_name: str) -> bool:
|
91
93
|
"""
|
92
94
|
Unload a plugin.
|
93
|
-
|
95
|
+
|
94
96
|
Args:
|
95
97
|
plugin_name: Name of the plugin to unload
|
96
|
-
|
98
|
+
|
97
99
|
Returns:
|
98
100
|
True if plugin unloaded successfully
|
99
101
|
"""
|
@@ -101,65 +103,65 @@ class PluginManager:
|
|
101
103
|
if plugin_name not in self.plugins:
|
102
104
|
logger.warning(f"Plugin {plugin_name} not loaded")
|
103
105
|
return False
|
104
|
-
|
106
|
+
|
105
107
|
plugin = self.plugins[plugin_name]
|
106
|
-
|
108
|
+
|
107
109
|
# Unregister tools
|
108
110
|
tools = plugin.get_tools()
|
109
111
|
for tool_class in tools:
|
110
|
-
tool_name = getattr(tool_class(),
|
112
|
+
tool_name = getattr(tool_class(), "tool_name", None)
|
111
113
|
if tool_name:
|
112
114
|
self.tools_adapter.unregister_tool(tool_name)
|
113
|
-
|
115
|
+
|
114
116
|
# Cleanup plugin
|
115
117
|
plugin.cleanup()
|
116
|
-
|
118
|
+
|
117
119
|
# Remove from registry
|
118
120
|
del self.plugins[plugin_name]
|
119
121
|
if plugin_name in self.plugin_configs:
|
120
122
|
del self.plugin_configs[plugin_name]
|
121
|
-
|
123
|
+
|
122
124
|
logger.info(f"Successfully unloaded plugin: {plugin_name}")
|
123
125
|
return True
|
124
|
-
|
126
|
+
|
125
127
|
except Exception as e:
|
126
128
|
logger.error(f"Failed to unload plugin {plugin_name}: {e}")
|
127
129
|
return False
|
128
|
-
|
130
|
+
|
129
131
|
def list_plugins(self) -> List[str]:
|
130
132
|
"""Return list of loaded plugin names."""
|
131
133
|
return list(self.plugins.keys())
|
132
|
-
|
134
|
+
|
133
135
|
def get_plugin(self, plugin_name: str) -> Optional[Plugin]:
|
134
136
|
"""Get a loaded plugin by name."""
|
135
137
|
return self.plugins.get(plugin_name)
|
136
|
-
|
138
|
+
|
137
139
|
def get_plugin_metadata(self, plugin_name: str) -> Optional[PluginMetadata]:
|
138
140
|
"""Get metadata for a loaded plugin."""
|
139
141
|
plugin = self.plugins.get(plugin_name)
|
140
142
|
return plugin.metadata if plugin else None
|
141
|
-
|
143
|
+
|
142
144
|
def load_plugins_from_config(self, config: Dict[str, Any]) -> None:
|
143
145
|
"""
|
144
146
|
Load plugins from configuration.
|
145
|
-
|
147
|
+
|
146
148
|
Args:
|
147
149
|
config: Configuration dict with plugin settings
|
148
150
|
"""
|
149
|
-
plugins_config = config.get(
|
150
|
-
|
151
|
+
plugins_config = config.get("plugins", {})
|
152
|
+
|
151
153
|
# Add plugin paths
|
152
|
-
for path in plugins_config.get(
|
154
|
+
for path in plugins_config.get("paths", []):
|
153
155
|
self.add_plugin_path(path)
|
154
|
-
|
156
|
+
|
155
157
|
# Load plugins
|
156
|
-
for plugin_name, plugin_config in plugins_config.get(
|
158
|
+
for plugin_name, plugin_config in plugins_config.get("load", {}).items():
|
157
159
|
if isinstance(plugin_config, bool):
|
158
160
|
if plugin_config:
|
159
161
|
self.load_plugin(plugin_name)
|
160
162
|
else:
|
161
163
|
self.load_plugin(plugin_name, plugin_config)
|
162
|
-
|
164
|
+
|
163
165
|
def load_plugins_from_user_config(self) -> None:
|
164
166
|
"""
|
165
167
|
Load plugins from user configuration directory.
|
@@ -167,63 +169,63 @@ class PluginManager:
|
|
167
169
|
"""
|
168
170
|
config = load_plugins_config()
|
169
171
|
self.load_plugins_from_config(config)
|
170
|
-
|
172
|
+
|
171
173
|
def reload_plugin(self, plugin_name: str) -> bool:
|
172
174
|
"""
|
173
175
|
Reload a plugin.
|
174
|
-
|
176
|
+
|
175
177
|
Args:
|
176
178
|
plugin_name: Name of the plugin to reload
|
177
|
-
|
179
|
+
|
178
180
|
Returns:
|
179
181
|
True if plugin reloaded successfully
|
180
182
|
"""
|
181
183
|
config = self.plugin_configs.get(plugin_name)
|
182
184
|
self.unload_plugin(plugin_name)
|
183
185
|
return self.load_plugin(plugin_name, config)
|
184
|
-
|
186
|
+
|
185
187
|
def get_loaded_plugins_info(self) -> Dict[str, Dict[str, Any]]:
|
186
188
|
"""Get information about all loaded plugins."""
|
187
189
|
info = {}
|
188
190
|
for name, plugin in self.plugins.items():
|
189
191
|
info[name] = {
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
192
|
+
"metadata": plugin.metadata,
|
193
|
+
"tools": [tool.__name__ for tool in plugin.get_tools()],
|
194
|
+
"commands": list(plugin.get_commands().keys()),
|
195
|
+
"config": self.plugin_configs.get(name, {}),
|
196
|
+
"builtin": BuiltinPluginRegistry.is_builtin(name),
|
197
|
+
"resources": [
|
196
198
|
{
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
199
|
+
"name": resource.name,
|
200
|
+
"type": resource.type,
|
201
|
+
"description": resource.description,
|
202
|
+
"schema": resource.schema,
|
201
203
|
}
|
202
204
|
for resource in plugin.get_resources()
|
203
|
-
]
|
205
|
+
],
|
204
206
|
}
|
205
207
|
return info
|
206
208
|
|
207
209
|
def get_plugin_resources(self, plugin_name: str) -> List[Dict[str, Any]]:
|
208
210
|
"""
|
209
211
|
Get resources provided by a specific plugin.
|
210
|
-
|
212
|
+
|
211
213
|
Args:
|
212
214
|
plugin_name: Name of the plugin
|
213
|
-
|
215
|
+
|
214
216
|
Returns:
|
215
217
|
List of resource dictionaries
|
216
218
|
"""
|
217
219
|
plugin = self.plugins.get(plugin_name)
|
218
220
|
if not plugin:
|
219
221
|
return []
|
220
|
-
|
222
|
+
|
221
223
|
return [
|
222
224
|
{
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
225
|
+
"name": resource.name,
|
226
|
+
"type": resource.type,
|
227
|
+
"description": resource.description,
|
228
|
+
"schema": resource.schema,
|
227
229
|
}
|
228
230
|
for resource in plugin.get_resources()
|
229
231
|
]
|
@@ -231,11 +233,11 @@ class PluginManager:
|
|
231
233
|
def list_all_resources(self) -> Dict[str, List[Dict[str, Any]]]:
|
232
234
|
"""
|
233
235
|
List all resources from all loaded plugins.
|
234
|
-
|
236
|
+
|
235
237
|
Returns:
|
236
238
|
Dict mapping plugin names to their resources
|
237
239
|
"""
|
238
240
|
all_resources = {}
|
239
241
|
for plugin_name in self.plugins:
|
240
242
|
all_resources[plugin_name] = self.get_plugin_resources(plugin_name)
|
241
|
-
return all_resources
|
243
|
+
return all_resources
|
janito/provider_registry.py
CHANGED
@@ -39,16 +39,16 @@ class ProviderRegistry:
|
|
39
39
|
if len(info) == 4 and info[3]:
|
40
40
|
continue # skip providers flagged as not implemented
|
41
41
|
rows.append(info[:3])
|
42
|
-
|
42
|
+
|
43
43
|
# Group providers by openness (open-source first, then proprietary)
|
44
|
-
open_providers = {
|
45
|
-
|
44
|
+
open_providers = {"cerebras", "deepseek", "alibaba", "moonshotai", "zai"}
|
45
|
+
|
46
46
|
def sort_key(row):
|
47
47
|
provider_name = row[0]
|
48
48
|
is_open = provider_name in open_providers
|
49
49
|
# Sort open providers alphabetically first, then proprietary alphabetically
|
50
50
|
return (not is_open, provider_name)
|
51
|
-
|
51
|
+
|
52
52
|
rows.sort(key=sort_key)
|
53
53
|
return rows
|
54
54
|
|
@@ -101,21 +101,21 @@ class ProviderRegistry:
|
|
101
101
|
model_specs = None
|
102
102
|
if hasattr(model_info_mod, "MODEL_SPECS"):
|
103
103
|
model_specs = model_info_mod.MODEL_SPECS
|
104
|
-
elif hasattr(model_info_mod, "
|
105
|
-
model_specs = model_info_mod.
|
106
|
-
|
104
|
+
elif hasattr(model_info_mod, "MOONSHOT_MODEL_SPECS"):
|
105
|
+
model_specs = model_info_mod.MOONSHOT_MODEL_SPECS
|
106
|
+
|
107
107
|
if model_specs:
|
108
108
|
default_model = getattr(provider_class, "DEFAULT_MODEL", None)
|
109
109
|
model_names = []
|
110
|
-
|
110
|
+
|
111
111
|
for model_key in model_specs.keys():
|
112
112
|
if model_key == default_model:
|
113
113
|
# Highlight the default model with color and star icon
|
114
114
|
model_names.append(f"[bold green]⭐ {model_key}[/bold green]")
|
115
115
|
else:
|
116
116
|
model_names.append(model_key)
|
117
|
-
|
118
|
-
if provider_name == "
|
117
|
+
|
118
|
+
if provider_name == "moonshot":
|
119
119
|
return ", ".join(model_names)
|
120
120
|
return ", ".join(model_names)
|
121
121
|
return "-"
|
janito/providers/__init__.py
CHANGED
@@ -4,7 +4,7 @@ import janito.providers.google.provider
|
|
4
4
|
import janito.providers.azure_openai.provider
|
5
5
|
import janito.providers.anthropic.provider
|
6
6
|
import janito.providers.deepseek.provider
|
7
|
-
import janito.providers.
|
7
|
+
import janito.providers.moonshot.provider
|
8
8
|
import janito.providers.alibaba.provider
|
9
9
|
import janito.providers.zai.provider
|
10
10
|
import janito.providers.cerebras.provider
|