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.
Files changed (137) hide show
  1. janito/__init__.py +6 -6
  2. janito/agent/setup_agent.py +14 -5
  3. janito/agent/templates/profiles/system_prompt_template_main.txt.j2 +3 -1
  4. janito/cli/chat_mode/bindings.py +6 -0
  5. janito/cli/chat_mode/session.py +16 -0
  6. janito/cli/chat_mode/shell/autocomplete.py +21 -21
  7. janito/cli/chat_mode/shell/commands/__init__.py +3 -2
  8. janito/cli/chat_mode/shell/commands/clear.py +12 -12
  9. janito/cli/chat_mode/shell/commands/exec.py +27 -0
  10. janito/cli/chat_mode/shell/commands/multi.py +51 -51
  11. janito/cli/chat_mode/shell/commands/tools.py +17 -6
  12. janito/cli/chat_mode/shell/input_history.py +62 -62
  13. janito/cli/chat_mode/shell/session/manager.py +1 -0
  14. janito/cli/chat_mode/toolbar.py +3 -1
  15. janito/cli/cli_commands/list_models.py +35 -35
  16. janito/cli/cli_commands/list_providers.py +9 -9
  17. janito/cli/cli_commands/list_tools.py +53 -53
  18. janito/cli/cli_commands/model_selection.py +50 -50
  19. janito/cli/cli_commands/model_utils.py +13 -2
  20. janito/cli/cli_commands/set_api_key.py +19 -19
  21. janito/cli/cli_commands/show_config.py +51 -51
  22. janito/cli/cli_commands/show_system_prompt.py +62 -62
  23. janito/cli/config.py +2 -1
  24. janito/cli/core/__init__.py +4 -4
  25. janito/cli/core/event_logger.py +59 -59
  26. janito/cli/core/getters.py +3 -1
  27. janito/cli/core/runner.py +27 -6
  28. janito/cli/core/setters.py +5 -1
  29. janito/cli/core/unsetters.py +54 -54
  30. janito/cli/main_cli.py +12 -1
  31. janito/cli/prompt_core.py +5 -2
  32. janito/cli/rich_terminal_reporter.py +22 -3
  33. janito/cli/single_shot_mode/__init__.py +6 -6
  34. janito/cli/single_shot_mode/handler.py +11 -1
  35. janito/cli/verbose_output.py +1 -1
  36. janito/config.py +5 -5
  37. janito/config_manager.py +2 -0
  38. janito/driver_events.py +14 -0
  39. janito/drivers/anthropic/driver.py +113 -113
  40. janito/drivers/azure_openai/driver.py +38 -3
  41. janito/drivers/driver_registry.py +0 -2
  42. janito/drivers/openai/driver.py +196 -36
  43. janito/formatting_token.py +54 -54
  44. janito/i18n/__init__.py +35 -35
  45. janito/i18n/messages.py +23 -23
  46. janito/i18n/pt.py +47 -47
  47. janito/llm/__init__.py +5 -5
  48. janito/llm/agent.py +443 -443
  49. janito/llm/auth.py +1 -0
  50. janito/llm/driver.py +7 -1
  51. janito/llm/driver_config.py +1 -0
  52. janito/llm/driver_config_builder.py +34 -34
  53. janito/llm/driver_input.py +12 -12
  54. janito/llm/message_parts.py +60 -60
  55. janito/llm/model.py +38 -38
  56. janito/llm/provider.py +196 -196
  57. janito/provider_config.py +7 -3
  58. janito/provider_registry.py +29 -5
  59. janito/providers/__init__.py +1 -0
  60. janito/providers/anthropic/model_info.py +22 -22
  61. janito/providers/anthropic/provider.py +2 -2
  62. janito/providers/azure_openai/model_info.py +7 -6
  63. janito/providers/azure_openai/provider.py +44 -2
  64. janito/providers/deepseek/__init__.py +1 -1
  65. janito/providers/deepseek/model_info.py +16 -16
  66. janito/providers/deepseek/provider.py +91 -91
  67. janito/providers/google/model_info.py +21 -29
  68. janito/providers/google/provider.py +49 -38
  69. janito/providers/mistralai/provider.py +2 -2
  70. janito/providers/openai/model_info.py +0 -11
  71. janito/providers/openai/provider.py +1 -1
  72. janito/providers/provider_static_info.py +2 -3
  73. janito/providers/registry.py +26 -26
  74. janito/tools/adapters/__init__.py +1 -1
  75. janito/tools/adapters/local/__init__.py +62 -62
  76. janito/tools/adapters/local/adapter.py +33 -11
  77. janito/tools/adapters/local/ask_user.py +102 -102
  78. janito/tools/adapters/local/copy_file.py +84 -84
  79. janito/tools/adapters/local/create_directory.py +69 -69
  80. janito/tools/adapters/local/create_file.py +82 -82
  81. janito/tools/adapters/local/delete_text_in_file.py +4 -7
  82. janito/tools/adapters/local/fetch_url.py +97 -97
  83. janito/tools/adapters/local/find_files.py +138 -140
  84. janito/tools/adapters/local/get_file_outline/__init__.py +1 -1
  85. janito/tools/adapters/local/get_file_outline/core.py +117 -151
  86. janito/tools/adapters/local/get_file_outline/java_outline.py +40 -0
  87. janito/tools/adapters/local/get_file_outline/markdown_outline.py +14 -14
  88. janito/tools/adapters/local/get_file_outline/python_outline.py +303 -303
  89. janito/tools/adapters/local/get_file_outline/python_outline_v2.py +156 -156
  90. janito/tools/adapters/local/get_file_outline/search_outline.py +33 -33
  91. janito/tools/adapters/local/move_file.py +3 -13
  92. janito/tools/adapters/local/open_html_in_browser.py +24 -29
  93. janito/tools/adapters/local/open_url.py +3 -2
  94. janito/tools/adapters/local/python_code_run.py +166 -166
  95. janito/tools/adapters/local/python_command_run.py +164 -164
  96. janito/tools/adapters/local/python_file_run.py +163 -163
  97. janito/tools/adapters/local/remove_directory.py +6 -17
  98. janito/tools/adapters/local/remove_file.py +9 -15
  99. janito/tools/adapters/local/replace_text_in_file.py +6 -9
  100. janito/tools/adapters/local/run_bash_command.py +176 -176
  101. janito/tools/adapters/local/run_powershell_command.py +219 -219
  102. janito/tools/adapters/local/search_text/__init__.py +1 -1
  103. janito/tools/adapters/local/search_text/core.py +201 -201
  104. janito/tools/adapters/local/search_text/match_lines.py +1 -1
  105. janito/tools/adapters/local/search_text/pattern_utils.py +73 -73
  106. janito/tools/adapters/local/search_text/traverse_directory.py +145 -145
  107. janito/tools/adapters/local/validate_file_syntax/__init__.py +1 -1
  108. janito/tools/adapters/local/validate_file_syntax/core.py +106 -106
  109. janito/tools/adapters/local/validate_file_syntax/css_validator.py +35 -35
  110. janito/tools/adapters/local/validate_file_syntax/html_validator.py +93 -93
  111. janito/tools/adapters/local/validate_file_syntax/js_validator.py +27 -27
  112. janito/tools/adapters/local/validate_file_syntax/json_validator.py +6 -6
  113. janito/tools/adapters/local/validate_file_syntax/markdown_validator.py +109 -109
  114. janito/tools/adapters/local/validate_file_syntax/ps1_validator.py +32 -32
  115. janito/tools/adapters/local/validate_file_syntax/python_validator.py +5 -5
  116. janito/tools/adapters/local/validate_file_syntax/xml_validator.py +11 -11
  117. janito/tools/adapters/local/validate_file_syntax/yaml_validator.py +6 -6
  118. janito/tools/adapters/local/view_file.py +167 -167
  119. janito/tools/inspect_registry.py +17 -17
  120. janito/tools/tool_base.py +105 -105
  121. janito/tools/tool_events.py +58 -58
  122. janito/tools/tool_run_exception.py +12 -12
  123. janito/tools/tool_use_tracker.py +81 -81
  124. janito/tools/tool_utils.py +45 -45
  125. janito/tools/tools_adapter.py +78 -6
  126. janito/tools/tools_schema.py +104 -104
  127. janito/version.py +4 -4
  128. {janito-2.1.1.dist-info → janito-2.3.0.dist-info}/METADATA +388 -232
  129. janito-2.3.0.dist-info/RECORD +181 -0
  130. janito-2.3.0.dist-info/licenses/LICENSE +21 -0
  131. janito/cli/chat_mode/shell/commands/last.py +0 -137
  132. janito/drivers/google_genai/driver.py +0 -54
  133. janito/drivers/google_genai/schema_generator.py +0 -67
  134. janito-2.1.1.dist-info/RECORD +0 -181
  135. {janito-2.1.1.dist-info → janito-2.3.0.dist-info}/WHEEL +0 -0
  136. {janito-2.1.1.dist-info → janito-2.3.0.dist-info}/entry_points.txt +0 -0
  137. {janito-2.1.1.dist-info → janito-2.3.0.dist-info}/top_level.txt +0 -0
@@ -1,4 +1,5 @@
1
1
  """Handlers for get-type CLI commands (show_config, list_providers, models, tools)."""
2
+ import sys
2
3
 
3
4
  from janito.cli.cli_commands.list_providers import handle_list_providers
4
5
  from janito.cli.cli_commands.list_models import handle_list_models
@@ -15,10 +16,11 @@ def handle_getter(args, config_mgr=None):
15
16
  if getattr(args, "list_models", False):
16
17
  provider = getattr(args, "provider", None)
17
18
  if not provider:
19
+ import sys
18
20
  print(
19
21
  "Error: No provider selected. Please set a provider using '-p PROVIDER', '--set provider=name', or configure a provider."
20
22
  )
21
- return
23
+ sys.exit(1)
22
24
  provider_instance = ProviderRegistry().get_instance(provider)
23
25
  GETTER_DISPATCH = {
24
26
  "list_providers": partial(handle_list_providers, args),
janito/cli/core/runner.py CHANGED
@@ -28,6 +28,10 @@ def _populate_driver_config_data(args, modifiers, provider, model):
28
28
  driver_config_data = {"model": model}
29
29
  if getattr(args, "verbose_api", None) is not None:
30
30
  driver_config_data["verbose_api"] = args.verbose_api
31
+
32
+ # Add reasoning_effort from --effort CLI argument
33
+ if getattr(args, "effort", None) is not None:
34
+ driver_config_data["reasoning_effort"] = args.effort
31
35
  for field in LLMDriverConfig.__dataclass_fields__:
32
36
  if field in CONFIG_LOOKUP_KEYS:
33
37
  if field in modifiers and modifiers[field] is not None:
@@ -64,8 +68,10 @@ def prepare_llm_driver_config(args, modifiers):
64
68
  from janito.provider_registry import ProviderRegistry
65
69
 
66
70
  provider_instance = None
71
+ provider_instance = ProviderRegistry().get_instance(provider)
72
+ if provider_instance is None:
73
+ return provider, None, None
67
74
  try:
68
- provider_instance = ProviderRegistry().get_instance(provider)
69
75
  if not provider_instance.is_model_available(model):
70
76
  print(
71
77
  f"Error: Model '{model}' is not available for provider '{provider}'."
@@ -98,16 +104,28 @@ def handle_runner(args, provider, llm_driver_config, agent_role, verbose_tools=F
98
104
  from janito.provider_registry import ProviderRegistry
99
105
 
100
106
  # Patch: disable execution/run tools if not enabled
107
+ import janito.tools
108
+ adapter = janito.tools.get_local_tools_adapter(workdir=getattr(args, "workdir", None))
101
109
  if not exec_enabled:
102
- import janito.tools
103
- adapter = janito.tools.get_local_tools_adapter(workdir=getattr(args, "workdir", None))
104
110
  if hasattr(adapter, "disable_execution_tools"):
105
111
  adapter.disable_execution_tools()
106
112
  else:
107
- import janito.tools
108
- adapter = janito.tools.get_local_tools_adapter(workdir=getattr(args, "workdir", None))
113
+ # Try to re-register execution tools if possible (print warning if not supported)
114
+ if hasattr(adapter, "register_tool"):
115
+ # This is a no-op if already registered, but we can attempt to re-register known execution tools
116
+ try:
117
+ from janito.tools.adapters.local import PythonCodeRunTool, PythonCommandRunTool, PythonFileRunTool, RunBashCommandTool, RunPowershellCommandTool
118
+ for tool_cls in [PythonCodeRunTool, PythonCommandRunTool, PythonFileRunTool, RunBashCommandTool, RunPowershellCommandTool]:
119
+ try:
120
+ adapter.register_tool(tool_cls)
121
+ except Exception:
122
+ pass # Already registered or error
123
+ except Exception:
124
+ pass
109
125
 
110
126
  provider_instance = ProviderRegistry().get_instance(provider, llm_driver_config)
127
+ if provider_instance is None:
128
+ return
111
129
  mode = get_prompt_mode(args)
112
130
  if getattr(args, "verbose", False):
113
131
  print_verbose_info(
@@ -119,8 +137,10 @@ def handle_runner(args, provider, llm_driver_config, agent_role, verbose_tools=F
119
137
  PromptHandler as SingleShotPromptHandler,
120
138
  )
121
139
 
140
+ # DEBUG: Print exec_enabled propagation at runner
141
+
122
142
  handler = SingleShotPromptHandler(
123
- args, provider_instance, llm_driver_config, role=agent_role
143
+ args, provider_instance, llm_driver_config, role=agent_role, exec_enabled=exec_enabled
124
144
  )
125
145
  handler.handle()
126
146
  else:
@@ -136,6 +156,7 @@ def handle_runner(args, provider, llm_driver_config, agent_role, verbose_tools=F
136
156
  args=args,
137
157
  verbose_tools=verbose_tools,
138
158
  verbose_agent=getattr(args, "verbose_agent", False),
159
+ exec_enabled=exec_enabled,
139
160
  )
140
161
  session.run()
141
162
 
@@ -37,10 +37,14 @@ def handle_set(args, config_mgr=None):
37
37
  return _handle_set_max_tokens(value)
38
38
  if key == "base_url":
39
39
  return _handle_set_base_url(value)
40
+ if key in ["azure_deployment_name", "azure-deployment-name"]:
41
+ global_config.file_set("azure_deployment_name", value)
42
+ print(f"Azure deployment name set to '{value}'.")
43
+ return True
40
44
  if ".max_tokens" in key or ".base_url" in key:
41
45
  return _handle_set_provider_level_setting(key, value)
42
46
  print(
43
- f"Error: Unknown config key '{key}'. Supported: provider, model, <provider>.model, max_tokens, base_url, <provider>.max_tokens, <provider>.base_url, <provider>.<model>.max_tokens, <provider>.<model>.base_url"
47
+ f"Error: Unknown config key '{key}'. Supported: provider, model, <provider>.model, max_tokens, base_url, azure_deployment_name, <provider>.max_tokens, <provider>.base_url, <provider>.<model>.max_tokens, <provider>.<model>.base_url"
44
48
  )
45
49
  return True
46
50
  except Exception as e:
@@ -1,54 +1,54 @@
1
- from janito.config import config as global_config
2
-
3
-
4
- def handle_unset(args):
5
- unset_arg = getattr(args, "unset", None)
6
- if not unset_arg:
7
- return False
8
- key = unset_arg.strip().replace("-", "_")
9
- if "." in key:
10
- # Provider or model-specific keys
11
- parts = key.split(".")
12
- if len(parts) == 2:
13
- provider, subkey = parts
14
- current_val = (
15
- global_config.file_config.get("providers", {})
16
- .get(provider, {})
17
- .get(subkey)
18
- )
19
- if current_val is not None:
20
- del global_config.file_config["providers"][provider][subkey]
21
- global_config.save()
22
- print(f"{key}={current_val} was removed.")
23
- return True
24
- elif len(parts) == 3:
25
- provider, model, subkey = parts
26
- model_conf = (
27
- global_config.file_config.get("providers", {})
28
- .get(provider, {})
29
- .get("models", {})
30
- .get(model, {})
31
- )
32
- current_val = model_conf.get(subkey)
33
- if current_val is not None:
34
- del global_config.file_config["providers"][provider]["models"][model][
35
- subkey
36
- ]
37
- global_config.save()
38
- print(f"{key}={current_val} was removed.")
39
- return True
40
- else:
41
- current_val = global_config.file_config.get(key)
42
- if current_val is not None:
43
- del global_config.file_config[key]
44
- global_config.save()
45
- print(f"{key}={current_val} was removed.")
46
- return True
47
- if "=" in unset_arg:
48
- provided_key = unset_arg.split("=")[0].strip()
49
- print(
50
- f"Error: --unset expected a key, not key=value. Did you mean: --unset {provided_key}?"
51
- )
52
- else:
53
- print(f"Error: no value set for {key} (cannot remove)")
54
- return True
1
+ from janito.config import config as global_config
2
+
3
+
4
+ def handle_unset(args):
5
+ unset_arg = getattr(args, "unset", None)
6
+ if not unset_arg:
7
+ return False
8
+ key = unset_arg.strip().replace("-", "_")
9
+ if "." in key:
10
+ # Provider or model-specific keys
11
+ parts = key.split(".")
12
+ if len(parts) == 2:
13
+ provider, subkey = parts
14
+ current_val = (
15
+ global_config.file_config.get("providers", {})
16
+ .get(provider, {})
17
+ .get(subkey)
18
+ )
19
+ if current_val is not None:
20
+ del global_config.file_config["providers"][provider][subkey]
21
+ global_config.save()
22
+ print(f"{key}={current_val} was removed.")
23
+ return True
24
+ elif len(parts) == 3:
25
+ provider, model, subkey = parts
26
+ model_conf = (
27
+ global_config.file_config.get("providers", {})
28
+ .get(provider, {})
29
+ .get("models", {})
30
+ .get(model, {})
31
+ )
32
+ current_val = model_conf.get(subkey)
33
+ if current_val is not None:
34
+ del global_config.file_config["providers"][provider]["models"][model][
35
+ subkey
36
+ ]
37
+ global_config.save()
38
+ print(f"{key}={current_val} was removed.")
39
+ return True
40
+ else:
41
+ current_val = global_config.file_config.get(key)
42
+ if current_val is not None:
43
+ del global_config.file_config[key]
44
+ global_config.save()
45
+ print(f"{key}={current_val} was removed.")
46
+ return True
47
+ if "=" in unset_arg:
48
+ provided_key = unset_arg.split("=")[0].strip()
49
+ print(
50
+ f"Error: --unset expected a key, not key=value. Did you mean: --unset {provided_key}?"
51
+ )
52
+ else:
53
+ print(f"Error: no value set for {key} (cannot remove)")
54
+ return True
janito/cli/main_cli.py CHANGED
@@ -1,4 +1,5 @@
1
1
  import argparse
2
+ import sys
2
3
  import enum
3
4
  from janito.cli.core.setters import handle_api_key_set, handle_set
4
5
  from janito.cli.core.getters import handle_getter
@@ -118,6 +119,13 @@ definition = [
118
119
  "help": "Port for the termweb server (default: 8088)",
119
120
  },
120
121
  ),
122
+ (["--effort"],
123
+ {
124
+ "choices": ["low", "medium", "high", "none"],
125
+ "default": None,
126
+ "help": "Set the reasoning effort for models that support it (low, medium, high, none)",
127
+ },
128
+ ),
121
129
  (["user_prompt"], {"nargs": argparse.REMAINDER, "help": "Prompt to submit"}),
122
130
  (
123
131
  ["-e", "--event-log"],
@@ -138,6 +146,7 @@ MODIFIER_KEYS = [
138
146
  "role",
139
147
  "system",
140
148
  "temperature",
149
+
141
150
  "verbose",
142
151
  "raw",
143
152
  "web",
@@ -238,7 +247,7 @@ class JanitoCLI:
238
247
  print(
239
248
  "Error: No provider selected and no provider found in config. Please set a provider using '-p PROVIDER', '--set provider=name', or configure a provider."
240
249
  )
241
- return
250
+ sys.exit(1)
242
251
  modifiers = self.collect_modifiers()
243
252
  self._maybe_print_verbose_modifiers(modifiers)
244
253
  setup_event_logger_if_needed(self.args)
@@ -251,6 +260,8 @@ class JanitoCLI:
251
260
  self._maybe_print_verbose_llm_config(llm_driver_config, run_mode)
252
261
  if run_mode == RunMode.RUN:
253
262
  self._maybe_print_verbose_run_mode()
263
+ # DEBUG: Print exec_enabled propagation at main_cli
264
+
254
265
  handle_runner(
255
266
  self.args,
256
267
  provider,
janito/cli/prompt_core.py CHANGED
@@ -8,7 +8,7 @@ from janito.performance_collector import PerformanceCollector
8
8
  from rich.status import Status
9
9
  from rich.console import Console
10
10
  from typing import Any, Optional, Callable
11
- from janito.driver_events import RequestStarted, RequestFinished, RequestStatus
11
+ from janito.driver_events import RequestStarted, RequestFinished, RequestStatus, RateLimitRetry
12
12
  from janito.tools.tool_events import ToolCallError
13
13
  import threading
14
14
  from janito.cli.verbose_output import print_verbose_header
@@ -59,13 +59,16 @@ class PromptHandler:
59
59
  else:
60
60
  self.console.print(inner_event.result)
61
61
  return None
62
+ if isinstance(inner_event, RateLimitRetry):
63
+ status.update(f"[yellow]Rate limited. Waiting {inner_event.retry_delay:.0f}s before retry (attempt {inner_event.attempt}).[yellow]")
64
+ return None
62
65
  if isinstance(inner_event, RequestFinished):
63
66
  status.update("[bold green]Received response![bold green]")
64
67
  return "break"
65
68
  elif (
66
69
  isinstance(inner_event, RequestFinished)
67
70
  and getattr(inner_event, "status", None) == "error"
68
- ):
71
+ ): # noqa
69
72
  error_msg = (
70
73
  inner_event.error if hasattr(inner_event, "error") else "Unknown error"
71
74
  )
@@ -32,17 +32,25 @@ class RichTerminalReporter(EventHandlerBase):
32
32
  self._waiting_printed = False
33
33
 
34
34
  def on_RequestStarted(self, event):
35
- # Print waiting message with provider name
35
+ # Print waiting message with provider and model name
36
36
  provider = None
37
+ model = None
37
38
  if hasattr(event, "payload") and isinstance(event.payload, dict):
38
39
  provider = event.payload.get("provider_name")
40
+ model = event.payload.get("model") or event.payload.get("model_name")
39
41
  if not provider:
40
42
  provider = getattr(event, "provider_name", None)
41
43
  if not provider:
42
44
  provider = getattr(event, "driver_name", None)
43
45
  if not provider:
44
46
  provider = "LLM"
45
- self.console.print(f"[bold cyan]Waiting for {provider}...[/bold cyan]", end="")
47
+ if not model:
48
+ model = getattr(event, "model", None)
49
+ if not model:
50
+ model = getattr(event, "model_name", None)
51
+ if not model:
52
+ model = "?"
53
+ self.console.print(f"[bold cyan]Waiting for {provider} (model: {model})...[/bold cyan]", end="")
46
54
 
47
55
  def on_ResponseReceived(self, event):
48
56
  parts = event.parts if hasattr(event, "parts") else None
@@ -58,7 +66,18 @@ class RichTerminalReporter(EventHandlerBase):
58
66
  def on_RequestFinished(self, event):
59
67
  self.console.print("") # Print end of line after waiting message
60
68
  self._waiting_printed = False
61
- response = event.response if hasattr(event, "response") else None
69
+ response = getattr(event, "response", None)
70
+ error = getattr(event, "error", None)
71
+ exception = getattr(event, "exception", None)
72
+
73
+ # Print error and exception if present
74
+ if error:
75
+ self.console.print(f"[bold red]Error:[/] {error}")
76
+ self.console.file.flush()
77
+ if exception:
78
+ self.console.print(f"[red]Exception:[/] {exception}")
79
+ self.console.file.flush()
80
+
62
81
  if response is not None:
63
82
  if self.raw_mode:
64
83
  self.console.print(Pretty(response, expand_all=True))
@@ -1,6 +1,6 @@
1
- # janito.cli.single_shot_mode package
2
- from .handler import PromptHandler
3
-
4
- __all__ = [
5
- "PromptHandler",
6
- ]
1
+ # janito.cli.single_shot_mode package
2
+ from .handler import PromptHandler
3
+
4
+ __all__ = [
5
+ "PromptHandler",
6
+ ]
@@ -15,21 +15,31 @@ from janito.cli.console import shared_console
15
15
 
16
16
 
17
17
  class PromptHandler:
18
- def __init__(self, args, provider_instance, llm_driver_config, role=None):
18
+ def __init__(self, args, provider_instance, llm_driver_config, role=None, exec_enabled=False):
19
19
  self.args = args
20
20
  self.provider_instance = provider_instance
21
21
  self.llm_driver_config = llm_driver_config
22
22
  self.role = role
23
+ self.exec_enabled = exec_enabled
23
24
  from janito.agent.setup_agent import create_configured_agent
24
25
 
26
+ # DEBUG: Print exec_enabled propagation
25
27
  self.agent = create_configured_agent(
26
28
  provider_instance=provider_instance,
27
29
  llm_driver_config=llm_driver_config,
28
30
  role=role,
29
31
  verbose_tools=getattr(args, "verbose_tools", False),
30
32
  verbose_agent=getattr(args, "verbose_agent", False),
33
+ exec_enabled=exec_enabled,
31
34
  )
32
35
  # Setup conversation/history if needed
36
+ # Dynamically enable/disable execution tools in the registry
37
+ try:
38
+ registry = __import__('janito.tools', fromlist=['get_local_tools_adapter']).get_local_tools_adapter()
39
+ if hasattr(registry, 'set_execution_tools_enabled'):
40
+ registry.set_execution_tools_enabled(exec_enabled)
41
+ except Exception as e:
42
+ shared_console.print(f"[yellow]Warning: Could not update execution tools dynamically in single-shot mode: {e}[/yellow]")
33
43
  self.generic_handler = GenericPromptHandler(
34
44
  args, [], provider_instance=provider_instance
35
45
  )
@@ -19,7 +19,7 @@ def print_verbose_header(agent, args):
19
19
  parts = [
20
20
  f"Janito {VERSION}",
21
21
  f"Provider: {agent.llm_provider.__class__.__name__}",
22
- f"Model: {agent.llm_provider.model_name}{role_part}",
22
+ (f"Model: {agent.llm_provider.driver_config.extra.get('azure_deployment_name', agent.llm_provider.driver_config.model)}{role_part}" if agent.llm_provider.__class__.__name__ == 'AzureOpenAIProvider' else f"Model: {getattr(agent.llm_provider.driver_config, 'model', '-')}{role_part}"),
23
23
  f"Driver: {agent.llm_provider.__class__.__module__.split('.')[-2] if len(agent.llm_provider.__class__.__module__.split('.')) > 1 else agent.llm_provider.__class__.__name__}",
24
24
  ]
25
25
  if hasattr(args, "think") and args.think:
janito/config.py CHANGED
@@ -1,5 +1,5 @@
1
- # Shared Janito ConfigManager singleton
2
- from janito.config_manager import ConfigManager
3
-
4
- # Only one global instance! Used by CLI, provider_config, others:
5
- config = ConfigManager(config_path=None)
1
+ # Shared Janito ConfigManager singleton
2
+ from janito.config_manager import ConfigManager
3
+
4
+ # Only one global instance! Used by CLI, provider_config, others:
5
+ config = ConfigManager(config_path=None)
janito/config_manager.py CHANGED
@@ -46,6 +46,7 @@ class ConfigManager:
46
46
  self.config_path.parent.mkdir(parents=True, exist_ok=True)
47
47
  with open(self.config_path, "w", encoding="utf-8") as f:
48
48
  json.dump(self.file_config, f, indent=2)
49
+ f.write("\n")
49
50
 
50
51
  def get(self, key, default=None):
51
52
  # Precedence: runtime_overrides > file_config > defaults
@@ -63,6 +64,7 @@ class ConfigManager:
63
64
  self.file_config[key] = value
64
65
  with open(self.config_path, "w", encoding="utf-8") as f:
65
66
  json.dump(self.file_config, f, indent=2)
67
+ f.write("\n")
66
68
 
67
69
  def all(self, layered=False):
68
70
  merged = dict(self.defaults)
janito/driver_events.py CHANGED
@@ -90,6 +90,20 @@ class ToolCallFinished(DriverEvent):
90
90
  return self.name
91
91
 
92
92
 
93
+ @attr.s(auto_attribs=True, kw_only=True)
94
+ @attr.s(auto_attribs=True, kw_only=True)
95
+ class RateLimitRetry(DriverEvent):
96
+ """Emitted by a driver when it encounters a provider rate-limit (HTTP 429) and
97
+ decides to retry the request after a delay. This allows UIs or logging layers
98
+ to give feedback to the user while the driver automatically waits.
99
+ """
100
+
101
+ attempt: int = 0 # Retry attempt number (starting at 1)
102
+ retry_delay: float = 0 # Delay in seconds before the next attempt
103
+ error: str = None # The original error message
104
+ details: dict = None # Additional details extracted from the provider response
105
+
106
+
93
107
  @attr.s(auto_attribs=True, kw_only=True)
94
108
  class ResponseReceived(DriverEvent):
95
109
  parts: list = None