Charles Azam commited on
Commit
7f58cad
·
1 Parent(s): 0868311

feat: init agents scrawl web

Browse files
src/deepengineer/deepsearch/scawl_web_agent.py CHANGED
@@ -1,52 +1,335 @@
1
- def create_agent(model_id="o1"):
2
- model_params = {
3
- "model_id": model_id,
4
- "custom_role_conversions": custom_role_conversions,
5
- "max_completion_tokens": 8192,
6
- }
7
- if model_id == "o1":
8
- model_params["reasoning_effort"] = "high"
9
- model = LiteLLMModel(model_id="deepseek/deepseek-chat")
10
-
11
- text_limit = 100000
12
- browser = SimpleTextBrowser(**BROWSER_CONFIG)
13
- WEB_TOOLS = [
14
- GoogleSearchTool(provider="serper"),
15
- VisitTool(browser),
16
- PageUpTool(browser),
17
- PageDownTool(browser),
18
- FinderTool(browser),
19
- FindNextTool(browser),
20
- ArchiveSearchTool(browser),
21
- TextInspectorTool(model, text_limit),
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
22
  ]
23
- text_webbrowser_agent = ToolCallingAgent(
 
24
  model=model,
25
- tools=WEB_TOOLS,
26
  max_steps=20,
27
  verbosity_level=2,
28
  planning_interval=4,
29
- name="search_agent",
30
- description="""A team member that will search the internet to answer your question.
31
- Ask him for all your questions that require browsing the web.
32
- Provide him as much context as possible, in particular if you need to search on a specific timeframe!
33
- And don't hesitate to provide him with a complex search task, like finding a difference between two webpages.
34
- Your request must be a real sentence, not a google search! Like "Find me this information (...)" rather than a few keywords.
35
- """,
36
- provide_run_summary=True,
37
  )
38
- text_webbrowser_agent.prompt_templates["managed_agent"]["task"] += """You can navigate to .txt online files.
39
- If a non-html page is in another format, especially .pdf or a Youtube video, use tool 'inspect_file_as_text' to inspect it.
40
- Additionally, if after some searching you find out that you need more information to answer the question, you can use `final_answer` with your request for clarification as argument to request for more information."""
 
 
 
 
 
 
41
 
42
- manager_agent = CodeAgent(
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
43
  model=model,
44
- tools=[visualizer, TextInspectorTool(model, text_limit)],
45
- max_steps=12,
46
  verbosity_level=2,
47
- additional_authorized_imports=["*"],
48
  planning_interval=4,
49
- managed_agents=[text_webbrowser_agent],
 
50
  )
 
 
 
 
 
 
 
51
 
52
- return manager_agent
 
1
+ from smolagents import CodeAgent, Tool, LiteLLMModel
2
+ from deepengineer.webcrawler.async_search import (
3
+ linkup_search_async, tavily_search_async, arxiv_search_async,
4
+ pubmed_search_async, scientific_search_async,
5
+ )
6
+ from deepengineer.webcrawler.async_crawl import (
7
+ crawl4ai_extract_markdown_of_url_async, arxiv_download_pdf_async, download_pdf_async
8
+ )
9
+ from deepengineer.webcrawler.pdf_utils import get_table_of_contents_per_page_markdown, convert_ocr_response_to_markdown, get_markdown_by_page_numbers, find_in_markdown
10
+ from mistralai import OCRResponse
11
+ from enum import Enum
12
+ from pathlib import Path
13
+ import asyncio
14
+
15
+ class ToolNames(Enum):
16
+ # Search tools
17
+ TAVILY_SEARCH = "tavily_search"
18
+ LINKUP_SEARCH = "linkup_search"
19
+ ARXIV_SEARCH = "arxiv_search"
20
+ PUBMED_SEARCH = "pubmed_search"
21
+ SCIENCEDIRECT_SEARCH = "sciencedirect_search"
22
+ SCIENTIFIC_SEARCH = "scientific_search"
23
+
24
+ # Crawling tools
25
+ CRAWL_URL = "crawl_url"
26
+ DOWNLOAD_PDF = "download_pdf"
27
+ ARXIV_DOWNLOAD_PDF = "arxiv_download_pdf"
28
+
29
+ # PDF analysis tools (reusing from markdown agent)
30
+ GET_TABLE_OF_CONTENTS = "get_table_of_contents"
31
+ GET_MARKDOWN = "get_markdown"
32
+ GET_PAGES_CONTENT = "get_pages_content"
33
+ FIND_IN_MARKDOWN = "find_in_markdown"
34
+
35
+ class TavilySearchTool(Tool):
36
+ name = ToolNames.TAVILY_SEARCH.value
37
+ description = "Search the web using Tavily API. Good for general web searches with advanced features."
38
+ inputs = {
39
+ "search_query": {
40
+ "type": "string",
41
+ "description": "The search query to execute"
42
+ },
43
+ }
44
+ output_type = "object"
45
+ def forward(self, search_query: str) -> dict:
46
+ result = asyncio.run(tavily_search_async(
47
+ search_query=search_query,
48
+ ))
49
+ return result.model_dump()
50
+
51
+ class LinkupSearchTool(Tool):
52
+ name = ToolNames.LINKUP_SEARCH.value
53
+ description = "Search the web using Linkup API. Good for deep research with sourced answers."
54
+ inputs = {
55
+ "search_query": {
56
+ "type": "string",
57
+ "description": "The search query to execute"
58
+ },
59
+ }
60
+ output_type = "object"
61
+
62
+ def forward(self, search_query: str, depth: str = "standard",
63
+ output_type: str = "sourcedAnswer") -> dict:
64
+ result = asyncio.run(linkup_search_async(
65
+ search_query=search_query,
66
+ depth=depth,
67
+ output_type=output_type
68
+ ))
69
+ return result.model_dump()
70
+
71
+ class ArxivSearchTool(Tool):
72
+ name = ToolNames.ARXIV_SEARCH.value
73
+ description = "Search arXiv for academic papers and preprints."
74
+ inputs = {
75
+ "search_query": {
76
+ "type": "string",
77
+ "description": "The search query to execute on arXiv"
78
+ }
79
+ }
80
+ output_type = "object"
81
+
82
+ def forward(self, search_query: str) -> dict:
83
+ result = asyncio.run(arxiv_search_async(search_query))
84
+ return result.model_dump()
85
+
86
+ class PubmedSearchTool(Tool):
87
+ name = ToolNames.PUBMED_SEARCH.value
88
+ description = "Search PubMed for medical and scientific literature."
89
+ inputs = {
90
+ "search_query": {
91
+ "type": "string",
92
+ "description": "The search query to execute on PubMed"
93
+ }
94
+ }
95
+ output_type = "object"
96
+
97
+ def forward(self, search_query: str) -> dict:
98
+ result = asyncio.run(pubmed_search_async(search_query))
99
+ return result.model_dump()
100
+
101
+ class ScientificSearchTool(Tool):
102
+ name = ToolNames.SCIENTIFIC_SEARCH.value
103
+ description = "Search across multiple scientific domains: Wikipedia, arXiv, PubMed, and ScienceDirect."
104
+ inputs = {
105
+ "search_query": {
106
+ "type": "string",
107
+ "description": "The search query to execute across scientific domains"
108
+ }
109
+ }
110
+ output_type = "object"
111
+
112
+ def forward(self, search_query: str) -> dict:
113
+ result = asyncio.run(scientific_search_async(search_query))
114
+ return result.model_dump()
115
+
116
+ class CrawlUrlTool(Tool):
117
+ name = ToolNames.CRAWL_URL.value
118
+ description = "Extract markdown content from a URL using crawl4ai."
119
+ inputs = {
120
+ "url": {
121
+ "type": "string",
122
+ "description": "The URL to crawl and extract markdown from"
123
+ }
124
+ }
125
+ output_type = "string"
126
+
127
+ def forward(self, url: str) -> str:
128
+ return asyncio.run(crawl4ai_extract_markdown_of_url_async(url))
129
+
130
+ class DownloadPdfTool(Tool):
131
+ name = ToolNames.DOWNLOAD_PDF.value
132
+ description = "Download a PDF file from a URL and store it in the data directory."
133
+ inputs = {
134
+ "url": {
135
+ "type": "string",
136
+ "description": "The URL of the PDF to download"
137
+ },
138
+ "filename": {
139
+ "type": "string",
140
+ "description": "The filename to save the PDF as (without .pdf extension)"
141
+ }
142
+ }
143
+ output_type = "string"
144
+
145
+ def forward(self, url: str, filename: str) -> str:
146
+ # Create data directory if it doesn't exist
147
+ data_dir = Path("data")
148
+ data_dir.mkdir(exist_ok=True)
149
+
150
+ # Create PDFs subdirectory
151
+ pdfs_dir = data_dir / "pdfs"
152
+ pdfs_dir.mkdir(exist_ok=True)
153
+
154
+ output_path = pdfs_dir / f"{filename}.pdf"
155
+
156
+ # Download the PDF
157
+ result_path = asyncio.run(download_pdf_async(url, output_path))
158
+ return f"PDF downloaded successfully to: {result_path}"
159
+
160
+ class ArxivDownloadPdfTool(Tool):
161
+ name = ToolNames.ARXIV_DOWNLOAD_PDF.value
162
+ description = "Download a PDF from arXiv by converting the abstract URL to PDF URL."
163
+ inputs = {
164
+ "url": {
165
+ "type": "string",
166
+ "description": "The arXiv abstract URL (e.g., https://arxiv.org/abs/1234.5678)"
167
+ },
168
+ "filename": {
169
+ "type": "string",
170
+ "description": "The filename to save the PDF as (without .pdf extension)"
171
+ }
172
+ }
173
+ output_type = "string"
174
+
175
+ def forward(self, url: str, filename: str) -> str:
176
+ # Create data directory if it doesn't exist
177
+ data_dir = Path("data")
178
+ data_dir.mkdir(exist_ok=True)
179
+
180
+ # Create PDFs subdirectory
181
+ pdfs_dir = data_dir / "pdfs"
182
+ pdfs_dir.mkdir(exist_ok=True)
183
+
184
+ output_path = pdfs_dir / f"{filename}.pdf"
185
+
186
+ # Download the PDF
187
+ result_path = asyncio.run(arxiv_download_pdf_async(url, output_path))
188
+ return f"arXiv PDF downloaded successfully to: {result_path}"
189
+
190
+ # Reuse the markdown analysis tools from analyse_markdown_agent.py
191
+ class GetTableOfContentsTool(Tool):
192
+ name = ToolNames.GET_TABLE_OF_CONTENTS.value
193
+ description = "Returns all of the titles in the document along with the page number they are on."
194
+ inputs = {}
195
+ output_type = "string"
196
+
197
+ def __init__(self, markdown: OCRResponse):
198
+ super().__init__()
199
+ self.markdown: OCRResponse = markdown
200
+ self.table_of_contents: str = get_table_of_contents_per_page_markdown(self.markdown)
201
+
202
+ def forward(self) -> str:
203
+ return self.table_of_contents
204
+
205
+ class GetMarkdownTool(Tool):
206
+ name = ToolNames.GET_MARKDOWN.value
207
+ description = f"Returns the markdown entire content of the document. Beware this might be too long to be useful, except for small documents, use {ToolNames.GET_PAGES_CONTENT.value} instead. You can use {ToolNames.GET_TABLE_OF_CONTENTS.value} to get the table of contents of the document including the number of pages."
208
+ inputs = {}
209
+ output_type = "string"
210
+
211
+ def __init__(self, markdown: OCRResponse):
212
+ super().__init__()
213
+ self.markdown: OCRResponse = markdown
214
+ self.markdown_content: str = convert_ocr_response_to_markdown(self.markdown)
215
+
216
+ def forward(self) -> str:
217
+ return self.markdown_content
218
+
219
+ class GetPagesContentTool(Tool):
220
+ name = ToolNames.GET_PAGES_CONTENT.value
221
+ description = f"Returns the content of the pages. You can use {ToolNames.GET_TABLE_OF_CONTENTS.value} to get the table of contents of the document including the number of pages. Expects a list of page numbers as integers as input."
222
+ inputs = {
223
+ "page_numbers": {
224
+ "type": "array",
225
+ "description": "The page numbers to get the content of."
226
+ },
227
+ }
228
+ output_type = "string"
229
+
230
+ def __init__(self, markdown: OCRResponse):
231
+ super().__init__()
232
+ self.markdown: OCRResponse = markdown
233
+
234
+ def forward(self, page_numbers: list[int]) -> str:
235
+ return get_markdown_by_page_numbers(self.markdown, page_numbers)
236
+
237
+ class FindInMarkdownTool(Tool):
238
+ name = ToolNames.FIND_IN_MARKDOWN.value
239
+ description = f"Finds the page numbers of the document that contain the search queries. If you are looking for a specific information, you can use this tool to find the page numbers of the document that contain the information and then use {ToolNames.GET_PAGES_CONTENT.value} to get the content of the pages."
240
+ inputs = {
241
+ "search_queries": {
242
+ "type": "array",
243
+ "description": "The search queries to find in the document. List of strings."
244
+ }
245
+ }
246
+ output_type = "array"
247
+
248
+ def __init__(self, markdown: OCRResponse):
249
+ super().__init__()
250
+ self.markdown: OCRResponse = markdown
251
+
252
+ def forward(self, search_queries: list[str]) -> list[int]:
253
+ return find_in_markdown(self.markdown, search_queries)
254
+
255
+ def create_web_search_agent(model_id="deepseek/deepseek-chat"):
256
+ """Create a web search agent with search, crawling, and PDF analysis capabilities."""
257
+
258
+ model = LiteLLMModel(model_id=model_id)
259
+
260
+ # Web search and crawling tools
261
+ WEB_SEARCH_TOOLS = [
262
+ TavilySearchTool(),
263
+ LinkupSearchTool(),
264
+ ArxivSearchTool(),
265
+ PubmedSearchTool(),
266
+ ScientificSearchTool(),
267
+ CrawlUrlTool(),
268
+ DownloadPdfTool(),
269
+ ArxivDownloadPdfTool(),
270
  ]
271
+
272
+ web_search_agent = CodeAgent(
273
  model=model,
274
+ tools=WEB_SEARCH_TOOLS,
275
  max_steps=20,
276
  verbosity_level=2,
277
  planning_interval=4,
278
+ name="web_search_agent",
279
+ description="""A team member that can search the web, crawl URLs, download PDFs, and analyze documents.""",
 
 
 
 
 
 
280
  )
281
+
282
+ web_search_agent.prompt_templates["managed_agent"]["task"] += """
283
+ You can search the web using various APIs (Tavily, Linkup, arXiv, PubMed, ScienceDirect).
284
+ You can crawl URLs to extract markdown content.
285
+ You can download PDFs from URLs or arXiv and store them in the data/pdfs directory.
286
+ For PDF analysis, you'll need to first download the PDF and then use the markdown analysis tools.
287
+ """
288
+
289
+ return web_search_agent
290
 
291
+ def create_web_search_agent_with_pdf_analysis(markdown: OCRResponse, model_id="deepseek/deepseek-chat"):
292
+ """Create a web search agent that also includes PDF analysis capabilities."""
293
+
294
+ model = LiteLLMModel(model_id=model_id)
295
+
296
+ # Web search and crawling tools
297
+ WEB_SEARCH_TOOLS = [
298
+ TavilySearchTool(),
299
+ LinkupSearchTool(),
300
+ ArxivSearchTool(),
301
+ PubmedSearchTool(),
302
+ ScientificSearchTool(),
303
+ CrawlUrlTool(),
304
+ DownloadPdfTool(),
305
+ ArxivDownloadPdfTool(),
306
+ ]
307
+
308
+ # PDF analysis tools (if markdown is provided)
309
+ PDF_ANALYSIS_TOOLS = [
310
+ GetTableOfContentsTool(markdown),
311
+ GetMarkdownTool(markdown),
312
+ GetPagesContentTool(markdown),
313
+ FindInMarkdownTool(markdown),
314
+ ]
315
+
316
+ all_tools = WEB_SEARCH_TOOLS + PDF_ANALYSIS_TOOLS
317
+
318
+ web_search_agent = CodeAgent(
319
  model=model,
320
+ tools=all_tools,
321
+ max_steps=20,
322
  verbosity_level=2,
 
323
  planning_interval=4,
324
+ name="web_search_agent_with_pdf_analysis",
325
+ description="""A team member that can search the web, crawl URLs, download PDFs, and analyze the provided PDF document.""",
326
  )
327
+
328
+ web_search_agent.prompt_templates["managed_agent"]["task"] += """
329
+ You can search the web using various APIs (Linkup, arXiv, PubMed, ScienceDirect).
330
+ You can crawl URLs to extract markdown content.
331
+ You can download PDFs from URLs or arXiv and store them in the data/pdfs directory.
332
+ You can analyze the provided PDF document using the markdown analysis tools.
333
+ """
334
 
335
+ return web_search_agent