janito 3.8.0__py3-none-any.whl → 3.10.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/agent_events.py +75 -0
- janito/cli/chat_mode/session.py +1 -0
- janito/cli/chat_mode/shell/commands/__init__.py +2 -0
- janito/cli/chat_mode/shell/commands/interactive.py +33 -0
- janito/cli/chat_mode/toolbar.py +16 -1
- janito/cli/cli_commands/list_tools.py +1 -1
- janito/cli/core/runner.py +33 -0
- janito/cli/main_cli.py +9 -0
- janito/cli/prompt_core.py +301 -257
- janito/cli/rich_terminal_reporter.py +170 -171
- janito/cli/single_shot_mode/handler.py +19 -0
- janito/llm/agent.py +65 -0
- janito/{tools/adapters/local → plugins/core/filemanager/tools}/validate_file_syntax/core.py +7 -2
- janito/plugins/core/filemanager/tools/validate_file_syntax/txt_validator.py +28 -0
- janito/plugins/manager.py +1 -1
- janito/{tools/adapters → plugins/tools}/local/__init__.py +7 -0
- janito/{tools/adapters → plugins/tools}/local/adapter.py +1 -1
- janito/{tools/adapters → plugins/tools}/local/ask_user.py +1 -1
- janito/{tools/adapters → plugins/tools}/local/copy_file.py +1 -1
- janito/{tools/adapters → plugins/tools}/local/create_directory.py +45 -2
- janito/{tools/adapters → plugins/tools}/local/create_file.py +10 -6
- janito/{tools/adapters → plugins/tools}/local/delete_text_in_file.py +2 -2
- janito/{tools/adapters → plugins/tools}/local/fetch_url.py +3 -3
- janito/{tools/adapters → plugins/tools}/local/find_files.py +1 -1
- janito/{tools/adapters → plugins/tools}/local/get_file_outline/core.py +2 -2
- janito/{tools/adapters → plugins/tools}/local/move_file.py +1 -1
- janito/{tools/adapters → plugins/tools}/local/open_html_in_browser.py +1 -1
- janito/{tools/adapters → plugins/tools}/local/open_url.py +1 -1
- janito/{tools/adapters → plugins/tools}/local/python_code_run.py +1 -1
- janito/{tools/adapters → plugins/tools}/local/python_command_run.py +1 -1
- janito/{tools/adapters → plugins/tools}/local/python_file_run.py +1 -1
- janito/{tools/adapters → plugins/tools}/local/read_chart.py +1 -1
- janito/{tools/adapters → plugins/tools}/local/read_files.py +1 -1
- janito/{tools/adapters → plugins/tools}/local/remove_directory.py +1 -1
- janito/{tools/adapters → plugins/tools}/local/remove_file.py +1 -1
- janito/{tools/adapters → plugins/tools}/local/replace_text_in_file.py +2 -2
- janito/{tools/adapters → plugins/tools}/local/run_bash_command.py +1 -1
- janito/{tools/adapters → plugins/tools}/local/run_powershell_command.py +1 -1
- janito/{tools/adapters → plugins/tools}/local/search_text/core.py +2 -2
- janito/{tools/adapters → plugins/tools}/local/show_image.py +1 -1
- janito/{tools/adapters → plugins/tools}/local/show_image_grid.py +1 -1
- janito/plugins/tools/local/validate_file_syntax/__init__.py +1 -0
- janito/plugins/tools/local/validate_file_syntax/core.py +119 -0
- janito/plugins/tools/local/validate_file_syntax/txt_validator.py +28 -0
- janito/{tools/adapters → plugins/tools}/local/view_file.py +1 -1
- janito/tests/test_tool_adapter_case_insensitive.py +112 -0
- janito/tools/__init__.py +2 -2
- janito/tools/inspect_registry.py +1 -1
- janito/tools/tool_base.py +8 -1
- janito/tools/tools_adapter.py +514 -510
- {janito-3.8.0.dist-info → janito-3.10.0.dist-info}/METADATA +84 -84
- {janito-3.8.0.dist-info → janito-3.10.0.dist-info}/RECORD +77 -70
- /janito/{tools/adapters/local → plugins/core/filemanager/tools}/validate_file_syntax/__init__.py +0 -0
- /janito/{tools/adapters → plugins/tools}/__init__.py +0 -0
- /janito/{tools/adapters → plugins/tools}/local/get_file_outline/__init__.py +0 -0
- /janito/{tools/adapters → plugins/tools}/local/get_file_outline/java_outline.py +0 -0
- /janito/{tools/adapters → plugins/tools}/local/get_file_outline/markdown_outline.py +0 -0
- /janito/{tools/adapters → plugins/tools}/local/get_file_outline/python_outline.py +0 -0
- /janito/{tools/adapters → plugins/tools}/local/get_file_outline/search_outline.py +0 -0
- /janito/{tools/adapters → plugins/tools}/local/search_text/__init__.py +0 -0
- /janito/{tools/adapters → plugins/tools}/local/search_text/match_lines.py +0 -0
- /janito/{tools/adapters → plugins/tools}/local/search_text/pattern_utils.py +0 -0
- /janito/{tools/adapters → plugins/tools}/local/search_text/traverse_directory.py +0 -0
- /janito/{tools/adapters → plugins/tools}/local/validate_file_syntax/css_validator.py +0 -0
- /janito/{tools/adapters → plugins/tools}/local/validate_file_syntax/html_validator.py +0 -0
- /janito/{tools/adapters → plugins/tools}/local/validate_file_syntax/jinja2_validator.py +0 -0
- /janito/{tools/adapters → plugins/tools}/local/validate_file_syntax/js_validator.py +0 -0
- /janito/{tools/adapters → plugins/tools}/local/validate_file_syntax/json_validator.py +0 -0
- /janito/{tools/adapters → plugins/tools}/local/validate_file_syntax/markdown_validator.py +0 -0
- /janito/{tools/adapters → plugins/tools}/local/validate_file_syntax/ps1_validator.py +0 -0
- /janito/{tools/adapters → plugins/tools}/local/validate_file_syntax/python_validator.py +0 -0
- /janito/{tools/adapters → plugins/tools}/local/validate_file_syntax/xml_validator.py +0 -0
- /janito/{tools/adapters → plugins/tools}/local/validate_file_syntax/yaml_validator.py +0 -0
- {janito-3.8.0.dist-info → janito-3.10.0.dist-info}/WHEEL +0 -0
- {janito-3.8.0.dist-info → janito-3.10.0.dist-info}/entry_points.txt +0 -0
- {janito-3.8.0.dist-info → janito-3.10.0.dist-info}/licenses/LICENSE +0 -0
- {janito-3.8.0.dist-info → janito-3.10.0.dist-info}/top_level.txt +0 -0
@@ -1,171 +1,170 @@
|
|
1
|
-
from rich.console import Console
|
2
|
-
from rich.markdown import Markdown
|
3
|
-
from rich.pretty import Pretty
|
4
|
-
from rich.panel import Panel
|
5
|
-
from rich.text import Text
|
6
|
-
from janito.event_bus.handler import EventHandlerBase
|
7
|
-
import janito.driver_events as driver_events
|
8
|
-
from janito.report_events import ReportSubtype, ReportAction
|
9
|
-
from janito.event_bus.bus import event_bus
|
10
|
-
from janito.llm import message_parts
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
-
|
23
|
-
|
24
|
-
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
self.
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
provider
|
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
|
-
if
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
return
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
getattr(ReportAction, "
|
145
|
-
getattr(ReportAction, "
|
146
|
-
getattr(ReportAction, "
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
"
|
151
|
-
|
152
|
-
|
153
|
-
)
|
154
|
-
self.console.
|
155
|
-
|
156
|
-
|
157
|
-
ReportSubtype.
|
158
|
-
ReportSubtype.
|
159
|
-
|
160
|
-
|
161
|
-
self.console.
|
162
|
-
|
163
|
-
|
164
|
-
self.console.
|
165
|
-
|
166
|
-
|
167
|
-
self.console.
|
168
|
-
|
169
|
-
|
170
|
-
self.console.
|
171
|
-
self.console.file.flush()
|
1
|
+
from rich.console import Console
|
2
|
+
from rich.markdown import Markdown
|
3
|
+
from rich.pretty import Pretty
|
4
|
+
from rich.panel import Panel
|
5
|
+
from rich.text import Text
|
6
|
+
from janito.event_bus.handler import EventHandlerBase
|
7
|
+
import janito.driver_events as driver_events
|
8
|
+
from janito.report_events import ReportSubtype, ReportAction
|
9
|
+
from janito.event_bus.bus import event_bus
|
10
|
+
from janito.llm import message_parts
|
11
|
+
import janito.agent_events as agent_events
|
12
|
+
|
13
|
+
|
14
|
+
import sys
|
15
|
+
|
16
|
+
|
17
|
+
class RichTerminalReporter(EventHandlerBase):
|
18
|
+
"""
|
19
|
+
Handles UI rendering for janito events using Rich.
|
20
|
+
|
21
|
+
- For ResponseReceived events, iterates over the 'parts' field and displays each part appropriately:
|
22
|
+
- TextMessagePart: rendered as Markdown (uses 'content' field)
|
23
|
+
- Other MessageParts: displayed using Pretty or a suitable Rich representation
|
24
|
+
- For RequestFinished events, output is printed only if raw mode is enabled (using Pretty formatting).
|
25
|
+
- Report events (info, success, error, etc.) are always printed with appropriate styling.
|
26
|
+
"""
|
27
|
+
|
28
|
+
def __init__(self, raw_mode=False):
|
29
|
+
from janito.cli.console import shared_console
|
30
|
+
|
31
|
+
self.console = shared_console
|
32
|
+
self.raw_mode = raw_mode
|
33
|
+
import janito.report_events as report_events
|
34
|
+
|
35
|
+
import janito.tools.tool_events as tool_events
|
36
|
+
|
37
|
+
super().__init__(driver_events, report_events, tool_events, agent_events)
|
38
|
+
self._waiting_printed = False
|
39
|
+
|
40
|
+
def on_RequestStarted(self, event):
|
41
|
+
# Print waiting message with provider and model name
|
42
|
+
provider = None
|
43
|
+
model = None
|
44
|
+
if hasattr(event, "payload") and isinstance(event.payload, dict):
|
45
|
+
provider = event.payload.get("provider_name")
|
46
|
+
model = event.payload.get("model") or event.payload.get("model_name")
|
47
|
+
if not provider:
|
48
|
+
provider = getattr(event, "provider_name", None)
|
49
|
+
if not provider:
|
50
|
+
provider = getattr(event, "driver_name", None)
|
51
|
+
if not provider:
|
52
|
+
provider = "LLM"
|
53
|
+
if not model:
|
54
|
+
model = getattr(event, "model", None)
|
55
|
+
if not model:
|
56
|
+
model = getattr(event, "model_name", None)
|
57
|
+
if not model:
|
58
|
+
model = "?"
|
59
|
+
self.console.print(
|
60
|
+
f"[bold cyan]Waiting for {provider} (model: {model})...[/bold cyan]", end=""
|
61
|
+
)
|
62
|
+
self._waiting_printed = True
|
63
|
+
|
64
|
+
def on_AgentWaitingForResponse(self, event):
|
65
|
+
# Agent waiting - set flag but don't print anything
|
66
|
+
self._waiting_printed = True
|
67
|
+
|
68
|
+
def on_ResponseReceived(self, event):
|
69
|
+
parts = event.parts if hasattr(event, "parts") else None
|
70
|
+
if not parts:
|
71
|
+
self.console.print("[No response parts to display]")
|
72
|
+
self.console.file.flush()
|
73
|
+
return
|
74
|
+
for part in parts:
|
75
|
+
if isinstance(part, message_parts.TextMessagePart):
|
76
|
+
self.console.print(Markdown(part.content))
|
77
|
+
self.console.file.flush()
|
78
|
+
|
79
|
+
def delete_current_line(self):
|
80
|
+
"""
|
81
|
+
Clears the entire current line in the terminal and returns the cursor to column 1.
|
82
|
+
"""
|
83
|
+
# Use raw ANSI escape sequences but write directly to the underlying file
|
84
|
+
# to bypass Rich's escaping/interpretation
|
85
|
+
if hasattr(self.console, 'file') and hasattr(self.console.file, 'write'):
|
86
|
+
self.console.file.write("\r\033[2K")
|
87
|
+
self.console.file.flush()
|
88
|
+
else:
|
89
|
+
# Fallback to sys.stdout if console.file is not available
|
90
|
+
import sys
|
91
|
+
sys.stdout.write("\r\033[2K")
|
92
|
+
sys.stdout.flush()
|
93
|
+
|
94
|
+
def on_RequestFinished(self, event):
|
95
|
+
if self._waiting_printed:
|
96
|
+
self.delete_current_line()
|
97
|
+
self._waiting_printed = False
|
98
|
+
|
99
|
+
def on_AgentReceivedResponse(self, event):
|
100
|
+
# Clear any waiting message when agent receives response
|
101
|
+
if self._waiting_printed:
|
102
|
+
self.delete_current_line()
|
103
|
+
self._waiting_printed = False
|
104
|
+
|
105
|
+
def on_ToolCallError(self, event):
|
106
|
+
# Optionally handle tool call errors in a user-friendly way
|
107
|
+
error = getattr(event, "error", None)
|
108
|
+
tool = getattr(event, "tool_name", None)
|
109
|
+
if error and tool:
|
110
|
+
self.console.print(f"[bold red]Tool Error ({tool}):[/] {error}")
|
111
|
+
self.console.file.flush()
|
112
|
+
|
113
|
+
def on_ReportEvent(self, event):
|
114
|
+
# Special handling for security-related report events
|
115
|
+
subtype = getattr(event, "subtype", None)
|
116
|
+
msg = getattr(event, "message", None)
|
117
|
+
action = getattr(event, "action", None)
|
118
|
+
tool = getattr(event, "tool", None)
|
119
|
+
context = getattr(event, "context", None)
|
120
|
+
if (
|
121
|
+
subtype == ReportSubtype.ERROR
|
122
|
+
and msg
|
123
|
+
and "[SECURITY] Path access denied" in msg
|
124
|
+
):
|
125
|
+
# Highlight security errors with a distinct style
|
126
|
+
self.console.print(
|
127
|
+
Panel(f"{msg}", title="[red]SECURITY VIOLATION[/red]", style="bold red")
|
128
|
+
)
|
129
|
+
self.console.file.flush()
|
130
|
+
return
|
131
|
+
|
132
|
+
msg = event.message if hasattr(event, "message") else None
|
133
|
+
subtype = event.subtype if hasattr(event, "subtype") else None
|
134
|
+
if not msg or not subtype:
|
135
|
+
return
|
136
|
+
if subtype == ReportSubtype.ACTION_INFO:
|
137
|
+
# Clear any waiting message before showing action info
|
138
|
+
if self._waiting_printed:
|
139
|
+
self.delete_current_line()
|
140
|
+
self._waiting_printed = False
|
141
|
+
# Use orange for all write/modification actions
|
142
|
+
modification_actions = (
|
143
|
+
getattr(ReportAction, "UPDATE", None),
|
144
|
+
getattr(ReportAction, "WRITE", None),
|
145
|
+
getattr(ReportAction, "DELETE", None),
|
146
|
+
getattr(ReportAction, "CREATE", None),
|
147
|
+
)
|
148
|
+
style = (
|
149
|
+
"orange1"
|
150
|
+
if getattr(event, "action", None) in modification_actions
|
151
|
+
else "cyan"
|
152
|
+
)
|
153
|
+
self.console.print(Text(msg, style=style), end="")
|
154
|
+
self.console.file.flush()
|
155
|
+
elif subtype in (
|
156
|
+
ReportSubtype.SUCCESS,
|
157
|
+
ReportSubtype.ERROR,
|
158
|
+
ReportSubtype.WARNING,
|
159
|
+
):
|
160
|
+
self.console.print(msg)
|
161
|
+
self.console.file.flush()
|
162
|
+
elif subtype == ReportSubtype.STDOUT:
|
163
|
+
self.console.print(msg)
|
164
|
+
self.console.file.flush()
|
165
|
+
elif subtype == ReportSubtype.STDERR:
|
166
|
+
self.console.print(Text(msg, style="on red"))
|
167
|
+
self.console.file.flush()
|
168
|
+
else:
|
169
|
+
self.console.print(msg)
|
170
|
+
self.console.file.flush()
|
@@ -45,6 +45,25 @@ class PromptHandler:
|
|
45
45
|
def handle(self) -> None:
|
46
46
|
import traceback
|
47
47
|
|
48
|
+
# Check if interactive mode is requested - if so, switch to chat mode
|
49
|
+
if getattr(self.args, "interactive", False):
|
50
|
+
from janito.cli.chat_mode.session import ChatSession
|
51
|
+
from rich.console import Console
|
52
|
+
|
53
|
+
console = Console()
|
54
|
+
session = ChatSession(
|
55
|
+
console,
|
56
|
+
self.provider_instance,
|
57
|
+
self.llm_driver_config,
|
58
|
+
role=self.role,
|
59
|
+
args=self.args,
|
60
|
+
verbose_tools=getattr(self.args, "verbose_tools", False),
|
61
|
+
verbose_agent=getattr(self.args, "verbose_agent", False),
|
62
|
+
allowed_permissions=getattr(self, 'allowed_permissions', None),
|
63
|
+
)
|
64
|
+
session.run()
|
65
|
+
return
|
66
|
+
|
48
67
|
user_prompt = " ".join(getattr(self.args, "user_prompt", [])).strip()
|
49
68
|
# UTF-8 sanitize user_prompt
|
50
69
|
sanitized = user_prompt
|
janito/llm/agent.py
CHANGED
@@ -4,6 +4,17 @@ from janito.conversation_history import LLMConversationHistory
|
|
4
4
|
from janito.tools.tools_adapter import ToolsAdapterBase
|
5
5
|
from queue import Queue, Empty
|
6
6
|
from janito.driver_events import RequestStatus
|
7
|
+
from janito.agent_events import (
|
8
|
+
AgentInitialized,
|
9
|
+
AgentChatStarted,
|
10
|
+
AgentChatFinished,
|
11
|
+
AgentProcessingResponse,
|
12
|
+
AgentToolCallStarted,
|
13
|
+
AgentToolCallFinished,
|
14
|
+
AgentWaitingForResponse,
|
15
|
+
AgentReceivedResponse,
|
16
|
+
AgentShutdown
|
17
|
+
)
|
7
18
|
from typing import Any, Optional, List, Iterator, Union
|
8
19
|
import threading
|
9
20
|
import logging
|
@@ -53,6 +64,9 @@ class LLMAgent:
|
|
53
64
|
self._latest_event = None
|
54
65
|
self.verbose_agent = verbose_agent
|
55
66
|
self.driver = None # Will be set by setup_agent if available
|
67
|
+
|
68
|
+
# Emit agent initialized event
|
69
|
+
event_bus.publish(AgentInitialized(agent_name=self.agent_name))
|
56
70
|
|
57
71
|
def get_provider_name(self):
|
58
72
|
# Try to get provider name from driver, fallback to llm_provider, else '?'
|
@@ -178,11 +192,17 @@ class LLMAgent:
|
|
178
192
|
Wait for a single event from the output queue (with timeout), process it, and return the result.
|
179
193
|
This function is intended to be called from the main agent loop, which controls the overall flow.
|
180
194
|
"""
|
195
|
+
# Emit agent waiting for response event
|
196
|
+
event_bus.publish(AgentWaitingForResponse(agent_name=self.agent_name))
|
197
|
+
|
181
198
|
if getattr(self, "verbose_agent", False):
|
182
199
|
print("[agent] [DEBUG] Entered _process_next_response")
|
183
200
|
elapsed = 0.0
|
184
201
|
if getattr(self, "verbose_agent", False):
|
185
202
|
print("[agent] [DEBUG] Waiting for event from output_queue...")
|
203
|
+
# Show initial wait message
|
204
|
+
if getattr(self, "verbose_agent", False):
|
205
|
+
print(f"[agent] [DEBUG] Starting to wait for LLM response... (timeout: {max_wait_time}s)")
|
186
206
|
# Let KeyboardInterrupt propagate to caller
|
187
207
|
return self._poll_for_event(poll_timeout, max_wait_time)
|
188
208
|
|
@@ -197,7 +217,14 @@ class LLMAgent:
|
|
197
217
|
print(error_msg)
|
198
218
|
print("[DEBUG] Exiting _process_next_response due to timeout")
|
199
219
|
return None, False
|
220
|
+
# Show elapsed time info while waiting
|
221
|
+
if getattr(self, "verbose_agent", False):
|
222
|
+
print(f"[agent] [DEBUG] Waiting for LLM response... ({elapsed:.1f}s elapsed)")
|
200
223
|
continue
|
224
|
+
|
225
|
+
# Emit agent received response event
|
226
|
+
event_bus.publish(AgentReceivedResponse(agent_name=self.agent_name, response=event))
|
227
|
+
|
201
228
|
if getattr(self, "verbose_agent", False):
|
202
229
|
print(f"[agent] [DEBUG] Received event from output_queue: {event}")
|
203
230
|
event_bus.publish(event)
|
@@ -227,6 +254,10 @@ class LLMAgent:
|
|
227
254
|
"""
|
228
255
|
if getattr(self, "verbose_agent", False):
|
229
256
|
print("[agent] [INFO] Handling ResponseReceived event.")
|
257
|
+
|
258
|
+
# Emit agent processing response event
|
259
|
+
event_bus.publish(AgentProcessingResponse(agent_name=self.agent_name, response=event))
|
260
|
+
|
230
261
|
from janito.llm.message_parts import FunctionCallMessagePart
|
231
262
|
|
232
263
|
# Skip tool processing if no tools adapter is available
|
@@ -243,6 +274,15 @@ class LLMAgent:
|
|
243
274
|
print(
|
244
275
|
f"[agent] [DEBUG] Tool call detected: {getattr(part, 'name', repr(part))} with arguments: {getattr(part, 'arguments', None)}"
|
245
276
|
)
|
277
|
+
|
278
|
+
# Emit agent tool call started event
|
279
|
+
event_bus.publish(AgentToolCallStarted(
|
280
|
+
agent_name=self.agent_name,
|
281
|
+
tool_call_id=getattr(part, 'tool_call_id', None),
|
282
|
+
name=getattr(part, 'name', None),
|
283
|
+
arguments=getattr(part, 'arguments', None)
|
284
|
+
))
|
285
|
+
|
246
286
|
tool_calls.append(part)
|
247
287
|
try:
|
248
288
|
result = self.tools_adapter.execute_function_call_message_part(part)
|
@@ -251,6 +291,14 @@ class LLMAgent:
|
|
251
291
|
# instead of letting it propagate to the user
|
252
292
|
result = str(e)
|
253
293
|
tool_results.append(result)
|
294
|
+
|
295
|
+
# Emit agent tool call finished event
|
296
|
+
event_bus.publish(AgentToolCallFinished(
|
297
|
+
agent_name=self.agent_name,
|
298
|
+
tool_call_id=getattr(part, 'tool_call_id', None),
|
299
|
+
name=getattr(part, 'name', None),
|
300
|
+
result=result
|
301
|
+
))
|
254
302
|
if tool_calls:
|
255
303
|
# Prepare tool_calls message for assistant
|
256
304
|
tool_calls_list = []
|
@@ -310,6 +358,14 @@ class LLMAgent:
|
|
310
358
|
role: str = "user",
|
311
359
|
config=None,
|
312
360
|
):
|
361
|
+
# Emit agent chat started event
|
362
|
+
event_bus.publish(AgentChatStarted(
|
363
|
+
agent_name=self.agent_name,
|
364
|
+
prompt=prompt,
|
365
|
+
messages=messages,
|
366
|
+
role=role
|
367
|
+
))
|
368
|
+
|
313
369
|
self._clear_driver_queues()
|
314
370
|
self._validate_and_update_history(prompt, messages, role)
|
315
371
|
self._ensure_system_prompt()
|
@@ -333,6 +389,12 @@ class LLMAgent:
|
|
333
389
|
f"[agent] [DEBUG] Returned from _process_next_response: result={result}, added_tool_results={added_tool_results}"
|
334
390
|
)
|
335
391
|
if self._should_exit_chat_loop(result, added_tool_results):
|
392
|
+
# Emit agent chat finished event
|
393
|
+
event_bus.publish(AgentChatFinished(
|
394
|
+
agent_name=self.agent_name,
|
395
|
+
result=result,
|
396
|
+
loop_count=loop_count
|
397
|
+
))
|
336
398
|
return result
|
337
399
|
loop_count += 1
|
338
400
|
|
@@ -496,6 +558,9 @@ class LLMAgent:
|
|
496
558
|
:param timeout: Optional timeout in seconds.
|
497
559
|
Handles KeyboardInterrupt gracefully.
|
498
560
|
"""
|
561
|
+
# Emit agent shutdown event
|
562
|
+
event_bus.publish(AgentShutdown(agent_name=self.agent_name))
|
563
|
+
|
499
564
|
if (
|
500
565
|
hasattr(self, "driver")
|
501
566
|
and self.driver
|
@@ -3,9 +3,9 @@ from janito.tools.path_utils import expand_path
|
|
3
3
|
from janito.i18n import tr
|
4
4
|
from janito.tools.tool_base import ToolBase, ToolPermissions
|
5
5
|
from janito.report_events import ReportAction
|
6
|
-
from janito.tools.
|
6
|
+
from janito.plugins.tools.local.adapter import register_local_tool
|
7
7
|
from janito.tools.tool_utils import display_path
|
8
|
-
from janito.tools.
|
8
|
+
from janito.plugins.tools.local.adapter import register_local_tool as register_tool
|
9
9
|
|
10
10
|
from .python_validator import validate_python
|
11
11
|
from .json_validator import validate_json
|
@@ -17,6 +17,7 @@ from .markdown_validator import validate_markdown
|
|
17
17
|
from .js_validator import validate_js
|
18
18
|
from .css_validator import validate_css
|
19
19
|
from .jinja2_validator import validate_jinja2
|
20
|
+
from .txt_validator import validate_txt
|
20
21
|
from janito.tools.loop_protection_decorator import protect_against_loops
|
21
22
|
|
22
23
|
|
@@ -37,6 +38,8 @@ def _get_validator(ext):
|
|
37
38
|
".css": validate_css,
|
38
39
|
".j2": validate_jinja2,
|
39
40
|
".jinja2": validate_jinja2,
|
41
|
+
".txt": validate_txt,
|
42
|
+
".text": validate_txt,
|
40
43
|
}
|
41
44
|
return mapping.get(ext)
|
42
45
|
|
@@ -79,6 +82,7 @@ class ValidateFileSyntaxTool(ToolBase):
|
|
79
82
|
- Markdown (.md)
|
80
83
|
- JavaScript (.js)
|
81
84
|
- Jinja2 templates (.j2, .jinja2)
|
85
|
+
- Text files (.txt, .text) [UTF-8 validation]
|
82
86
|
|
83
87
|
Args:
|
84
88
|
path (str): Path to the file to validate.
|
@@ -86,6 +90,7 @@ class ValidateFileSyntaxTool(ToolBase):
|
|
86
90
|
str: Validation status message. Example:
|
87
91
|
- "✅ Syntax OK"
|
88
92
|
- "⚠️ Warning: Syntax error: <error message>"
|
93
|
+
- "⚠️ Warning: UTF-8 decoding error: <error details>"
|
89
94
|
- "⚠️ Warning: Unsupported file extension: <ext>"
|
90
95
|
"""
|
91
96
|
|
@@ -0,0 +1,28 @@
|
|
1
|
+
"""Text file validator for UTF-8 encoding validation."""
|
2
|
+
|
3
|
+
import codecs
|
4
|
+
from pathlib import Path
|
5
|
+
|
6
|
+
|
7
|
+
def validate_txt(path: str) -> str:
|
8
|
+
"""
|
9
|
+
Validate a text file for UTF-8 encoding issues.
|
10
|
+
|
11
|
+
Args:
|
12
|
+
path (str): Path to the text file to validate
|
13
|
+
|
14
|
+
Returns:
|
15
|
+
str: Validation status message
|
16
|
+
- "✅ Syntax OK" if file is valid UTF-8
|
17
|
+
- "⚠️ Warning: UTF-8 decoding error: <error details>" if invalid
|
18
|
+
"""
|
19
|
+
try:
|
20
|
+
# Try to read the file with UTF-8 encoding
|
21
|
+
with codecs.open(path, 'r', encoding='utf-8') as f:
|
22
|
+
# Read the entire file to trigger any decoding errors
|
23
|
+
f.read()
|
24
|
+
return "✅ Syntax OK"
|
25
|
+
except UnicodeDecodeError as e:
|
26
|
+
return f"⚠️ Warning: UTF-8 decoding error: {e}"
|
27
|
+
except Exception as e:
|
28
|
+
return f"⚠️ Warning: File read error: {e}"
|
janito/plugins/manager.py
CHANGED
@@ -14,7 +14,7 @@ from .base import Plugin, PluginMetadata
|
|
14
14
|
from .discovery import discover_plugins
|
15
15
|
from .config import load_plugins_config, get_user_plugins_dir
|
16
16
|
from .builtin import BuiltinPluginRegistry, load_builtin_plugin
|
17
|
-
from janito.tools.
|
17
|
+
from janito.plugins.tools.local import LocalToolsAdapter
|
18
18
|
|
19
19
|
logger = logging.getLogger(__name__)
|
20
20
|
|
@@ -30,6 +30,7 @@ from .show_image_grid import ShowImageGridTool
|
|
30
30
|
from janito.tools.tool_base import ToolPermissions
|
31
31
|
import os
|
32
32
|
from janito.tools.permissions import get_global_allowed_permissions
|
33
|
+
from janito.platform_discovery import PlatformDiscovery
|
33
34
|
|
34
35
|
# Singleton tools adapter with all standard tools registered
|
35
36
|
local_tools_adapter = LocalToolsAdapter(workdir=os.getcwd())
|
@@ -40,6 +41,9 @@ def get_local_tools_adapter(workdir=None):
|
|
40
41
|
|
41
42
|
|
42
43
|
# Register tools
|
44
|
+
pd = PlatformDiscovery()
|
45
|
+
is_powershell = pd.detect_shell().startswith("PowerShell")
|
46
|
+
|
43
47
|
for tool_class in [
|
44
48
|
AskUserTool,
|
45
49
|
CopyFileTool,
|
@@ -68,6 +72,9 @@ for tool_class in [
|
|
68
72
|
ShowImageTool,
|
69
73
|
ShowImageGridTool,
|
70
74
|
]:
|
75
|
+
# Skip bash tools when running in PowerShell
|
76
|
+
if is_powershell and tool_class.__name__ in ["RunBashCommandTool"]:
|
77
|
+
continue
|
71
78
|
local_tools_adapter.register_tool(tool_class)
|
72
79
|
|
73
80
|
# DEBUG: Print registered tools at startup
|
@@ -207,7 +207,7 @@ def register_local_tool(tool=None):
|
|
207
207
|
# Register the tool on a *fresh* adapter instance to avoid circular
|
208
208
|
# import issues during package initialisation. This keeps behaviour
|
209
209
|
# identical to the original implementation while still allowing
|
210
|
-
# immediate use via the singleton in janito.tools.
|
210
|
+
# immediate use via the singleton in janito.plugins.tools.local.
|
211
211
|
LocalToolsAdapter().register_tool(cls)
|
212
212
|
return cls
|
213
213
|
|
@@ -1,5 +1,5 @@
|
|
1
1
|
from janito.tools.tool_base import ToolBase, ToolPermissions
|
2
|
-
from janito.tools.
|
2
|
+
from janito.plugins.tools.local.adapter import register_local_tool
|
3
3
|
from janito.tools.loop_protection_decorator import protect_against_loops
|
4
4
|
|
5
5
|
from rich import print as rich_print
|
@@ -2,7 +2,7 @@ import os
|
|
2
2
|
from janito.tools.path_utils import expand_path
|
3
3
|
import shutil
|
4
4
|
from typing import List, Union
|
5
|
-
from janito.tools.
|
5
|
+
from janito.plugins.tools.local.adapter import register_local_tool
|
6
6
|
from janito.tools.tool_base import ToolBase, ToolPermissions
|
7
7
|
from janito.tools.tool_utils import display_path
|
8
8
|
from janito.report_events import ReportAction
|