janito 3.0.0__py3-none-any.whl → 3.2.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.
@@ -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
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: janito
3
- Version: 3.0.0
3
+ Version: 3.2.0
4
4
  Summary: A new Python package called janito.
5
5
  Author-email: João Pinto <janito@ikignosis.org>
6
6
  Project-URL: Homepage, https://github.com/ikignosis/janito
@@ -1,4 +1,4 @@
1
- janito/README.md,sha256=Kd4GcEYIt04520J2AIMCZbp1enAGRzlLswCfyi1g5AY,4737
1
+ janito/README.md,sha256=Wrt6pMVT9UaXsFpoE3m9hgn28OAHWhkZ_vwWTNcSH8Q,4947
2
2
  janito/__init__.py,sha256=a0pFui3A_AfWJiUfg93yE-Vf4868bqG3y9yg2fkTIuY,244
3
3
  janito/__main__.py,sha256=lPQ8kAyYfyeS1KopmJ8EVY5g1YswlIqCS615mM_B_rM,70
4
4
  janito/_version.py,sha256=PtAVr2K9fOS5sv6aXzmcb7UaR5NLGMFOofL7Ndjh75o,2344
@@ -30,17 +30,17 @@ janito/cli/config.py,sha256=HkZ14701HzIqrvaNyDcDhGlVHfpX_uHlLp2rHmhRm_k,872
30
30
  janito/cli/console.py,sha256=gJolqzWL7jEPLxeuH-CwBDRFpXt976KdZOEAB2tdBDs,64
31
31
  janito/cli/main.py,sha256=s5odou0txf8pzTf1ADk2yV7T5m8B6cejJ81e7iu776U,312
32
32
  janito/cli/main_cli.py,sha256=_OOQqeLiqvmYeB_928Av920Gk43rbXecMIOTL6JeT0Y,16674
33
- janito/cli/prompt_core.py,sha256=F68J4Xl6jZMYFN4oBBYZFj15Jp-HTYoLub4bw2XpNRU,11648
33
+ janito/cli/prompt_core.py,sha256=GSpq0_J-PS455SPkF-nCe9WtOQqseR08DWN7XhGIJXA,12044
34
34
  janito/cli/prompt_handler.py,sha256=SnPTlL64noeAMGlI08VBDD5IDD8jlVMIYA4-fS8zVLg,215
35
35
  janito/cli/prompt_setup.py,sha256=s48gvNfZhKjsEhf4EzL1tKIGm4wDidPMDvlM6TAPYes,2116
36
36
  janito/cli/rich_terminal_reporter.py,sha256=Hitf5U13gncad4GPVAcDMfdSwlfzQzOn9KdeX4TjTWU,6806
37
37
  janito/cli/utils.py,sha256=plCQiDKIf3V8mFhhX5H9-MF2W86i-xRdWf8Xi117Z0w,677
38
38
  janito/cli/verbose_output.py,sha256=wY_B4of5e8Vv7w1fRwOZzNGU2JqbMdcFnGjtEr4hLus,7686
39
- janito/cli/chat_mode/bindings.py,sha256=odjc5_-YW1t2FRhBUNRNoBMoQIg5sMz3ktV7xG0ADFU,975
39
+ janito/cli/chat_mode/bindings.py,sha256=vyaT5k454dSqxXPFWW0bOCHwBDCpTuxhcAu0AsDO-Gg,2013
40
40
  janito/cli/chat_mode/chat_entry.py,sha256=RFdPd23jsA2DMHRacpjAdwI_1dFBaWrtnwyQEgb2fHA,475
41
41
  janito/cli/chat_mode/prompt_style.py,sha256=vsqQ9xxmrYjj1pWuVe9CayQf39fo2EIXrkKPkflSVn4,805
42
42
  janito/cli/chat_mode/script_runner.py,sha256=WFTFVWzg_VQrD2Ujj02XWjscfGgHwmjBeRxaEjWw9ps,6505
43
- janito/cli/chat_mode/session.py,sha256=1mCET4V9u1FGEMnr8HJGOc6X8lhTNkhAYlZ3cvIvefw,18540
43
+ janito/cli/chat_mode/session.py,sha256=QYJ2wpA3IlB756ooFXTSkUVa5mOWgllAkONG3Dgqm8I,19002
44
44
  janito/cli/chat_mode/toolbar.py,sha256=SzdWAJdcY1g2rTPZCPL6G5X8jO6ZQYjwko2-nw54_nU,3397
45
45
  janito/cli/chat_mode/shell/autocomplete.py,sha256=lE68MaVaodbA2VfUM0_YLqQVLBJAE_BJsd5cMtwuD-g,793
46
46
  janito/cli/chat_mode/shell/commands.bak.zip,sha256=I7GFjXg2ORT5NzFpicH1vQ3kchhduQsZinzqo0xO8wU,74238
@@ -73,7 +73,7 @@ janito/cli/chat_mode/shell/commands/utility.py,sha256=K982P-UwgPLzpEvSuSBGwiQ3zC
73
73
  janito/cli/chat_mode/shell/commands/verbose.py,sha256=HDTe0NhdcjBuhh-eJSW8iLPDeeBO95K5iSDAMAljxa4,953
74
74
  janito/cli/chat_mode/shell/commands/write.py,sha256=NHPj2xe8icUBcpxALIoZZ1zAsX6bhBFqBhnQPqq7fHs,2341
75
75
  janito/cli/chat_mode/shell/commands/security/__init__.py,sha256=od-vkuI7LqmUqz2Q42YToWauRM6IPHGeNj14867yIgM,50
76
- janito/cli/chat_mode/shell/commands/security/allowed_sites.py,sha256=K_izmERZlumlTVxiTMvMXbMhADlJBvhyeeM5IGva_X8,3240
76
+ janito/cli/chat_mode/shell/commands/security/allowed_sites.py,sha256=pmyQnLD7Do_CaIITDlZ9Md7PqSpQAhRdnnVMMdQVFGk,3679
77
77
  janito/cli/chat_mode/shell/session/__init__.py,sha256=uTYE_QpZFEn7v9QE5o1LdulpCWa9vmk0OsefbBGWg_c,37
78
78
  janito/cli/chat_mode/shell/session/history.py,sha256=tYav6GgjAZkvWhlI_rfG6OArNqW6Wn2DTv39Hb20QYc,1262
79
79
  janito/cli/chat_mode/shell/session/manager.py,sha256=MwD9reHsRaly0CyRB-S1JJ0wPKz2g8Xdj2VvlU35Hgc,1001
@@ -96,7 +96,7 @@ janito/cli/cli_commands/show_system_prompt.py,sha256=WQclY_bmJrHbIBRU1qx1WV4Vyoo
96
96
  janito/cli/core/__init__.py,sha256=YH95fhgY9yBX8RgqX9dSrEkl4exjV0T4rbmJ6xUpG-Y,196
97
97
  janito/cli/core/event_logger.py,sha256=1X6lR0Ax7AgF8HlPWFoY5Ystuu7Bh4ooTo78vXzeGB0,2008
98
98
  janito/cli/core/getters.py,sha256=53umV7aab6WrmcCle8WqU2aYnww7a7IiqtM1YZN0YvE,3016
99
- janito/cli/core/model_guesser.py,sha256=V7LBkIllSp_tP9-2B1gcl5b4b-La7mrOvE3AZQQm8lk,1716
99
+ janito/cli/core/model_guesser.py,sha256=557c0FckGIVmPYCgp2hBNsjsmHAiORtoxvLTKnOk8kg,2082
100
100
  janito/cli/core/runner.py,sha256=gi8xke6re9AoHHNCivV50i0eUAliw8QTUdXyqMkMplM,9044
101
101
  janito/cli/core/setters.py,sha256=zjSUxy6iUzcrmEunFk7dA90KqbMaq2O7LTGP8wn2HxA,5378
102
102
  janito/cli/core/unsetters.py,sha256=FEw9gCt0vRvoCt0kRSNfVB2tzi_TqppJIx2nHPP59-k,2012
@@ -117,33 +117,35 @@ janito/event_bus/event.py,sha256=MtgcBPD7cvCuubiLIyo-BWcsNSO-941HLk6bScHTJtQ,427
117
117
  janito/event_bus/handler.py,sha256=RhBtT1E48VEM2-k8u3HYsESw7VX5qAgiB8ZTJeKXhHc,1322
118
118
  janito/event_bus/queue_bus.py,sha256=l4phun3pxXxi8ZlIq8ChYaiYDVO1PZeXoOzyi3vyu20,1558
119
119
  janito/i18n/__init__.py,sha256=6zCIu6pSQpoMJvIRK9oOD3pkzbNeJik3lFDXse0X6ag,994
120
- janito/i18n/it.py,sha256=jpWyCrwoZXxHaLEbmYL2B1ouOa854ecdCfpXq4mqroc,4052
120
+ janito/i18n/it.py,sha256=57xW4YE1kkMz8oNO5fBqK_YllWkHT97asaThDsGsBdM,4097
121
121
  janito/i18n/messages.py,sha256=fBuwOTFoygyHPkYphm6Y0r1iE8497Z4iryVAmPhMEkg,1851
122
122
  janito/i18n/pt.py,sha256=NlTgpDSftUfFG7FGbs7TK54vQlJVMyaZDHGcWjelwMc,4168
123
123
  janito/llm/README.md,sha256=6GRqCu_a9va5HCB1YqNqbshyWKFyAGlnXugrjom-xj8,1213
124
124
  janito/llm/__init__.py,sha256=dpyVH51qVRCw-PDyAFLAxq0zd4jl5MDcuV6Cri0D-dQ,134
125
- janito/llm/agent.py,sha256=T0JfeMoOudTWsHwWCcaocrHyq9k0TvkL4_YePlXvZfo,21269
125
+ janito/llm/agent.py,sha256=R3jzva-Er5W1TmHXHf9APxYB51H0z1vxOF6jVda7m7U,21940
126
126
  janito/llm/auth.py,sha256=8Dl_orUEPhn2X6XjkO2Nr-j1HFT2YDxk1qJl9hSFI88,2286
127
127
  janito/llm/auth_utils.py,sha256=mcPWT2p2kGMhvR9QsHqk84utDejPJh3uyjrDOJ0dKYA,1068
128
- janito/llm/driver.py,sha256=stiicPe_MXTuWW4q6MSwK7PCj8UZcA_30pGACu6xYUQ,10039
128
+ janito/llm/cancellation_manager.py,sha256=u-hzuuVjQmLV15YPp3cZdF8KmyQLoLv8P5ZTvgYWr7o,2194
129
+ janito/llm/driver.py,sha256=iVCYGUE5bqa_MTF6p_l2VUl_2y_-IOStNeC0IWfkA3I,10394
129
130
  janito/llm/driver_config.py,sha256=OW0ae49EfgKDqaThuDjZBiaN78voNzwiZ6szERMqJos,1406
130
131
  janito/llm/driver_config_builder.py,sha256=BvWGx7vaBR5NyvPY1XNAP3lAgo1uf-T25CSsIo2kkCU,1401
131
132
  janito/llm/driver_input.py,sha256=Zq7IO4KdQPUraeIo6XoOaRy1IdQAyYY15RQw4JU30uA,389
133
+ janito/llm/enter_cancellation.py,sha256=sE6yadsFGnKDQVBKenv1sF96wIcAP2LqymiUFJBlijU,3312
132
134
  janito/llm/message_parts.py,sha256=QY_0kDjaxdoErDgKPRPv1dNkkYJuXIBmHWNLiOEKAH4,1365
133
135
  janito/llm/model.py,sha256=EioBkdgn8hJ0iQaKN-0KbXlsrk3YKmwR9IbvoEbdVTE,1159
134
136
  janito/llm/provider.py,sha256=3FbhQPrWBSEoIdIi-5DWIh0DD_CM570EFf1NcuGyGko,7961
135
137
  janito/plugin_system/__init__.py,sha256=HoTBUooSTEsp_1FaBn-5BmzZX_p_982e4T_fCOeUlD0,290
136
138
  janito/plugin_system/base.py,sha256=Z0PSIj7R-hAKOeCSXC2JyBqm4rlsJoZSqkqxLe-juks,4346
137
- janito/plugin_system/core_loader.py,sha256=LPOJRQwSqjZ8P2G6sh5lN4enA8hc2An-kO4dgr8e0as,4361
138
- janito/plugin_system/core_loader_fixed.py,sha256=VQsEIucX-NFjAnimzzAtK1o3Y4R9gq_a9IuxSrhJZxU,4586
139
+ janito/plugin_system/core_loader.py,sha256=3zhKPWWSFlThzaRTLjGDPI_DVNRafMP5LZ5r7ei6Jo0,6843
140
+ janito/plugin_system/core_loader_fixed.py,sha256=3JZnEkM4i0x4k3SJhomtVevmNl4uWhozhVyh6rcMtvE,7159
139
141
  janito/plugins/__init__.py,sha256=q-z28oV-0BaagZ43f__qeg13R9oA7kK7XnVEUHNRZbs,910
140
- janito/plugins/auto_loader.py,sha256=8DRpPZtAUtP-et4Lzaam93MH4s_sqQXXKI8Ly5-GQf8,2329
142
+ janito/plugins/auto_loader.py,sha256=-uVDTKQToGCw1nqwAxrt4IDvgP3z3pvZoC5EXFWNm_k,2288
141
143
  janito/plugins/auto_loader_fixed.py,sha256=3JnGDp1enombEHsR8spRDpVrD6tSPdudysWvhyuk5Ec,2304
142
144
  janito/plugins/builtin.py,sha256=kbIOVmqucJKyISC5kWcgda_6IhHGA6yWoxUghc1Qtk0,3409
143
145
  janito/plugins/config.py,sha256=ex5f1XsKPEgIO4DgJCohPzMKAQoUdx1OdSVU0FlSr5o,2331
144
- janito/plugins/core_adapter.py,sha256=WfFDb_DzG2gqNYlwlAaXVsh4VjIRZ5VPszhQFg0IXZs,1857
146
+ janito/plugins/core_adapter.py,sha256=tFi9VYvb15n3yUGDF7ihALbjL7pz_PDeeEgu_3JFqTQ,4430
145
147
  janito/plugins/discovery.py,sha256=IGQAFPzzzZjlpMzPNdChD7ZJrCYN8QmvXPsvLBOSSjY,10120
146
- janito/plugins/discovery_core.py,sha256=O5OccNWNyrSXGeeTk5xSG0qbsjNsajclNNCiHljFRpI,1613
148
+ janito/plugins/discovery_core.py,sha256=3pL8kinyFuz1owqIpZDYYIvj3RrJVJHulUa36Aqsc6g,1617
147
149
  janito/plugins/example_plugin.py,sha256=1B0PYOSFsb2ya2VCwHr1pMJC9ivUJ7t9TtQkc8lkaGU,3136
148
150
  janito/plugins/manager.py,sha256=R1B-2sUD5YFLOTnp4Pfx1wZ-3Hzff--PqaOJHfllc5g,8155
149
151
  janito/plugins/core/__init__.py,sha256=74-nOHG97Jo7cSm_sOLwYYhS0maB1kgKdB69-bmi03k,144
@@ -162,7 +164,7 @@ janito/plugins/core/codeanalyzer/tools/search_text/traverse_directory.py,sha256=
162
164
  janito/plugins/core/filemanager/__init__.py,sha256=waUhGQ2OL59Ooes4cQOOlQdBsFQSIffevJkcZq_kaCQ,3241
163
165
  janito/plugins/core/filemanager/tools/copy_file.py,sha256=SBJm19Ipe5dqRE1Mxl6JSrn4bNmfObVnDr5b1mcEu6c,3682
164
166
  janito/plugins/core/filemanager/tools/create_directory.py,sha256=LxwqQEsnOrEphCIoaMRRx9P9bu0MzidP3Fc5q6letxc,2584
165
- janito/plugins/core/filemanager/tools/create_file.py,sha256=nZf8iPScO9_nrvmHwXqOcqpLZkLABTh9uLVNddC4PCk,3760
167
+ janito/plugins/core/filemanager/tools/create_file.py,sha256=MDMTt3ocp1Ejo3vo7K5Ht2cMPObXiQTr5OTtLv9qOuw,3821
166
168
  janito/plugins/core/filemanager/tools/delete_text_in_file.py,sha256=uEeedRxXAR7_CqUc_qhbEdM0OzRi_pgnP-iDjs2Zvjk,5087
167
169
  janito/plugins/core/filemanager/tools/find_files.py,sha256=Zbag3aP34vc7ffJh8bOqAwXj3KiZhV--uzTVHtNb-fI,6250
168
170
  janito/plugins/core/filemanager/tools/move_file.py,sha256=LMGm8bn3NNyIPJG4vrlO09smXQcgzA09EwoooZxkIA8,4695
@@ -201,9 +203,9 @@ janito/plugins/dev/visualization/tools/read_chart.py,sha256=qQebp_MEE_x2AL_pl85u
201
203
  janito/plugins/tools/__init__.py,sha256=vNqUVWA0guwAYuPgdmiDsjmgibj9siP0e61pfTHZ8-8,271
202
204
  janito/plugins/tools/ask_user.py,sha256=7Wgl_9XEPWty3qTG_xcQdwJWypXRorMv3WYM_AZDrBY,4050
203
205
  janito/plugins/tools/copy_file.py,sha256=A0LxkN9b4eDBD7Ww_4LO412kldbx-DlrUELUB-27jOk,3672
204
- janito/plugins/tools/core_tools_plugin.py,sha256=J186ZSA0FjZtAmzBwGYSrE7SFmO8lUEOCZ3apDg4RBA,2685
206
+ janito/plugins/tools/core_tools_plugin.py,sha256=QgRE3KcbF-KgRR5y-ADYeuTTLDlenxuKM-68wliVfbs,2683
205
207
  janito/plugins/tools/create_directory.py,sha256=f8fccqwRR3xj_IJ7KZacsxBo2k754bLgVyAefC4jsnI,2574
206
- janito/plugins/tools/create_file.py,sha256=UaQbKUFl8LXMfNq1UlMjgdOo8b2LCC5EsgDVyGwk6fE,6368
208
+ janito/plugins/tools/create_file.py,sha256=8yv1Mkxsg9KCE7o8B2e0fQdvqMcmBHohRV2z5AQ8fIs,6417
207
209
  janito/plugins/tools/decorators.py,sha256=BRAjjo4NF4_JxwPPVrrF2wtzsJXuZJkwv_QhZgPFMS4,382
208
210
  janito/plugins/tools/delete_text_in_file.py,sha256=nZb-QuJopA3NDfOW3BEsG6Aj9EaMlfpuEYiG9aGp4z4,5070
209
211
  janito/plugins/tools/fetch_url.py,sha256=hqtllaqx5rgZ-5wAih6-R1tvX5sIFUHy3DTP68JImQM,18202
@@ -255,7 +257,7 @@ janito/plugins/web/webtools/__init__.py,sha256=vGFtbZvV2DnooPmw4B8hHd0UgpVfNugLs
255
257
  janito/plugins/web/webtools/tools/fetch_url.py,sha256=VjB89FjLNrqLmOVL6wm1RHyixB3n-RViC0DJjW33iRM,17804
256
258
  janito/plugins/web/webtools/tools/open_html_in_browser.py,sha256=XqICIwVx5vEE77gHkaNAC-bAeEEy0DBmDksATiL-sRY,2101
257
259
  janito/plugins/web/webtools/tools/open_url.py,sha256=V3Sv7iLynUreLjVl5-bl-XFH_LzpTeBrS8-cvzp_7rM,1552
258
- janito/providers/__init__.py,sha256=EvOFeiqucAY9tgCosJ81p0QA6wQwMT1cR3EeIGrhSQQ,528
260
+ janito/providers/__init__.py,sha256=LZcyllhmGyOXQ8duQA2Qbg0fSrahog5ATNZppNOxFK0,571
259
261
  janito/providers/dashscope.bak.zip,sha256=BwXxRmZreEivvRtmqbr5BR62IFVlNjAf4y6DrF2BVJo,5998
260
262
  janito/providers/registry.py,sha256=Ygwv9eVrTXOKhv0EKxSWQXO5WMHvajWE2Q_Lc3p7dKo,730
261
263
  janito/providers/alibaba/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -288,6 +290,9 @@ janito/providers/openai/__init__.py,sha256=f0m16-sIqScjL9Mp4A0CQBZx6H3PTEy0cnE08
288
290
  janito/providers/openai/model_info.py,sha256=VTkq3xcx2vk0tXlFVHQxKeFzl-DL1T1J2elVOEwCdHI,4265
289
291
  janito/providers/openai/provider.py,sha256=PPr_qmSe5GyysnZCxhjeUVhE2LWKjKOSRel-8aaxq_U,4761
290
292
  janito/providers/openai/schema_generator.py,sha256=hTqeLcPTR8jeKn5DUUpo7b-EZ-V-g1WwXiX7MbHnFzE,2234
293
+ janito/providers/together/__init__.py,sha256=8JguGMgLW_QUJ-G6VIypp_Uq6avi3LGwKWpEiyGRWOU,33
294
+ janito/providers/together/model_info.py,sha256=4EsluZgPfZW68Uc9o5vDnUmo-hQAGIs5eTDWuR24kPg,2407
295
+ janito/providers/together/provider.py,sha256=BiM8p8rlqPem_PHdOyXq08J3LZFrAKtP7PDqDSToLdI,3847
291
296
  janito/providers/zai/__init__.py,sha256=qtIr9_QBFaXG8xB6cRDGhS7se6ir11CWseI9azLMRBo,24
292
297
  janito/providers/zai/model_info.py,sha256=ldwD8enpxXv1G-YsDw4YJn31YsVueQ4vj5HgoYvnPxo,1183
293
298
  janito/providers/zai/provider.py,sha256=aKqDTdcwWk-FT70wTbTrTzlWx2NwRWkLoB64upiliU8,5066
@@ -302,11 +307,10 @@ janito/tools/__init__.py,sha256=PoMIw_JgsEGP5SO77mqsZFl0Z-p2W2WnyHaaAKSnirk,1961
302
307
  janito/tools/base.py,sha256=R38A9xWYh3JRYZMDSom2d1taNDy9J7HpLbZo9X2wH_o,316
303
308
  janito/tools/cli_initializer.py,sha256=KJbUL6_iAsaeBZl_AVrdtczSZRQBJ9RPZufHJVKwAtU,2632
304
309
  janito/tools/disabled_tools.py,sha256=Tx__16wtMWZ9z34cYLdH1gukwot5MCL-9kLjd5MPX6Y,2110
305
- janito/tools/function_adapter.py,sha256=kjU15jy2gLmKw__BRFe99lRrLCXoHdcgVMFf2W4LEw0,4870
306
310
  janito/tools/initialize.py,sha256=BSwzCPCmis2n4q5W3tWBEr4MgVS4tAwBjfKoNM4CefI,2002
307
311
  janito/tools/inspect_registry.py,sha256=Jo7PrMPRKLuR-j_mBAk9PBcTzeJf1eQrS1ChGofgQk0,538
308
312
  janito/tools/loop_protection.py,sha256=WQ2Cqt459vXvrO0T1EqkEHynHlRkPzfaC83RSmXzjkM,4718
309
- janito/tools/loop_protection_decorator.py,sha256=R1j2ouscKbVcDm2wlxRZ6zQuKExgj633ijeDq4j0oO0,6457
313
+ janito/tools/loop_protection_decorator.py,sha256=ZCccrg24B_titDeOhaEORPq8wnRyLf1bdb8YEHK25lo,4957
310
314
  janito/tools/outline_file.bak.zip,sha256=EeI2cBXCwTdWVgJDNiroxKeYlkjwo6NLKeXz3J-2iZI,15607
311
315
  janito/tools/path_security.py,sha256=40b0hV0X3449Dht93A04Q3c9AYSsBQsBFy2BjzM83lA,8214
312
316
  janito/tools/path_utils.py,sha256=Rg5GE4kiu7rky6I2KTtivW6wPXzc9Qmq0_lOjwkPYlI,832
@@ -323,9 +327,9 @@ janito/tools/url_whitelist.py,sha256=0CPLkHTp5HgnwgjxwgXnJmwPeZQ30q4j3YjW59hiUUE
323
327
  janito/tools/adapters/__init__.py,sha256=H25uYM2ETMLKpKPPEPAu9-AFjxkKfSyfx3pnoXSQlVA,255
324
328
  janito/tools/adapters/local/__init__.py,sha256=1DVnka4iEQp8Xrs7rJVVx45fPpuVahjTFmuJhv-gN8s,249
325
329
  janito/tools/adapters/local/adapter.py,sha256=u4nLHTaYdwZXMi1J8lsKvlG6rOmdq9xjey_3zeyCG4k,8707
326
- janito-3.0.0.dist-info/licenses/LICENSE,sha256=dXV4fOF2ZErugtN8l_Nrj5tsRTYgtjE3cgiya0UfBio,11356
327
- janito-3.0.0.dist-info/METADATA,sha256=JLYLflkFEBonYah38ghiqq0OP4HzA1wYCNyXSQ0FRC4,2265
328
- janito-3.0.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
329
- janito-3.0.0.dist-info/entry_points.txt,sha256=wIo5zZxbmu4fC-ZMrsKD0T0vq7IqkOOLYhrqRGypkx4,48
330
- janito-3.0.0.dist-info/top_level.txt,sha256=m0NaVCq0-ivxbazE2-ND0EA9Hmuijj_OGkmCbnBcCig,7
331
- janito-3.0.0.dist-info/RECORD,,
330
+ janito-3.2.0.dist-info/licenses/LICENSE,sha256=dXV4fOF2ZErugtN8l_Nrj5tsRTYgtjE3cgiya0UfBio,11356
331
+ janito-3.2.0.dist-info/METADATA,sha256=FgU9kTrodtpQrm-Ky5X_IArx59u80QV--sW2HHTmw1M,2265
332
+ janito-3.2.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
333
+ janito-3.2.0.dist-info/entry_points.txt,sha256=wIo5zZxbmu4fC-ZMrsKD0T0vq7IqkOOLYhrqRGypkx4,48
334
+ janito-3.2.0.dist-info/top_level.txt,sha256=m0NaVCq0-ivxbazE2-ND0EA9Hmuijj_OGkmCbnBcCig,7
335
+ janito-3.2.0.dist-info/RECORD,,
@@ -1,142 +0,0 @@
1
- """
2
- Function-to-Tool adapter for core plugins.
3
-
4
- This module provides a way to wrap function-based tools into proper ToolBase classes.
5
- """
6
-
7
- import inspect
8
- from typing import Any, Dict, List, Optional, get_type_hints
9
- from janito.tools.tool_base import ToolBase, ToolPermissions
10
-
11
-
12
- class FunctionToolAdapter(ToolBase):
13
- """Adapter that wraps a function into a ToolBase class."""
14
-
15
- def __init__(self, func, tool_name: str = None, description: str = None):
16
- super().__init__()
17
- self._func = func
18
- self.tool_name = tool_name or getattr(func, "tool_name", func.__name__)
19
- self._description = description or func.__doc__ or f"Tool: {self.tool_name}"
20
- self.permissions = ToolPermissions(read=True, write=True, execute=True)
21
-
22
- def run(
23
- self,
24
- path: str = None,
25
- content: str = None,
26
- overwrite: bool = None,
27
- sources: str = None,
28
- target: str = None,
29
- recursive: bool = None,
30
- from_line: int = None,
31
- to_line: int = None,
32
- search_text: str = None,
33
- replacement_text: str = None,
34
- use_regex: bool = None,
35
- case_sensitive: bool = None,
36
- max_depth: int = None,
37
- include_gitignored: bool = None,
38
- timeout: int = None,
39
- require_confirmation: bool = None,
40
- data: dict = None,
41
- title: str = None,
42
- width: int = None,
43
- height: int = None,
44
- query: str = None,
45
- paths: str = None,
46
- src_path: str = None,
47
- dest_path: str = None,
48
- code: str = None,
49
- pattern: str = None,
50
- ) -> str:
51
- """Execute the wrapped function."""
52
- # Build kwargs from non-None parameters
53
- import inspect
54
-
55
- sig = inspect.signature(self._func)
56
- filtered_kwargs = {}
57
-
58
- # Map parameter names to their actual values
59
- param_map = {
60
- "path": path,
61
- "content": content,
62
- "overwrite": overwrite,
63
- "sources": sources,
64
- "target": target,
65
- "recursive": recursive,
66
- "from_line": from_line,
67
- "to_line": to_line,
68
- "search_text": search_text,
69
- "replacement_text": replacement_text,
70
- "use_regex": use_regex,
71
- "case_sensitive": case_sensitive,
72
- "max_depth": max_depth,
73
- "include_gitignored": include_gitignored,
74
- "timeout": timeout,
75
- "require_confirmation": require_confirmation,
76
- "data": data,
77
- "title": title,
78
- "width": width,
79
- "height": height,
80
- "query": query,
81
- "paths": paths,
82
- "src_path": src_path,
83
- "dest_path": dest_path,
84
- "code": code,
85
- "pattern": pattern,
86
- }
87
-
88
- # Only include parameters that exist in the function signature
89
- for name, param in sig.parameters.items():
90
- if name in param_map and param_map[name] is not None:
91
- filtered_kwargs[name] = param_map[name]
92
-
93
- result = self._func(**filtered_kwargs)
94
- return str(result) if result is not None else ""
95
-
96
- def get_signature(self) -> Dict[str, Any]:
97
- """Get function signature for documentation."""
98
- sig = inspect.signature(self._func)
99
- type_hints = get_type_hints(self._func)
100
-
101
- params = {}
102
- for name, param in sig.parameters.items():
103
- param_info = {
104
- "type": str(type_hints.get(name, Any)),
105
- "default": (
106
- param.default if param.default != inspect.Parameter.empty else None
107
- ),
108
- "required": param.default == inspect.Parameter.empty,
109
- }
110
- params[name] = param_info
111
-
112
- return {
113
- "name": self.tool_name,
114
- "description": self._description,
115
- "parameters": params,
116
- "return_type": str(type_hints.get("return", Any)),
117
- }
118
-
119
-
120
- def create_function_tool(func, tool_name: str = None, description: str = None) -> type:
121
- """
122
- Create a ToolBase class from a function.
123
-
124
- Args:
125
- func: The function to wrap
126
- tool_name: Optional custom tool name
127
- description: Optional custom description
128
-
129
- Returns:
130
- A ToolBase subclass that wraps the function
131
- """
132
- # Resolve tool_name in outer scope
133
- resolved_tool_name = tool_name or getattr(func, "tool_name", func.__name__)
134
-
135
- class DynamicFunctionTool(FunctionToolAdapter):
136
- permissions = ToolPermissions(read=True, write=True, execute=True)
137
- tool_name = resolved_tool_name
138
-
139
- def __init__(self):
140
- super().__init__(func, DynamicFunctionTool.tool_name, description)
141
-
142
- return DynamicFunctionTool
File without changes