janito 2.3.0__py3-none-any.whl → 2.3.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.
Files changed (93) hide show
  1. janito/__init__.py +6 -6
  2. janito/cli/chat_mode/shell/autocomplete.py +21 -21
  3. janito/cli/chat_mode/shell/commands/clear.py +12 -12
  4. janito/cli/chat_mode/shell/commands/multi.py +51 -51
  5. janito/cli/chat_mode/shell/input_history.py +62 -62
  6. janito/cli/cli_commands/list_models.py +35 -35
  7. janito/cli/cli_commands/list_providers.py +9 -9
  8. janito/cli/cli_commands/list_tools.py +53 -53
  9. janito/cli/cli_commands/model_selection.py +50 -50
  10. janito/cli/cli_commands/model_utils.py +95 -95
  11. janito/cli/cli_commands/set_api_key.py +19 -19
  12. janito/cli/cli_commands/show_config.py +51 -51
  13. janito/cli/cli_commands/show_system_prompt.py +62 -62
  14. janito/cli/core/__init__.py +4 -4
  15. janito/cli/core/event_logger.py +59 -59
  16. janito/cli/core/getters.py +33 -33
  17. janito/cli/core/unsetters.py +54 -54
  18. janito/cli/single_shot_mode/__init__.py +6 -6
  19. janito/config.py +5 -5
  20. janito/config_manager.py +112 -112
  21. janito/drivers/anthropic/driver.py +113 -113
  22. janito/formatting_token.py +54 -54
  23. janito/i18n/__init__.py +35 -35
  24. janito/i18n/messages.py +23 -23
  25. janito/i18n/pt.py +47 -47
  26. janito/llm/__init__.py +5 -5
  27. janito/llm/agent.py +443 -443
  28. janito/llm/auth.py +63 -63
  29. janito/llm/driver_config_builder.py +34 -34
  30. janito/llm/driver_input.py +12 -12
  31. janito/llm/message_parts.py +60 -60
  32. janito/llm/model.py +38 -38
  33. janito/llm/provider.py +196 -196
  34. janito/provider_registry.py +176 -176
  35. janito/providers/anthropic/model_info.py +22 -22
  36. janito/providers/anthropic/provider.py +2 -0
  37. janito/providers/azure_openai/model_info.py +16 -16
  38. janito/providers/azure_openai/provider.py +3 -0
  39. janito/providers/deepseek/__init__.py +1 -1
  40. janito/providers/deepseek/model_info.py +16 -16
  41. janito/providers/deepseek/provider.py +94 -91
  42. janito/providers/google/provider.py +3 -0
  43. janito/providers/mistralai/provider.py +3 -0
  44. janito/providers/openai/provider.py +4 -0
  45. janito/tools/adapters/__init__.py +1 -1
  46. janito/tools/adapters/local/ask_user.py +102 -102
  47. janito/tools/adapters/local/copy_file.py +84 -84
  48. janito/tools/adapters/local/create_directory.py +69 -69
  49. janito/tools/adapters/local/create_file.py +82 -82
  50. janito/tools/adapters/local/fetch_url.py +97 -97
  51. janito/tools/adapters/local/find_files.py +138 -138
  52. janito/tools/adapters/local/get_file_outline/__init__.py +1 -1
  53. janito/tools/adapters/local/get_file_outline/core.py +117 -117
  54. janito/tools/adapters/local/get_file_outline/java_outline.py +40 -40
  55. janito/tools/adapters/local/get_file_outline/markdown_outline.py +14 -14
  56. janito/tools/adapters/local/get_file_outline/python_outline.py +303 -303
  57. janito/tools/adapters/local/get_file_outline/python_outline_v2.py +156 -156
  58. janito/tools/adapters/local/get_file_outline/search_outline.py +33 -33
  59. janito/tools/adapters/local/python_code_run.py +166 -166
  60. janito/tools/adapters/local/python_command_run.py +164 -164
  61. janito/tools/adapters/local/python_file_run.py +163 -163
  62. janito/tools/adapters/local/run_bash_command.py +176 -176
  63. janito/tools/adapters/local/run_powershell_command.py +219 -219
  64. janito/tools/adapters/local/search_text/__init__.py +1 -1
  65. janito/tools/adapters/local/search_text/core.py +201 -201
  66. janito/tools/adapters/local/search_text/pattern_utils.py +73 -73
  67. janito/tools/adapters/local/search_text/traverse_directory.py +145 -145
  68. janito/tools/adapters/local/validate_file_syntax/__init__.py +1 -1
  69. janito/tools/adapters/local/validate_file_syntax/core.py +106 -106
  70. janito/tools/adapters/local/validate_file_syntax/css_validator.py +35 -35
  71. janito/tools/adapters/local/validate_file_syntax/html_validator.py +93 -93
  72. janito/tools/adapters/local/validate_file_syntax/js_validator.py +27 -27
  73. janito/tools/adapters/local/validate_file_syntax/json_validator.py +6 -6
  74. janito/tools/adapters/local/validate_file_syntax/markdown_validator.py +109 -109
  75. janito/tools/adapters/local/validate_file_syntax/ps1_validator.py +32 -32
  76. janito/tools/adapters/local/validate_file_syntax/python_validator.py +5 -5
  77. janito/tools/adapters/local/validate_file_syntax/xml_validator.py +11 -11
  78. janito/tools/adapters/local/validate_file_syntax/yaml_validator.py +6 -6
  79. janito/tools/adapters/local/view_file.py +167 -167
  80. janito/tools/inspect_registry.py +17 -17
  81. janito/tools/tool_base.py +105 -105
  82. janito/tools/tool_events.py +58 -58
  83. janito/tools/tool_run_exception.py +12 -12
  84. janito/tools/tool_use_tracker.py +81 -81
  85. janito/tools/tool_utils.py +45 -45
  86. janito/tools/tools_schema.py +104 -104
  87. janito/version.py +4 -4
  88. {janito-2.3.0.dist-info → janito-2.3.1.dist-info}/METADATA +390 -388
  89. {janito-2.3.0.dist-info → janito-2.3.1.dist-info}/RECORD +93 -93
  90. {janito-2.3.0.dist-info → janito-2.3.1.dist-info}/WHEEL +0 -0
  91. {janito-2.3.0.dist-info → janito-2.3.1.dist-info}/entry_points.txt +0 -0
  92. {janito-2.3.0.dist-info → janito-2.3.1.dist-info}/licenses/LICENSE +0 -0
  93. {janito-2.3.0.dist-info → janito-2.3.1.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)