janito 1.14.2__py3-none-any.whl → 2.0.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 (282) hide show
  1. janito/__init__.py +6 -1
  2. janito/__main__.py +1 -1
  3. janito/agent/setup_agent.py +139 -0
  4. janito/agent/templates/profiles/{system_prompt_template_base.txt.j2 → system_prompt_template_main.txt.j2} +1 -1
  5. janito/cli/__init__.py +9 -0
  6. janito/cli/chat_mode/bindings.py +37 -0
  7. janito/cli/chat_mode/chat_entry.py +23 -0
  8. janito/cli/chat_mode/prompt_style.py +19 -0
  9. janito/cli/chat_mode/session.py +272 -0
  10. janito/{shell/prompt/completer.py → cli/chat_mode/shell/autocomplete.py} +7 -6
  11. janito/cli/chat_mode/shell/commands/__init__.py +55 -0
  12. janito/cli/chat_mode/shell/commands/base.py +9 -0
  13. janito/cli/chat_mode/shell/commands/clear.py +12 -0
  14. janito/{shell → cli/chat_mode/shell}/commands/conversation_restart.py +34 -30
  15. janito/cli/chat_mode/shell/commands/edit.py +25 -0
  16. janito/cli/chat_mode/shell/commands/help.py +16 -0
  17. janito/cli/chat_mode/shell/commands/history_view.py +93 -0
  18. janito/cli/chat_mode/shell/commands/lang.py +25 -0
  19. janito/cli/chat_mode/shell/commands/last.py +137 -0
  20. janito/cli/chat_mode/shell/commands/livelogs.py +49 -0
  21. janito/cli/chat_mode/shell/commands/multi.py +51 -0
  22. janito/cli/chat_mode/shell/commands/prompt.py +64 -0
  23. janito/cli/chat_mode/shell/commands/role.py +36 -0
  24. janito/cli/chat_mode/shell/commands/session.py +40 -0
  25. janito/{shell → cli/chat_mode/shell}/commands/session_control.py +2 -2
  26. janito/cli/chat_mode/shell/commands/termweb_log.py +92 -0
  27. janito/cli/chat_mode/shell/commands/tools.py +32 -0
  28. janito/{shell → cli/chat_mode/shell}/commands/utility.py +4 -7
  29. janito/{shell → cli/chat_mode/shell}/commands/verbose.py +5 -5
  30. janito/cli/chat_mode/shell/session/__init__.py +1 -0
  31. janito/{shell → cli/chat_mode/shell}/session/manager.py +9 -1
  32. janito/cli/chat_mode/toolbar.py +90 -0
  33. janito/cli/cli_commands/list_models.py +35 -0
  34. janito/cli/cli_commands/list_providers.py +9 -0
  35. janito/cli/cli_commands/list_tools.py +53 -0
  36. janito/cli/cli_commands/model_selection.py +50 -0
  37. janito/cli/cli_commands/model_utils.py +84 -0
  38. janito/cli/cli_commands/set_api_key.py +19 -0
  39. janito/cli/cli_commands/show_config.py +51 -0
  40. janito/cli/cli_commands/show_system_prompt.py +62 -0
  41. janito/cli/config.py +28 -0
  42. janito/cli/console.py +3 -0
  43. janito/cli/core/__init__.py +4 -0
  44. janito/cli/core/event_logger.py +59 -0
  45. janito/cli/core/getters.py +31 -0
  46. janito/cli/core/runner.py +141 -0
  47. janito/cli/core/setters.py +174 -0
  48. janito/cli/core/unsetters.py +54 -0
  49. janito/cli/main.py +8 -196
  50. janito/cli/main_cli.py +312 -0
  51. janito/cli/prompt_core.py +230 -0
  52. janito/cli/prompt_handler.py +6 -0
  53. janito/cli/rich_terminal_reporter.py +101 -0
  54. janito/cli/single_shot_mode/__init__.py +6 -0
  55. janito/cli/single_shot_mode/handler.py +137 -0
  56. janito/cli/termweb_starter.py +73 -24
  57. janito/cli/utils.py +25 -0
  58. janito/cli/verbose_output.py +196 -0
  59. janito/config.py +5 -0
  60. janito/config_manager.py +110 -0
  61. janito/conversation_history.py +30 -0
  62. janito/{agent/tools_utils/dir_walk_utils.py → dir_walk_utils.py} +3 -2
  63. janito/driver_events.py +98 -0
  64. janito/drivers/anthropic/driver.py +113 -0
  65. janito/drivers/azure_openai/driver.py +36 -0
  66. janito/drivers/driver_registry.py +33 -0
  67. janito/drivers/google_genai/driver.py +54 -0
  68. janito/drivers/google_genai/schema_generator.py +67 -0
  69. janito/drivers/mistralai/driver.py +41 -0
  70. janito/drivers/openai/driver.py +334 -0
  71. janito/event_bus/__init__.py +2 -0
  72. janito/event_bus/bus.py +68 -0
  73. janito/event_bus/event.py +15 -0
  74. janito/event_bus/handler.py +31 -0
  75. janito/event_bus/queue_bus.py +57 -0
  76. janito/exceptions.py +23 -0
  77. janito/formatting_token.py +54 -0
  78. janito/i18n/pt.py +1 -0
  79. janito/llm/__init__.py +5 -0
  80. janito/llm/agent.py +443 -0
  81. janito/llm/auth.py +62 -0
  82. janito/llm/driver.py +239 -0
  83. janito/llm/driver_config.py +34 -0
  84. janito/llm/driver_config_builder.py +34 -0
  85. janito/llm/driver_input.py +12 -0
  86. janito/llm/message_parts.py +60 -0
  87. janito/llm/model.py +38 -0
  88. janito/llm/provider.py +187 -0
  89. janito/perf_singleton.py +3 -0
  90. janito/performance_collector.py +167 -0
  91. janito/provider_config.py +98 -0
  92. janito/provider_registry.py +152 -0
  93. janito/providers/__init__.py +7 -0
  94. janito/providers/anthropic/model_info.py +22 -0
  95. janito/providers/anthropic/provider.py +65 -0
  96. janito/providers/azure_openai/model_info.py +15 -0
  97. janito/providers/azure_openai/provider.py +72 -0
  98. janito/providers/deepseek/__init__.py +1 -0
  99. janito/providers/deepseek/model_info.py +16 -0
  100. janito/providers/deepseek/provider.py +91 -0
  101. janito/providers/google/__init__.py +1 -0
  102. janito/providers/google/model_info.py +40 -0
  103. janito/providers/google/provider.py +69 -0
  104. janito/providers/mistralai/model_info.py +37 -0
  105. janito/providers/mistralai/provider.py +69 -0
  106. janito/providers/openai/__init__.py +1 -0
  107. janito/providers/openai/model_info.py +137 -0
  108. janito/providers/openai/provider.py +107 -0
  109. janito/providers/openai/schema_generator.py +63 -0
  110. janito/providers/provider_static_info.py +21 -0
  111. janito/providers/registry.py +26 -0
  112. janito/report_events.py +38 -0
  113. janito/termweb/app.py +1 -1
  114. janito/tools/__init__.py +16 -0
  115. janito/tools/adapters/__init__.py +1 -0
  116. janito/tools/adapters/local/__init__.py +54 -0
  117. janito/tools/adapters/local/adapter.py +92 -0
  118. janito/{agent/tools → tools/adapters/local}/ask_user.py +30 -13
  119. janito/tools/adapters/local/copy_file.py +84 -0
  120. janito/{agent/tools → tools/adapters/local}/create_directory.py +11 -10
  121. janito/tools/adapters/local/create_file.py +82 -0
  122. janito/tools/adapters/local/delete_text_in_file.py +136 -0
  123. janito/{agent/tools → tools/adapters/local}/fetch_url.py +18 -19
  124. janito/tools/adapters/local/find_files.py +140 -0
  125. janito/tools/adapters/local/get_file_outline/core.py +151 -0
  126. janito/{agent/tools → tools/adapters/local}/get_file_outline/python_outline.py +125 -0
  127. janito/tools/adapters/local/get_file_outline/python_outline_v2.py +156 -0
  128. janito/{agent/tools → tools/adapters/local}/get_file_outline/search_outline.py +12 -7
  129. janito/{agent/tools → tools/adapters/local}/move_file.py +13 -9
  130. janito/{agent/tools → tools/adapters/local}/open_url.py +7 -5
  131. janito/tools/adapters/local/python_code_run.py +165 -0
  132. janito/tools/adapters/local/python_command_run.py +163 -0
  133. janito/tools/adapters/local/python_file_run.py +162 -0
  134. janito/{agent/tools → tools/adapters/local}/remove_directory.py +15 -9
  135. janito/{agent/tools → tools/adapters/local}/remove_file.py +17 -14
  136. janito/{agent/tools → tools/adapters/local}/replace_text_in_file.py +27 -22
  137. janito/tools/adapters/local/run_bash_command.py +176 -0
  138. janito/tools/adapters/local/run_powershell_command.py +219 -0
  139. janito/{agent/tools → tools/adapters/local}/search_text/core.py +32 -12
  140. janito/{agent/tools → tools/adapters/local}/search_text/match_lines.py +13 -4
  141. janito/{agent/tools → tools/adapters/local}/search_text/pattern_utils.py +12 -4
  142. janito/{agent/tools → tools/adapters/local}/search_text/traverse_directory.py +15 -2
  143. janito/{agent/tools → tools/adapters/local}/validate_file_syntax/core.py +12 -11
  144. janito/{agent/tools → tools/adapters/local}/validate_file_syntax/css_validator.py +1 -1
  145. janito/{agent/tools → tools/adapters/local}/validate_file_syntax/html_validator.py +1 -1
  146. janito/{agent/tools → tools/adapters/local}/validate_file_syntax/js_validator.py +1 -1
  147. janito/{agent/tools → tools/adapters/local}/validate_file_syntax/json_validator.py +1 -1
  148. janito/{agent/tools → tools/adapters/local}/validate_file_syntax/markdown_validator.py +1 -1
  149. janito/{agent/tools → tools/adapters/local}/validate_file_syntax/ps1_validator.py +1 -1
  150. janito/{agent/tools → tools/adapters/local}/validate_file_syntax/python_validator.py +1 -1
  151. janito/{agent/tools → tools/adapters/local}/validate_file_syntax/xml_validator.py +1 -1
  152. janito/{agent/tools → tools/adapters/local}/validate_file_syntax/yaml_validator.py +1 -1
  153. janito/{agent/tools/get_lines.py → tools/adapters/local/view_file.py} +45 -27
  154. janito/tools/inspect_registry.py +17 -0
  155. janito/tools/tool_base.py +105 -0
  156. janito/tools/tool_events.py +58 -0
  157. janito/tools/tool_run_exception.py +12 -0
  158. janito/{agent → tools}/tool_use_tracker.py +2 -4
  159. janito/{agent/tools_utils/utils.py → tools/tool_utils.py} +18 -9
  160. janito/tools/tools_adapter.py +207 -0
  161. janito/tools/tools_schema.py +104 -0
  162. janito/utils.py +11 -0
  163. janito/version.py +4 -0
  164. janito-2.0.0.dist-info/METADATA +232 -0
  165. janito-2.0.0.dist-info/RECORD +180 -0
  166. janito/agent/__init__.py +0 -0
  167. janito/agent/api_exceptions.py +0 -4
  168. janito/agent/config.py +0 -147
  169. janito/agent/config_defaults.py +0 -12
  170. janito/agent/config_utils.py +0 -0
  171. janito/agent/content_handler.py +0 -0
  172. janito/agent/conversation.py +0 -238
  173. janito/agent/conversation_api.py +0 -306
  174. janito/agent/conversation_exceptions.py +0 -18
  175. janito/agent/conversation_tool_calls.py +0 -39
  176. janito/agent/conversation_ui.py +0 -17
  177. janito/agent/event.py +0 -24
  178. janito/agent/event_dispatcher.py +0 -24
  179. janito/agent/event_handler_protocol.py +0 -5
  180. janito/agent/event_system.py +0 -15
  181. janito/agent/llm_conversation_history.py +0 -82
  182. janito/agent/message_handler.py +0 -20
  183. janito/agent/message_handler_protocol.py +0 -5
  184. janito/agent/openai_client.py +0 -149
  185. janito/agent/openai_schema_generator.py +0 -187
  186. janito/agent/profile_manager.py +0 -96
  187. janito/agent/queued_message_handler.py +0 -50
  188. janito/agent/rich_live.py +0 -32
  189. janito/agent/rich_message_handler.py +0 -115
  190. janito/agent/runtime_config.py +0 -36
  191. janito/agent/test_handler_protocols.py +0 -47
  192. janito/agent/test_openai_schema_generator.py +0 -93
  193. janito/agent/tests/__init__.py +0 -1
  194. janito/agent/tool_base.py +0 -63
  195. janito/agent/tool_executor.py +0 -122
  196. janito/agent/tool_registry.py +0 -49
  197. janito/agent/tools/__init__.py +0 -47
  198. janito/agent/tools/create_file.py +0 -59
  199. janito/agent/tools/delete_text_in_file.py +0 -97
  200. janito/agent/tools/find_files.py +0 -106
  201. janito/agent/tools/get_file_outline/core.py +0 -81
  202. janito/agent/tools/present_choices.py +0 -64
  203. janito/agent/tools/python_command_runner.py +0 -201
  204. janito/agent/tools/python_file_runner.py +0 -199
  205. janito/agent/tools/python_stdin_runner.py +0 -208
  206. janito/agent/tools/replace_file.py +0 -72
  207. janito/agent/tools/run_bash_command.py +0 -218
  208. janito/agent/tools/run_powershell_command.py +0 -251
  209. janito/agent/tools_utils/__init__.py +0 -1
  210. janito/agent/tools_utils/action_type.py +0 -7
  211. janito/agent/tools_utils/test_gitignore_utils.py +0 -46
  212. janito/cli/_livereload_log_utils.py +0 -13
  213. janito/cli/_print_config.py +0 -96
  214. janito/cli/_termweb_log_utils.py +0 -17
  215. janito/cli/_utils.py +0 -9
  216. janito/cli/arg_parser.py +0 -272
  217. janito/cli/cli_main.py +0 -281
  218. janito/cli/config_commands.py +0 -211
  219. janito/cli/config_runner.py +0 -35
  220. janito/cli/formatting_runner.py +0 -12
  221. janito/cli/livereload_starter.py +0 -60
  222. janito/cli/logging_setup.py +0 -38
  223. janito/cli/one_shot.py +0 -80
  224. janito/livereload/app.py +0 -25
  225. janito/rich_utils.py +0 -59
  226. janito/shell/__init__.py +0 -0
  227. janito/shell/commands/__init__.py +0 -61
  228. janito/shell/commands/config.py +0 -22
  229. janito/shell/commands/edit.py +0 -24
  230. janito/shell/commands/history_view.py +0 -18
  231. janito/shell/commands/lang.py +0 -19
  232. janito/shell/commands/livelogs.py +0 -42
  233. janito/shell/commands/prompt.py +0 -62
  234. janito/shell/commands/termweb_log.py +0 -94
  235. janito/shell/commands/tools.py +0 -26
  236. janito/shell/commands/track.py +0 -36
  237. janito/shell/main.py +0 -326
  238. janito/shell/prompt/load_prompt.py +0 -57
  239. janito/shell/prompt/session_setup.py +0 -57
  240. janito/shell/session/config.py +0 -109
  241. janito/shell/session/history.py +0 -0
  242. janito/shell/ui/interactive.py +0 -226
  243. janito/termweb/static/editor.css +0 -158
  244. janito/termweb/static/editor.css.bak +0 -145
  245. janito/termweb/static/editor.html +0 -46
  246. janito/termweb/static/editor.html.bak +0 -46
  247. janito/termweb/static/editor.js +0 -265
  248. janito/termweb/static/editor.js.bak +0 -259
  249. janito/termweb/static/explorer.html.bak +0 -59
  250. janito/termweb/static/favicon.ico +0 -0
  251. janito/termweb/static/favicon.ico.bak +0 -0
  252. janito/termweb/static/index.html +0 -53
  253. janito/termweb/static/index.html.bak +0 -54
  254. janito/termweb/static/index.html.bak.bak +0 -175
  255. janito/termweb/static/landing.html.bak +0 -36
  256. janito/termweb/static/termicon.svg +0 -1
  257. janito/termweb/static/termweb.css +0 -214
  258. janito/termweb/static/termweb.css.bak +0 -237
  259. janito/termweb/static/termweb.js +0 -162
  260. janito/termweb/static/termweb.js.bak +0 -168
  261. janito/termweb/static/termweb.js.bak.bak +0 -157
  262. janito/termweb/static/termweb_quickopen.js +0 -135
  263. janito/termweb/static/termweb_quickopen.js.bak +0 -125
  264. janito/tests/test_rich_utils.py +0 -44
  265. janito/web/__init__.py +0 -0
  266. janito/web/__main__.py +0 -25
  267. janito/web/app.py +0 -145
  268. janito-1.14.2.dist-info/METADATA +0 -306
  269. janito-1.14.2.dist-info/RECORD +0 -162
  270. janito-1.14.2.dist-info/licenses/LICENSE +0 -21
  271. /janito/{shell → cli/chat_mode/shell}/input_history.py +0 -0
  272. /janito/{shell/commands/session.py → cli/chat_mode/shell/session/history.py} +0 -0
  273. /janito/{agent/tools_utils/formatting.py → formatting.py} +0 -0
  274. /janito/{agent/tools_utils/gitignore_utils.py → gitignore_utils.py} +0 -0
  275. /janito/{agent/platform_discovery.py → platform_discovery.py} +0 -0
  276. /janito/{agent/tools → tools/adapters/local}/get_file_outline/__init__.py +0 -0
  277. /janito/{agent/tools → tools/adapters/local}/get_file_outline/markdown_outline.py +0 -0
  278. /janito/{agent/tools → tools/adapters/local}/search_text/__init__.py +0 -0
  279. /janito/{agent/tools → tools/adapters/local}/validate_file_syntax/__init__.py +0 -0
  280. {janito-1.14.2.dist-info → janito-2.0.0.dist-info}/WHEEL +0 -0
  281. {janito-1.14.2.dist-info → janito-2.0.0.dist-info}/entry_points.txt +0 -0
  282. {janito-1.14.2.dist-info → janito-2.0.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,167 @@
1
+ from collections import defaultdict, Counter
2
+ from janito.event_bus.handler import EventHandlerBase
3
+ import janito.driver_events as driver_events
4
+ import janito.report_events as report_events
5
+ import janito.tools.tool_events as tool_events
6
+
7
+
8
+ class PerformanceCollector(EventHandlerBase):
9
+ _last_request_usage = None
10
+
11
+ """
12
+ Aggregates performance metrics and statistics from LLM driver and report events.
13
+ Collects timing, token usage, status, error, turn, content part, and tool usage data.
14
+ Also tracks request durations.
15
+ """
16
+
17
+ def __init__(self):
18
+ super().__init__(driver_events, report_events, tool_events)
19
+ # Aggregated stats
20
+ self.total_requests = 0
21
+ self.status_counter = Counter()
22
+ self.token_usage = defaultdict(
23
+ int
24
+ ) # keys: total_tokens, prompt_tokens, completion_tokens
25
+ self.error_count = 0
26
+ self.error_messages = []
27
+ self.error_exceptions = []
28
+ self.total_turns = 0
29
+ self.generation_finished_count = 0
30
+ self.content_part_count = 0
31
+ # Duration tracking
32
+ self._request_start_times = dict() # request_id -> timestamp
33
+ self._durations = [] # list of elapsed times (seconds)
34
+ # Tool stats
35
+ self.total_tool_events = 0
36
+ self.tool_names_counter = Counter()
37
+ self.tool_error_count = 0
38
+ self.tool_error_messages = []
39
+ self.tool_action_counter = Counter()
40
+ self.tool_subtype_counter = Counter()
41
+ # Raw events for reference
42
+ self._events = []
43
+
44
+ def on_RequestStarted(self, event):
45
+ self._events.append(("RequestStarted", event))
46
+ # Store the start time if possible
47
+ # Assumes 'event' has a unique .request_id and a .timestamp (in seconds)
48
+ request_id = event.request_id
49
+ timestamp = event.timestamp
50
+ if request_id is not None and timestamp is not None:
51
+ self._request_start_times[request_id] = timestamp
52
+
53
+ def on_RequestFinished(self, event):
54
+ self._events.append(("RequestFinished", event))
55
+ # Calculate and record the duration if start time is available
56
+ request_id = getattr(event, "request_id", None)
57
+ finish_time = getattr(event, "timestamp", None)
58
+ if request_id is not None and finish_time is not None:
59
+ start_time = self._request_start_times.pop(request_id, None)
60
+ if start_time is not None:
61
+ delta = finish_time - start_time
62
+ if hasattr(delta, "total_seconds"):
63
+ self._durations.append(delta.total_seconds())
64
+ else:
65
+ self._durations.append(float(delta))
66
+ self.total_requests += 1
67
+ self.status_counter[getattr(event, "status", None)] += 1
68
+ usage = getattr(event, "usage", None)
69
+ if usage:
70
+ self._last_request_usage = usage.copy()
71
+ for k, v in usage.items():
72
+ if isinstance(v, (int, float)):
73
+ self.token_usage[k] += v
74
+ # Error handling
75
+ if getattr(event, "status", None) in ("error", "cancelled"):
76
+ self.error_count += 1
77
+ self.error_messages.append(getattr(event, "error", None))
78
+ self.error_exceptions.append(getattr(event, "exception", None))
79
+
80
+ def on_GenerationFinished(self, event):
81
+ self._events.append(("GenerationFinished", event))
82
+ self.generation_finished_count += 1
83
+ self.total_turns += event.total_turns
84
+
85
+ def on_ContentPartFound(self, event):
86
+ self._events.append(("ContentPartFound", event))
87
+ self.content_part_count += 1
88
+
89
+ def on_ToolCallStarted(self, event):
90
+ self._events.append(("ToolCallStarted", event))
91
+ self.total_tool_events += 1
92
+ self.tool_names_counter[event.tool_name] += 1
93
+
94
+ def on_ReportEvent(self, event):
95
+ self._events.append(("ReportEvent", event))
96
+ # Only count errors for reporting
97
+ if event.subtype:
98
+ self.tool_subtype_counter[str(event.subtype)] += 1
99
+ if str(event.subtype).lower() == "error":
100
+ self.tool_error_count += 1
101
+ self.tool_error_messages.append(event.message)
102
+
103
+ # --- Aggregated Data Accessors ---
104
+ def get_average_duration(self):
105
+ if not self._durations:
106
+ return 0.0
107
+ return sum(self._durations) / len(self._durations)
108
+
109
+ def get_total_requests(self):
110
+ return self.total_requests
111
+
112
+ def get_status_counts(self):
113
+ return dict(self.status_counter)
114
+
115
+ def get_token_usage(self):
116
+ return dict(self.token_usage)
117
+
118
+ def get_error_count(self):
119
+ return self.error_count
120
+
121
+ def get_error_messages(self):
122
+ return list(self.error_messages)
123
+
124
+ def get_total_turns(self):
125
+ return self.total_turns
126
+
127
+ def get_average_turns(self):
128
+ if self.generation_finished_count == 0:
129
+ return 0.0
130
+ return self.total_turns / self.generation_finished_count
131
+
132
+ def get_content_part_count(self):
133
+ return self.content_part_count
134
+
135
+ # --- Tool Stats Accessors ---
136
+ def get_total_tool_events(self):
137
+ return self.total_tool_events
138
+
139
+ def get_tool_names_counter(self):
140
+ return dict(self.tool_names_counter)
141
+
142
+ def get_tool_error_count(self):
143
+ return self.tool_error_count
144
+
145
+ def get_tool_error_messages(self):
146
+ return list(self.tool_error_messages)
147
+
148
+ def get_tool_action_counter(self):
149
+ return dict(self.tool_action_counter)
150
+
151
+ def get_tool_subtype_counter(self):
152
+ return dict(self.tool_subtype_counter)
153
+
154
+ def get_all_events(self):
155
+ return list(self._events)
156
+
157
+ def get_last_request_usage(self):
158
+ """
159
+ Returns the usage dict (tokens) from the most recent RequestFinished event, or None if not available.
160
+ """
161
+ return self._last_request_usage.copy() if self._last_request_usage else None
162
+
163
+ def reset_last_request_usage(self):
164
+ """
165
+ Clears the most recent usage dict. Use this (e.g. on session reset) to remove token/usage stats for toolbar.
166
+ """
167
+ self._last_request_usage = None
@@ -0,0 +1,98 @@
1
+ """
2
+ ProviderConfigManager: Handles reading and writing provider configuration for janito.
3
+ """
4
+
5
+ from janito.config import config
6
+ from janito.llm.auth import LLMAuthManager
7
+
8
+
9
+ def get_config_provider():
10
+ return config.get("provider")
11
+
12
+
13
+ def set_config_provider(provider_name):
14
+ config.file_set("provider", provider_name)
15
+
16
+
17
+ def get_config_path():
18
+ return str(config.config_path)
19
+
20
+
21
+ def set_api_key(provider, api_key):
22
+ auth_manager = LLMAuthManager()
23
+ auth_manager.set_credentials(provider, api_key)
24
+
25
+
26
+ def get_provider_config(provider):
27
+ return config.get_provider_config(provider)
28
+
29
+
30
+ def set_provider_config(provider, key, value):
31
+ # Update provider config and persist immediately
32
+ cfg = config.file_config.get("providers", {})
33
+ if provider not in cfg:
34
+ cfg[provider] = {}
35
+ cfg[provider][key] = value
36
+ config.file_config["providers"] = cfg
37
+ with open(config.config_path, "w", encoding="utf-8") as f:
38
+ json.dump(config.file_config, f, indent=2)
39
+
40
+
41
+ def set_provider_model_config(provider, model, key, value):
42
+ # Update provider-model config and persist immediately
43
+ cfg = config.file_config.get("providers", {})
44
+ if provider not in cfg:
45
+ cfg[provider] = {}
46
+ if "models" not in cfg[provider]:
47
+ cfg[provider]["models"] = {}
48
+ if model not in cfg[provider]["models"]:
49
+ cfg[provider]["models"][model] = {}
50
+ cfg[provider]["models"][model][key] = value
51
+ config.file_config["providers"] = cfg
52
+ with open(config.config_path, "w", encoding="utf-8") as f:
53
+ json.dump(config.file_config, f, indent=2)
54
+
55
+
56
+ def get_provider_model_config(provider, model):
57
+ return config.get_provider_model_config(provider, model)
58
+
59
+
60
+ def get_effective_model(provider=None, requested_model=None):
61
+ """
62
+ Returns the best model selection according to the following precedence:
63
+ 1. If requested_model is provided, use it.
64
+ 2. If a provider is set and provider.model is set, use it.
65
+ 3. If a global model is set, use it.
66
+ Returns None if not found.
67
+ """
68
+ if requested_model:
69
+ return requested_model
70
+ if provider:
71
+ provider_model = config.get_provider_config(provider).get("model")
72
+ if provider_model:
73
+ return provider_model
74
+ global_model = config.get("model")
75
+ if global_model:
76
+ return global_model
77
+ return None
78
+
79
+
80
+ def get_effective_setting(provider, model, setting):
81
+ """
82
+ Look up setting with the following order:
83
+ 1. providers.{provider}.models.{model}.{setting}
84
+ 2. providers.{provider}.{setting}
85
+ 3. top-level {setting}
86
+ Returns None if not found.
87
+ """
88
+ # 1. provider-model
89
+ val = config.get_provider_model_config(provider, model).get(setting)
90
+ if val is not None:
91
+ return val
92
+ # 2. provider
93
+ val = config.get_provider_config(provider).get(setting)
94
+ if val is not None:
95
+ return val
96
+ # 3. top-level
97
+ val = config.get(setting)
98
+ return val
@@ -0,0 +1,152 @@
1
+ """
2
+ ProviderRegistry: Handles provider listing and selection logic for janito CLI.
3
+ """
4
+
5
+ from rich.table import Table
6
+ from janito.cli.console import shared_console
7
+ from janito.providers.registry import LLMProviderRegistry
8
+ from janito.providers.provider_static_info import STATIC_PROVIDER_METADATA
9
+ from janito.llm.auth import LLMAuthManager
10
+ import sys
11
+ from janito.exceptions import MissingProviderSelectionException
12
+
13
+
14
+ class ProviderRegistry:
15
+ def list_providers(self):
16
+ """List all supported LLM providers as a table using rich, showing if auth is configured and supported model names."""
17
+ providers = self._get_provider_names()
18
+ table = self._create_table()
19
+ rows = self._get_all_provider_rows(providers)
20
+ self._add_rows_to_table(table, rows)
21
+ self._print_table(table)
22
+
23
+ def _get_provider_names(self):
24
+ return list(STATIC_PROVIDER_METADATA.keys())
25
+
26
+ def _create_table(self):
27
+ table = Table(title="Supported LLM Providers")
28
+ table.add_column("Provider", style="cyan")
29
+ table.add_column("Maintainer", style="yellow", justify="center")
30
+ table.add_column("Model Names", style="magenta")
31
+ return table
32
+
33
+ def _get_all_provider_rows(self, providers):
34
+ rows = []
35
+ for p in providers:
36
+ info = self._get_provider_info(p)
37
+ # info is (provider_name, maintainer, model_names, skip)
38
+ if len(info) == 4 and info[3]:
39
+ continue # skip providers flagged as not implemented
40
+ rows.append(info[:3])
41
+ rows.sort(key=self._maintainer_sort_key)
42
+ return rows
43
+
44
+ def _add_rows_to_table(self, table, rows):
45
+ for idx, (p, maintainer, model_names) in enumerate(rows):
46
+ table.add_row(p, maintainer, model_names)
47
+ if idx != len(rows) - 1:
48
+ table.add_section()
49
+
50
+ def _print_table(self, table):
51
+ shared_console.print(table)
52
+
53
+ def _get_provider_info(self, provider_name):
54
+ static_info = STATIC_PROVIDER_METADATA.get(provider_name, {})
55
+ maintainer_val = static_info.get("maintainer", "-")
56
+ maintainer = (
57
+ "[red]🚨 Needs maintainer[/red]"
58
+ if maintainer_val == "Needs maintainer"
59
+ else f"👤 {maintainer_val}"
60
+ )
61
+ model_names = "-"
62
+ unavailable_reason = None
63
+ skip = False
64
+ try:
65
+ provider_class = LLMProviderRegistry.get(provider_name)
66
+ creds = LLMAuthManager().get_credentials(provider_name)
67
+ provider_instance = None
68
+ instantiation_failed = False
69
+ try:
70
+ provider_instance = provider_class()
71
+ except NotImplementedError:
72
+ skip = True
73
+ unavailable_reason = "Not implemented"
74
+ model_names = f"[red]❌ Not implemented[/red]"
75
+ except Exception as e:
76
+ instantiation_failed = True
77
+ unavailable_reason = (
78
+ f"Unavailable (import error or missing dependency): {str(e)}"
79
+ )
80
+ model_names = f"[red]❌ {unavailable_reason}[/red]"
81
+ if not instantiation_failed and provider_instance is not None:
82
+ available, unavailable_reason = self._get_availability(
83
+ provider_instance
84
+ )
85
+ if (
86
+ not available
87
+ and unavailable_reason
88
+ and "not implemented" in str(unavailable_reason).lower()
89
+ ):
90
+ skip = True
91
+ if available:
92
+ model_names = self._get_model_names(provider_name)
93
+ else:
94
+ model_names = f"[red]❌ {unavailable_reason}[/red]"
95
+ except Exception as import_error:
96
+ model_names = f"[red]❌ Unavailable (cannot import provider module): {str(import_error)}[/red]"
97
+ return (provider_name, maintainer, model_names, skip)
98
+
99
+ def _get_availability(self, provider_instance):
100
+ try:
101
+ available = getattr(provider_instance, "available", True)
102
+ unavailable_reason = getattr(provider_instance, "unavailable_reason", None)
103
+ except Exception as e:
104
+ available = False
105
+ unavailable_reason = f"Error reading runtime availability: {str(e)}"
106
+ return available, unavailable_reason
107
+
108
+ def _get_model_names(self, provider_name):
109
+ provider_to_specs = {
110
+ "openai": "janito.providers.openai.model_info",
111
+ "azure_openai": "janito.providers.azure_openai.model_info",
112
+ "google": "janito.providers.google.model_info",
113
+ "mistralai": "janito.providers.mistralai.model_info",
114
+ "deepseek": "janito.providers.deepseek.model_info",
115
+ }
116
+ if provider_name in provider_to_specs:
117
+ try:
118
+ mod = __import__(
119
+ provider_to_specs[provider_name], fromlist=["MODEL_SPECS"]
120
+ )
121
+ return ", ".join(mod.MODEL_SPECS.keys())
122
+ except Exception:
123
+ return "(Error)"
124
+ return "-"
125
+
126
+ def _maintainer_sort_key(self, row):
127
+ maint = row[1]
128
+ is_needs_maint = "Needs maintainer" in maint
129
+ return (is_needs_maint, row[2] != "✅ Auth")
130
+
131
+ def get_provider(self, provider_name):
132
+ """Return the provider class for the given provider name."""
133
+ from janito.providers.registry import LLMProviderRegistry
134
+
135
+ if not provider_name:
136
+ raise ValueError("Provider name must be specified.")
137
+ return LLMProviderRegistry.get(provider_name)
138
+
139
+ 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."""
141
+ provider_class = self.get_provider(provider_name)
142
+ if provider_class is None:
143
+ raise ValueError(f"No provider class found for '{provider_name}'")
144
+ if config is not None:
145
+ return provider_class(config=config)
146
+ return provider_class()
147
+
148
+
149
+ # For backward compatibility
150
+ def list_providers():
151
+ """Legacy function for listing providers, now uses ProviderRegistry class."""
152
+ ProviderRegistry().list_providers()
@@ -0,0 +1,7 @@
1
+ # Ensure all providers are registered by importing their modules
2
+ import janito.providers.openai.provider
3
+ import janito.providers.mistralai.provider
4
+ import janito.providers.google.provider
5
+ import janito.providers.azure_openai.provider
6
+ import janito.providers.anthropic.provider
7
+ import janito.providers.deepseek.provider
@@ -0,0 +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
+ }
@@ -0,0 +1,65 @@
1
+ from janito.llm.provider import LLMProvider
2
+ from janito.llm.model import LLMModelInfo
3
+ from janito.llm.auth import LLMAuthManager
4
+ from janito.llm.driver_config import LLMDriverConfig
5
+ from janito.tools.adapters.local.adapter import LocalToolsAdapter
6
+ from janito.providers.registry import LLMProviderRegistry
7
+
8
+ from .model_info import MODEL_SPECS
9
+
10
+ from janito.drivers.anthropic.driver import AnthropicModelDriver
11
+
12
+ available = AnthropicModelDriver.available
13
+ unavailable_reason = AnthropicModelDriver.unavailable_reason
14
+ maintainer = "Needs maintainer"
15
+
16
+
17
+ class AnthropicProvider(LLMProvider):
18
+ name = "anthropic"
19
+ maintainer = "Needs maintainer"
20
+ MODEL_SPECS = MODEL_SPECS
21
+ DEFAULT_MODEL = "claude-3-opus-20240229"
22
+
23
+ def __init__(
24
+ self, auth_manager: LLMAuthManager = None, config: LLMDriverConfig = None
25
+ ):
26
+ if not self.available:
27
+ self._driver = None
28
+ return
29
+ self.auth_manager = auth_manager or LLMAuthManager()
30
+ self._api_key = self.auth_manager.get_credentials(type(self).name)
31
+ self._tools_adapter = LocalToolsAdapter()
32
+ self._info = config or LLMDriverConfig(model=None)
33
+ if not self._info.model:
34
+ self._info.model = self.DEFAULT_MODEL
35
+ if not self._info.api_key:
36
+ self._info.api_key = self._api_key
37
+ self.fill_missing_device_info(self._info)
38
+ self._driver = AnthropicModelDriver(tools_adapter=self._tools_adapter)
39
+
40
+ @property
41
+ def driver(self):
42
+ if not self.available:
43
+ raise ImportError(
44
+ f"AnthropicProvider unavailable: {self.unavailable_reason}"
45
+ )
46
+ return self._driver
47
+
48
+ @property
49
+ def available(self):
50
+ return available
51
+
52
+ @property
53
+ def unavailable_reason(self):
54
+ return unavailable_reason
55
+
56
+ def create_agent(self, tools_adapter=None, agent_name: str = None, **kwargs):
57
+ from janito.llm.agent import LLMAgent
58
+ from janito.drivers.anthropic.driver import AnthropicModelDriver
59
+
60
+ # Always create a new driver with the passed-in tools_adapter
61
+ driver = AnthropicModelDriver(tools_adapter=tools_adapter)
62
+ return LLMAgent(self, tools_adapter, agent_name=agent_name, **kwargs)
63
+
64
+
65
+ LLMProviderRegistry.register(AnthropicProvider.name, AnthropicProvider)
@@ -0,0 +1,15 @@
1
+ from janito.llm.model import LLMModelInfo
2
+
3
+ MODEL_SPECS = {
4
+ "azure_openai_deployment": LLMModelInfo(
5
+ name="azure_openai_deployment",
6
+ context="N/A",
7
+ max_input="N/A",
8
+ max_cot="N/A",
9
+ max_response="N/A",
10
+ thinking_supported=False,
11
+ default_temp=0.2,
12
+ open="azure_openai",
13
+ driver="AzureOpenAIModelDriver",
14
+ )
15
+ }
@@ -0,0 +1,72 @@
1
+ from janito.llm.provider import LLMProvider
2
+ from janito.llm.model import LLMModelInfo
3
+ from janito.llm.auth import LLMAuthManager
4
+ from janito.llm.driver_config import LLMDriverConfig
5
+ from janito.tools.adapters.local.adapter import LocalToolsAdapter
6
+ from janito.providers.registry import LLMProviderRegistry
7
+
8
+ from .model_info import MODEL_SPECS
9
+
10
+ from janito.drivers.azure_openai.driver import AzureOpenAIModelDriver
11
+
12
+ available = AzureOpenAIModelDriver.available
13
+ unavailable_reason = AzureOpenAIModelDriver.unavailable_reason
14
+ maintainer = "João Pinto <lamego.pinto@gmail.com>"
15
+
16
+
17
+ class AzureOpenAIProvider(LLMProvider):
18
+ name = "azure_openai"
19
+ maintainer = "João Pinto <lamego.pinto@gmail.com>"
20
+ MODEL_SPECS = MODEL_SPECS
21
+ DEFAULT_MODEL = "azure_openai_deployment"
22
+
23
+ def __init__(
24
+ self, auth_manager: LLMAuthManager = None, config: LLMDriverConfig = None
25
+ ):
26
+ if not self.available:
27
+ self._driver = None
28
+ return
29
+ self._auth_manager = auth_manager or LLMAuthManager()
30
+ self._api_key = self._auth_manager.get_credentials(type(self).name)
31
+ self._tools_adapter = LocalToolsAdapter()
32
+ self._driver_config = config or LLMDriverConfig(model=None)
33
+ if not self._driver_config.model:
34
+ self._driver_config.model = self.DEFAULT_MODEL
35
+ if not self._driver_config.api_key:
36
+ self._driver_config.api_key = self._api_key
37
+ if not self._driver_config.extra.get("api_version"):
38
+ self._driver_config.extra["api_version"] = "2023-05-15"
39
+ self.fill_missing_device_info(self._driver_config)
40
+ self._driver = AzureOpenAIModelDriver(tools_adapter=self._tools_adapter)
41
+
42
+ @property
43
+ def driver(self):
44
+ if not self.available:
45
+ raise ImportError(
46
+ f"AzureOpenAIProvider unavailable: {self.unavailable_reason}"
47
+ )
48
+ return self._driver
49
+
50
+ @property
51
+ def available(self):
52
+ return available
53
+
54
+ @property
55
+ def unavailable_reason(self):
56
+ return unavailable_reason
57
+
58
+ def create_agent(self, tools_adapter=None, agent_name: str = None, **kwargs):
59
+ from janito.llm.agent import LLMAgent
60
+ from janito.drivers.azure_openai.driver import AzureOpenAIModelDriver
61
+
62
+ # Always create a new driver with the passed-in tools_adapter
63
+ driver = AzureOpenAIModelDriver(tools_adapter=tools_adapter)
64
+ return LLMAgent(self, tools_adapter, agent_name=agent_name, **kwargs)
65
+
66
+ def execute_tool(self, tool_name: str, event_bus, *args, **kwargs):
67
+ # Use direct execution via adapter:
68
+ self._tools_adapter.event_bus = event_bus
69
+ return self._tools_adapter.execute_by_name(tool_name, *args, **kwargs)
70
+
71
+
72
+ LLMProviderRegistry.register(AzureOpenAIProvider.name, AzureOpenAIProvider)
@@ -0,0 +1 @@
1
+ # Deepseek provider package marker
@@ -0,0 +1,16 @@
1
+ MODEL_SPECS = {
2
+ "deepseek-chat": {
3
+ "description": "DeepSeek Chat Model (OpenAI-compatible)",
4
+ "context_window": 8192,
5
+ "max_tokens": 4096,
6
+ "family": "deepseek",
7
+ "default": True,
8
+ },
9
+ "deepseek-coder": {
10
+ "description": "DeepSeek Coder Model (OpenAI-compatible)",
11
+ "context_window": 8192,
12
+ "max_tokens": 4096,
13
+ "family": "deepseek",
14
+ "default": False,
15
+ },
16
+ }