janito 2.1.1__py3-none-any.whl → 2.3.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/__init__.py +6 -6
- janito/agent/setup_agent.py +14 -5
- janito/agent/templates/profiles/system_prompt_template_main.txt.j2 +3 -1
- janito/cli/chat_mode/bindings.py +6 -0
- janito/cli/chat_mode/session.py +16 -0
- janito/cli/chat_mode/shell/autocomplete.py +21 -21
- janito/cli/chat_mode/shell/commands/__init__.py +3 -2
- janito/cli/chat_mode/shell/commands/clear.py +12 -12
- janito/cli/chat_mode/shell/commands/exec.py +27 -0
- janito/cli/chat_mode/shell/commands/multi.py +51 -51
- janito/cli/chat_mode/shell/commands/tools.py +17 -6
- janito/cli/chat_mode/shell/input_history.py +62 -62
- janito/cli/chat_mode/shell/session/manager.py +1 -0
- janito/cli/chat_mode/toolbar.py +3 -1
- janito/cli/cli_commands/list_models.py +35 -35
- janito/cli/cli_commands/list_providers.py +9 -9
- janito/cli/cli_commands/list_tools.py +53 -53
- janito/cli/cli_commands/model_selection.py +50 -50
- janito/cli/cli_commands/model_utils.py +13 -2
- janito/cli/cli_commands/set_api_key.py +19 -19
- janito/cli/cli_commands/show_config.py +51 -51
- janito/cli/cli_commands/show_system_prompt.py +62 -62
- janito/cli/config.py +2 -1
- janito/cli/core/__init__.py +4 -4
- janito/cli/core/event_logger.py +59 -59
- janito/cli/core/getters.py +3 -1
- janito/cli/core/runner.py +27 -6
- janito/cli/core/setters.py +5 -1
- janito/cli/core/unsetters.py +54 -54
- janito/cli/main_cli.py +12 -1
- janito/cli/prompt_core.py +5 -2
- janito/cli/rich_terminal_reporter.py +22 -3
- janito/cli/single_shot_mode/__init__.py +6 -6
- janito/cli/single_shot_mode/handler.py +11 -1
- janito/cli/verbose_output.py +1 -1
- janito/config.py +5 -5
- janito/config_manager.py +2 -0
- janito/driver_events.py +14 -0
- janito/drivers/anthropic/driver.py +113 -113
- janito/drivers/azure_openai/driver.py +38 -3
- janito/drivers/driver_registry.py +0 -2
- janito/drivers/openai/driver.py +196 -36
- janito/formatting_token.py +54 -54
- janito/i18n/__init__.py +35 -35
- janito/i18n/messages.py +23 -23
- janito/i18n/pt.py +47 -47
- janito/llm/__init__.py +5 -5
- janito/llm/agent.py +443 -443
- janito/llm/auth.py +1 -0
- janito/llm/driver.py +7 -1
- janito/llm/driver_config.py +1 -0
- janito/llm/driver_config_builder.py +34 -34
- janito/llm/driver_input.py +12 -12
- janito/llm/message_parts.py +60 -60
- janito/llm/model.py +38 -38
- janito/llm/provider.py +196 -196
- janito/provider_config.py +7 -3
- janito/provider_registry.py +29 -5
- janito/providers/__init__.py +1 -0
- janito/providers/anthropic/model_info.py +22 -22
- janito/providers/anthropic/provider.py +2 -2
- janito/providers/azure_openai/model_info.py +7 -6
- janito/providers/azure_openai/provider.py +44 -2
- janito/providers/deepseek/__init__.py +1 -1
- janito/providers/deepseek/model_info.py +16 -16
- janito/providers/deepseek/provider.py +91 -91
- janito/providers/google/model_info.py +21 -29
- janito/providers/google/provider.py +49 -38
- janito/providers/mistralai/provider.py +2 -2
- janito/providers/openai/model_info.py +0 -11
- janito/providers/openai/provider.py +1 -1
- janito/providers/provider_static_info.py +2 -3
- janito/providers/registry.py +26 -26
- janito/tools/adapters/__init__.py +1 -1
- janito/tools/adapters/local/__init__.py +62 -62
- janito/tools/adapters/local/adapter.py +33 -11
- janito/tools/adapters/local/ask_user.py +102 -102
- janito/tools/adapters/local/copy_file.py +84 -84
- janito/tools/adapters/local/create_directory.py +69 -69
- janito/tools/adapters/local/create_file.py +82 -82
- janito/tools/adapters/local/delete_text_in_file.py +4 -7
- janito/tools/adapters/local/fetch_url.py +97 -97
- janito/tools/adapters/local/find_files.py +138 -140
- janito/tools/adapters/local/get_file_outline/__init__.py +1 -1
- janito/tools/adapters/local/get_file_outline/core.py +117 -151
- janito/tools/adapters/local/get_file_outline/java_outline.py +40 -0
- janito/tools/adapters/local/get_file_outline/markdown_outline.py +14 -14
- janito/tools/adapters/local/get_file_outline/python_outline.py +303 -303
- janito/tools/adapters/local/get_file_outline/python_outline_v2.py +156 -156
- janito/tools/adapters/local/get_file_outline/search_outline.py +33 -33
- janito/tools/adapters/local/move_file.py +3 -13
- janito/tools/adapters/local/open_html_in_browser.py +24 -29
- janito/tools/adapters/local/open_url.py +3 -2
- janito/tools/adapters/local/python_code_run.py +166 -166
- janito/tools/adapters/local/python_command_run.py +164 -164
- janito/tools/adapters/local/python_file_run.py +163 -163
- janito/tools/adapters/local/remove_directory.py +6 -17
- janito/tools/adapters/local/remove_file.py +9 -15
- janito/tools/adapters/local/replace_text_in_file.py +6 -9
- janito/tools/adapters/local/run_bash_command.py +176 -176
- janito/tools/adapters/local/run_powershell_command.py +219 -219
- janito/tools/adapters/local/search_text/__init__.py +1 -1
- janito/tools/adapters/local/search_text/core.py +201 -201
- janito/tools/adapters/local/search_text/match_lines.py +1 -1
- janito/tools/adapters/local/search_text/pattern_utils.py +73 -73
- janito/tools/adapters/local/search_text/traverse_directory.py +145 -145
- janito/tools/adapters/local/validate_file_syntax/__init__.py +1 -1
- janito/tools/adapters/local/validate_file_syntax/core.py +106 -106
- janito/tools/adapters/local/validate_file_syntax/css_validator.py +35 -35
- janito/tools/adapters/local/validate_file_syntax/html_validator.py +93 -93
- janito/tools/adapters/local/validate_file_syntax/js_validator.py +27 -27
- janito/tools/adapters/local/validate_file_syntax/json_validator.py +6 -6
- janito/tools/adapters/local/validate_file_syntax/markdown_validator.py +109 -109
- janito/tools/adapters/local/validate_file_syntax/ps1_validator.py +32 -32
- janito/tools/adapters/local/validate_file_syntax/python_validator.py +5 -5
- janito/tools/adapters/local/validate_file_syntax/xml_validator.py +11 -11
- janito/tools/adapters/local/validate_file_syntax/yaml_validator.py +6 -6
- janito/tools/adapters/local/view_file.py +167 -167
- janito/tools/inspect_registry.py +17 -17
- janito/tools/tool_base.py +105 -105
- janito/tools/tool_events.py +58 -58
- janito/tools/tool_run_exception.py +12 -12
- janito/tools/tool_use_tracker.py +81 -81
- janito/tools/tool_utils.py +45 -45
- janito/tools/tools_adapter.py +78 -6
- janito/tools/tools_schema.py +104 -104
- janito/version.py +4 -4
- {janito-2.1.1.dist-info → janito-2.3.0.dist-info}/METADATA +388 -232
- janito-2.3.0.dist-info/RECORD +181 -0
- janito-2.3.0.dist-info/licenses/LICENSE +21 -0
- janito/cli/chat_mode/shell/commands/last.py +0 -137
- janito/drivers/google_genai/driver.py +0 -54
- janito/drivers/google_genai/schema_generator.py +0 -67
- janito-2.1.1.dist-info/RECORD +0 -181
- {janito-2.1.1.dist-info → janito-2.3.0.dist-info}/WHEEL +0 -0
- {janito-2.1.1.dist-info → janito-2.3.0.dist-info}/entry_points.txt +0 -0
- {janito-2.1.1.dist-info → janito-2.3.0.dist-info}/top_level.txt +0 -0
janito/llm/provider.py
CHANGED
@@ -1,196 +1,196 @@
|
|
1
|
-
from abc import ABC, abstractmethod
|
2
|
-
import importlib
|
3
|
-
from janito.llm.driver import LLMDriver
|
4
|
-
|
5
|
-
|
6
|
-
class LLMProvider(ABC):
|
7
|
-
def create_driver(self):
|
8
|
-
"""
|
9
|
-
Returns a new instance of the configured driver for this provider.
|
10
|
-
Subclasses must implement this method.
|
11
|
-
"""
|
12
|
-
raise NotImplementedError(
|
13
|
-
"LLMProvider subclasses must implement create_driver()."
|
14
|
-
)
|
15
|
-
|
16
|
-
"""
|
17
|
-
Abstract base class for Large Language Model (LLM) providers.
|
18
|
-
|
19
|
-
Provider Usage and Driver Communication Flow:
|
20
|
-
1. Provider class is selected (e.g., OpenAIProvider, MistralProvider).
|
21
|
-
2. An instance of the provider is created. This instance is bound to a specific configuration (LLMDriverConfig) containing model, credentials, etc.
|
22
|
-
3. All drivers created by that provider instance are associated with the bound config.
|
23
|
-
4. To communicate with an LLM, call create_driver() on the provider instance, which yields a driver configured for the attached config. Every driver created via this method inherits the provider's configuration.
|
24
|
-
|
25
|
-
Key: You do not create/configure a driver directly—always go through the provider to ensure correct configuration binding to the provider instance.
|
26
|
-
|
27
|
-
Subclasses must implement the core interface for interacting with LLM APIs and define `provider_name` as a class attribute.
|
28
|
-
"""
|
29
|
-
|
30
|
-
name: str = None # Must be set on subclasses
|
31
|
-
DEFAULT_MODEL: str = None # Should be set by subclasses
|
32
|
-
|
33
|
-
def __init_subclass__(cls, **kwargs):
|
34
|
-
super().__init_subclass__(**kwargs)
|
35
|
-
if (
|
36
|
-
not hasattr(cls, "name")
|
37
|
-
or not isinstance(getattr(cls, "name"), str)
|
38
|
-
or not cls.name
|
39
|
-
):
|
40
|
-
raise TypeError(
|
41
|
-
f"Class {cls.__name__} must define a class attribute 'name' (non-empty str)"
|
42
|
-
)
|
43
|
-
if (
|
44
|
-
not hasattr(cls, "DEFAULT_MODEL")
|
45
|
-
or getattr(cls, "DEFAULT_MODEL", None) is None
|
46
|
-
):
|
47
|
-
raise TypeError(
|
48
|
-
f"Class {cls.__name__} must define a class attribute 'DEFAULT_MODEL' (non-empty str)"
|
49
|
-
)
|
50
|
-
|
51
|
-
def fill_missing_device_info(self, config):
|
52
|
-
"""
|
53
|
-
Fill missing LLMDriverConfig fields (max_tokens, temperature, etc) from MODEL_SPECS for the chosen model.
|
54
|
-
Mutates the config in place.
|
55
|
-
"""
|
56
|
-
if not hasattr(self, "MODEL_SPECS"):
|
57
|
-
return
|
58
|
-
model_name = getattr(config, "model", None) or getattr(
|
59
|
-
self, "DEFAULT_MODEL", None
|
60
|
-
)
|
61
|
-
model_info = self.MODEL_SPECS.get(model_name)
|
62
|
-
if not model_info:
|
63
|
-
return
|
64
|
-
# Handle common fields from model_info
|
65
|
-
spec_dict = (
|
66
|
-
model_info.to_dict() if hasattr(model_info, "to_dict") else dict(model_info)
|
67
|
-
)
|
68
|
-
if (
|
69
|
-
hasattr(config, "max_tokens")
|
70
|
-
and getattr(config, "max_tokens", None) is None
|
71
|
-
):
|
72
|
-
val = spec_dict.get("max_tokens") or spec_dict.get("max_response")
|
73
|
-
if val is not None:
|
74
|
-
try:
|
75
|
-
config.max_tokens = int(val)
|
76
|
-
except Exception:
|
77
|
-
pass
|
78
|
-
if (
|
79
|
-
hasattr(config, "temperature")
|
80
|
-
and getattr(config, "temperature", None) is None
|
81
|
-
):
|
82
|
-
val = spec_dict.get("temperature")
|
83
|
-
if val is None:
|
84
|
-
val = spec_dict.get("default_temp")
|
85
|
-
if val is not None:
|
86
|
-
try:
|
87
|
-
config.temperature = float(val)
|
88
|
-
except Exception:
|
89
|
-
pass
|
90
|
-
|
91
|
-
@property
|
92
|
-
@abstractmethod
|
93
|
-
def driver(self) -> LLMDriver:
|
94
|
-
pass
|
95
|
-
|
96
|
-
def is_model_available(self, model_name):
|
97
|
-
"""
|
98
|
-
Returns True if the given model is available for this provider.
|
99
|
-
Default implementation checks MODEL_SPECS; override for dynamic providers.
|
100
|
-
"""
|
101
|
-
if not hasattr(self, "MODEL_SPECS"):
|
102
|
-
return False
|
103
|
-
return model_name in self.MODEL_SPECS
|
104
|
-
|
105
|
-
def get_model_info(self, model_name=None):
|
106
|
-
"""
|
107
|
-
Return the info dict for a given model (driver, params, etc). If model_name is None, return all model info dicts.
|
108
|
-
MODEL_SPECS must be dict[str, LLMModelInfo].
|
109
|
-
"""
|
110
|
-
if not hasattr(self, "MODEL_SPECS"):
|
111
|
-
raise NotImplementedError(
|
112
|
-
"This provider does not have a MODEL_SPECS attribute."
|
113
|
-
)
|
114
|
-
if model_name is None:
|
115
|
-
return {
|
116
|
-
name: model_info.to_dict()
|
117
|
-
for name, model_info in self.MODEL_SPECS.items()
|
118
|
-
}
|
119
|
-
if model_name in self.MODEL_SPECS:
|
120
|
-
return self.MODEL_SPECS[model_name].to_dict()
|
121
|
-
return None
|
122
|
-
|
123
|
-
def _validate_model_specs(self):
|
124
|
-
if not hasattr(self, "MODEL_SPECS"):
|
125
|
-
raise NotImplementedError(
|
126
|
-
"This provider does not have a MODEL_SPECS attribute."
|
127
|
-
)
|
128
|
-
|
129
|
-
def _get_model_name_from_config(self, config):
|
130
|
-
return (config or {}).get("model_name", getattr(self, "DEFAULT_MODEL", None))
|
131
|
-
|
132
|
-
def _get_model_spec_entry(self, model_name):
|
133
|
-
spec = self.MODEL_SPECS.get(model_name, None)
|
134
|
-
if spec is None:
|
135
|
-
raise ValueError(f"Model '{model_name}' not found in MODEL_SPECS.")
|
136
|
-
return spec
|
137
|
-
|
138
|
-
def _get_driver_name_from_spec(self, spec):
|
139
|
-
driver_name = None
|
140
|
-
if hasattr(spec, "driver") and spec.driver:
|
141
|
-
driver_name = spec.driver
|
142
|
-
elif hasattr(spec, "other") and isinstance(spec.other, dict):
|
143
|
-
driver_name = spec.other.get("driver", None)
|
144
|
-
return driver_name
|
145
|
-
|
146
|
-
def _resolve_driver_class(self, driver_name):
|
147
|
-
if not driver_name:
|
148
|
-
raise NotImplementedError(
|
149
|
-
"No driver class found or specified for this MODEL_SPECS entry."
|
150
|
-
)
|
151
|
-
module_root = "janito.drivers"
|
152
|
-
probable_path = None
|
153
|
-
mapping = {
|
154
|
-
"OpenAIResponsesModelDriver": "openai_responses.driver",
|
155
|
-
"OpenAIModelDriver": "openai.driver",
|
156
|
-
"AzureOpenAIModelDriver": "azure_openai.driver",
|
157
|
-
"GoogleGenaiModelDriver": "google_genai.driver",
|
158
|
-
}
|
159
|
-
if driver_name in mapping:
|
160
|
-
probable_path = mapping[driver_name]
|
161
|
-
module_path = f"{module_root}.{probable_path}"
|
162
|
-
mod = importlib.import_module(module_path)
|
163
|
-
return getattr(mod, driver_name)
|
164
|
-
# Attempt dynamic fallback based on convention
|
165
|
-
if driver_name.endswith("ModelDriver"):
|
166
|
-
base = driver_name[: -len("ModelDriver")]
|
167
|
-
mod_name = base.replace("_", "").lower()
|
168
|
-
module_path = f"{module_root}.{mod_name}.driver"
|
169
|
-
try:
|
170
|
-
mod = importlib.import_module(module_path)
|
171
|
-
return getattr(mod, driver_name)
|
172
|
-
except Exception:
|
173
|
-
pass
|
174
|
-
raise NotImplementedError(
|
175
|
-
"No driver class found for driver_name: {}".format(driver_name)
|
176
|
-
)
|
177
|
-
|
178
|
-
def _validate_required_config(self, driver_class, config, driver_name):
|
179
|
-
required = getattr(driver_class, "required_config", None)
|
180
|
-
if required:
|
181
|
-
missing = [
|
182
|
-
k
|
183
|
-
for k in required
|
184
|
-
if not config or k not in config or config.get(k) in (None, "")
|
185
|
-
]
|
186
|
-
if missing:
|
187
|
-
raise ValueError(
|
188
|
-
f"Missing required config for {driver_name}: {', '.join(missing)}"
|
189
|
-
)
|
190
|
-
|
191
|
-
def create_agent(self, tools_adapter=None, agent_name: str = None, **kwargs):
|
192
|
-
from janito.llm.agent import LLMAgent
|
193
|
-
|
194
|
-
# Dynamically create driver if supported, else fallback to existing.
|
195
|
-
driver = self.driver
|
196
|
-
return LLMAgent(self, tools_adapter, agent_name=agent_name, **kwargs)
|
1
|
+
from abc import ABC, abstractmethod
|
2
|
+
import importlib
|
3
|
+
from janito.llm.driver import LLMDriver
|
4
|
+
|
5
|
+
|
6
|
+
class LLMProvider(ABC):
|
7
|
+
def create_driver(self):
|
8
|
+
"""
|
9
|
+
Returns a new instance of the configured driver for this provider.
|
10
|
+
Subclasses must implement this method.
|
11
|
+
"""
|
12
|
+
raise NotImplementedError(
|
13
|
+
"LLMProvider subclasses must implement create_driver()."
|
14
|
+
)
|
15
|
+
|
16
|
+
"""
|
17
|
+
Abstract base class for Large Language Model (LLM) providers.
|
18
|
+
|
19
|
+
Provider Usage and Driver Communication Flow:
|
20
|
+
1. Provider class is selected (e.g., OpenAIProvider, MistralProvider).
|
21
|
+
2. An instance of the provider is created. This instance is bound to a specific configuration (LLMDriverConfig) containing model, credentials, etc.
|
22
|
+
3. All drivers created by that provider instance are associated with the bound config.
|
23
|
+
4. To communicate with an LLM, call create_driver() on the provider instance, which yields a driver configured for the attached config. Every driver created via this method inherits the provider's configuration.
|
24
|
+
|
25
|
+
Key: You do not create/configure a driver directly—always go through the provider to ensure correct configuration binding to the provider instance.
|
26
|
+
|
27
|
+
Subclasses must implement the core interface for interacting with LLM APIs and define `provider_name` as a class attribute.
|
28
|
+
"""
|
29
|
+
|
30
|
+
name: str = None # Must be set on subclasses
|
31
|
+
DEFAULT_MODEL: str = None # Should be set by subclasses
|
32
|
+
|
33
|
+
def __init_subclass__(cls, **kwargs):
|
34
|
+
super().__init_subclass__(**kwargs)
|
35
|
+
if (
|
36
|
+
not hasattr(cls, "name")
|
37
|
+
or not isinstance(getattr(cls, "name"), str)
|
38
|
+
or not cls.name
|
39
|
+
):
|
40
|
+
raise TypeError(
|
41
|
+
f"Class {cls.__name__} must define a class attribute 'name' (non-empty str)"
|
42
|
+
)
|
43
|
+
if (
|
44
|
+
not hasattr(cls, "DEFAULT_MODEL")
|
45
|
+
or getattr(cls, "DEFAULT_MODEL", None) is None
|
46
|
+
):
|
47
|
+
raise TypeError(
|
48
|
+
f"Class {cls.__name__} must define a class attribute 'DEFAULT_MODEL' (non-empty str)"
|
49
|
+
)
|
50
|
+
|
51
|
+
def fill_missing_device_info(self, config):
|
52
|
+
"""
|
53
|
+
Fill missing LLMDriverConfig fields (max_tokens, temperature, etc) from MODEL_SPECS for the chosen model.
|
54
|
+
Mutates the config in place.
|
55
|
+
"""
|
56
|
+
if not hasattr(self, "MODEL_SPECS"):
|
57
|
+
return
|
58
|
+
model_name = getattr(config, "model", None) or getattr(
|
59
|
+
self, "DEFAULT_MODEL", None
|
60
|
+
)
|
61
|
+
model_info = self.MODEL_SPECS.get(model_name)
|
62
|
+
if not model_info:
|
63
|
+
return
|
64
|
+
# Handle common fields from model_info
|
65
|
+
spec_dict = (
|
66
|
+
model_info.to_dict() if hasattr(model_info, "to_dict") else dict(model_info)
|
67
|
+
)
|
68
|
+
if (
|
69
|
+
hasattr(config, "max_tokens")
|
70
|
+
and getattr(config, "max_tokens", None) is None
|
71
|
+
):
|
72
|
+
val = spec_dict.get("max_tokens") or spec_dict.get("max_response")
|
73
|
+
if val is not None:
|
74
|
+
try:
|
75
|
+
config.max_tokens = int(val)
|
76
|
+
except Exception:
|
77
|
+
pass
|
78
|
+
if (
|
79
|
+
hasattr(config, "temperature")
|
80
|
+
and getattr(config, "temperature", None) is None
|
81
|
+
):
|
82
|
+
val = spec_dict.get("temperature")
|
83
|
+
if val is None:
|
84
|
+
val = spec_dict.get("default_temp")
|
85
|
+
if val is not None:
|
86
|
+
try:
|
87
|
+
config.temperature = float(val)
|
88
|
+
except Exception:
|
89
|
+
pass
|
90
|
+
|
91
|
+
@property
|
92
|
+
@abstractmethod
|
93
|
+
def driver(self) -> LLMDriver:
|
94
|
+
pass
|
95
|
+
|
96
|
+
def is_model_available(self, model_name):
|
97
|
+
"""
|
98
|
+
Returns True if the given model is available for this provider.
|
99
|
+
Default implementation checks MODEL_SPECS; override for dynamic providers.
|
100
|
+
"""
|
101
|
+
if not hasattr(self, "MODEL_SPECS"):
|
102
|
+
return False
|
103
|
+
return model_name in self.MODEL_SPECS
|
104
|
+
|
105
|
+
def get_model_info(self, model_name=None):
|
106
|
+
"""
|
107
|
+
Return the info dict for a given model (driver, params, etc). If model_name is None, return all model info dicts.
|
108
|
+
MODEL_SPECS must be dict[str, LLMModelInfo].
|
109
|
+
"""
|
110
|
+
if not hasattr(self, "MODEL_SPECS"):
|
111
|
+
raise NotImplementedError(
|
112
|
+
"This provider does not have a MODEL_SPECS attribute."
|
113
|
+
)
|
114
|
+
if model_name is None:
|
115
|
+
return {
|
116
|
+
name: model_info.to_dict()
|
117
|
+
for name, model_info in self.MODEL_SPECS.items()
|
118
|
+
}
|
119
|
+
if model_name in self.MODEL_SPECS:
|
120
|
+
return self.MODEL_SPECS[model_name].to_dict()
|
121
|
+
return None
|
122
|
+
|
123
|
+
def _validate_model_specs(self):
|
124
|
+
if not hasattr(self, "MODEL_SPECS"):
|
125
|
+
raise NotImplementedError(
|
126
|
+
"This provider does not have a MODEL_SPECS attribute."
|
127
|
+
)
|
128
|
+
|
129
|
+
def _get_model_name_from_config(self, config):
|
130
|
+
return (config or {}).get("model_name", getattr(self, "DEFAULT_MODEL", None))
|
131
|
+
|
132
|
+
def _get_model_spec_entry(self, model_name):
|
133
|
+
spec = self.MODEL_SPECS.get(model_name, None)
|
134
|
+
if spec is None:
|
135
|
+
raise ValueError(f"Model '{model_name}' not found in MODEL_SPECS.")
|
136
|
+
return spec
|
137
|
+
|
138
|
+
def _get_driver_name_from_spec(self, spec):
|
139
|
+
driver_name = None
|
140
|
+
if hasattr(spec, "driver") and spec.driver:
|
141
|
+
driver_name = spec.driver
|
142
|
+
elif hasattr(spec, "other") and isinstance(spec.other, dict):
|
143
|
+
driver_name = spec.other.get("driver", None)
|
144
|
+
return driver_name
|
145
|
+
|
146
|
+
def _resolve_driver_class(self, driver_name):
|
147
|
+
if not driver_name:
|
148
|
+
raise NotImplementedError(
|
149
|
+
"No driver class found or specified for this MODEL_SPECS entry."
|
150
|
+
)
|
151
|
+
module_root = "janito.drivers"
|
152
|
+
probable_path = None
|
153
|
+
mapping = {
|
154
|
+
"OpenAIResponsesModelDriver": "openai_responses.driver",
|
155
|
+
"OpenAIModelDriver": "openai.driver",
|
156
|
+
"AzureOpenAIModelDriver": "azure_openai.driver",
|
157
|
+
"GoogleGenaiModelDriver": "google_genai.driver",
|
158
|
+
}
|
159
|
+
if driver_name in mapping:
|
160
|
+
probable_path = mapping[driver_name]
|
161
|
+
module_path = f"{module_root}.{probable_path}"
|
162
|
+
mod = importlib.import_module(module_path)
|
163
|
+
return getattr(mod, driver_name)
|
164
|
+
# Attempt dynamic fallback based on convention
|
165
|
+
if driver_name.endswith("ModelDriver"):
|
166
|
+
base = driver_name[: -len("ModelDriver")]
|
167
|
+
mod_name = base.replace("_", "").lower()
|
168
|
+
module_path = f"{module_root}.{mod_name}.driver"
|
169
|
+
try:
|
170
|
+
mod = importlib.import_module(module_path)
|
171
|
+
return getattr(mod, driver_name)
|
172
|
+
except Exception:
|
173
|
+
pass
|
174
|
+
raise NotImplementedError(
|
175
|
+
"No driver class found for driver_name: {}".format(driver_name)
|
176
|
+
)
|
177
|
+
|
178
|
+
def _validate_required_config(self, driver_class, config, driver_name):
|
179
|
+
required = getattr(driver_class, "required_config", None)
|
180
|
+
if required:
|
181
|
+
missing = [
|
182
|
+
k
|
183
|
+
for k in required
|
184
|
+
if not config or k not in config or config.get(k) in (None, "")
|
185
|
+
]
|
186
|
+
if missing:
|
187
|
+
raise ValueError(
|
188
|
+
f"Missing required config for {driver_name}: {', '.join(missing)}"
|
189
|
+
)
|
190
|
+
|
191
|
+
def create_agent(self, tools_adapter=None, agent_name: str = None, **kwargs):
|
192
|
+
from janito.llm.agent import LLMAgent
|
193
|
+
|
194
|
+
# Dynamically create driver if supported, else fallback to existing.
|
195
|
+
driver = self.driver
|
196
|
+
return LLMAgent(self, tools_adapter, agent_name=agent_name, **kwargs)
|
janito/provider_config.py
CHANGED
@@ -36,6 +36,7 @@ def set_provider_config(provider, key, value):
|
|
36
36
|
config.file_config["providers"] = cfg
|
37
37
|
with open(config.config_path, "w", encoding="utf-8") as f:
|
38
38
|
json.dump(config.file_config, f, indent=2)
|
39
|
+
f.write("\n")
|
39
40
|
|
40
41
|
|
41
42
|
def set_provider_model_config(provider, model, key, value):
|
@@ -51,6 +52,7 @@ def set_provider_model_config(provider, model, key, value):
|
|
51
52
|
config.file_config["providers"] = cfg
|
52
53
|
with open(config.config_path, "w", encoding="utf-8") as f:
|
53
54
|
json.dump(config.file_config, f, indent=2)
|
55
|
+
f.write("\n")
|
54
56
|
|
55
57
|
|
56
58
|
def get_provider_model_config(provider, model):
|
@@ -71,9 +73,11 @@ def get_effective_model(provider=None, requested_model=None):
|
|
71
73
|
provider_model = config.get_provider_config(provider).get("model")
|
72
74
|
if provider_model:
|
73
75
|
return provider_model
|
74
|
-
|
75
|
-
if
|
76
|
-
|
76
|
+
# Only use global model if no provider is specified
|
77
|
+
if provider is None:
|
78
|
+
global_model = config.get("model")
|
79
|
+
if global_model:
|
80
|
+
return global_model
|
77
81
|
return None
|
78
82
|
|
79
83
|
|
janito/provider_registry.py
CHANGED
@@ -48,6 +48,24 @@ class ProviderRegistry:
|
|
48
48
|
table.add_section()
|
49
49
|
|
50
50
|
def _print_table(self, table):
|
51
|
+
"""Print the table using rich when running in a terminal; otherwise fall back to a plain ASCII listing.
|
52
|
+
This avoids UnicodeDecodeError when the parent process captures the output with a non-UTF8 encoding.
|
53
|
+
"""
|
54
|
+
import sys
|
55
|
+
|
56
|
+
if sys.stdout.isatty():
|
57
|
+
# Safe to use rich's unicode output when attached to an interactive terminal.
|
58
|
+
shared_console.print(table)
|
59
|
+
return
|
60
|
+
|
61
|
+
# Fallback: plain ASCII output
|
62
|
+
print("Supported LLM Providers")
|
63
|
+
print("Provider | Maintainer | Model Names")
|
64
|
+
for row in table.rows:
|
65
|
+
# row is a rich.table.Row -> row.cells is a list of Text objects
|
66
|
+
cells_text = [str(cell) for cell in row.cells]
|
67
|
+
ascii_row = " | ".join(cells_text).encode("ascii", "ignore").decode("ascii")
|
68
|
+
print(ascii_row)
|
51
69
|
shared_console.print(table)
|
52
70
|
|
53
71
|
def _get_provider_info(self, provider_name):
|
@@ -129,18 +147,24 @@ class ProviderRegistry:
|
|
129
147
|
return (is_needs_maint, row[2] != "✅ Auth")
|
130
148
|
|
131
149
|
def get_provider(self, provider_name):
|
132
|
-
"""Return the provider class for the given provider name."""
|
150
|
+
"""Return the provider class for the given provider name. Returns None if not found."""
|
133
151
|
from janito.providers.registry import LLMProviderRegistry
|
134
152
|
|
135
153
|
if not provider_name:
|
136
|
-
|
137
|
-
|
154
|
+
print("Error: Provider name must be specified.")
|
155
|
+
return None
|
156
|
+
provider_class = LLMProviderRegistry.get(provider_name)
|
157
|
+
if provider_class is None:
|
158
|
+
available = ', '.join(LLMProviderRegistry.list_providers())
|
159
|
+
print(f"Error: Provider '{provider_name}' is not recognized. Available providers: {available}.")
|
160
|
+
return None
|
161
|
+
return provider_class
|
138
162
|
|
139
163
|
def get_instance(self, provider_name, config=None):
|
140
|
-
"""Return an instance of the provider for the given provider name, optionally passing a config object."""
|
164
|
+
"""Return an instance of the provider for the given provider name, optionally passing a config object. Returns None if not found."""
|
141
165
|
provider_class = self.get_provider(provider_name)
|
142
166
|
if provider_class is None:
|
143
|
-
|
167
|
+
return None
|
144
168
|
if config is not None:
|
145
169
|
return provider_class(config=config)
|
146
170
|
return provider_class()
|
janito/providers/__init__.py
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
# Ensure all providers are registered by importing their modules
|
2
2
|
import janito.providers.openai.provider
|
3
|
+
import janito.providers.google.provider
|
3
4
|
import janito.providers.mistralai.provider
|
4
5
|
import janito.providers.google.provider
|
5
6
|
import janito.providers.azure_openai.provider
|
@@ -1,22 +1,22 @@
|
|
1
|
-
from janito.llm.model import LLMModelInfo
|
2
|
-
|
3
|
-
MODEL_SPECS = {
|
4
|
-
"claude-3-opus-20240229": LLMModelInfo(
|
5
|
-
name="claude-3-opus-20240229",
|
6
|
-
max_response=200000,
|
7
|
-
default_temp=0.7,
|
8
|
-
driver="AnthropicModelDriver",
|
9
|
-
),
|
10
|
-
"claude-3-sonnet-20240229": LLMModelInfo(
|
11
|
-
name="claude-3-sonnet-20240229",
|
12
|
-
max_response=200000,
|
13
|
-
default_temp=0.7,
|
14
|
-
driver="AnthropicModelDriver",
|
15
|
-
),
|
16
|
-
"claude-3-haiku-20240307": LLMModelInfo(
|
17
|
-
name="claude-3-haiku-20240307",
|
18
|
-
max_response=200000,
|
19
|
-
default_temp=0.7,
|
20
|
-
driver="AnthropicModelDriver",
|
21
|
-
),
|
22
|
-
}
|
1
|
+
from janito.llm.model import LLMModelInfo
|
2
|
+
|
3
|
+
MODEL_SPECS = {
|
4
|
+
"claude-3-opus-20240229": LLMModelInfo(
|
5
|
+
name="claude-3-opus-20240229",
|
6
|
+
max_response=200000,
|
7
|
+
default_temp=0.7,
|
8
|
+
driver="AnthropicModelDriver",
|
9
|
+
),
|
10
|
+
"claude-3-sonnet-20240229": LLMModelInfo(
|
11
|
+
name="claude-3-sonnet-20240229",
|
12
|
+
max_response=200000,
|
13
|
+
default_temp=0.7,
|
14
|
+
driver="AnthropicModelDriver",
|
15
|
+
),
|
16
|
+
"claude-3-haiku-20240307": LLMModelInfo(
|
17
|
+
name="claude-3-haiku-20240307",
|
18
|
+
max_response=200000,
|
19
|
+
default_temp=0.7,
|
20
|
+
driver="AnthropicModelDriver",
|
21
|
+
),
|
22
|
+
}
|
@@ -2,7 +2,7 @@ from janito.llm.provider import LLMProvider
|
|
2
2
|
from janito.llm.model import LLMModelInfo
|
3
3
|
from janito.llm.auth import LLMAuthManager
|
4
4
|
from janito.llm.driver_config import LLMDriverConfig
|
5
|
-
from janito.tools
|
5
|
+
from janito.tools import get_local_tools_adapter
|
6
6
|
from janito.providers.registry import LLMProviderRegistry
|
7
7
|
|
8
8
|
from .model_info import MODEL_SPECS
|
@@ -28,7 +28,7 @@ class AnthropicProvider(LLMProvider):
|
|
28
28
|
return
|
29
29
|
self.auth_manager = auth_manager or LLMAuthManager()
|
30
30
|
self._api_key = self.auth_manager.get_credentials(type(self).name)
|
31
|
-
self._tools_adapter =
|
31
|
+
self._tools_adapter = get_local_tools_adapter()
|
32
32
|
self._info = config or LLMDriverConfig(model=None)
|
33
33
|
if not self._info.model:
|
34
34
|
self._info.model = self.DEFAULT_MODEL
|
@@ -1,14 +1,15 @@
|
|
1
1
|
from janito.llm.model import LLMModelInfo
|
2
|
+
from janito.providers.openai.model_info import MODEL_SPECS as OPENAI_MODEL_SPECS
|
2
3
|
|
3
4
|
MODEL_SPECS = {
|
4
5
|
"azure_openai_deployment": LLMModelInfo(
|
5
6
|
name="azure_openai_deployment",
|
6
|
-
context="
|
7
|
-
max_input="
|
8
|
-
max_cot="
|
9
|
-
max_response="
|
10
|
-
thinking_supported=
|
11
|
-
default_temp=
|
7
|
+
context=OPENAI_MODEL_SPECS["gpt-4o"].context,
|
8
|
+
max_input=OPENAI_MODEL_SPECS["gpt-4o"].max_input,
|
9
|
+
max_cot=OPENAI_MODEL_SPECS["gpt-4o"].max_cot,
|
10
|
+
max_response=OPENAI_MODEL_SPECS["gpt-4o"].max_response,
|
11
|
+
thinking_supported=OPENAI_MODEL_SPECS["gpt-4o"].thinking_supported,
|
12
|
+
default_temp=OPENAI_MODEL_SPECS["gpt-4o"].default_temp,
|
12
13
|
open="azure_openai",
|
13
14
|
driver="AzureOpenAIModelDriver",
|
14
15
|
)
|
@@ -2,7 +2,7 @@ from janito.llm.provider import LLMProvider
|
|
2
2
|
from janito.llm.model import LLMModelInfo
|
3
3
|
from janito.llm.auth import LLMAuthManager
|
4
4
|
from janito.llm.driver_config import LLMDriverConfig
|
5
|
-
from janito.tools
|
5
|
+
from janito.tools import get_local_tools_adapter
|
6
6
|
from janito.providers.registry import LLMProviderRegistry
|
7
7
|
|
8
8
|
from .model_info import MODEL_SPECS
|
@@ -28,7 +28,7 @@ class AzureOpenAIProvider(LLMProvider):
|
|
28
28
|
return
|
29
29
|
self._auth_manager = auth_manager or LLMAuthManager()
|
30
30
|
self._api_key = self._auth_manager.get_credentials(type(self).name)
|
31
|
-
self._tools_adapter =
|
31
|
+
self._tools_adapter = get_local_tools_adapter()
|
32
32
|
self._driver_config = config or LLMDriverConfig(model=None)
|
33
33
|
if not self._driver_config.model:
|
34
34
|
self._driver_config.model = self.DEFAULT_MODEL
|
@@ -36,6 +36,11 @@ class AzureOpenAIProvider(LLMProvider):
|
|
36
36
|
self._driver_config.api_key = self._api_key
|
37
37
|
if not self._driver_config.extra.get("api_version"):
|
38
38
|
self._driver_config.extra["api_version"] = "2023-05-15"
|
39
|
+
# Inject azure_deployment_name from config if present
|
40
|
+
from janito.config import config as global_config
|
41
|
+
deployment_name = global_config.get("azure_deployment_name")
|
42
|
+
if deployment_name:
|
43
|
+
self._driver_config.extra["azure_deployment_name"] = deployment_name
|
39
44
|
self.fill_missing_device_info(self._driver_config)
|
40
45
|
self._driver = AzureOpenAIModelDriver(tools_adapter=self._tools_adapter)
|
41
46
|
|
@@ -61,6 +66,38 @@ class AzureOpenAIProvider(LLMProvider):
|
|
61
66
|
"""
|
62
67
|
return True
|
63
68
|
|
69
|
+
def get_model_info(self, model_name=None):
|
70
|
+
"""
|
71
|
+
For Azure OpenAI, accept any deployment name as a valid model name.
|
72
|
+
If the model_name is not in MODEL_SPECS, return a generic info dict.
|
73
|
+
"""
|
74
|
+
if model_name is None:
|
75
|
+
# Return all known specs, but note: only static ones are listed
|
76
|
+
return {name: model_info.to_dict() for name, model_info in self.MODEL_SPECS.items()}
|
77
|
+
if model_name in self.MODEL_SPECS:
|
78
|
+
return self.MODEL_SPECS[model_name].to_dict()
|
79
|
+
# Accept any deployment name as a valid model
|
80
|
+
return {
|
81
|
+
"name": model_name,
|
82
|
+
"context": "N/A",
|
83
|
+
"max_input": "N/A",
|
84
|
+
"max_cot": "N/A",
|
85
|
+
"max_response": "N/A",
|
86
|
+
"thinking_supported": False,
|
87
|
+
"default_temp": 0.2,
|
88
|
+
"open": "azure_openai",
|
89
|
+
"driver": "AzureOpenAIModelDriver",
|
90
|
+
}
|
91
|
+
|
92
|
+
def create_driver(self):
|
93
|
+
"""
|
94
|
+
Creates and returns a new AzureOpenAIModelDriver instance with the provider's configuration and tools adapter.
|
95
|
+
"""
|
96
|
+
driver = AzureOpenAIModelDriver(tools_adapter=self._tools_adapter)
|
97
|
+
driver.config = self._driver_config
|
98
|
+
# NOTE: The caller is responsible for calling driver.start() if background processing is needed.
|
99
|
+
return driver
|
100
|
+
|
64
101
|
def create_agent(self, tools_adapter=None, agent_name: str = None, **kwargs):
|
65
102
|
from janito.llm.agent import LLMAgent
|
66
103
|
from janito.drivers.azure_openai.driver import AzureOpenAIModelDriver
|
@@ -69,6 +106,11 @@ class AzureOpenAIProvider(LLMProvider):
|
|
69
106
|
driver = AzureOpenAIModelDriver(tools_adapter=tools_adapter)
|
70
107
|
return LLMAgent(self, tools_adapter, agent_name=agent_name, **kwargs)
|
71
108
|
|
109
|
+
@property
|
110
|
+
def driver_config(self):
|
111
|
+
"""Public, read-only access to the provider's LLMDriverConfig object."""
|
112
|
+
return self._driver_config
|
113
|
+
|
72
114
|
def execute_tool(self, tool_name: str, event_bus, *args, **kwargs):
|
73
115
|
# Use direct execution via adapter:
|
74
116
|
self._tools_adapter.event_bus = event_bus
|
@@ -1 +1 @@
|
|
1
|
-
# Deepseek provider package marker
|
1
|
+
# Deepseek provider package marker
|