janito 2.3.0__py3-none-any.whl → 2.3.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.
- janito/__init__.py +6 -6
- janito/cli/chat_mode/shell/autocomplete.py +21 -21
- janito/cli/chat_mode/shell/commands/clear.py +12 -12
- janito/cli/chat_mode/shell/commands/multi.py +51 -51
- janito/cli/chat_mode/shell/input_history.py +62 -62
- 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 +53 -53
- janito/cli/cli_commands/model_selection.py +50 -50
- janito/cli/cli_commands/model_utils.py +95 -95
- 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 +62 -62
- janito/cli/core/__init__.py +4 -4
- janito/cli/core/event_logger.py +59 -59
- janito/cli/core/getters.py +33 -33
- janito/cli/core/unsetters.py +54 -54
- janito/cli/single_shot_mode/__init__.py +6 -6
- janito/config.py +5 -5
- janito/config_manager.py +112 -112
- janito/drivers/anthropic/driver.py +113 -113
- janito/formatting_token.py +54 -54
- janito/i18n/__init__.py +35 -35
- janito/i18n/messages.py +23 -23
- janito/i18n/pt.py +47 -47
- janito/llm/__init__.py +5 -5
- janito/llm/agent.py +443 -443
- janito/llm/auth.py +63 -63
- 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 +176 -176
- janito/providers/anthropic/model_info.py +22 -22
- janito/providers/anthropic/provider.py +2 -0
- janito/providers/azure_openai/model_info.py +16 -16
- janito/providers/azure_openai/provider.py +3 -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/tools/adapters/__init__.py +1 -1
- janito/tools/adapters/local/ask_user.py +102 -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/fetch_url.py +97 -97
- janito/tools/adapters/local/find_files.py +138 -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/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/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 +167 -167
- janito/tools/inspect_registry.py +17 -17
- janito/tools/tool_base.py +105 -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 +45 -45
- janito/tools/tools_schema.py +104 -104
- janito/version.py +4 -4
- {janito-2.3.0.dist-info → janito-2.3.1.dist-info}/METADATA +390 -388
- {janito-2.3.0.dist-info → janito-2.3.1.dist-info}/RECORD +93 -93
- {janito-2.3.0.dist-info → janito-2.3.1.dist-info}/WHEEL +0 -0
- {janito-2.3.0.dist-info → janito-2.3.1.dist-info}/entry_points.txt +0 -0
- {janito-2.3.0.dist-info → janito-2.3.1.dist-info}/licenses/LICENSE +0 -0
- {janito-2.3.0.dist-info → janito-2.3.1.dist-info}/top_level.txt +0 -0
@@ -1,69 +1,69 @@
|
|
1
|
-
from janito.tools.adapters.local.adapter import register_local_tool
|
2
|
-
|
3
|
-
from janito.tools.tool_utils import display_path
|
4
|
-
from janito.tools.tool_base import ToolBase
|
5
|
-
from janito.report_events import ReportAction
|
6
|
-
from janito.i18n import tr
|
7
|
-
import os
|
8
|
-
|
9
|
-
|
10
|
-
@register_local_tool
|
11
|
-
class CreateDirectoryTool(ToolBase):
|
12
|
-
"""
|
13
|
-
Create a new directory at the specified file_path.
|
14
|
-
Args:
|
15
|
-
file_path (str): Path for the new directory.
|
16
|
-
Returns:
|
17
|
-
str: Status message indicating the result. Example:
|
18
|
-
- "5c5 Successfully created the directory at ..."
|
19
|
-
- "5d7 Cannot create directory: ..."
|
20
|
-
"""
|
21
|
-
|
22
|
-
tool_name = "create_directory"
|
23
|
-
|
24
|
-
def run(self, file_path: str) -> str:
|
25
|
-
# file_path = expand_path(file_path)
|
26
|
-
# Using file_path as is
|
27
|
-
disp_path = display_path(file_path)
|
28
|
-
self.report_action(
|
29
|
-
tr("📁 Create directory '{disp_path}' ...", disp_path=disp_path),
|
30
|
-
ReportAction.CREATE,
|
31
|
-
)
|
32
|
-
try:
|
33
|
-
if os.path.exists(file_path):
|
34
|
-
if not os.path.isdir(file_path):
|
35
|
-
self.report_error(
|
36
|
-
tr(
|
37
|
-
"❌ Path '{disp_path}' exists and is not a directory.",
|
38
|
-
disp_path=disp_path,
|
39
|
-
)
|
40
|
-
)
|
41
|
-
return tr(
|
42
|
-
"❌ Path '{disp_path}' exists and is not a directory.",
|
43
|
-
disp_path=disp_path,
|
44
|
-
)
|
45
|
-
self.report_error(
|
46
|
-
tr(
|
47
|
-
"❗ Directory '{disp_path}' already exists.",
|
48
|
-
disp_path=disp_path,
|
49
|
-
)
|
50
|
-
)
|
51
|
-
return tr(
|
52
|
-
"❗ Cannot create directory: '{disp_path}' already exists.",
|
53
|
-
disp_path=disp_path,
|
54
|
-
)
|
55
|
-
os.makedirs(file_path, exist_ok=True)
|
56
|
-
self.report_success(tr("✅ Directory created"))
|
57
|
-
return tr(
|
58
|
-
"✅ Successfully created the directory at '{disp_path}'.",
|
59
|
-
disp_path=disp_path,
|
60
|
-
)
|
61
|
-
except Exception as e:
|
62
|
-
self.report_error(
|
63
|
-
tr(
|
64
|
-
"❌ Error creating directory '{disp_path}': {error}",
|
65
|
-
disp_path=disp_path,
|
66
|
-
error=e,
|
67
|
-
)
|
68
|
-
)
|
69
|
-
return tr("❌ Cannot create directory: {error}", error=e)
|
1
|
+
from janito.tools.adapters.local.adapter import register_local_tool
|
2
|
+
|
3
|
+
from janito.tools.tool_utils import display_path
|
4
|
+
from janito.tools.tool_base import ToolBase
|
5
|
+
from janito.report_events import ReportAction
|
6
|
+
from janito.i18n import tr
|
7
|
+
import os
|
8
|
+
|
9
|
+
|
10
|
+
@register_local_tool
|
11
|
+
class CreateDirectoryTool(ToolBase):
|
12
|
+
"""
|
13
|
+
Create a new directory at the specified file_path.
|
14
|
+
Args:
|
15
|
+
file_path (str): Path for the new directory.
|
16
|
+
Returns:
|
17
|
+
str: Status message indicating the result. Example:
|
18
|
+
- "5c5 Successfully created the directory at ..."
|
19
|
+
- "5d7 Cannot create directory: ..."
|
20
|
+
"""
|
21
|
+
|
22
|
+
tool_name = "create_directory"
|
23
|
+
|
24
|
+
def run(self, file_path: str) -> str:
|
25
|
+
# file_path = expand_path(file_path)
|
26
|
+
# Using file_path as is
|
27
|
+
disp_path = display_path(file_path)
|
28
|
+
self.report_action(
|
29
|
+
tr("📁 Create directory '{disp_path}' ...", disp_path=disp_path),
|
30
|
+
ReportAction.CREATE,
|
31
|
+
)
|
32
|
+
try:
|
33
|
+
if os.path.exists(file_path):
|
34
|
+
if not os.path.isdir(file_path):
|
35
|
+
self.report_error(
|
36
|
+
tr(
|
37
|
+
"❌ Path '{disp_path}' exists and is not a directory.",
|
38
|
+
disp_path=disp_path,
|
39
|
+
)
|
40
|
+
)
|
41
|
+
return tr(
|
42
|
+
"❌ Path '{disp_path}' exists and is not a directory.",
|
43
|
+
disp_path=disp_path,
|
44
|
+
)
|
45
|
+
self.report_error(
|
46
|
+
tr(
|
47
|
+
"❗ Directory '{disp_path}' already exists.",
|
48
|
+
disp_path=disp_path,
|
49
|
+
)
|
50
|
+
)
|
51
|
+
return tr(
|
52
|
+
"❗ Cannot create directory: '{disp_path}' already exists.",
|
53
|
+
disp_path=disp_path,
|
54
|
+
)
|
55
|
+
os.makedirs(file_path, exist_ok=True)
|
56
|
+
self.report_success(tr("✅ Directory created"))
|
57
|
+
return tr(
|
58
|
+
"✅ Successfully created the directory at '{disp_path}'.",
|
59
|
+
disp_path=disp_path,
|
60
|
+
)
|
61
|
+
except Exception as e:
|
62
|
+
self.report_error(
|
63
|
+
tr(
|
64
|
+
"❌ Error creating directory '{disp_path}': {error}",
|
65
|
+
disp_path=disp_path,
|
66
|
+
error=e,
|
67
|
+
)
|
68
|
+
)
|
69
|
+
return tr("❌ Cannot create directory: {error}", error=e)
|
@@ -1,82 +1,82 @@
|
|
1
|
-
import os
|
2
|
-
from janito.tools.adapters.local.adapter import register_local_tool
|
3
|
-
|
4
|
-
from janito.tools.tool_utils import display_path
|
5
|
-
from janito.tools.tool_base import ToolBase
|
6
|
-
from janito.report_events import ReportAction
|
7
|
-
from janito.i18n import tr
|
8
|
-
|
9
|
-
|
10
|
-
from janito.tools.adapters.local.validate_file_syntax.core import validate_file_syntax
|
11
|
-
|
12
|
-
|
13
|
-
@register_local_tool
|
14
|
-
class CreateFileTool(ToolBase):
|
15
|
-
"""
|
16
|
-
Create a new file with the given content.
|
17
|
-
|
18
|
-
Args:
|
19
|
-
file_path (str): Path to the file to create.
|
20
|
-
content (str): Content to write to the file.
|
21
|
-
overwrite (bool, optional): Overwrite existing file if True. Default: False. Recommended only after reading the file to be overwritten.
|
22
|
-
Returns:
|
23
|
-
str: Status message indicating the result. Example:
|
24
|
-
- "✅ Successfully created the file at ..."
|
25
|
-
|
26
|
-
Note: Syntax validation is automatically performed after this operation.
|
27
|
-
"""
|
28
|
-
|
29
|
-
tool_name = "create_file"
|
30
|
-
|
31
|
-
def run(self, file_path: str, content: str, overwrite: bool = False) -> str:
|
32
|
-
expanded_file_path = file_path # Using file_path as is
|
33
|
-
disp_path = display_path(expanded_file_path)
|
34
|
-
file_path = expanded_file_path
|
35
|
-
if os.path.exists(file_path) and not overwrite:
|
36
|
-
try:
|
37
|
-
with open(file_path, "r", encoding="utf-8", errors="replace") as f:
|
38
|
-
existing_content = f.read()
|
39
|
-
except Exception as e:
|
40
|
-
existing_content = f"[Error reading file: {e}]"
|
41
|
-
return tr(
|
42
|
-
"❗ Cannot create file: file already exists at '{disp_path}'.\n--- Current file content ---\n{existing_content}",
|
43
|
-
disp_path=disp_path,
|
44
|
-
existing_content=existing_content,
|
45
|
-
)
|
46
|
-
# Determine if we are overwriting an existing file
|
47
|
-
is_overwrite = os.path.exists(file_path) and overwrite
|
48
|
-
if is_overwrite:
|
49
|
-
# Overwrite branch: log only overwrite warning (no create message)
|
50
|
-
self.report_action(
|
51
|
-
tr("⚠️ Overwriting file '{disp_path}'", disp_path=disp_path),
|
52
|
-
ReportAction.CREATE,
|
53
|
-
)
|
54
|
-
dir_name = os.path.dirname(file_path)
|
55
|
-
if dir_name:
|
56
|
-
os.makedirs(dir_name, exist_ok=True)
|
57
|
-
if not is_overwrite:
|
58
|
-
# Create branch: log file creation message
|
59
|
-
self.report_action(
|
60
|
-
tr("📝 Create file '{disp_path}' ...", disp_path=disp_path),
|
61
|
-
ReportAction.CREATE,
|
62
|
-
)
|
63
|
-
with open(file_path, "w", encoding="utf-8", errors="replace") as f:
|
64
|
-
f.write(content)
|
65
|
-
new_lines = content.count("\n") + 1 if content else 0
|
66
|
-
self.report_success(
|
67
|
-
tr("✅ {new_lines} lines", new_lines=new_lines), ReportAction.CREATE
|
68
|
-
)
|
69
|
-
# Perform syntax validation and append result
|
70
|
-
validation_result = validate_file_syntax(file_path)
|
71
|
-
if is_overwrite:
|
72
|
-
# Overwrite branch: return minimal overwrite info to user
|
73
|
-
return (
|
74
|
-
tr("✅ {new_lines} lines", new_lines=new_lines)
|
75
|
-
+ f"\n{validation_result}"
|
76
|
-
)
|
77
|
-
else:
|
78
|
-
# Create branch: return detailed create success to user
|
79
|
-
return (
|
80
|
-
tr("✅ Created file {new_lines} lines.", new_lines=new_lines)
|
81
|
-
+ f"\n{validation_result}"
|
82
|
-
)
|
1
|
+
import os
|
2
|
+
from janito.tools.adapters.local.adapter import register_local_tool
|
3
|
+
|
4
|
+
from janito.tools.tool_utils import display_path
|
5
|
+
from janito.tools.tool_base import ToolBase
|
6
|
+
from janito.report_events import ReportAction
|
7
|
+
from janito.i18n import tr
|
8
|
+
|
9
|
+
|
10
|
+
from janito.tools.adapters.local.validate_file_syntax.core import validate_file_syntax
|
11
|
+
|
12
|
+
|
13
|
+
@register_local_tool
|
14
|
+
class CreateFileTool(ToolBase):
|
15
|
+
"""
|
16
|
+
Create a new file with the given content.
|
17
|
+
|
18
|
+
Args:
|
19
|
+
file_path (str): Path to the file to create.
|
20
|
+
content (str): Content to write to the file.
|
21
|
+
overwrite (bool, optional): Overwrite existing file if True. Default: False. Recommended only after reading the file to be overwritten.
|
22
|
+
Returns:
|
23
|
+
str: Status message indicating the result. Example:
|
24
|
+
- "✅ Successfully created the file at ..."
|
25
|
+
|
26
|
+
Note: Syntax validation is automatically performed after this operation.
|
27
|
+
"""
|
28
|
+
|
29
|
+
tool_name = "create_file"
|
30
|
+
|
31
|
+
def run(self, file_path: str, content: str, overwrite: bool = False) -> str:
|
32
|
+
expanded_file_path = file_path # Using file_path as is
|
33
|
+
disp_path = display_path(expanded_file_path)
|
34
|
+
file_path = expanded_file_path
|
35
|
+
if os.path.exists(file_path) and not overwrite:
|
36
|
+
try:
|
37
|
+
with open(file_path, "r", encoding="utf-8", errors="replace") as f:
|
38
|
+
existing_content = f.read()
|
39
|
+
except Exception as e:
|
40
|
+
existing_content = f"[Error reading file: {e}]"
|
41
|
+
return tr(
|
42
|
+
"❗ Cannot create file: file already exists at '{disp_path}'.\n--- Current file content ---\n{existing_content}",
|
43
|
+
disp_path=disp_path,
|
44
|
+
existing_content=existing_content,
|
45
|
+
)
|
46
|
+
# Determine if we are overwriting an existing file
|
47
|
+
is_overwrite = os.path.exists(file_path) and overwrite
|
48
|
+
if is_overwrite:
|
49
|
+
# Overwrite branch: log only overwrite warning (no create message)
|
50
|
+
self.report_action(
|
51
|
+
tr("⚠️ Overwriting file '{disp_path}'", disp_path=disp_path),
|
52
|
+
ReportAction.CREATE,
|
53
|
+
)
|
54
|
+
dir_name = os.path.dirname(file_path)
|
55
|
+
if dir_name:
|
56
|
+
os.makedirs(dir_name, exist_ok=True)
|
57
|
+
if not is_overwrite:
|
58
|
+
# Create branch: log file creation message
|
59
|
+
self.report_action(
|
60
|
+
tr("📝 Create file '{disp_path}' ...", disp_path=disp_path),
|
61
|
+
ReportAction.CREATE,
|
62
|
+
)
|
63
|
+
with open(file_path, "w", encoding="utf-8", errors="replace") as f:
|
64
|
+
f.write(content)
|
65
|
+
new_lines = content.count("\n") + 1 if content else 0
|
66
|
+
self.report_success(
|
67
|
+
tr("✅ {new_lines} lines", new_lines=new_lines), ReportAction.CREATE
|
68
|
+
)
|
69
|
+
# Perform syntax validation and append result
|
70
|
+
validation_result = validate_file_syntax(file_path)
|
71
|
+
if is_overwrite:
|
72
|
+
# Overwrite branch: return minimal overwrite info to user
|
73
|
+
return (
|
74
|
+
tr("✅ {new_lines} lines", new_lines=new_lines)
|
75
|
+
+ f"\n{validation_result}"
|
76
|
+
)
|
77
|
+
else:
|
78
|
+
# Create branch: return detailed create success to user
|
79
|
+
return (
|
80
|
+
tr("✅ Created file {new_lines} lines.", new_lines=new_lines)
|
81
|
+
+ f"\n{validation_result}"
|
82
|
+
)
|
@@ -1,97 +1,97 @@
|
|
1
|
-
import requests
|
2
|
-
from bs4 import BeautifulSoup
|
3
|
-
from janito.tools.adapters.local.adapter import register_local_tool
|
4
|
-
from janito.tools.tool_base import ToolBase
|
5
|
-
from janito.report_events import ReportAction
|
6
|
-
from janito.i18n import tr
|
7
|
-
from janito.tools.tool_utils import pluralize
|
8
|
-
|
9
|
-
|
10
|
-
@register_local_tool
|
11
|
-
class FetchUrlTool(ToolBase):
|
12
|
-
"""
|
13
|
-
Fetch the content of a web page and extract its text.
|
14
|
-
|
15
|
-
Args:
|
16
|
-
url (str): The URL of the web page to fetch.
|
17
|
-
search_strings (list[str], optional): Strings to search for in the page content.
|
18
|
-
Returns:
|
19
|
-
str: Extracted text content from the web page, or a warning message. Example:
|
20
|
-
- "<main text content...>"
|
21
|
-
- "No lines found for the provided search strings."
|
22
|
-
- "Warning: Empty URL provided. Operation skipped."
|
23
|
-
"""
|
24
|
-
|
25
|
-
tool_name = "fetch_url"
|
26
|
-
|
27
|
-
def run(self, url: str, search_strings: list[str] = None) -> str:
|
28
|
-
if not url.strip():
|
29
|
-
self.report_warning(tr("ℹ️ Empty URL provided."), ReportAction.READ)
|
30
|
-
return tr("Warning: Empty URL provided. Operation skipped.")
|
31
|
-
self.report_action(tr("🌐 Fetch URL '{url}' ...", url=url), ReportAction.READ)
|
32
|
-
try:
|
33
|
-
response = requests.get(url, timeout=10)
|
34
|
-
response.raise_for_status()
|
35
|
-
except requests.exceptions.HTTPError as http_err:
|
36
|
-
status_code = http_err.response.status_code if http_err.response else None
|
37
|
-
if status_code and 400 <= status_code < 500:
|
38
|
-
self.report_error(
|
39
|
-
tr(
|
40
|
-
"❗ HTTP {status_code} error for URL: {url}",
|
41
|
-
status_code=status_code,
|
42
|
-
url=url,
|
43
|
-
),
|
44
|
-
ReportAction.READ,
|
45
|
-
)
|
46
|
-
return tr(
|
47
|
-
"Warning: HTTP {status_code} error for URL: {url}",
|
48
|
-
status_code=status_code,
|
49
|
-
url=url,
|
50
|
-
)
|
51
|
-
else:
|
52
|
-
self.report_error(
|
53
|
-
tr(
|
54
|
-
"❗ HTTP error for URL: {url}: {err}",
|
55
|
-
url=url,
|
56
|
-
err=str(http_err),
|
57
|
-
),
|
58
|
-
ReportAction.READ,
|
59
|
-
)
|
60
|
-
return tr(
|
61
|
-
"Warning: HTTP error for URL: {url}: {err}",
|
62
|
-
url=url,
|
63
|
-
err=str(http_err),
|
64
|
-
)
|
65
|
-
except Exception as err:
|
66
|
-
self.report_error(
|
67
|
-
tr("❗ Error fetching URL: {url}: {err}", url=url, err=str(err)),
|
68
|
-
ReportAction.READ,
|
69
|
-
)
|
70
|
-
return tr(
|
71
|
-
"Warning: Error fetching URL: {url}: {err}", url=url, err=str(err)
|
72
|
-
)
|
73
|
-
soup = BeautifulSoup(response.text, "html.parser")
|
74
|
-
text = soup.get_text(separator="\n")
|
75
|
-
if search_strings:
|
76
|
-
filtered = []
|
77
|
-
for s in search_strings:
|
78
|
-
idx = text.find(s)
|
79
|
-
if idx != -1:
|
80
|
-
start = max(0, idx - 200)
|
81
|
-
end = min(len(text), idx + len(s) + 200)
|
82
|
-
snippet = text[start:end]
|
83
|
-
filtered.append(snippet)
|
84
|
-
if filtered:
|
85
|
-
text = "\n...\n".join(filtered)
|
86
|
-
else:
|
87
|
-
text = tr("No lines found for the provided search strings.")
|
88
|
-
num_lines = len(text.splitlines())
|
89
|
-
self.report_success(
|
90
|
-
tr(
|
91
|
-
"✅ {num_lines} {line_word}",
|
92
|
-
num_lines=num_lines,
|
93
|
-
line_word=pluralize("line", num_lines),
|
94
|
-
),
|
95
|
-
ReportAction.READ,
|
96
|
-
)
|
97
|
-
return text
|
1
|
+
import requests
|
2
|
+
from bs4 import BeautifulSoup
|
3
|
+
from janito.tools.adapters.local.adapter import register_local_tool
|
4
|
+
from janito.tools.tool_base import ToolBase
|
5
|
+
from janito.report_events import ReportAction
|
6
|
+
from janito.i18n import tr
|
7
|
+
from janito.tools.tool_utils import pluralize
|
8
|
+
|
9
|
+
|
10
|
+
@register_local_tool
|
11
|
+
class FetchUrlTool(ToolBase):
|
12
|
+
"""
|
13
|
+
Fetch the content of a web page and extract its text.
|
14
|
+
|
15
|
+
Args:
|
16
|
+
url (str): The URL of the web page to fetch.
|
17
|
+
search_strings (list[str], optional): Strings to search for in the page content.
|
18
|
+
Returns:
|
19
|
+
str: Extracted text content from the web page, or a warning message. Example:
|
20
|
+
- "<main text content...>"
|
21
|
+
- "No lines found for the provided search strings."
|
22
|
+
- "Warning: Empty URL provided. Operation skipped."
|
23
|
+
"""
|
24
|
+
|
25
|
+
tool_name = "fetch_url"
|
26
|
+
|
27
|
+
def run(self, url: str, search_strings: list[str] = None) -> str:
|
28
|
+
if not url.strip():
|
29
|
+
self.report_warning(tr("ℹ️ Empty URL provided."), ReportAction.READ)
|
30
|
+
return tr("Warning: Empty URL provided. Operation skipped.")
|
31
|
+
self.report_action(tr("🌐 Fetch URL '{url}' ...", url=url), ReportAction.READ)
|
32
|
+
try:
|
33
|
+
response = requests.get(url, timeout=10)
|
34
|
+
response.raise_for_status()
|
35
|
+
except requests.exceptions.HTTPError as http_err:
|
36
|
+
status_code = http_err.response.status_code if http_err.response else None
|
37
|
+
if status_code and 400 <= status_code < 500:
|
38
|
+
self.report_error(
|
39
|
+
tr(
|
40
|
+
"❗ HTTP {status_code} error for URL: {url}",
|
41
|
+
status_code=status_code,
|
42
|
+
url=url,
|
43
|
+
),
|
44
|
+
ReportAction.READ,
|
45
|
+
)
|
46
|
+
return tr(
|
47
|
+
"Warning: HTTP {status_code} error for URL: {url}",
|
48
|
+
status_code=status_code,
|
49
|
+
url=url,
|
50
|
+
)
|
51
|
+
else:
|
52
|
+
self.report_error(
|
53
|
+
tr(
|
54
|
+
"❗ HTTP error for URL: {url}: {err}",
|
55
|
+
url=url,
|
56
|
+
err=str(http_err),
|
57
|
+
),
|
58
|
+
ReportAction.READ,
|
59
|
+
)
|
60
|
+
return tr(
|
61
|
+
"Warning: HTTP error for URL: {url}: {err}",
|
62
|
+
url=url,
|
63
|
+
err=str(http_err),
|
64
|
+
)
|
65
|
+
except Exception as err:
|
66
|
+
self.report_error(
|
67
|
+
tr("❗ Error fetching URL: {url}: {err}", url=url, err=str(err)),
|
68
|
+
ReportAction.READ,
|
69
|
+
)
|
70
|
+
return tr(
|
71
|
+
"Warning: Error fetching URL: {url}: {err}", url=url, err=str(err)
|
72
|
+
)
|
73
|
+
soup = BeautifulSoup(response.text, "html.parser")
|
74
|
+
text = soup.get_text(separator="\n")
|
75
|
+
if search_strings:
|
76
|
+
filtered = []
|
77
|
+
for s in search_strings:
|
78
|
+
idx = text.find(s)
|
79
|
+
if idx != -1:
|
80
|
+
start = max(0, idx - 200)
|
81
|
+
end = min(len(text), idx + len(s) + 200)
|
82
|
+
snippet = text[start:end]
|
83
|
+
filtered.append(snippet)
|
84
|
+
if filtered:
|
85
|
+
text = "\n...\n".join(filtered)
|
86
|
+
else:
|
87
|
+
text = tr("No lines found for the provided search strings.")
|
88
|
+
num_lines = len(text.splitlines())
|
89
|
+
self.report_success(
|
90
|
+
tr(
|
91
|
+
"✅ {num_lines} {line_word}",
|
92
|
+
num_lines=num_lines,
|
93
|
+
line_word=pluralize("line", num_lines),
|
94
|
+
),
|
95
|
+
ReportAction.READ,
|
96
|
+
)
|
97
|
+
return text
|