quantalogic 0.2.13__py3-none-any.whl → 0.2.15__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.
@@ -24,6 +24,8 @@ from quantalogic.tools import (
24
24
  SearchDefinitionNames,
25
25
  TaskCompleteTool,
26
26
  WriteFileTool,
27
+ DuckDuckGoSearchTool,
28
+ WikipediaSearchTool,
27
29
  )
28
30
 
29
31
  MODEL_NAME = "deepseek/deepseek-chat"
@@ -124,6 +126,8 @@ def create_full_agent(model_name: str, vision_model_name: str | None) -> Agent:
124
126
  MarkitdownTool(),
125
127
  LLMTool(model_name=model_name),
126
128
  DownloadHttpFileTool(),
129
+ WikipediaSearchTool(),
130
+ DuckDuckGoSearchTool(),
127
131
  ]
128
132
 
129
133
  if vision_model_name:
@@ -1,5 +1,7 @@
1
1
  """Generative model module for AI-powered text generation."""
2
2
 
3
+ import functools
4
+
3
5
  import openai
4
6
  from litellm import completion, exceptions, get_max_tokens, get_model_info, token_counter
5
7
  from loguru import logger
@@ -83,6 +85,7 @@ class GenerativeModel:
83
85
  logger.debug(f"Initializing GenerativeModel with model={model}, temperature={temperature}")
84
86
  self.model = model
85
87
  self.temperature = temperature
88
+ self._get_model_info_cached = functools.lru_cache(maxsize=32)(self._get_model_info_impl)
86
89
 
87
90
  # Define retriable exceptions based on LiteLLM's exception mapping
88
91
  RETRIABLE_EXCEPTIONS = (
@@ -235,21 +238,46 @@ class GenerativeModel:
235
238
  litellm_messages.append({"role": "user", "content": str(prompt)})
236
239
  return token_counter(model=self.model, messages=litellm_messages)
237
240
 
238
- def get_model_info(self) -> dict | None:
239
- """Get information about the model."""
240
- logger.debug(f"Retrieving model info for {self.model}")
241
- model_info = get_model_info(self.model)
242
-
243
- if not model_info:
244
- logger.debug("Model info not found, trying without openrouter/ prefix")
245
- model_info = get_model_info(self.model.replace("openrouter/", ""))
246
-
247
- if model_info:
248
- logger.debug(f"Model info retrieved: {model_info.keys()}")
249
- else:
250
- logger.debug("No model info available")
251
-
252
- return model_info
241
+ def _get_model_info_impl(self, model_name: str) -> dict:
242
+ """Get information about the model with prefix fallback logic.
243
+
244
+ Attempts to find model info by progressively removing provider prefixes.
245
+ Raises ValueError if no valid model configuration is found.
246
+ Results are cached to improve performance.
247
+
248
+ Example:
249
+ openrouter/openai/gpt-4o-mini → openai/gpt-4o-mini → gpt-4o-mini
250
+ """
251
+ original_model = model_name
252
+
253
+ while True:
254
+ try:
255
+ logger.debug(f"Attempting to retrieve model info for: {model_name}")
256
+ model_info = get_model_info(model_name)
257
+ if model_info:
258
+ logger.debug(f"Found model info for {model_name}: {model_info}")
259
+ return model_info
260
+ except Exception:
261
+ pass
262
+
263
+ # Try removing one prefix level
264
+ parts = model_name.split('/')
265
+ if len(parts) <= 1:
266
+ break
267
+ model_name = '/'.join(parts[1:])
268
+
269
+ error_msg = f"Could not find model info for {original_model} after trying: {self.model} → {model_name}"
270
+ logger.error(error_msg)
271
+ raise ValueError(error_msg)
272
+
273
+ def get_model_info(self, model_name: str = None) -> dict:
274
+ """Get cached information about the model.
275
+
276
+ If no model name is provided, uses the current model.
277
+ """
278
+ if model_name is None:
279
+ model_name = self.model
280
+ return self._get_model_info_cached(model_name)
253
281
 
254
282
  def get_model_max_input_tokens(self) -> int:
255
283
  """Get the maximum number of input tokens for the model."""
quantalogic/main.py CHANGED
@@ -32,7 +32,6 @@ from quantalogic.agent_config import ( # noqa: E402
32
32
  )
33
33
  from quantalogic.interactive_text_editor import get_multiline_input # noqa: E402
34
34
  from quantalogic.print_event import console_print_events # noqa: E402
35
-
36
35
  from quantalogic.search_agent import create_search_agent
37
36
 
38
37
  AGENT_MODES = ["code", "basic", "interpreter", "full", "code-basic","search"]
@@ -1,16 +1,26 @@
1
1
  from quantalogic.agent import Agent
2
- from quantalogic.tools import InputQuestionTool, SerpApiSearchTool, TaskCompleteTool, WikipediaSearchTool, ReadFileBlockTool,ReadFileTool, MarkitdownTool, RipgrepTool
2
+ from quantalogic.tools import (
3
+ InputQuestionTool,
4
+ SerpApiSearchTool,
5
+ DuckDuckGoSearchTool,
6
+ TaskCompleteTool,
7
+ WikipediaSearchTool,
8
+ ReadFileBlockTool,
9
+ ReadFileTool,
10
+ MarkitdownTool,
11
+ RipgrepTool
12
+ )
3
13
 
4
14
 
5
15
  def create_search_agent(model_name: str) -> Agent:
6
- """Creates and configures a search agent with web and knowledge search tools.
16
+ """Creates and configures a search agent with web, knowledge, and privacy-focused search tools.
7
17
 
8
18
  Args:
9
19
  model_name (str): Name of the language model to use for the agent's core capabilities
10
20
 
11
21
  Returns:
12
22
  Agent: A fully configured search agent instance with:
13
- - Web search capabilities (SerpAPI)
23
+ - Web search capabilities (SerpAPI, DuckDuckGo)
14
24
  - Knowledge search capabilities (Wikipedia)
15
25
  - Basic interaction tools
16
26
  """
@@ -22,6 +32,7 @@ def create_search_agent(model_name: str) -> Agent:
22
32
  tools = [
23
33
  # Search tools
24
34
  SerpApiSearchTool(), # Web search capabilities
35
+ DuckDuckGoSearchTool(), # Privacy-focused web search
25
36
  WikipediaSearchTool(), # Knowledge search capabilities
26
37
  # Basic interaction tools
27
38
  TaskCompleteTool(), # Marks task completion
@@ -18,6 +18,7 @@ from .replace_in_file_tool import ReplaceInFileTool
18
18
  from .ripgrep_tool import RipgrepTool
19
19
  from .search_definition_names import SearchDefinitionNames
20
20
  from .serpapi_search_tool import SerpApiSearchTool
21
+ from .duckduckgo_search_tool import DuckDuckGoSearchTool
21
22
  from .task_complete_tool import TaskCompleteTool
22
23
  from .tool import Tool, ToolArgument
23
24
  from .unified_diff_tool import UnifiedDiffTool
@@ -27,6 +28,7 @@ from .write_file_tool import WriteFileTool
27
28
  __all__ = [
28
29
  "WikipediaSearchTool",
29
30
  "SerpApiSearchTool",
31
+ "DuckDuckGoSearchTool",
30
32
  "Tool",
31
33
  "ToolArgument",
32
34
  "TaskCompleteTool",
@@ -0,0 +1,214 @@
1
+ """Tool for interacting with DuckDuckGo for search results."""
2
+
3
+ from duckduckgo_search import DDGS
4
+
5
+ from quantalogic.tools.tool import Tool, ToolArgument
6
+
7
+
8
+ class DuckDuckGoSearchTool(Tool):
9
+ """Tool for retrieving search results from DuckDuckGo.
10
+
11
+ This tool provides a convenient interface to DuckDuckGo's search capabilities,
12
+ supporting multiple search types and structured JSON output.
13
+
14
+ Example usage:
15
+ ```python
16
+ tool = DuckDuckGoSearchTool()
17
+ results = tool.execute(
18
+ query="machine learning",
19
+ search_type="text",
20
+ max_results=10,
21
+ region="us-en",
22
+ safesearch="moderate"
23
+ )
24
+ print(results)
25
+ ```
26
+
27
+ The tool handles:
28
+ - Query validation
29
+ - API error handling
30
+ - Multiple search types (text, images, videos, news)
31
+ - Scope filtering (region, safesearch, timelimit)
32
+ - JSON result formatting
33
+ """
34
+
35
+ name: str = "duckduckgo_tool"
36
+ description: str = (
37
+ "Retrieves search results from DuckDuckGo. "
38
+ "Provides structured output of search results."
39
+ )
40
+ arguments: list = [
41
+ ToolArgument(
42
+ name="query",
43
+ arg_type="string",
44
+ description="The search query to execute",
45
+ required=True,
46
+ example="machine learning",
47
+ ),
48
+ ToolArgument(
49
+ name="max_results",
50
+ arg_type="int",
51
+ description="Maximum number of results to retrieve (1-50)",
52
+ required=True,
53
+ default="10",
54
+ example="20",
55
+ ),
56
+ ToolArgument(
57
+ name="search_type",
58
+ arg_type="string",
59
+ description="Type of search to perform (text, images, videos, news)",
60
+ required=False,
61
+ default="text",
62
+ example="images",
63
+ ),
64
+ ToolArgument(
65
+ name="region",
66
+ arg_type="string",
67
+ description="Region for search results (e.g., 'wt-wt', 'us-en')",
68
+ required=False,
69
+ default="wt-wt",
70
+ example="us-en",
71
+ ),
72
+ ToolArgument(
73
+ name="safesearch",
74
+ arg_type="string",
75
+ description="Safesearch level ('on', 'moderate', 'off')",
76
+ required=False,
77
+ default="moderate",
78
+ example="moderate",
79
+ ),
80
+ ToolArgument(
81
+ name="timelimit",
82
+ arg_type="string",
83
+ description="Time limit for results (e.g., 'd' for day, 'w' for week)",
84
+ required=False,
85
+ default=None,
86
+ example="d",
87
+ ),
88
+ ]
89
+
90
+ def execute(
91
+ self,
92
+ query: str,
93
+ max_results: int = 10,
94
+ search_type: str = "text",
95
+ region: str = "wt-wt",
96
+ safesearch: str = "moderate",
97
+ timelimit: str = None,
98
+ ) -> str:
99
+ """Execute a search query using DuckDuckGo and return results.
100
+
101
+ Args:
102
+ query: The search query to execute
103
+ max_results: Maximum number of results to retrieve (1-50)
104
+ search_type: Type of search to perform (text, images, videos, news)
105
+ region: Region for search results (e.g., "wt-wt", "us-en")
106
+ safesearch: Safesearch level ("on", "moderate", "off")
107
+ timelimit: Time limit for results (e.g., "d" for day, "w" for week)
108
+
109
+ Returns:
110
+ Pretty-printed JSON string of search results.
111
+
112
+ Raises:
113
+ ValueError: If any parameter is invalid
114
+ RuntimeError: If search fails
115
+ """
116
+ # Handle empty string parameters by setting to defaults
117
+ query = str(query) if query else query
118
+ search_type = search_type if search_type else "text"
119
+ region = region if region else "wt-wt"
120
+ safesearch = safesearch if safesearch else "moderate"
121
+ timelimit = timelimit if timelimit else None
122
+
123
+ # Validate and convert query
124
+ if not query:
125
+ raise ValueError("Query must be a non-empty string")
126
+ try:
127
+ query = str(query)
128
+ except (TypeError, ValueError) as e:
129
+ raise ValueError(f"Query must be convertible to string: {str(e)}")
130
+
131
+ # Validate and convert max_results
132
+ try:
133
+ max_results = int(max_results)
134
+ if max_results < 1 or max_results > 50:
135
+ raise ValueError("Number of results must be between 1 and 50")
136
+ except (TypeError, ValueError) as e:
137
+ raise ValueError(f"Invalid number of results: {str(e)}")
138
+
139
+ # Validate search_type
140
+ if search_type not in ["text", "images", "videos", "news"]:
141
+ raise ValueError("search_type must be one of: text, images, videos, news")
142
+
143
+ # Validate safesearch
144
+ if safesearch not in ["on", "moderate", "off"]:
145
+ raise ValueError("safesearch must be one of: on, moderate, off")
146
+
147
+ try:
148
+ ddgs = DDGS()
149
+
150
+ # Perform the appropriate search based on search_type
151
+ if search_type == "text":
152
+ results = ddgs.text(
153
+ keywords=query,
154
+ region=region,
155
+ safesearch=safesearch,
156
+ timelimit=timelimit,
157
+ max_results=max_results,
158
+ )
159
+ elif search_type == "images":
160
+ results = ddgs.images(
161
+ keywords=query,
162
+ region=region,
163
+ safesearch=safesearch,
164
+ timelimit=timelimit,
165
+ max_results=max_results,
166
+ )
167
+ elif search_type == "videos":
168
+ results = ddgs.videos(
169
+ keywords=query,
170
+ region=region,
171
+ safesearch=safesearch,
172
+ timelimit=timelimit,
173
+ max_results=max_results,
174
+ )
175
+ elif search_type == "news":
176
+ results = ddgs.news(
177
+ keywords=query,
178
+ region=region,
179
+ safesearch=safesearch,
180
+ timelimit=timelimit,
181
+ max_results=max_results,
182
+ )
183
+
184
+ # Return pretty-printed JSON
185
+ import json
186
+ return json.dumps(results, indent=4, ensure_ascii=False)
187
+
188
+ except Exception as e:
189
+ raise RuntimeError(f"Search failed: {str(e)}")
190
+
191
+
192
+ def main():
193
+ """Demonstrate DuckDuckGoSearchTool functionality."""
194
+ try:
195
+ tool = DuckDuckGoSearchTool()
196
+
197
+ # Test basic search functionality
198
+ print("Testing DuckDuckGoSearchTool with sample query...")
199
+ results = tool.execute(query="Python programming", max_results=3)
200
+ print(results)
201
+
202
+ # Test error handling
203
+ print("\nTesting error handling with invalid query...")
204
+ try:
205
+ tool.execute(query="")
206
+ except ValueError as e:
207
+ print(f"Caught expected ValueError: {e}")
208
+
209
+ except Exception as e:
210
+ print(f"Error in main: {e}")
211
+
212
+
213
+ if __name__ == "__main__":
214
+ main()
@@ -47,50 +47,53 @@ class MarkitdownTool(Tool):
47
47
  Returns:
48
48
  str: The Markdown content or a success message.
49
49
  """
50
- # Handle tilde expansion for local paths
51
- if file_path.startswith("~"):
52
- file_path = os.path.expanduser(file_path)
53
-
54
- # Handle URL paths
55
- if file_path.startswith(("http://", "https://")):
56
- try:
57
- # Create a temporary file
58
- with tempfile.NamedTemporaryFile(delete=False) as temp_file:
59
- temp_path = temp_file.name
60
- # Download the file from URL
61
- download_http_file(file_path, temp_path)
62
- # Use the temporary file path for conversion
63
- file_path = temp_path
64
- is_temp_file = True
65
- except Exception as e:
66
- return f"Error downloading file from URL: {str(e)}"
67
- else:
68
- is_temp_file = False
69
-
70
50
  try:
71
- from markitdown import MarkItDown
51
+ # Handle URL paths first
52
+ if file_path.startswith(("http://", "https://")):
53
+ try:
54
+ with tempfile.NamedTemporaryFile(delete=False) as temp_file:
55
+ temp_path = temp_file.name
56
+ download_http_file(file_path, temp_path)
57
+ file_path = temp_path
58
+ is_temp_file = True
59
+ except Exception as e:
60
+ return f"Error downloading file from URL: {str(e)}"
61
+ else:
62
+ is_temp_file = False
63
+ # Handle local paths
64
+ if file_path.startswith("~"):
65
+ file_path = os.path.expanduser(file_path)
66
+ if not os.path.isabs(file_path):
67
+ file_path = os.path.abspath(file_path)
68
+
69
+ # Verify file exists
70
+ if not os.path.exists(file_path):
71
+ return f"Error: File not found at path: {file_path}"
72
72
 
73
+ from markitdown import MarkItDown
73
74
  md = MarkItDown()
74
75
  result = md.convert(file_path)
75
76
 
76
77
  if output_file_path:
78
+ # Ensure output directory exists
79
+ output_dir = os.path.dirname(output_file_path)
80
+ if output_dir and not os.path.exists(output_dir):
81
+ os.makedirs(output_dir)
82
+
77
83
  with open(output_file_path, "w", encoding="utf-8") as f:
78
84
  f.write(result.text_content)
79
- output_message = f"Markdown content successfully written to {output_file_path}"
80
- else:
81
- # Truncate content if it exceeds MAX_LINES
82
- lines = result.text_content.splitlines()
83
- if len(lines) > MAX_LINES:
84
- truncated_content = "\n".join(lines[:MAX_LINES])
85
- output_message = f"Markdown content truncated to {MAX_LINES} lines:\n{truncated_content}"
86
- else:
87
- output_message = result.text_content
88
-
89
- return output_message
85
+ return f"Markdown content successfully written to {output_file_path}"
86
+
87
+ # Handle content truncation
88
+ lines = result.text_content.splitlines()
89
+ if len(lines) > MAX_LINES:
90
+ truncated_content = "\n".join(lines[:MAX_LINES])
91
+ return f"Markdown content truncated to {MAX_LINES} lines:\n{truncated_content}"
92
+ return result.text_content
93
+
90
94
  except Exception as e:
91
95
  return f"Error converting file to Markdown: {str(e)}"
92
96
  finally:
93
- # Clean up temporary file if it was created
94
97
  if is_temp_file and os.path.exists(file_path):
95
98
  os.remove(file_path)
96
99
 
@@ -16,7 +16,8 @@ def check_if_is_latest_version() -> (bool,str|None):
16
16
  response = requests.get("https://pypi.org/pypi/quantalogic/json", timeout=5)
17
17
  response.raise_for_status()
18
18
  latest_version = response.json()["info"]["version"]
19
- return version.parse(current_version) < version.parse(latest_version), latest_version
19
+ has_new_version = version.parse(current_version) < version.parse(latest_version)
20
+ return has_new_version, latest_version
20
21
  except (requests.RequestException, KeyError):
21
22
  return False, None
22
23
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: quantalogic
3
- Version: 0.2.13
3
+ Version: 0.2.15
4
4
  Summary: QuantaLogic ReAct Agents
5
5
  Author: Raphaël MANSUY
6
6
  Author-email: raphael.mansuy@gmail.com
@@ -10,6 +10,7 @@ Classifier: Programming Language :: Python :: 3.12
10
10
  Classifier: Programming Language :: Python :: 3.13
11
11
  Requires-Dist: boto3 (>=1.35.86,<2.0.0)
12
12
  Requires-Dist: click (>=8.1.8,<9.0.0)
13
+ Requires-Dist: duckduckgo-search (>=7.2.1,<8.0.0)
13
14
  Requires-Dist: fastapi (>=0.115.6,<0.116.0)
14
15
  Requires-Dist: google-auth (>=2.20.0,<3.0.0)
15
16
  Requires-Dist: google-search-results (>=2.4.2,<3.0.0)
@@ -1,16 +1,16 @@
1
1
  quantalogic/__init__.py,sha256=HFk7_19UzHzYwvPzb9QTQ4w_lPwTTPda61AYb8qggZY,686
2
2
  quantalogic/agent.py,sha256=ROE9hJe9b2eRhhuH64-rxWWHsXO4lnjV-PdjqqdxS_g,26653
3
- quantalogic/agent_config.py,sha256=iPJjY8OZDOoV7lYuL5TAj5JXPcRL4VUOzu5CZVeB3Sg,4520
3
+ quantalogic/agent_config.py,sha256=RWXnRpZeT_twtDIFVHY0K7EdHLmEbb8LvVGRr0oc3Q8,4634
4
4
  quantalogic/coding_agent.py,sha256=FrodyypgtOOV-AvJiQP8PLkUIDQkDrye26dbOxIEKjM,3486
5
5
  quantalogic/event_emitter.py,sha256=jqot2g4JRXc88K6PW837Oqxbf7shZfO-xdPaUWmzupk,7901
6
- quantalogic/generative_model.py,sha256=LKuv_aRpEwMgeEUzHg4r3XkQdPHAr4Mu-ZfWIAqR4Ik,10651
6
+ quantalogic/generative_model.py,sha256=pDbjwPfOQEtuZBk8o9uT8vxjauFfXwTM7GkMco73jfU,11787
7
7
  quantalogic/interactive_text_editor.py,sha256=kYeTA2qej5kxtPvAUHy_Dr2MhrGQAyenLFpW9mU9Rmw,6855
8
- quantalogic/main.py,sha256=LitSK1GfFgBAAbe6WiL9xOPyu9-UZB3PDqWINH_cPLU,11144
8
+ quantalogic/main.py,sha256=jIIFuQQXK29F-Ue95H6Wv0g-u0Q1VYTNRzZJlXP57NI,11143
9
9
  quantalogic/memory.py,sha256=zbtRuM05jaS2lJll-92dt5JfYVLERnF_m_9xqp2x-k0,6304
10
10
  quantalogic/model_names.py,sha256=UZlz25zG9B2dpfwdw_e1Gw5qFsKQ7iME9FJh9Ts4u6s,938
11
11
  quantalogic/print_event.py,sha256=nl1aRdYnsU72SRezafePF82zKtrqGfY8OoTx2QfbdE8,2206
12
12
  quantalogic/prompts.py,sha256=BHIST57DYcTeTb7rvV1QkGLt0_B8Wk8a_9tsnsN6suk,3547
13
- quantalogic/search_agent.py,sha256=HD13Ug8Zn6GpCn_3WwzNE0fhyLoitldv9lwmZ4SxCfk,1545
13
+ quantalogic/search_agent.py,sha256=kGHu-tVm85rEMx7dpcxScAraVC5V0Wp_JeIZ42iNJRI,1700
14
14
  quantalogic/server/__init__.py,sha256=8sz_PYAUCrkM6JM5EAUeIzNM4NPW6j6UT72JVkc21WQ,91
15
15
  quantalogic/server/agent_server.py,sha256=38GEK_MpLp--CX_dCkopTFOU7KcGuOw4-GciwmRJyyg,22502
16
16
  quantalogic/server/models.py,sha256=nVUGWElOsUw8QnRCGJylk25wCew_5gohe6nldYighUA,1322
@@ -20,9 +20,10 @@ quantalogic/server/static/js/event_visualizer.js,sha256=eFkkWyNZw3zOZlF18kxbfsWq
20
20
  quantalogic/server/static/js/quantalogic.js,sha256=x7TrlZGR1Y0WLK2DWl1xY847BhEWMPnL0Ua7KtOldUc,22311
21
21
  quantalogic/server/templates/index.html,sha256=nDnXJoQEm1vXbhXtgaYk0G5VXj0wwzE6KrqEDhHFpj4,7773
22
22
  quantalogic/tool_manager.py,sha256=JAC5E5kLfYzYJx0QRIWbG14q1hlkOcwJFBG7HE8twpU,2425
23
- quantalogic/tools/__init__.py,sha256=ZjcsmkDkg79P8EkH7SkyqP2KiJndRqBjEji7KUDv6mw,1653
23
+ quantalogic/tools/__init__.py,sha256=hAHP1-jrDAbvzoo-noV2mokYbUZ4Em7agagnq3W5dH4,1738
24
24
  quantalogic/tools/agent_tool.py,sha256=MXCXxWHRch7VK4UWhtRP1jeI8Np9Ne2CUGo8vm1oZiM,3064
25
25
  quantalogic/tools/download_http_file_tool.py,sha256=wTfanbXjIRi5-qrbluuLvNmDNhvmYAnlMVb3dO8C2ss,2210
26
+ quantalogic/tools/duckduckgo_search_tool.py,sha256=xVaEb_SUK5NL3lwMQXj1rGQYYvNT-td-qaB9QCes27Q,7014
26
27
  quantalogic/tools/edit_whole_content_tool.py,sha256=nXmpAvojvqvAcqNMy1kUKZ1ocboky_ZcnCR4SNCSPgw,2360
27
28
  quantalogic/tools/elixir_tool.py,sha256=fzPPtAW-Koy9KB0r5k2zV1f1U0WphL-LXPPOBkeNkug,7652
28
29
  quantalogic/tools/execute_bash_command_tool.py,sha256=fnx-zSPpxR2EofaleygAorrR21gRs43jBWh7IBAoNKw,4131
@@ -40,7 +41,7 @@ quantalogic/tools/language_handlers/typescript_handler.py,sha256=L4vuJMYxKO3_83d
40
41
  quantalogic/tools/list_directory_tool.py,sha256=8Hy38DelSh-mRqS_uDLpeBYoHLtEy5ji77xI-TJu3Ms,4176
41
42
  quantalogic/tools/llm_tool.py,sha256=9SNApqvNT2qF9GWxEVzMKq8XiVtc-UoyamhsIWvgZQI,5456
42
43
  quantalogic/tools/llm_vision_tool.py,sha256=OP2B6bYjnKvO7TjSG0gEtwvV5L0m4uFDWAaU54cOG3w,4913
43
- quantalogic/tools/markitdown_tool.py,sha256=GHJMPdWmwF-CBu3vHWhy-kXJYRDluFkh18KN06yNHOc,4101
44
+ quantalogic/tools/markitdown_tool.py,sha256=lpbJBLx43_x2DjiZAV1HSidkHeqkkV0KvgeLG2fphK4,4339
44
45
  quantalogic/tools/nodejs_tool.py,sha256=zdnE0VFj_5786uR2L0o-SKR0Gk8L-U7rdj7xGHJYIq0,19905
45
46
  quantalogic/tools/python_tool.py,sha256=70HLbfU2clOBgj4axDOtIKzXwEBMNGEAX1nGSf-KNNQ,18156
46
47
  quantalogic/tools/read_file_block_tool.py,sha256=FTcDAUOOPQOvWRjnRI6nMI1Upus90klR4PC0pbPP_S8,5266
@@ -56,7 +57,7 @@ quantalogic/tools/wikipedia_search_tool.py,sha256=bdZ_0dYTxpEfU04tBFsatnLM5P9Z3k
56
57
  quantalogic/tools/write_file_tool.py,sha256=_mx9_Zjg2oMAAVzlcHEKjZVZUxQVgbRfcoMKgWnoZcg,3764
57
58
  quantalogic/utils/__init__.py,sha256=Ltq7tzLuHCl9BpCvfRVA9Sjrtp1RJesrn7G980lbl_c,563
58
59
  quantalogic/utils/ask_user_validation.py,sha256=F0jkbFJVXAImcSSP7op6dov5i80hRvZGRvBHbfcZrxg,340
59
- quantalogic/utils/check_version.py,sha256=gGfT7hn64Rsrs-wWMJQJsoP4dGvyR6Q7AHHFogpmbLs,1094
60
+ quantalogic/utils/check_version.py,sha256=grxTfJE85GMue1OAk8z8_q8tjEJxQ8RO6fN3fJ_qedg,1136
60
61
  quantalogic/utils/download_http_file.py,sha256=FTN3brq9WvCFvuBX-lYAhjsdYTzQT4m9m2vqlcyjkNk,3472
61
62
  quantalogic/utils/get_coding_environment.py,sha256=ujZ2_nDryrLWe6ZUalSu9WDG6t53UJFn7FJ_ER7Jixc,389
62
63
  quantalogic/utils/get_environment.py,sha256=7wWruSHYTUlnQWW27qU3WFYZnncqqqdofsxAsUU7lhw,875
@@ -67,8 +68,8 @@ quantalogic/utils/read_http_text_content.py,sha256=n3IayT5KcqctIVVF2gOQQAMf3Ow6e
67
68
  quantalogic/version.py,sha256=ea_cRutaQk5_lwlLbUUvPFuOT7Of7-gAsDl7wdveS-g,107
68
69
  quantalogic/xml_parser.py,sha256=cTRorr5sVfkIzH72M0C-GQ9ROGPiz2FTT66U9ndjzhE,9538
69
70
  quantalogic/xml_tool_parser.py,sha256=lsVzClZBrZan7wjCuCKnGHWzksXI3VMy_vWthxu2_bo,3738
70
- quantalogic-0.2.13.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
71
- quantalogic-0.2.13.dist-info/METADATA,sha256=-OOOtDoX2-bad_R1v3kV-Zv_ujvPl8_dtiN3lXn7wcU,41650
72
- quantalogic-0.2.13.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
73
- quantalogic-0.2.13.dist-info/entry_points.txt,sha256=wgSq5SRU98yvlRHGEZD1Xn7sS5CSjH2RfUtTa6Qy28Q,52
74
- quantalogic-0.2.13.dist-info/RECORD,,
71
+ quantalogic-0.2.15.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
72
+ quantalogic-0.2.15.dist-info/METADATA,sha256=eIK_0Ll5qQfhvkqfVXCuuTmrwKzwC-_MVTrkZrDxNO4,41700
73
+ quantalogic-0.2.15.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
74
+ quantalogic-0.2.15.dist-info/entry_points.txt,sha256=wgSq5SRU98yvlRHGEZD1Xn7sS5CSjH2RfUtTa6Qy28Q,52
75
+ quantalogic-0.2.15.dist-info/RECORD,,