janito 3.4.0__py3-none-any.whl → 3.5.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 (159) hide show
  1. janito/README.md +3 -0
  2. janito/cli/chat_mode/bindings.py +50 -0
  3. janito/cli/chat_mode/session.py +12 -1
  4. janito/cli/chat_mode/shell/commands/multi.py +5 -0
  5. janito/cli/chat_mode/shell/commands/security/allowed_sites.py +47 -33
  6. janito/cli/cli_commands/check_tools.py +212 -0
  7. janito/cli/cli_commands/list_plugins.py +52 -43
  8. janito/cli/core/getters.py +3 -0
  9. janito/cli/core/model_guesser.py +40 -24
  10. janito/cli/main_cli.py +9 -12
  11. janito/cli/prompt_core.py +47 -9
  12. janito/cli/rich_terminal_reporter.py +2 -2
  13. janito/drivers/openai/driver.py +1 -0
  14. janito/drivers/zai/driver.py +1 -0
  15. janito/i18n/it.py +46 -46
  16. janito/llm/agent.py +32 -16
  17. janito/llm/auth_utils.py +14 -5
  18. janito/llm/cancellation_manager.py +63 -0
  19. janito/llm/driver.py +8 -0
  20. janito/llm/enter_cancellation.py +107 -0
  21. janito/plugin_system/__init__.py +10 -0
  22. janito/{plugins → plugin_system}/base.py +5 -2
  23. janito/plugin_system/core_loader.py +217 -0
  24. janito/plugin_system/core_loader_fixed.py +225 -0
  25. janito/plugins/__init__.py +31 -12
  26. janito/plugins/auto_loader.py +12 -11
  27. janito/plugins/auto_loader_fixed.py +12 -11
  28. janito/plugins/builtin.py +15 -1
  29. janito/plugins/core/__init__.py +7 -0
  30. janito/plugins/core/codeanalyzer/__init__.py +43 -0
  31. janito/plugins/core/filemanager/__init__.py +124 -0
  32. janito/plugins/core/filemanager/tools/create_file.py +87 -0
  33. janito/plugins/core/filemanager/tools/replace_text_in_file.py +270 -0
  34. janito/plugins/core/imagedisplay/__init__.py +14 -0
  35. janito/plugins/core/imagedisplay/plugin.py +51 -0
  36. janito/plugins/core/imagedisplay/tools/__init__.py +1 -0
  37. janito/plugins/core/imagedisplay/tools/show_image.py +83 -0
  38. janito/{tools/adapters/local → plugins/core/imagedisplay/tools}/show_image_grid.py +13 -5
  39. janito/plugins/core/system/__init__.py +23 -0
  40. janito/plugins/core/system/tools/run_bash_command.py +204 -0
  41. janito/plugins/core/system/tools/run_powershell_command.py +234 -0
  42. janito/plugins/core_adapter.py +89 -11
  43. janito/plugins/dev/__init__.py +7 -0
  44. janito/plugins/dev/pythondev/__init__.py +37 -0
  45. janito/plugins/dev/visualization/__init__.py +23 -0
  46. janito/plugins/discovery.py +5 -5
  47. janito/plugins/discovery_core.py +14 -9
  48. janito/plugins/example_plugin.py +108 -0
  49. janito/plugins/manager.py +1 -1
  50. janito/plugins/tools/__init__.py +10 -0
  51. janito/{tools/adapters/local → plugins/tools}/ask_user.py +3 -3
  52. janito/plugins/tools/copy_file.py +87 -0
  53. janito/plugins/tools/core_tools_plugin.py +87 -0
  54. janito/plugins/tools/create_directory.py +70 -0
  55. janito/{tools/adapters/local → plugins/tools}/create_file.py +6 -6
  56. janito/plugins/tools/decorators.py +19 -0
  57. janito/plugins/tools/delete_text_in_file.py +134 -0
  58. janito/{tools/adapters/local → plugins/tools}/fetch_url.py +3 -3
  59. janito/plugins/tools/find_files.py +143 -0
  60. janito/plugins/tools/get_file_outline/__init__.py +7 -0
  61. janito/plugins/tools/get_file_outline/core.py +122 -0
  62. janito/plugins/tools/get_file_outline/java_outline.py +47 -0
  63. janito/plugins/tools/get_file_outline/markdown_outline.py +14 -0
  64. janito/plugins/tools/get_file_outline/python_outline.py +303 -0
  65. janito/plugins/tools/get_file_outline/search_outline.py +36 -0
  66. janito/plugins/tools/move_file.py +131 -0
  67. janito/plugins/tools/open_html_in_browser.py +51 -0
  68. janito/plugins/tools/open_url.py +37 -0
  69. janito/{tools/adapters/local → plugins/tools}/python_code_run.py +23 -7
  70. janito/{tools/adapters/local → plugins/tools}/python_command_run.py +21 -5
  71. janito/{tools/adapters/local → plugins/tools}/python_file_run.py +21 -5
  72. janito/plugins/tools/read_chart.py +259 -0
  73. janito/plugins/tools/read_files.py +58 -0
  74. janito/plugins/tools/remove_directory.py +55 -0
  75. janito/plugins/tools/remove_file.py +58 -0
  76. janito/{tools/adapters/local → plugins/tools}/replace_text_in_file.py +4 -4
  77. janito/{tools/adapters/local → plugins/tools}/run_bash_command.py +3 -3
  78. janito/{tools/adapters/local → plugins/tools}/run_powershell_command.py +3 -3
  79. janito/plugins/tools/search_text/__init__.py +7 -0
  80. janito/plugins/tools/search_text/core.py +205 -0
  81. janito/plugins/tools/search_text/match_lines.py +67 -0
  82. janito/plugins/tools/search_text/pattern_utils.py +73 -0
  83. janito/plugins/tools/search_text/traverse_directory.py +145 -0
  84. janito/{tools/adapters/local → plugins/tools}/show_image.py +15 -6
  85. janito/plugins/tools/show_image_grid.py +85 -0
  86. janito/plugins/tools/validate_file_syntax/__init__.py +7 -0
  87. janito/plugins/tools/validate_file_syntax/core.py +114 -0
  88. janito/plugins/tools/validate_file_syntax/css_validator.py +35 -0
  89. janito/plugins/tools/validate_file_syntax/html_validator.py +100 -0
  90. janito/plugins/tools/validate_file_syntax/jinja2_validator.py +50 -0
  91. janito/plugins/tools/validate_file_syntax/js_validator.py +27 -0
  92. janito/plugins/tools/validate_file_syntax/json_validator.py +6 -0
  93. janito/plugins/tools/validate_file_syntax/markdown_validator.py +109 -0
  94. janito/plugins/tools/validate_file_syntax/ps1_validator.py +32 -0
  95. janito/plugins/tools/validate_file_syntax/python_validator.py +5 -0
  96. janito/plugins/tools/validate_file_syntax/xml_validator.py +11 -0
  97. janito/plugins/tools/validate_file_syntax/yaml_validator.py +6 -0
  98. janito/plugins/tools/view_file.py +172 -0
  99. janito/plugins/ui/__init__.py +7 -0
  100. janito/plugins/ui/userinterface/__init__.py +16 -0
  101. janito/plugins/ui/userinterface/tools/ask_user.py +110 -0
  102. janito/plugins/web/__init__.py +7 -0
  103. janito/plugins/web/webtools/__init__.py +33 -0
  104. janito/plugins/web/webtools/tools/fetch_url.py +458 -0
  105. janito/providers/__init__.py +1 -0
  106. janito/providers/together/__init__.py +1 -0
  107. janito/providers/together/model_info.py +69 -0
  108. janito/providers/together/provider.py +108 -0
  109. janito/tools/__init__.py +31 -7
  110. janito/tools/adapters/__init__.py +6 -1
  111. janito/tools/adapters/local/__init__.py +7 -70
  112. janito/tools/cli_initializer.py +88 -0
  113. janito/tools/initialize.py +70 -0
  114. janito/tools/loop_protection_decorator.py +114 -117
  115. janito-3.5.1.dist-info/METADATA +229 -0
  116. {janito-3.4.0.dist-info → janito-3.5.1.dist-info}/RECORD +155 -86
  117. janito/plugins/core_loader.py +0 -120
  118. janito/plugins/core_loader_fixed.py +0 -125
  119. janito/tools/function_adapter.py +0 -65
  120. janito-3.4.0.dist-info/METADATA +0 -84
  121. /janito/{tools/adapters/local → plugins/core/codeanalyzer/tools}/get_file_outline/__init__.py +0 -0
  122. /janito/{tools/adapters/local → plugins/core/codeanalyzer/tools}/get_file_outline/core.py +0 -0
  123. /janito/{tools/adapters/local → plugins/core/codeanalyzer/tools}/get_file_outline/java_outline.py +0 -0
  124. /janito/{tools/adapters/local → plugins/core/codeanalyzer/tools}/get_file_outline/markdown_outline.py +0 -0
  125. /janito/{tools/adapters/local → plugins/core/codeanalyzer/tools}/get_file_outline/python_outline.py +0 -0
  126. /janito/{tools/adapters/local → plugins/core/codeanalyzer/tools}/get_file_outline/search_outline.py +0 -0
  127. /janito/{tools/adapters/local → plugins/core/codeanalyzer/tools}/search_text/__init__.py +0 -0
  128. /janito/{tools/adapters/local → plugins/core/codeanalyzer/tools}/search_text/core.py +0 -0
  129. /janito/{tools/adapters/local → plugins/core/codeanalyzer/tools}/search_text/match_lines.py +0 -0
  130. /janito/{tools/adapters/local → plugins/core/codeanalyzer/tools}/search_text/pattern_utils.py +0 -0
  131. /janito/{tools/adapters/local → plugins/core/codeanalyzer/tools}/search_text/traverse_directory.py +0 -0
  132. /janito/{tools/adapters/local → plugins/core/filemanager/tools}/copy_file.py +0 -0
  133. /janito/{tools/adapters/local → plugins/core/filemanager/tools}/create_directory.py +0 -0
  134. /janito/{tools/adapters/local → plugins/core/filemanager/tools}/delete_text_in_file.py +0 -0
  135. /janito/{tools/adapters/local → plugins/core/filemanager/tools}/find_files.py +0 -0
  136. /janito/{tools/adapters/local → plugins/core/filemanager/tools}/move_file.py +0 -0
  137. /janito/{tools/adapters/local → plugins/core/filemanager/tools}/read_files.py +0 -0
  138. /janito/{tools/adapters/local → plugins/core/filemanager/tools}/remove_directory.py +0 -0
  139. /janito/{tools/adapters/local → plugins/core/filemanager/tools}/remove_file.py +0 -0
  140. /janito/{tools/adapters/local → plugins/core/filemanager/tools}/validate_file_syntax/__init__.py +0 -0
  141. /janito/{tools/adapters/local → plugins/core/filemanager/tools}/validate_file_syntax/core.py +0 -0
  142. /janito/{tools/adapters/local → plugins/core/filemanager/tools}/validate_file_syntax/css_validator.py +0 -0
  143. /janito/{tools/adapters/local → plugins/core/filemanager/tools}/validate_file_syntax/html_validator.py +0 -0
  144. /janito/{tools/adapters/local → plugins/core/filemanager/tools}/validate_file_syntax/jinja2_validator.py +0 -0
  145. /janito/{tools/adapters/local → plugins/core/filemanager/tools}/validate_file_syntax/js_validator.py +0 -0
  146. /janito/{tools/adapters/local → plugins/core/filemanager/tools}/validate_file_syntax/json_validator.py +0 -0
  147. /janito/{tools/adapters/local → plugins/core/filemanager/tools}/validate_file_syntax/markdown_validator.py +0 -0
  148. /janito/{tools/adapters/local → plugins/core/filemanager/tools}/validate_file_syntax/ps1_validator.py +0 -0
  149. /janito/{tools/adapters/local → plugins/core/filemanager/tools}/validate_file_syntax/python_validator.py +0 -0
  150. /janito/{tools/adapters/local → plugins/core/filemanager/tools}/validate_file_syntax/xml_validator.py +0 -0
  151. /janito/{tools/adapters/local → plugins/core/filemanager/tools}/validate_file_syntax/yaml_validator.py +0 -0
  152. /janito/{tools/adapters/local → plugins/core/filemanager/tools}/view_file.py +0 -0
  153. /janito/{tools/adapters/local → plugins/dev/visualization/tools}/read_chart.py +0 -0
  154. /janito/{tools/adapters/local → plugins/web/webtools/tools}/open_html_in_browser.py +0 -0
  155. /janito/{tools/adapters/local → plugins/web/webtools/tools}/open_url.py +0 -0
  156. {janito-3.4.0.dist-info → janito-3.5.1.dist-info}/WHEEL +0 -0
  157. {janito-3.4.0.dist-info → janito-3.5.1.dist-info}/entry_points.txt +0 -0
  158. {janito-3.4.0.dist-info → janito-3.5.1.dist-info}/licenses/LICENSE +0 -0
  159. {janito-3.4.0.dist-info → janito-3.5.1.dist-info}/top_level.txt +0 -0
@@ -1,143 +1,140 @@
1
1
  import functools
2
2
  import time
3
3
  import threading
4
- from typing import Callable, Any
5
- from janito.tools.loop_protection import LoopProtection
6
- from janito.tools.tool_use_tracker import normalize_path
7
-
4
+ from typing import Any, Tuple
8
5
 
9
6
  # Global tracking for decorator-based loop protection
10
7
  _decorator_call_tracker = {}
11
8
  _decorator_call_tracker_lock = threading.Lock()
12
9
 
13
10
 
11
+ def _normalize_key_value(key_field: str, key_value: Any) -> Any:
12
+ """Normalize key values, especially paths, so different representations map to the same key."""
13
+ if key_value is None:
14
+ return None
15
+
16
+ try:
17
+ if isinstance(key_field, str) and "path" in key_field.lower():
18
+ from janito.tools.tool_use_tracker import (
19
+ normalize_path as _norm, # reuse existing normalization
20
+ )
21
+
22
+ if isinstance(key_value, str):
23
+ return _norm(key_value)
24
+ if isinstance(key_value, (list, tuple)):
25
+ return tuple(_norm(v) if isinstance(v, str) else v for v in key_value)
26
+ except Exception:
27
+ # Best-effort normalization – fall back to original value
28
+ pass
29
+
30
+ return key_value
31
+
32
+
33
+ def _get_param_value(func, args, kwargs, key_field: str):
34
+ """Extract the watched parameter value from args/kwargs using function signature."""
35
+ if key_field in kwargs:
36
+ return kwargs[key_field]
37
+
38
+ # Handle positional arguments by mapping to parameter names
39
+ if len(args) > 1: # args[0] is self
40
+ import inspect
41
+
42
+ try:
43
+ sig = inspect.signature(func)
44
+ param_names = list(sig.parameters.keys())
45
+ if key_field in param_names:
46
+ idx = param_names.index(key_field)
47
+ if idx < len(args):
48
+ return args[idx]
49
+ except Exception:
50
+ return None
51
+
52
+ return None
53
+
54
+
55
+ def _determine_operation_name(func, args, kwargs, key_field: str) -> str:
56
+ """Build the operation name for rate limiting, optionally including a normalized key value."""
57
+ if key_field:
58
+ raw_value = _get_param_value(func, args, kwargs, key_field)
59
+ if raw_value is not None:
60
+ norm_value = _normalize_key_value(key_field, raw_value)
61
+ return f"{func.__name__}_{norm_value}"
62
+ return func.__name__
63
+
64
+
65
+ def _check_and_record(
66
+ op_name: str,
67
+ current_time: float,
68
+ time_window: float,
69
+ max_calls: int,
70
+ tool_instance: Any,
71
+ ) -> Tuple[bool, str]:
72
+ """Check loop limits for op_name and record the call. Returns (exceeded, message)."""
73
+ with _decorator_call_tracker_lock:
74
+ # Clean old timestamps
75
+ if op_name in _decorator_call_tracker:
76
+ _decorator_call_tracker[op_name] = [
77
+ ts
78
+ for ts in _decorator_call_tracker[op_name]
79
+ if current_time - ts <= time_window
80
+ ]
81
+
82
+ # Check limit
83
+ if (
84
+ op_name in _decorator_call_tracker
85
+ and len(_decorator_call_tracker[op_name]) >= max_calls
86
+ ):
87
+ if all(
88
+ current_time - ts <= time_window
89
+ for ts in _decorator_call_tracker[op_name]
90
+ ):
91
+ msg = (
92
+ f"Loop protection: Too many {op_name} operations in a short time period "
93
+ f"({max_calls} calls in {time_window}s). Please try a different approach or wait before retrying."
94
+ )
95
+ if hasattr(tool_instance, "report_error"):
96
+ try:
97
+ tool_instance.report_error(msg)
98
+ except Exception:
99
+ pass
100
+ return True, msg
101
+
102
+ # Record this call
103
+ if op_name not in _decorator_call_tracker:
104
+ _decorator_call_tracker[op_name] = []
105
+ _decorator_call_tracker[op_name].append(current_time)
106
+
107
+ return False, ""
108
+
109
+
14
110
  def protect_against_loops(
15
111
  max_calls: int = 5, time_window: float = 10.0, key_field: str = None
16
112
  ):
17
113
  """
18
114
  Decorator that adds loop protection to tool run methods.
19
115
 
20
- This decorator monitors tool executions and prevents excessive calls within
21
- a configurable time window. It helps prevent infinite loops or excessive
22
- resource consumption when tools are called repeatedly.
23
-
24
- When the configured limits are exceeded, the decorator raises a RuntimeError
25
- with a descriptive message. This exception will propagate up the call stack
26
- unless caught by a try/except block in the calling code.
27
-
28
- The decorator works by:
29
- 1. Tracking the number of calls to the decorated function
30
- 2. Checking if the calls exceed the configured limits
31
- 3. Raising a RuntimeError if a potential loop is detected
32
- 4. Allowing the method to proceed normally if the operation is safe
33
-
34
- Args:
35
- max_calls (int): Maximum number of calls allowed within the time window.
36
- Defaults to 5 calls.
37
- time_window (float): Time window in seconds for detecting excessive calls.
38
- Defaults to 10.0 seconds.
39
- key_field (str, optional): The parameter name to use for key matching instead of function name.
40
- If provided, the decorator will track calls based on the value of this
41
- parameter rather than the function name. Useful for tools that operate
42
- on specific files or resources.
43
-
44
- Example:
45
- >>> @protect_against_loops(max_calls=3, time_window=5.0)
46
- >>> def run(self, path: str) -> str:
47
- >>> # Implementation here
48
- >>> pass
49
-
50
- >>> @protect_against_loops(max_calls=10, time_window=30.0)
51
- >>> def run(self, file_paths: list) -> str:
52
- >>> # Implementation here
53
- >>> pass
54
-
55
- >>> @protect_against_loops(max_calls=5, time_window=10.0, key_field='path')
56
- >>> def run(self, path: str) -> str:
57
- >>> # This will track calls per unique path value
58
- >>> pass
59
-
60
- Note:
61
- When loop protection is triggered, a RuntimeError will be raised with a
62
- descriptive message. This exception will propagate up the call stack
63
- unless caught by a try/except block in the calling code.
116
+ Tracks calls within a sliding time window and prevents excessive repeated operations.
117
+ When key_field is provided, the limit is applied per unique normalized value of that parameter
118
+ (e.g., per-path protection for file tools).
64
119
  """
65
120
 
66
121
  def decorator(func):
67
122
  @functools.wraps(func)
68
123
  def wrapper(*args, **kwargs):
69
- # Get the tool instance (self)
124
+ # Methods should always have self; if not, execute directly.
70
125
  if not args:
71
- # This shouldn't happen in normal usage as methods need self
72
126
  return func(*args, **kwargs)
73
127
 
74
- # Determine the operation key
75
- if key_field:
76
- # Use the key_field parameter value as the operation key
77
- key_value = None
78
- if key_field in kwargs:
79
- key_value = kwargs[key_field]
80
- elif len(args) > 1:
81
- # Handle positional arguments - need to map parameter names
82
- import inspect
83
-
84
- try:
85
- sig = inspect.signature(func)
86
- param_names = list(sig.parameters.keys())
87
- if key_field in param_names:
88
- field_index = param_names.index(key_field)
89
- if field_index < len(args):
90
- key_value = args[field_index]
91
- except (ValueError, TypeError):
92
- pass
93
-
94
- if key_value is not None:
95
- op_name = f"{func.__name__}_{key_value}"
96
- else:
97
- op_name = func.__name__
98
- else:
99
- # Use the function name as the operation name
100
- op_name = func.__name__
101
-
102
- # Check call limits
103
- current_time = time.time()
104
-
105
- with _decorator_call_tracker_lock:
106
- # Clean up old entries outside the time window
107
- if op_name in _decorator_call_tracker:
108
- _decorator_call_tracker[op_name] = [
109
- timestamp
110
- for timestamp in _decorator_call_tracker[op_name]
111
- if current_time - timestamp <= time_window
112
- ]
113
-
114
- # Check if we're exceeding the limit
115
- if op_name in _decorator_call_tracker:
116
- if len(_decorator_call_tracker[op_name]) >= max_calls:
117
- # Check if all recent calls are within the time window
118
- if all(
119
- current_time - timestamp <= time_window
120
- for timestamp in _decorator_call_tracker[op_name]
121
- ):
122
- # Return loop protection message as string instead of raising exception
123
- error_msg = f"Loop protection: Too many {op_name} operations in a short time period ({max_calls} calls in {time_window}s). Please try a different approach or wait before retrying."
124
-
125
- # Try to report the error through the tool's reporting mechanism
126
- tool_instance = args[0] if args else None
127
- if hasattr(tool_instance, "report_error"):
128
- try:
129
- tool_instance.report_error(error_msg)
130
- except Exception:
131
- pass # If reporting fails, we still return the message
132
-
133
- return error_msg
134
-
135
- # Record this call
136
- if op_name not in _decorator_call_tracker:
137
- _decorator_call_tracker[op_name] = []
138
- _decorator_call_tracker[op_name].append(current_time)
139
-
140
- # Proceed with the original function
128
+ op_name = _determine_operation_name(func, args, kwargs, key_field)
129
+ exceeded, msg = _check_and_record(
130
+ op_name=op_name,
131
+ current_time=time.time(),
132
+ time_window=time_window,
133
+ max_calls=max_calls,
134
+ tool_instance=args[0],
135
+ )
136
+ if exceeded:
137
+ return msg
141
138
  return func(*args, **kwargs)
142
139
 
143
140
  return wrapper
@@ -0,0 +1,229 @@
1
+ Metadata-Version: 2.4
2
+ Name: janito
3
+ Version: 3.5.1
4
+ Summary: A new Python package called janito.
5
+ Author-email: João Pinto <janito@ikignosis.org>
6
+ Project-URL: Homepage, https://github.com/ikignosis/janito
7
+ Requires-Python: >=3.7
8
+ Description-Content-Type: text/markdown
9
+ License-File: LICENSE
10
+ Requires-Dist: attrs==25.3.0
11
+ Requires-Dist: rich==14.0.0
12
+ Requires-Dist: pathspec==0.12.1
13
+ Requires-Dist: setuptools>=61.0
14
+ Requires-Dist: pyyaml>=6.0
15
+ Requires-Dist: jinja2>=3.0.0
16
+ Requires-Dist: prompt_toolkit>=3.0.51
17
+ Requires-Dist: lxml>=5.4.0
18
+ Requires-Dist: requests>=2.32.4
19
+ Requires-Dist: bs4>=0.0.2
20
+ Requires-Dist: questionary>=2.0.1
21
+ Requires-Dist: openai>=1.68.0
22
+ Requires-Dist: Pillow>=10.0.0
23
+ Provides-Extra: dev
24
+ Requires-Dist: pytest; extra == "dev"
25
+ Requires-Dist: pre-commit; extra == "dev"
26
+ Requires-Dist: ruff==0.11.9; extra == "dev"
27
+ Requires-Dist: detect-secrets==1.4.0; extra == "dev"
28
+ Requires-Dist: codespell==2.4.1; extra == "dev"
29
+ Requires-Dist: black; extra == "dev"
30
+ Requires-Dist: questionary>=2.0.1; extra == "dev"
31
+ Requires-Dist: setuptools_scm>=8.0; extra == "dev"
32
+ Provides-Extra: coder
33
+ Dynamic: license-file
34
+
35
+ # Janito CLI
36
+
37
+ A powerful command-line tool for running LLM-powered workflows with built-in tool execution capabilities.
38
+
39
+ ## Quick Start
40
+
41
+ ### Installation
42
+
43
+ ```bash
44
+ pip install janito
45
+ ```
46
+
47
+ ### First-Time Setup
48
+
49
+ 1. **Get your API key**: Sign up at [Moonshot AI](https://platform.moonshot.cn/) and get your API key
50
+ 2. **Set your API key**:
51
+ ```bash
52
+ janito --set-api-key YOUR_MOONSHOT_API_KEY -p moonshot
53
+ ```
54
+
55
+ ### Basic Usage
56
+
57
+ **Moonshot (Recommended - Default Provider)**
58
+ ```bash
59
+ # Using the default provider (moonshot) and model
60
+ janito "Create a Python script that reads a CSV file"
61
+
62
+ # Using a specific Moonshot model
63
+ janito -m kimi-k1-8k "Explain quantum computing"
64
+ ```
65
+
66
+ **Other Providers**
67
+ ```bash
68
+ # OpenAI
69
+ janito -p openai -m gpt-4 "Write a React component"
70
+
71
+ # Anthropic
72
+ janito -p anthropic -m claude-3-5-sonnet-20241022 "Analyze this code"
73
+
74
+ # Google
75
+ janito -p google -m gemini-2.0-flash-exp "Generate unit tests"
76
+ ```
77
+
78
+ ### Interactive Chat Mode
79
+
80
+ Start an interactive session (default mode):
81
+ ```bash
82
+ janito
83
+ ```
84
+
85
+ Or explicitly:
86
+ ```bash
87
+ janito --chat
88
+ ```
89
+
90
+ In chat mode, you can:
91
+
92
+ - Have multi-turn conversations
93
+ - Execute code and commands
94
+ - Read and write files
95
+ - Use built-in tools
96
+
97
+ ### Available Commands
98
+
99
+ - `janito --list-providers` - List all supported providers
100
+ - `janito --list-models` - List all available models
101
+ - `janito --list-tools` - List available tools
102
+ - `janito --show-config` - Show current configuration
103
+
104
+ ### Configuration
105
+
106
+ Set default provider and model:
107
+ ```bash
108
+ janito --set provider=moonshot
109
+ janito --set model=kimi-k1-8k
110
+ ```
111
+
112
+ ## Providers
113
+
114
+ ### Moonshot (Recommended)
115
+
116
+ - **Models**: kimi-k1-8k, kimi-k1-32k, kimi-k1-128k, kimi-k2-turbo-preview
117
+ - **Strengths**: Excellent Chinese/English support, competitive pricing, fast responses
118
+ - **Setup**: Get API key from [Moonshot AI Platform](https://platform.moonshot.cn/)
119
+
120
+ ### OpenAI
121
+
122
+ - **Models**: gpt-5, gpt-4.1, gpt-4o, gpt-4-turbo, gpt-3.5-turbo
123
+ - **Setup**: Get API key from [OpenAI Platform](https://platform.openai.com/)
124
+
125
+ ### Anthropic
126
+
127
+ - **Models**: claude-3-7-sonnet-20250219, claude-3-5-sonnet-20241022, claude-3-opus-20250514
128
+ - **Setup**: Get API key from [Anthropic Console](https://console.anthropic.com/)
129
+
130
+ ### IBM WatsonX
131
+
132
+ - **Models**: ibm/granite-3-8b-instruct, ibm/granite-3-2b-instruct, meta-llama/llama-3-1-8b-instruct, meta-llama/llama-3-1-70b-instruct, mistralai/mistral-large
133
+ - **Strengths**: Enterprise-grade AI, IBM Granite models, hosted Llama and Mistral models
134
+ - **Setup**: Get API key and project ID from [IBM Cloud](https://cloud.ibm.com/)
135
+
136
+ ### Google
137
+
138
+ - **Models**: gemini-2.5-flash, gemini-2.5-pro, gemini-2.5-flash-lite-preview-06-17
139
+ - **Setup**: Get API key from [Google AI Studio](https://makersuite.google.com/)
140
+
141
+ ## Advanced Features
142
+
143
+ ### 🚀 New in v3.1.0: Enter Key Cancellation
144
+ **Chat Mode Enhancement**: Press **Enter** at any time to instantly cancel long-running requests in interactive chat mode. No more waiting for stuck requests!
145
+
146
+ ### Tool Usage
147
+
148
+ Janito includes powerful built-in tools for:
149
+
150
+ - File operations (read, write, search)
151
+ - Code execution
152
+ - Web scraping
153
+ - System commands
154
+ - And more...
155
+
156
+ ### Profiles
157
+ Use predefined system prompts:
158
+ ```bash
159
+ janito --developer "Create a REST API" # Same as --profile developer
160
+ janito --market "Analyze market trends" # Same as --profile market-analyst
161
+ ```
162
+
163
+ ### Environment Variables
164
+ You can also configure via environment variables:
165
+
166
+ **Moonshot:**
167
+ ```bash
168
+ export MOONSHOT_API_KEY=your_key_here
169
+ export JANITO_PROVIDER=moonshot
170
+ export JANITO_MODEL=kimi-k1-8k
171
+ ```
172
+
173
+ **OpenAI:**
174
+ ```bash
175
+ export OPENAI_API_KEY=your_key_here
176
+ export JANITO_PROVIDER=openai
177
+ export JANITO_MODEL=gpt-5
178
+ ```
179
+
180
+ **IBM WatsonX:**
181
+ ```bash
182
+ export WATSONX_API_KEY=your_key_here
183
+ export WATSONX_PROJECT_ID=your_project_id
184
+ export WATSONX_SPACE_ID=your_space_id # optional
185
+ export JANITO_PROVIDER=ibm
186
+ export JANITO_MODEL=ibm/granite-3-8b-instruct
187
+ ```
188
+
189
+ **Anthropic:**
190
+ ```bash
191
+ export ANTHROPIC_API_KEY=your_key_here
192
+ export JANITO_PROVIDER=anthropic
193
+ export JANITO_MODEL=claude-3-7-sonnet-20250219
194
+ ```
195
+
196
+ **Google:**
197
+ ```bash
198
+ export GOOGLE_API_KEY=your_key_here
199
+ export JANITO_PROVIDER=google
200
+ export JANITO_MODEL=gemini-2.5-flash
201
+ ```
202
+
203
+ ## Examples
204
+
205
+ ### Code Generation
206
+ ```bash
207
+ janito "Create a Python FastAPI application with user authentication"
208
+ ```
209
+
210
+ ### File Analysis
211
+ ```bash
212
+ janito "Analyze the performance bottlenecks in my_app.py"
213
+ ```
214
+
215
+ ### Data Processing
216
+ ```bash
217
+ janito "Process this CSV file and generate summary statistics"
218
+ ```
219
+
220
+ ### Web Development
221
+ ```bash
222
+ janito "Create a responsive landing page with Tailwind CSS"
223
+ ```
224
+
225
+ ## Support
226
+
227
+ - **Documentation**: Check individual provider directories for detailed setup guides
228
+ - **Issues**: Report bugs and feature requests on GitHub
229
+ - **Discord**: Join our community for help and discussions