quantalogic 0.59.2__py3-none-any.whl → 0.60.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.
- quantalogic/agent.py +268 -24
- quantalogic/create_custom_agent.py +26 -78
- quantalogic/prompts/chat_system_prompt.j2 +10 -7
- quantalogic/prompts/code_2_system_prompt.j2 +190 -0
- quantalogic/prompts/code_system_prompt.j2 +142 -0
- quantalogic/prompts/doc_system_prompt.j2 +178 -0
- quantalogic/prompts/legal_2_system_prompt.j2 +218 -0
- quantalogic/prompts/legal_system_prompt.j2 +140 -0
- quantalogic/prompts/system_prompt.j2 +6 -2
- quantalogic/prompts/task_prompt.j2 +1 -1
- quantalogic/prompts/tools_prompt.j2 +2 -4
- quantalogic/prompts.py +23 -4
- quantalogic/server/agent_server.py +1 -1
- quantalogic/tools/__init__.py +2 -0
- quantalogic/tools/duckduckgo_search_tool.py +1 -0
- quantalogic/tools/execute_bash_command_tool.py +114 -57
- quantalogic/tools/file_tracker_tool.py +49 -0
- quantalogic/tools/google_packages/google_news_tool.py +3 -0
- quantalogic/tools/image_generation/dalle_e.py +89 -137
- quantalogic/tools/rag_tool/__init__.py +2 -9
- quantalogic/tools/rag_tool/document_rag_sources_.py +728 -0
- quantalogic/tools/rag_tool/ocr_pdf_markdown.py +144 -0
- quantalogic/tools/replace_in_file_tool.py +1 -1
- quantalogic/tools/terminal_capture_tool.py +293 -0
- quantalogic/tools/tool.py +4 -0
- quantalogic/tools/utilities/__init__.py +2 -0
- quantalogic/tools/utilities/download_file_tool.py +3 -5
- quantalogic/tools/utilities/llm_tool.py +283 -0
- quantalogic/tools/utilities/selenium_tool.py +296 -0
- quantalogic/tools/utilities/vscode_tool.py +1 -1
- quantalogic/tools/web_navigation/__init__.py +5 -0
- quantalogic/tools/web_navigation/web_tool.py +145 -0
- quantalogic/tools/write_file_tool.py +72 -36
- {quantalogic-0.59.2.dist-info → quantalogic-0.60.0.dist-info}/METADATA +2 -2
- {quantalogic-0.59.2.dist-info → quantalogic-0.60.0.dist-info}/RECORD +38 -29
- quantalogic/tools/rag_tool/document_metadata.py +0 -15
- quantalogic/tools/rag_tool/query_response.py +0 -20
- quantalogic/tools/rag_tool/rag_tool.py +0 -566
- quantalogic/tools/rag_tool/rag_tool_beta.py +0 -264
- {quantalogic-0.59.2.dist-info → quantalogic-0.60.0.dist-info}/LICENSE +0 -0
- {quantalogic-0.59.2.dist-info → quantalogic-0.60.0.dist-info}/WHEEL +0 -0
- {quantalogic-0.59.2.dist-info → quantalogic-0.60.0.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,144 @@
|
|
1
|
+
|
2
|
+
import asyncio
|
3
|
+
import os
|
4
|
+
import tempfile
|
5
|
+
from pathlib import Path
|
6
|
+
from typing import Optional, Union, List
|
7
|
+
|
8
|
+
import typer
|
9
|
+
from loguru import logger
|
10
|
+
from pyzerox import zerox
|
11
|
+
|
12
|
+
# Import the flow API (assumes quantalogic/flow/flow.py is in your project structure)
|
13
|
+
from quantalogic.flow.flow import Nodes, Workflow
|
14
|
+
|
15
|
+
class PDFToMarkdownConverter:
|
16
|
+
"""A class to handle PDF to Markdown conversion using vision models."""
|
17
|
+
|
18
|
+
def __init__(
|
19
|
+
self,
|
20
|
+
model: str = "gemini/gemini-2.0-flash",
|
21
|
+
custom_system_prompt: Optional[str] = None,
|
22
|
+
output_dir: Optional[str] = None
|
23
|
+
):
|
24
|
+
self.model = model
|
25
|
+
self.custom_system_prompt = custom_system_prompt or (
|
26
|
+
"Convert the PDF page to a clean, well-formatted Markdown document. "
|
27
|
+
"Preserve structure, headings, and any code or mathematical notation. "
|
28
|
+
"For the images and chart, create a literal description what is visible. "
|
29
|
+
"Return only pure Markdown content, excluding any metadata or non-Markdown elements."
|
30
|
+
)
|
31
|
+
self.output_dir = output_dir
|
32
|
+
|
33
|
+
@staticmethod
|
34
|
+
def validate_pdf_path(pdf_path: str) -> bool:
|
35
|
+
"""Validate the PDF file path."""
|
36
|
+
if not pdf_path:
|
37
|
+
logger.error("PDF path is required")
|
38
|
+
return False
|
39
|
+
if not os.path.exists(pdf_path):
|
40
|
+
logger.error(f"PDF file not found: {pdf_path}")
|
41
|
+
return False
|
42
|
+
if not pdf_path.lower().endswith(".pdf"):
|
43
|
+
logger.error(f"File must be a PDF: {pdf_path}")
|
44
|
+
return False
|
45
|
+
return True
|
46
|
+
|
47
|
+
async def convert_pdf(
|
48
|
+
self,
|
49
|
+
pdf_path: str,
|
50
|
+
select_pages: Optional[Union[int, List[int]]] = None
|
51
|
+
) -> str:
|
52
|
+
"""Convert a PDF to Markdown using a vision model."""
|
53
|
+
if not self.validate_pdf_path(pdf_path):
|
54
|
+
raise ValueError("Invalid PDF path")
|
55
|
+
|
56
|
+
try:
|
57
|
+
logger.info(f"Calling zerox with model: {self.model}, file: {pdf_path}")
|
58
|
+
zerox_result = await zerox(
|
59
|
+
file_path=pdf_path,
|
60
|
+
model=self.model,
|
61
|
+
system_prompt=self.custom_system_prompt,
|
62
|
+
output_dir=self.output_dir,
|
63
|
+
select_pages=select_pages
|
64
|
+
)
|
65
|
+
|
66
|
+
markdown_content = ""
|
67
|
+
if hasattr(zerox_result, 'pages') and zerox_result.pages:
|
68
|
+
markdown_content = "\n\n".join(
|
69
|
+
page.content for page in zerox_result.pages
|
70
|
+
if hasattr(page, 'content') and page.content
|
71
|
+
)
|
72
|
+
elif isinstance(zerox_result, str):
|
73
|
+
markdown_content = zerox_result
|
74
|
+
elif hasattr(zerox_result, 'markdown'):
|
75
|
+
markdown_content = zerox_result.markdown
|
76
|
+
elif hasattr(zerox_result, 'text'):
|
77
|
+
markdown_content = zerox_result.text
|
78
|
+
else:
|
79
|
+
markdown_content = str(zerox_result)
|
80
|
+
logger.warning("Unexpected zerox_result type; converted to string.")
|
81
|
+
|
82
|
+
if not markdown_content.strip():
|
83
|
+
logger.warning("Generated Markdown content is empty.")
|
84
|
+
return ""
|
85
|
+
|
86
|
+
logger.info(f"Extracted Markdown content length: {len(markdown_content)} characters")
|
87
|
+
return markdown_content
|
88
|
+
|
89
|
+
except Exception as e:
|
90
|
+
logger.error(f"Error converting PDF to Markdown: {e}")
|
91
|
+
raise
|
92
|
+
|
93
|
+
async def convert_and_save(
|
94
|
+
self,
|
95
|
+
pdf_path: str,
|
96
|
+
output_md: Optional[str] = None,
|
97
|
+
select_pages: Optional[Union[int, List[int]]] = None
|
98
|
+
) -> str:
|
99
|
+
"""Convert PDF to Markdown and optionally save to file."""
|
100
|
+
markdown_content = await self.convert_pdf(pdf_path, select_pages)
|
101
|
+
|
102
|
+
if output_md:
|
103
|
+
output_path = Path(output_md)
|
104
|
+
output_path.parent.mkdir(parents=True, exist_ok=True)
|
105
|
+
with output_path.open("w", encoding="utf-8") as f:
|
106
|
+
f.write(markdown_content)
|
107
|
+
logger.info(f"Saved Markdown to: {output_path}")
|
108
|
+
return str(output_path)
|
109
|
+
|
110
|
+
return markdown_content
|
111
|
+
|
112
|
+
# Typer CLI app
|
113
|
+
app = typer.Typer()
|
114
|
+
|
115
|
+
@app.command()
|
116
|
+
def convert(
|
117
|
+
input_pdf: str = typer.Argument(..., help="Path to the input PDF file"),
|
118
|
+
output_md: Optional[str] = typer.Argument(None, help="Path to save the output Markdown file (defaults to input_pdf_name.md)"),
|
119
|
+
model: str = typer.Option("gemini/gemini-2.0-flash", help="LiteLLM-compatible model name"),
|
120
|
+
system_prompt: Optional[str] = typer.Option(None, help="Custom system prompt for the vision model")
|
121
|
+
):
|
122
|
+
"""Convert a PDF file to Markdown using vision models."""
|
123
|
+
if not PDFToMarkdownConverter.validate_pdf_path(input_pdf):
|
124
|
+
typer.echo(f"Error: Invalid PDF path: {input_pdf}", err=True)
|
125
|
+
raise typer.Exit(code=1)
|
126
|
+
|
127
|
+
if output_md is None:
|
128
|
+
output_md = str(Path(input_pdf).with_suffix(".md"))
|
129
|
+
|
130
|
+
with tempfile.TemporaryDirectory() as temp_dir:
|
131
|
+
try:
|
132
|
+
converter = PDFToMarkdownConverter(
|
133
|
+
model=model,
|
134
|
+
custom_system_prompt=system_prompt,
|
135
|
+
output_dir=temp_dir
|
136
|
+
)
|
137
|
+
output_path = asyncio.run(converter.convert_and_save(input_pdf, output_md))
|
138
|
+
typer.echo(f"PDF converted to Markdown: {output_path}")
|
139
|
+
except Exception as e:
|
140
|
+
typer.echo(f"Error during conversion: {e}", err=True)
|
141
|
+
raise typer.Exit(code=1)
|
142
|
+
|
143
|
+
if __name__ == "__main__":
|
144
|
+
app()
|
@@ -64,7 +64,7 @@ class ReplaceInFileTool(Tool):
|
|
64
64
|
"Returns the updated content or an error."
|
65
65
|
"⚠️ THIS TOOL MUST BE USED IN PRIORITY TO UPDATE AN EXISTING FILE."
|
66
66
|
)
|
67
|
-
need_validation: bool =
|
67
|
+
need_validation: bool = True
|
68
68
|
|
69
69
|
SIMILARITY_THRESHOLD: float = 0.85
|
70
70
|
|
@@ -0,0 +1,293 @@
|
|
1
|
+
"""Tool for capturing terminal recordings and screenshots."""
|
2
|
+
|
3
|
+
import os
|
4
|
+
import shlex
|
5
|
+
import subprocess
|
6
|
+
from datetime import datetime
|
7
|
+
from enum import Enum
|
8
|
+
from pathlib import Path
|
9
|
+
from typing import Optional
|
10
|
+
|
11
|
+
from loguru import logger
|
12
|
+
|
13
|
+
from quantalogic.tools.execute_bash_command_tool import ExecuteBashCommandTool
|
14
|
+
from quantalogic.tools.tool import Tool, ToolArgument
|
15
|
+
|
16
|
+
|
17
|
+
class CaptureType(str, Enum):
|
18
|
+
"""Type of terminal capture."""
|
19
|
+
RECORD = "record"
|
20
|
+
SCREENSHOT = "screenshot"
|
21
|
+
|
22
|
+
|
23
|
+
class TerminalCaptureTool(Tool):
|
24
|
+
"""Tool for capturing terminal output as recordings or screenshots."""
|
25
|
+
|
26
|
+
name: str = "terminal_capture_tool"
|
27
|
+
description: str = "Captures terminal output as recordings or screenshots."
|
28
|
+
need_validation: bool = False
|
29
|
+
arguments: list = [
|
30
|
+
ToolArgument(
|
31
|
+
name="capture_type",
|
32
|
+
arg_type="string",
|
33
|
+
description="Type of capture ('record' or 'screenshot')",
|
34
|
+
required=True,
|
35
|
+
example="screenshot",
|
36
|
+
),
|
37
|
+
ToolArgument(
|
38
|
+
name="output_path",
|
39
|
+
arg_type="string",
|
40
|
+
description="Path where to save the capture (use .cast for recordings, .png for screenshots)",
|
41
|
+
required=True,
|
42
|
+
example="/path/to/output.png",
|
43
|
+
),
|
44
|
+
ToolArgument(
|
45
|
+
name="command",
|
46
|
+
arg_type="string",
|
47
|
+
description="Command to execute and capture (only for recording)",
|
48
|
+
required=False,
|
49
|
+
example="ls -la",
|
50
|
+
),
|
51
|
+
ToolArgument(
|
52
|
+
name="duration",
|
53
|
+
arg_type="int",
|
54
|
+
description="Duration in seconds for recording (only for recording)",
|
55
|
+
required=False,
|
56
|
+
default="30",
|
57
|
+
),
|
58
|
+
ToolArgument(
|
59
|
+
name="overwrite",
|
60
|
+
arg_type="boolean",
|
61
|
+
description="Whether to overwrite existing file",
|
62
|
+
required=False,
|
63
|
+
default="true",
|
64
|
+
),
|
65
|
+
]
|
66
|
+
|
67
|
+
def __init__(self):
|
68
|
+
"""Initialize with ExecuteBashCommandTool for command execution."""
|
69
|
+
super().__init__()
|
70
|
+
self.bash_tool = ExecuteBashCommandTool()
|
71
|
+
|
72
|
+
def _ensure_dependencies(self, capture_type: CaptureType) -> bool:
|
73
|
+
"""Check if required dependencies are installed.
|
74
|
+
|
75
|
+
Args:
|
76
|
+
capture_type: Type of capture to check dependencies for
|
77
|
+
"""
|
78
|
+
if capture_type == CaptureType.RECORD:
|
79
|
+
try:
|
80
|
+
# Check for asciinema using poetry run
|
81
|
+
result = subprocess.run(
|
82
|
+
["poetry", "run", "which", "asciinema"],
|
83
|
+
capture_output=True,
|
84
|
+
text=True,
|
85
|
+
check=False
|
86
|
+
)
|
87
|
+
|
88
|
+
if result.returncode == 0 and result.stdout:
|
89
|
+
logger.info(f"Found asciinema at: {result.stdout.strip()}")
|
90
|
+
return True
|
91
|
+
|
92
|
+
# If not found, try installing it
|
93
|
+
logger.info("Installing asciinema...")
|
94
|
+
install_result = subprocess.run(
|
95
|
+
["poetry", "add", "asciinema"],
|
96
|
+
capture_output=True,
|
97
|
+
text=True,
|
98
|
+
check=False
|
99
|
+
)
|
100
|
+
|
101
|
+
if install_result.returncode == 0:
|
102
|
+
logger.info("Successfully installed asciinema")
|
103
|
+
return True
|
104
|
+
|
105
|
+
logger.error(f"Failed to install asciinema: {install_result.stderr}")
|
106
|
+
return False
|
107
|
+
|
108
|
+
except Exception as e:
|
109
|
+
logger.error(f"Error checking/installing asciinema: {e}")
|
110
|
+
return False
|
111
|
+
|
112
|
+
elif capture_type == CaptureType.SCREENSHOT:
|
113
|
+
try:
|
114
|
+
# Try gnome-screenshot first
|
115
|
+
result = subprocess.run(
|
116
|
+
["which", "gnome-screenshot"],
|
117
|
+
capture_output=True,
|
118
|
+
text=True,
|
119
|
+
check=False
|
120
|
+
)
|
121
|
+
|
122
|
+
if result.returncode == 0:
|
123
|
+
logger.info("Found gnome-screenshot")
|
124
|
+
return True
|
125
|
+
|
126
|
+
# Try import from ImageMagick as fallback
|
127
|
+
result = subprocess.run(
|
128
|
+
["which", "import"],
|
129
|
+
capture_output=True,
|
130
|
+
text=True,
|
131
|
+
check=False
|
132
|
+
)
|
133
|
+
|
134
|
+
if result.returncode == 0:
|
135
|
+
logger.info("Found ImageMagick import")
|
136
|
+
return True
|
137
|
+
|
138
|
+
logger.error("Neither gnome-screenshot nor ImageMagick found")
|
139
|
+
return False
|
140
|
+
|
141
|
+
except Exception as e:
|
142
|
+
logger.error(f"Error checking screenshot dependencies: {e}")
|
143
|
+
return False
|
144
|
+
|
145
|
+
def _capture_screenshot(
|
146
|
+
self,
|
147
|
+
output_path: str,
|
148
|
+
overwrite: bool = True
|
149
|
+
) -> str:
|
150
|
+
"""Capture terminal screenshot.
|
151
|
+
|
152
|
+
Args:
|
153
|
+
output_path: Path to save screenshot
|
154
|
+
overwrite: Whether to overwrite existing file
|
155
|
+
"""
|
156
|
+
try:
|
157
|
+
# Ensure output directory exists
|
158
|
+
output_path = Path(output_path)
|
159
|
+
output_path.parent.mkdir(parents=True, exist_ok=True)
|
160
|
+
|
161
|
+
# Check if file exists and handle overwrite
|
162
|
+
if output_path.exists():
|
163
|
+
if not overwrite:
|
164
|
+
return f"File {output_path} already exists and overwrite=False"
|
165
|
+
try:
|
166
|
+
output_path.unlink()
|
167
|
+
except OSError as e:
|
168
|
+
return f"Failed to remove existing file: {str(e)}"
|
169
|
+
|
170
|
+
# Get active window ID
|
171
|
+
window_id = subprocess.run(
|
172
|
+
["xdotool", "getactivewindow"],
|
173
|
+
capture_output=True,
|
174
|
+
text=True,
|
175
|
+
check=False
|
176
|
+
)
|
177
|
+
|
178
|
+
if window_id.returncode != 0:
|
179
|
+
return "Failed to get active window ID"
|
180
|
+
|
181
|
+
# Try gnome-screenshot first
|
182
|
+
if subprocess.run(["which", "gnome-screenshot"], capture_output=True).returncode == 0:
|
183
|
+
cmd = f"gnome-screenshot --window --file={shlex.quote(str(output_path))}"
|
184
|
+
else:
|
185
|
+
# Fallback to ImageMagick's import
|
186
|
+
cmd = f"import -window {window_id.stdout.strip()} {shlex.quote(str(output_path))}"
|
187
|
+
|
188
|
+
logger.debug(f"Running command: {cmd}")
|
189
|
+
result = self.bash_tool.execute(command=cmd)
|
190
|
+
|
191
|
+
if output_path.exists():
|
192
|
+
return f"Screenshot saved to {output_path}"
|
193
|
+
else:
|
194
|
+
return f"Failed to capture screenshot: {result}"
|
195
|
+
|
196
|
+
except Exception as e:
|
197
|
+
return f"Failed to capture screenshot: {str(e)}"
|
198
|
+
|
199
|
+
def _record_terminal(
|
200
|
+
self,
|
201
|
+
output_path: str,
|
202
|
+
command: str,
|
203
|
+
duration: int = 30,
|
204
|
+
overwrite: bool = True
|
205
|
+
) -> str:
|
206
|
+
"""Record terminal session using asciinema.
|
207
|
+
|
208
|
+
Args:
|
209
|
+
output_path: Path to save recording (.cast file)
|
210
|
+
command: Command to execute
|
211
|
+
duration: Recording duration in seconds
|
212
|
+
overwrite: Whether to overwrite existing file
|
213
|
+
"""
|
214
|
+
try:
|
215
|
+
# Ensure output directory exists
|
216
|
+
output_path = Path(output_path)
|
217
|
+
output_path.parent.mkdir(parents=True, exist_ok=True)
|
218
|
+
|
219
|
+
# Check if file exists and handle overwrite
|
220
|
+
if output_path.exists():
|
221
|
+
if not overwrite:
|
222
|
+
return f"File {output_path} already exists and overwrite=False"
|
223
|
+
try:
|
224
|
+
output_path.unlink()
|
225
|
+
except OSError as e:
|
226
|
+
return f"Failed to remove existing file: {str(e)}"
|
227
|
+
|
228
|
+
# Properly escape the command for shell execution
|
229
|
+
escaped_command = shlex.quote(command)
|
230
|
+
|
231
|
+
# Record terminal session using poetry run to ensure correct virtualenv
|
232
|
+
record_cmd = (
|
233
|
+
f"poetry run asciinema rec -c {escaped_command} "
|
234
|
+
f"-t 'Terminal Recording {datetime.now()}' "
|
235
|
+
f"{shlex.quote(str(output_path))}"
|
236
|
+
)
|
237
|
+
logger.debug(f"Running command: {record_cmd}")
|
238
|
+
|
239
|
+
result = self.bash_tool.execute(
|
240
|
+
command=record_cmd,
|
241
|
+
timeout=duration
|
242
|
+
)
|
243
|
+
|
244
|
+
if output_path.exists():
|
245
|
+
return f"Recording saved to {output_path}"
|
246
|
+
else:
|
247
|
+
return f"Failed to record: {result}"
|
248
|
+
|
249
|
+
except Exception as e:
|
250
|
+
return f"Failed to record terminal: {str(e)}"
|
251
|
+
|
252
|
+
def execute(
|
253
|
+
self,
|
254
|
+
capture_type: str,
|
255
|
+
output_path: str,
|
256
|
+
command: Optional[str] = None,
|
257
|
+
duration: Optional[int] = 30,
|
258
|
+
overwrite: Optional[bool] = True,
|
259
|
+
) -> str:
|
260
|
+
"""Execute the terminal capture.
|
261
|
+
|
262
|
+
Args:
|
263
|
+
capture_type: Type of capture ('record' or 'screenshot')
|
264
|
+
output_path: Path where to save the capture
|
265
|
+
command: Command to execute and capture (only for recording)
|
266
|
+
duration: Duration in seconds for recording (only for recording)
|
267
|
+
overwrite: Whether to overwrite existing file
|
268
|
+
|
269
|
+
Returns:
|
270
|
+
A string indicating success or failure
|
271
|
+
"""
|
272
|
+
try:
|
273
|
+
capture_type_enum = CaptureType(capture_type.lower())
|
274
|
+
except ValueError:
|
275
|
+
return f"Invalid capture type: {capture_type}. Must be 'record' or 'screenshot'"
|
276
|
+
|
277
|
+
if not self._ensure_dependencies(capture_type_enum):
|
278
|
+
if capture_type_enum == CaptureType.RECORD:
|
279
|
+
return "asciinema not found and failed to install it. Please install manually: poetry add asciinema"
|
280
|
+
else:
|
281
|
+
return "Screenshot tools not found. Please install: sudo apt install gnome-screenshot"
|
282
|
+
|
283
|
+
if capture_type_enum == CaptureType.SCREENSHOT:
|
284
|
+
return self._capture_screenshot(output_path, overwrite)
|
285
|
+
else:
|
286
|
+
if not command:
|
287
|
+
return "Command is required for recording"
|
288
|
+
return self._record_terminal(output_path, command, duration, overwrite)
|
289
|
+
|
290
|
+
|
291
|
+
if __name__ == "__main__":
|
292
|
+
tool = TerminalCaptureTool()
|
293
|
+
print(tool.to_markdown())
|
quantalogic/tools/tool.py
CHANGED
@@ -58,6 +58,10 @@ class ToolDefinition(BaseModel):
|
|
58
58
|
default=False,
|
59
59
|
description="When True, requires user confirmation before execution. Useful for tools that perform potentially destructive operations.",
|
60
60
|
)
|
61
|
+
need_post_process: bool = Field(
|
62
|
+
default=True,
|
63
|
+
description="When True, requires user confirmation before execution. Useful for tools that perform potentially destructive operations.",
|
64
|
+
)
|
61
65
|
need_variables: bool = Field(
|
62
66
|
default=False,
|
63
67
|
description="When True, provides access to the agent's variable store. Required for tools that need to interpolate variables (e.g., Jinja templates).",
|
@@ -11,6 +11,7 @@ from .csv_processor_tool import CSVProcessorTool
|
|
11
11
|
from .download_file_tool import PrepareDownloadTool
|
12
12
|
from .mermaid_validator_tool import MermaidValidatorTool
|
13
13
|
from .vscode_tool import VSCodeServerTool
|
14
|
+
from .llm_tool import OrientedLLMTool
|
14
15
|
|
15
16
|
# Define __all__ to control what is imported with `from ... import *`
|
16
17
|
__all__ = [
|
@@ -18,6 +19,7 @@ __all__ = [
|
|
18
19
|
'PrepareDownloadTool',
|
19
20
|
'MermaidValidatorTool',
|
20
21
|
'VSCodeServerTool',
|
22
|
+
'OrientedLLMTool',
|
21
23
|
]
|
22
24
|
|
23
25
|
# Optional: Add logging for import confirmation
|
@@ -20,7 +20,7 @@ class PrepareDownloadTool(Tool):
|
|
20
20
|
"If it's a directory, it will be zipped. "
|
21
21
|
"Returns an HTML link for downloading."
|
22
22
|
)
|
23
|
-
need_validation: bool =
|
23
|
+
need_validation: bool = False
|
24
24
|
arguments: list = [
|
25
25
|
ToolArgument(
|
26
26
|
name="path",
|
@@ -106,10 +106,8 @@ class PrepareDownloadTool(Tool):
|
|
106
106
|
)
|
107
107
|
|
108
108
|
# Create the HTML link with embedded styles
|
109
|
-
html = f'''<a href="{url}"
|
110
|
-
style="{style}"
|
111
|
-
onmouseover="this.style.backgroundColor='#0066cc'; this.style.color='white';"
|
112
|
-
onmouseout="this.style.backgroundColor='transparent'; this.style.color='#0066cc';"
|
109
|
+
html = f'''Using this download functionnal download link : <a href="{url}"
|
110
|
+
style="{style}"
|
113
111
|
download="{filename}">{text}</a>'''
|
114
112
|
|
115
113
|
return html
|