janito 2.3.0__py3-none-any.whl → 2.4.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.
- janito/__init__.py +6 -6
- janito/_version.py +57 -0
- janito/agent/setup_agent.py +92 -18
- janito/agent/templates/profiles/system_prompt_template_developer.txt.j2 +44 -0
- janito/cli/chat_mode/bindings.py +21 -2
- janito/cli/chat_mode/chat_entry.py +2 -3
- janito/cli/chat_mode/prompt_style.py +5 -0
- janito/cli/chat_mode/session.py +80 -94
- janito/cli/chat_mode/session_profile_select.py +80 -0
- janito/cli/chat_mode/shell/autocomplete.py +21 -21
- janito/cli/chat_mode/shell/commands/__init__.py +13 -7
- janito/cli/chat_mode/shell/commands/_priv_check.py +5 -0
- janito/cli/chat_mode/shell/commands/clear.py +12 -12
- janito/cli/chat_mode/shell/commands/conversation_restart.py +30 -0
- janito/cli/chat_mode/shell/commands/execute.py +42 -0
- janito/cli/chat_mode/shell/commands/help.py +6 -3
- janito/cli/chat_mode/shell/commands/model.py +28 -0
- janito/cli/chat_mode/shell/commands/multi.py +51 -51
- janito/cli/chat_mode/shell/commands/read.py +37 -0
- janito/cli/chat_mode/shell/commands/tools.py +45 -18
- janito/cli/chat_mode/shell/commands/write.py +37 -0
- janito/cli/chat_mode/shell/commands.bak.zip +0 -0
- janito/cli/chat_mode/shell/input_history.py +62 -62
- janito/cli/chat_mode/shell/session.bak.zip +0 -0
- janito/cli/chat_mode/toolbar.py +44 -27
- janito/cli/cli_commands/list_models.py +35 -35
- janito/cli/cli_commands/list_providers.py +9 -9
- janito/cli/cli_commands/list_tools.py +86 -53
- janito/cli/cli_commands/model_selection.py +50 -50
- janito/cli/cli_commands/set_api_key.py +19 -19
- janito/cli/cli_commands/show_config.py +51 -51
- janito/cli/cli_commands/show_system_prompt.py +105 -62
- janito/cli/config.py +5 -6
- janito/cli/core/__init__.py +4 -4
- janito/cli/core/event_logger.py +59 -59
- janito/cli/core/runner.py +25 -18
- janito/cli/core/setters.py +10 -1
- janito/cli/core/unsetters.py +54 -54
- janito/cli/main_cli.py +28 -5
- janito/cli/prompt_core.py +18 -2
- janito/cli/prompt_setup.py +56 -0
- janito/cli/single_shot_mode/__init__.py +6 -6
- janito/cli/single_shot_mode/handler.py +14 -73
- janito/cli/verbose_output.py +1 -1
- janito/config.py +5 -5
- janito/config_manager.py +13 -0
- janito/drivers/anthropic/driver.py +113 -113
- janito/drivers/dashscope.bak.zip +0 -0
- janito/drivers/openai/README.md +20 -0
- janito/drivers/openai_responses.bak.zip +0 -0
- janito/event_bus/event.py +2 -2
- janito/formatting_token.py +54 -54
- janito/i18n/__init__.py +35 -35
- janito/i18n/messages.py +23 -23
- janito/i18n/pt.py +46 -47
- janito/llm/README.md +23 -0
- janito/llm/__init__.py +5 -5
- janito/llm/agent.py +507 -443
- janito/llm/driver.py +8 -0
- janito/llm/driver_config_builder.py +34 -34
- janito/llm/driver_input.py +12 -12
- janito/llm/message_parts.py +60 -60
- janito/llm/model.py +38 -38
- janito/llm/provider.py +196 -196
- janito/provider_registry.py +8 -6
- janito/providers/anthropic/model_info.py +22 -22
- janito/providers/anthropic/provider.py +2 -0
- janito/providers/azure_openai/provider.py +3 -0
- janito/providers/dashscope.bak.zip +0 -0
- janito/providers/deepseek/__init__.py +1 -1
- janito/providers/deepseek/model_info.py +16 -16
- janito/providers/deepseek/provider.py +94 -91
- janito/providers/google/provider.py +3 -0
- janito/providers/mistralai/provider.py +3 -0
- janito/providers/openai/provider.py +4 -0
- janito/providers/registry.py +26 -26
- janito/shell.bak.zip +0 -0
- janito/tools/DOCSTRING_STANDARD.txt +33 -0
- janito/tools/README.md +3 -0
- janito/tools/__init__.py +20 -6
- janito/tools/adapters/__init__.py +1 -1
- janito/tools/adapters/local/__init__.py +65 -62
- janito/tools/adapters/local/adapter.py +18 -35
- janito/tools/adapters/local/ask_user.py +101 -102
- janito/tools/adapters/local/copy_file.py +84 -84
- janito/tools/adapters/local/create_directory.py +69 -69
- janito/tools/adapters/local/create_file.py +82 -82
- janito/tools/adapters/local/delete_text_in_file.py +2 -2
- janito/tools/adapters/local/fetch_url.py +97 -97
- janito/tools/adapters/local/find_files.py +139 -138
- janito/tools/adapters/local/get_file_outline/__init__.py +1 -1
- janito/tools/adapters/local/get_file_outline/core.py +117 -117
- janito/tools/adapters/local/get_file_outline/java_outline.py +40 -40
- janito/tools/adapters/local/get_file_outline/markdown_outline.py +14 -14
- janito/tools/adapters/local/get_file_outline/python_outline.py +303 -303
- janito/tools/adapters/local/get_file_outline/python_outline_v2.py +156 -156
- janito/tools/adapters/local/get_file_outline/search_outline.py +33 -33
- janito/tools/adapters/local/move_file.py +2 -2
- janito/tools/adapters/local/open_html_in_browser.py +2 -1
- janito/tools/adapters/local/open_url.py +2 -2
- janito/tools/adapters/local/python_code_run.py +166 -166
- janito/tools/adapters/local/python_command_run.py +164 -164
- janito/tools/adapters/local/python_file_run.py +163 -163
- janito/tools/adapters/local/remove_directory.py +2 -2
- janito/tools/adapters/local/remove_file.py +2 -2
- janito/tools/adapters/local/replace_text_in_file.py +2 -2
- janito/tools/adapters/local/run_bash_command.py +176 -176
- janito/tools/adapters/local/run_powershell_command.py +219 -219
- janito/tools/adapters/local/search_text/__init__.py +1 -1
- janito/tools/adapters/local/search_text/core.py +201 -201
- janito/tools/adapters/local/search_text/pattern_utils.py +73 -73
- janito/tools/adapters/local/search_text/traverse_directory.py +145 -145
- janito/tools/adapters/local/validate_file_syntax/__init__.py +1 -1
- janito/tools/adapters/local/validate_file_syntax/core.py +106 -106
- janito/tools/adapters/local/validate_file_syntax/css_validator.py +35 -35
- janito/tools/adapters/local/validate_file_syntax/html_validator.py +93 -93
- janito/tools/adapters/local/validate_file_syntax/js_validator.py +27 -27
- janito/tools/adapters/local/validate_file_syntax/json_validator.py +6 -6
- janito/tools/adapters/local/validate_file_syntax/markdown_validator.py +109 -109
- janito/tools/adapters/local/validate_file_syntax/ps1_validator.py +32 -32
- janito/tools/adapters/local/validate_file_syntax/python_validator.py +5 -5
- janito/tools/adapters/local/validate_file_syntax/xml_validator.py +11 -11
- janito/tools/adapters/local/validate_file_syntax/yaml_validator.py +6 -6
- janito/tools/adapters/local/view_file.py +168 -167
- janito/tools/inspect_registry.py +17 -17
- janito/tools/outline_file.bak.zip +0 -0
- janito/tools/permissions.py +45 -0
- janito/tools/permissions_parse.py +12 -0
- janito/tools/tool_base.py +118 -105
- janito/tools/tool_events.py +58 -58
- janito/tools/tool_run_exception.py +12 -12
- janito/tools/tool_use_tracker.py +81 -81
- janito/tools/tool_utils.py +43 -45
- janito/tools/tools_adapter.py +25 -20
- janito/tools/tools_schema.py +104 -104
- {janito-2.3.0.dist-info → janito-2.4.0.dist-info}/METADATA +425 -388
- janito-2.4.0.dist-info/RECORD +195 -0
- janito/agent/templates/profiles/system_prompt_template_base_pt.txt.j2 +0 -13
- janito/agent/templates/profiles/system_prompt_template_main.txt.j2 +0 -37
- janito/cli/chat_mode/shell/commands/edit.py +0 -25
- janito/cli/chat_mode/shell/commands/exec.py +0 -27
- janito/cli/chat_mode/shell/commands/termweb_log.py +0 -92
- janito/cli/termweb_starter.py +0 -122
- janito/termweb/app.py +0 -95
- janito/version.py +0 -4
- janito-2.3.0.dist-info/RECORD +0 -181
- {janito-2.3.0.dist-info → janito-2.4.0.dist-info}/WHEEL +0 -0
- {janito-2.3.0.dist-info → janito-2.4.0.dist-info}/entry_points.txt +0 -0
- {janito-2.3.0.dist-info → janito-2.4.0.dist-info}/licenses/LICENSE +0 -0
- {janito-2.3.0.dist-info → janito-2.4.0.dist-info}/top_level.txt +0 -0
janito/tools/tool_base.py
CHANGED
@@ -1,105 +1,118 @@
|
|
1
|
-
from janito.report_events import ReportEvent, ReportSubtype, ReportAction
|
2
|
-
from janito.event_bus.bus import event_bus as default_event_bus
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
def
|
13
|
-
self.
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
1
|
+
from janito.report_events import ReportEvent, ReportSubtype, ReportAction
|
2
|
+
from janito.event_bus.bus import event_bus as default_event_bus
|
3
|
+
|
4
|
+
|
5
|
+
from collections import namedtuple
|
6
|
+
|
7
|
+
class ToolPermissions(namedtuple('ToolPermissions', ['read', 'write', 'execute'])):
|
8
|
+
__slots__ = ()
|
9
|
+
def __new__(cls, read=False, write=False, execute=False):
|
10
|
+
return super().__new__(cls, read, write, execute)
|
11
|
+
|
12
|
+
def __repr__(self):
|
13
|
+
return f"ToolPermissions(read={self.read}, write={self.write}, execute={self.execute})"
|
14
|
+
|
15
|
+
|
16
|
+
class ToolBase:
|
17
|
+
"""
|
18
|
+
Base class for all tools in the janito project.
|
19
|
+
Extend this class to implement specific tool functionality.
|
20
|
+
"""
|
21
|
+
permissions: 'ToolPermissions' = None # Required: must be set by subclasses
|
22
|
+
|
23
|
+
def __init__(self, name=None, event_bus=None):
|
24
|
+
if self.permissions is None or not isinstance(self.permissions, ToolPermissions):
|
25
|
+
raise ValueError(f"Tool '{self.__class__.__name__}' must define a 'permissions' attribute of type ToolPermissions.")
|
26
|
+
self.name = name or self.__class__.__name__
|
27
|
+
self._event_bus = event_bus or default_event_bus
|
28
|
+
|
29
|
+
@property
|
30
|
+
def event_bus(self):
|
31
|
+
return self._event_bus
|
32
|
+
|
33
|
+
@event_bus.setter
|
34
|
+
def event_bus(self, bus):
|
35
|
+
self._event_bus = bus or default_event_bus
|
36
|
+
|
37
|
+
def report_action(self, message: str, action: ReportAction, context: dict = None):
|
38
|
+
"""
|
39
|
+
Report that a tool action is starting. This should be the first reporting call for every tool action.
|
40
|
+
"""
|
41
|
+
self._event_bus.publish(
|
42
|
+
ReportEvent(
|
43
|
+
subtype=ReportSubtype.ACTION_INFO,
|
44
|
+
message=" " + message,
|
45
|
+
action=action,
|
46
|
+
tool=self.name,
|
47
|
+
context=context,
|
48
|
+
)
|
49
|
+
)
|
50
|
+
|
51
|
+
def report_info(self, message: str, context: dict = None):
|
52
|
+
self._event_bus.publish(
|
53
|
+
ReportEvent(
|
54
|
+
subtype=ReportSubtype.ACTION_INFO,
|
55
|
+
message=message,
|
56
|
+
action=None,
|
57
|
+
tool=self.name,
|
58
|
+
context=context,
|
59
|
+
)
|
60
|
+
)
|
61
|
+
|
62
|
+
def report_error(self, message: str, context: dict = None):
|
63
|
+
self._event_bus.publish(
|
64
|
+
ReportEvent(
|
65
|
+
subtype=ReportSubtype.ERROR,
|
66
|
+
message=message,
|
67
|
+
action=None,
|
68
|
+
tool=self.name,
|
69
|
+
context=context,
|
70
|
+
)
|
71
|
+
)
|
72
|
+
|
73
|
+
def report_success(self, message: str, context: dict = None):
|
74
|
+
self._event_bus.publish(
|
75
|
+
ReportEvent(
|
76
|
+
subtype=ReportSubtype.SUCCESS,
|
77
|
+
message=message,
|
78
|
+
action=None,
|
79
|
+
tool=self.name,
|
80
|
+
context=context,
|
81
|
+
)
|
82
|
+
)
|
83
|
+
|
84
|
+
def report_warning(self, message: str, context: dict = None):
|
85
|
+
self._event_bus.publish(
|
86
|
+
ReportEvent(
|
87
|
+
subtype=ReportSubtype.WARNING,
|
88
|
+
message=message,
|
89
|
+
action=None,
|
90
|
+
tool=self.name,
|
91
|
+
context=context,
|
92
|
+
)
|
93
|
+
)
|
94
|
+
|
95
|
+
def report_stdout(self, message: str, context: dict = None):
|
96
|
+
self._event_bus.publish(
|
97
|
+
ReportEvent(
|
98
|
+
subtype=ReportSubtype.STDOUT,
|
99
|
+
message=message,
|
100
|
+
action=None,
|
101
|
+
tool=self.name,
|
102
|
+
context=context,
|
103
|
+
)
|
104
|
+
)
|
105
|
+
|
106
|
+
def report_stderr(self, message: str, context: dict = None):
|
107
|
+
self._event_bus.publish(
|
108
|
+
ReportEvent(
|
109
|
+
subtype=ReportSubtype.STDERR,
|
110
|
+
message=message,
|
111
|
+
action=None,
|
112
|
+
tool=self.name,
|
113
|
+
context=context,
|
114
|
+
)
|
115
|
+
)
|
116
|
+
|
117
|
+
def run(self, *args, **kwargs):
|
118
|
+
raise NotImplementedError("Subclasses must implement the run method.")
|
janito/tools/tool_events.py
CHANGED
@@ -1,58 +1,58 @@
|
|
1
|
-
import attr
|
2
|
-
from typing import Any, ClassVar
|
3
|
-
from janito.event_bus.event import Event
|
4
|
-
|
5
|
-
|
6
|
-
@attr.s(auto_attribs=True, kw_only=True)
|
7
|
-
class ToolEvent(Event):
|
8
|
-
"""
|
9
|
-
Base class for events related to tool calls (external or internal tools).
|
10
|
-
Includes tool name and request ID for correlation.
|
11
|
-
"""
|
12
|
-
|
13
|
-
category: ClassVar[str] = "tool"
|
14
|
-
tool_name: str
|
15
|
-
request_id: str
|
16
|
-
|
17
|
-
|
18
|
-
@attr.s(auto_attribs=True, kw_only=True)
|
19
|
-
class ToolCallStarted(ToolEvent):
|
20
|
-
"""
|
21
|
-
Event indicating that a tool call has started.
|
22
|
-
Contains the arguments passed to the tool.
|
23
|
-
"""
|
24
|
-
|
25
|
-
arguments: Any
|
26
|
-
|
27
|
-
|
28
|
-
@attr.s(auto_attribs=True, kw_only=True)
|
29
|
-
class ToolCallFinished(ToolEvent):
|
30
|
-
"""
|
31
|
-
Event indicating that a tool call has finished.
|
32
|
-
Contains the result returned by the tool.
|
33
|
-
"""
|
34
|
-
|
35
|
-
result: Any
|
36
|
-
|
37
|
-
|
38
|
-
@attr.s(auto_attribs=True, kw_only=True)
|
39
|
-
class ToolRunError(ToolEvent):
|
40
|
-
"""
|
41
|
-
Event indicating that an error occurred during tool execution (for event bus, not exception handling).
|
42
|
-
"""
|
43
|
-
|
44
|
-
error: str
|
45
|
-
exception: Exception = None
|
46
|
-
arguments: Any = None
|
47
|
-
|
48
|
-
|
49
|
-
@attr.s(auto_attribs=True, kw_only=True)
|
50
|
-
class ToolCallError(ToolEvent):
|
51
|
-
"""
|
52
|
-
Event indicating that the tool could not be called (e.g., tool not found, invalid arguments, or invocation failure).
|
53
|
-
This is distinct from ToolRunError, which is for errors during execution after the tool has started running.
|
54
|
-
"""
|
55
|
-
|
56
|
-
error: str
|
57
|
-
exception: Exception = None
|
58
|
-
arguments: Any = None
|
1
|
+
import attr
|
2
|
+
from typing import Any, ClassVar
|
3
|
+
from janito.event_bus.event import Event
|
4
|
+
|
5
|
+
|
6
|
+
@attr.s(auto_attribs=True, kw_only=True)
|
7
|
+
class ToolEvent(Event):
|
8
|
+
"""
|
9
|
+
Base class for events related to tool calls (external or internal tools).
|
10
|
+
Includes tool name and request ID for correlation.
|
11
|
+
"""
|
12
|
+
|
13
|
+
category: ClassVar[str] = "tool"
|
14
|
+
tool_name: str
|
15
|
+
request_id: str
|
16
|
+
|
17
|
+
|
18
|
+
@attr.s(auto_attribs=True, kw_only=True)
|
19
|
+
class ToolCallStarted(ToolEvent):
|
20
|
+
"""
|
21
|
+
Event indicating that a tool call has started.
|
22
|
+
Contains the arguments passed to the tool.
|
23
|
+
"""
|
24
|
+
|
25
|
+
arguments: Any
|
26
|
+
|
27
|
+
|
28
|
+
@attr.s(auto_attribs=True, kw_only=True)
|
29
|
+
class ToolCallFinished(ToolEvent):
|
30
|
+
"""
|
31
|
+
Event indicating that a tool call has finished.
|
32
|
+
Contains the result returned by the tool.
|
33
|
+
"""
|
34
|
+
|
35
|
+
result: Any
|
36
|
+
|
37
|
+
|
38
|
+
@attr.s(auto_attribs=True, kw_only=True)
|
39
|
+
class ToolRunError(ToolEvent):
|
40
|
+
"""
|
41
|
+
Event indicating that an error occurred during tool execution (for event bus, not exception handling).
|
42
|
+
"""
|
43
|
+
|
44
|
+
error: str
|
45
|
+
exception: Exception = None
|
46
|
+
arguments: Any = None
|
47
|
+
|
48
|
+
|
49
|
+
@attr.s(auto_attribs=True, kw_only=True)
|
50
|
+
class ToolCallError(ToolEvent):
|
51
|
+
"""
|
52
|
+
Event indicating that the tool could not be called (e.g., tool not found, invalid arguments, or invocation failure).
|
53
|
+
This is distinct from ToolRunError, which is for errors during execution after the tool has started running.
|
54
|
+
"""
|
55
|
+
|
56
|
+
error: str
|
57
|
+
exception: Exception = None
|
58
|
+
arguments: Any = None
|
@@ -1,12 +1,12 @@
|
|
1
|
-
class ToolRunException(Exception):
|
2
|
-
"""
|
3
|
-
Exception raised when a tool runs but fails due to an internal error or runtime exception.
|
4
|
-
This is distinct from ToolRunError event, which is for event bus notification.
|
5
|
-
"""
|
6
|
-
|
7
|
-
def __init__(self, tool_name, error, arguments=None, exception=None):
|
8
|
-
self.tool_name = tool_name
|
9
|
-
self.error = error
|
10
|
-
self.arguments = arguments
|
11
|
-
self.original_exception = exception
|
12
|
-
super().__init__(f"ToolRunException: {tool_name}: {error}")
|
1
|
+
class ToolRunException(Exception):
|
2
|
+
"""
|
3
|
+
Exception raised when a tool runs but fails due to an internal error or runtime exception.
|
4
|
+
This is distinct from ToolRunError event, which is for event bus notification.
|
5
|
+
"""
|
6
|
+
|
7
|
+
def __init__(self, tool_name, error, arguments=None, exception=None):
|
8
|
+
self.tool_name = tool_name
|
9
|
+
self.error = error
|
10
|
+
self.arguments = arguments
|
11
|
+
self.original_exception = exception
|
12
|
+
super().__init__(f"ToolRunException: {tool_name}: {error}")
|
janito/tools/tool_use_tracker.py
CHANGED
@@ -1,81 +1,81 @@
|
|
1
|
-
import threading
|
2
|
-
import os
|
3
|
-
from typing import Any, Dict, List
|
4
|
-
|
5
|
-
|
6
|
-
def normalize_path(path: str) -> str:
|
7
|
-
if not isinstance(path, str):
|
8
|
-
return path
|
9
|
-
return os.path.normcase(os.path.abspath(path))
|
10
|
-
|
11
|
-
|
12
|
-
class ToolUseTracker:
|
13
|
-
_instance = None
|
14
|
-
_lock = threading.Lock()
|
15
|
-
|
16
|
-
def __new__(cls):
|
17
|
-
if not cls._instance:
|
18
|
-
with cls._lock:
|
19
|
-
if not cls._instance:
|
20
|
-
cls._instance = super().__new__(cls)
|
21
|
-
cls._instance._history = []
|
22
|
-
return cls._instance
|
23
|
-
|
24
|
-
def record(self, tool_name: str, params: Dict[str, Any], result: Any = None):
|
25
|
-
# Normalize file_path in params if present
|
26
|
-
norm_params = params.copy()
|
27
|
-
if "file_path" in norm_params:
|
28
|
-
norm_params["file_path"] = normalize_path(norm_params["file_path"])
|
29
|
-
self._history.append(
|
30
|
-
{"tool": tool_name, "params": norm_params, "result": result}
|
31
|
-
)
|
32
|
-
|
33
|
-
def get_history(self) -> List[Dict[str, Any]]:
|
34
|
-
return list(self._history)
|
35
|
-
|
36
|
-
def get_operations_on_file(self, file_path: str) -> List[Dict[str, Any]]:
|
37
|
-
norm_file_path = normalize_path(file_path)
|
38
|
-
ops = []
|
39
|
-
for entry in self._history:
|
40
|
-
params = entry["params"]
|
41
|
-
# Normalize any string param values for comparison
|
42
|
-
for v in params.values():
|
43
|
-
if isinstance(v, str) and normalize_path(v) == norm_file_path:
|
44
|
-
ops.append(entry)
|
45
|
-
break
|
46
|
-
return ops
|
47
|
-
|
48
|
-
def file_fully_read(self, file_path: str) -> bool:
|
49
|
-
norm_file_path = normalize_path(file_path)
|
50
|
-
for entry in self._history:
|
51
|
-
if entry["tool"] == "view_file":
|
52
|
-
params = entry["params"]
|
53
|
-
if (
|
54
|
-
"file_path" in params
|
55
|
-
and normalize_path(params["file_path"]) == norm_file_path
|
56
|
-
):
|
57
|
-
# If both from_line and to_line are None, full file was read
|
58
|
-
if (
|
59
|
-
params.get("from_line") is None
|
60
|
-
and params.get("to_line") is None
|
61
|
-
):
|
62
|
-
return True
|
63
|
-
return False
|
64
|
-
|
65
|
-
def last_operation_is_full_read_or_replace(self, file_path: str) -> bool:
|
66
|
-
ops = self.get_operations_on_file(file_path)
|
67
|
-
if not ops:
|
68
|
-
return False
|
69
|
-
last = ops[-1]
|
70
|
-
if last["tool"] == "view_file":
|
71
|
-
params = last["params"]
|
72
|
-
if params.get("from_line") is None and params.get("to_line") is None:
|
73
|
-
return True
|
74
|
-
return False
|
75
|
-
|
76
|
-
def clear_history(self):
|
77
|
-
self._history.clear()
|
78
|
-
|
79
|
-
@classmethod
|
80
|
-
def instance(cls):
|
81
|
-
return cls()
|
1
|
+
import threading
|
2
|
+
import os
|
3
|
+
from typing import Any, Dict, List
|
4
|
+
|
5
|
+
|
6
|
+
def normalize_path(path: str) -> str:
|
7
|
+
if not isinstance(path, str):
|
8
|
+
return path
|
9
|
+
return os.path.normcase(os.path.abspath(path))
|
10
|
+
|
11
|
+
|
12
|
+
class ToolUseTracker:
|
13
|
+
_instance = None
|
14
|
+
_lock = threading.Lock()
|
15
|
+
|
16
|
+
def __new__(cls):
|
17
|
+
if not cls._instance:
|
18
|
+
with cls._lock:
|
19
|
+
if not cls._instance:
|
20
|
+
cls._instance = super().__new__(cls)
|
21
|
+
cls._instance._history = []
|
22
|
+
return cls._instance
|
23
|
+
|
24
|
+
def record(self, tool_name: str, params: Dict[str, Any], result: Any = None):
|
25
|
+
# Normalize file_path in params if present
|
26
|
+
norm_params = params.copy()
|
27
|
+
if "file_path" in norm_params:
|
28
|
+
norm_params["file_path"] = normalize_path(norm_params["file_path"])
|
29
|
+
self._history.append(
|
30
|
+
{"tool": tool_name, "params": norm_params, "result": result}
|
31
|
+
)
|
32
|
+
|
33
|
+
def get_history(self) -> List[Dict[str, Any]]:
|
34
|
+
return list(self._history)
|
35
|
+
|
36
|
+
def get_operations_on_file(self, file_path: str) -> List[Dict[str, Any]]:
|
37
|
+
norm_file_path = normalize_path(file_path)
|
38
|
+
ops = []
|
39
|
+
for entry in self._history:
|
40
|
+
params = entry["params"]
|
41
|
+
# Normalize any string param values for comparison
|
42
|
+
for v in params.values():
|
43
|
+
if isinstance(v, str) and normalize_path(v) == norm_file_path:
|
44
|
+
ops.append(entry)
|
45
|
+
break
|
46
|
+
return ops
|
47
|
+
|
48
|
+
def file_fully_read(self, file_path: str) -> bool:
|
49
|
+
norm_file_path = normalize_path(file_path)
|
50
|
+
for entry in self._history:
|
51
|
+
if entry["tool"] == "view_file":
|
52
|
+
params = entry["params"]
|
53
|
+
if (
|
54
|
+
"file_path" in params
|
55
|
+
and normalize_path(params["file_path"]) == norm_file_path
|
56
|
+
):
|
57
|
+
# If both from_line and to_line are None, full file was read
|
58
|
+
if (
|
59
|
+
params.get("from_line") is None
|
60
|
+
and params.get("to_line") is None
|
61
|
+
):
|
62
|
+
return True
|
63
|
+
return False
|
64
|
+
|
65
|
+
def last_operation_is_full_read_or_replace(self, file_path: str) -> bool:
|
66
|
+
ops = self.get_operations_on_file(file_path)
|
67
|
+
if not ops:
|
68
|
+
return False
|
69
|
+
last = ops[-1]
|
70
|
+
if last["tool"] == "view_file":
|
71
|
+
params = last["params"]
|
72
|
+
if params.get("from_line") is None and params.get("to_line") is None:
|
73
|
+
return True
|
74
|
+
return False
|
75
|
+
|
76
|
+
def clear_history(self):
|
77
|
+
self._history.clear()
|
78
|
+
|
79
|
+
@classmethod
|
80
|
+
def instance(cls):
|
81
|
+
return cls()
|
janito/tools/tool_utils.py
CHANGED
@@ -1,45 +1,43 @@
|
|
1
|
-
"""
|
2
|
-
Utility functions for the janito project.
|
3
|
-
Add your shared helper functions here.
|
4
|
-
"""
|
5
|
-
|
6
|
-
import os
|
7
|
-
import urllib.parse
|
8
|
-
|
9
|
-
|
10
|
-
def example_utility_function(x):
|
11
|
-
"""A simple example utility function."""
|
12
|
-
return f"Processed: {x}"
|
13
|
-
|
14
|
-
|
15
|
-
def display_path(path):
|
16
|
-
"""
|
17
|
-
Returns a display-friendly path. Injects an ANSI hyperlink to a local web file viewer using a hardcoded port.
|
18
|
-
Args:
|
19
|
-
path (str): Path to display.
|
20
|
-
Returns:
|
21
|
-
str: Display path, as an ANSI hyperlink.
|
22
|
-
"""
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
return word
|
45
|
-
return word + "s"
|
1
|
+
"""
|
2
|
+
Utility functions for the janito project.
|
3
|
+
Add your shared helper functions here.
|
4
|
+
"""
|
5
|
+
|
6
|
+
import os
|
7
|
+
import urllib.parse
|
8
|
+
|
9
|
+
|
10
|
+
def example_utility_function(x):
|
11
|
+
"""A simple example utility function."""
|
12
|
+
return f"Processed: {x}"
|
13
|
+
|
14
|
+
|
15
|
+
def display_path(path):
|
16
|
+
"""
|
17
|
+
Returns a display-friendly path. Injects an ANSI hyperlink to a local web file viewer using a hardcoded port.
|
18
|
+
Args:
|
19
|
+
path (str): Path to display.
|
20
|
+
Returns:
|
21
|
+
str: Display path, as an ANSI hyperlink.
|
22
|
+
"""
|
23
|
+
|
24
|
+
port = 8088
|
25
|
+
if os.path.isabs(path):
|
26
|
+
cwd = os.path.abspath(os.getcwd())
|
27
|
+
abs_path = os.path.abspath(path)
|
28
|
+
# Check if the absolute path is within the current working directory
|
29
|
+
if abs_path.startswith(cwd + os.sep):
|
30
|
+
disp = os.path.relpath(abs_path, cwd)
|
31
|
+
else:
|
32
|
+
disp = path
|
33
|
+
else:
|
34
|
+
disp = os.path.relpath(path)
|
35
|
+
# URL injection removed; just return display path
|
36
|
+
return disp
|
37
|
+
|
38
|
+
|
39
|
+
def pluralize(word: str, count: int) -> str:
|
40
|
+
"""Return the pluralized form of word if count != 1, unless word already ends with 's'."""
|
41
|
+
if count == 1 or word.endswith("s"):
|
42
|
+
return word
|
43
|
+
return word + "s"
|