ellements 0.2.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.
- ellements/__init__.py +57 -0
- ellements/agents/__init__.py +45 -0
- ellements/agents/backend.py +100 -0
- ellements/agents/builder.py +303 -0
- ellements/agents/claude_backend.py +200 -0
- ellements/agents/controller.py +237 -0
- ellements/agents/openai_backend.py +187 -0
- ellements/agents/py.typed +0 -0
- ellements/agents/runner.py +358 -0
- ellements/agents/tools.py +30 -0
- ellements/benchmarking/__init__.py +52 -0
- ellements/benchmarking/harness.py +342 -0
- ellements/benchmarking/py.typed +0 -0
- ellements/benchmarking/results.py +173 -0
- ellements/cli/__init__.py +80 -0
- ellements/cli/adapters.py +73 -0
- ellements/cli/agent_tui.py +1178 -0
- ellements/cli/components.py +411 -0
- ellements/cli/printer.py +229 -0
- ellements/cli/py.typed +0 -0
- ellements/core/__init__.py +112 -0
- ellements/core/async_utils.py +42 -0
- ellements/core/budgeting/__init__.py +43 -0
- ellements/core/budgeting/client.py +276 -0
- ellements/core/budgeting/protocol.py +51 -0
- ellements/core/budgeting/trackers.py +177 -0
- ellements/core/caching/__init__.py +51 -0
- ellements/core/caching/cache.py +73 -0
- ellements/core/caching/client.py +300 -0
- ellements/core/caching/disk.py +128 -0
- ellements/core/caching/keys.py +97 -0
- ellements/core/caching/memory.py +104 -0
- ellements/core/chunking.py +262 -0
- ellements/core/config.py +180 -0
- ellements/core/exceptions.py +145 -0
- ellements/core/llm/__init__.py +46 -0
- ellements/core/llm/client.py +1124 -0
- ellements/core/llm/images.py +226 -0
- ellements/core/llm/messages.py +202 -0
- ellements/core/llm/model_params.py +66 -0
- ellements/core/llm/protocol.py +146 -0
- ellements/core/llm/requests.py +100 -0
- ellements/core/llm/structured.py +91 -0
- ellements/core/llm/wrapper.py +79 -0
- ellements/core/observability/__init__.py +39 -0
- ellements/core/observability/events.py +169 -0
- ellements/core/observability/jsonl_logger.py +244 -0
- ellements/core/observability/markdown_formatter.py +197 -0
- ellements/core/observability/observer.py +56 -0
- ellements/core/prompting/__init__.py +14 -0
- ellements/core/prompting/context.py +185 -0
- ellements/core/prompting/guideline.py +133 -0
- ellements/core/prompting/persona.py +267 -0
- ellements/core/prompting/sources.py +92 -0
- ellements/core/py.typed +0 -0
- ellements/core/rate_limit/__init__.py +44 -0
- ellements/core/rate_limit/bucket.py +85 -0
- ellements/core/rate_limit/client.py +216 -0
- ellements/core/rate_limit/protocol.py +27 -0
- ellements/core/templating.py +126 -0
- ellements/core/tools/__init__.py +51 -0
- ellements/core/tools/dialects.py +136 -0
- ellements/core/tools/executor.py +48 -0
- ellements/core/tools/protocol.py +36 -0
- ellements/core/tools/records.py +28 -0
- ellements/core/tools/registry.py +205 -0
- ellements/core/tools/schemas.py +80 -0
- ellements/core/tools/simple.py +119 -0
- ellements/core/tools/spec.py +33 -0
- ellements/domain_specific/__init__.py +7 -0
- ellements/domain_specific/finance/__init__.py +188 -0
- ellements/domain_specific/finance/calculations.py +837 -0
- ellements/domain_specific/finance/charts.py +426 -0
- ellements/domain_specific/finance/portfolio.py +129 -0
- ellements/domain_specific/finance/quant_analysis.py +279 -0
- ellements/domain_specific/finance/risk.py +362 -0
- ellements/domain_specific/finance/technical_indicators.py +241 -0
- ellements/domain_specific/finance/tools.py +483 -0
- ellements/domain_specific/finance/valuation.py +312 -0
- ellements/domain_specific/finance/yahoo_finance.py +1523 -0
- ellements/domain_specific/finance/yahoo_finance_models.py +321 -0
- ellements/domain_specific/py.typed +0 -0
- ellements/execution/__init__.py +56 -0
- ellements/execution/callbacks.py +149 -0
- ellements/execution/catalog.py +70 -0
- ellements/execution/collaborative.py +191 -0
- ellements/execution/config.py +135 -0
- ellements/execution/py.typed +0 -0
- ellements/execution/reflection.py +156 -0
- ellements/execution/self_consistency.py +189 -0
- ellements/execution/single_call.py +48 -0
- ellements/execution/strategies.py +237 -0
- ellements/execution/tree_of_thought.py +541 -0
- ellements/fslm/__init__.py +108 -0
- ellements/fslm/builtins.py +103 -0
- ellements/fslm/cli.py +199 -0
- ellements/fslm/context.py +50 -0
- ellements/fslm/definition.py +163 -0
- ellements/fslm/det.py +173 -0
- ellements/fslm/dsl.py +458 -0
- ellements/fslm/errors.py +38 -0
- ellements/fslm/evaluators.py +141 -0
- ellements/fslm/kernel.py +603 -0
- ellements/fslm/loading.py +123 -0
- ellements/fslm/models.py +623 -0
- ellements/fslm/nl.py +87 -0
- ellements/fslm/observers.py +107 -0
- ellements/fslm/persistence.py +72 -0
- ellements/fslm/py.typed +0 -0
- ellements/fslm/rendering.py +551 -0
- ellements/fslm/visualization.py +25 -0
- ellements/reporting/__init__.py +12 -0
- ellements/reporting/charts.py +364 -0
- ellements/reporting/html_generation.py +132 -0
- ellements/reporting/py.typed +0 -0
- ellements/reporting/visualization.py +73 -0
- ellements/standard_tools/__init__.py +22 -0
- ellements/standard_tools/py.typed +0 -0
- ellements/standard_tools/terminal.py +205 -0
- ellements/standard_tools/web/__init__.py +37 -0
- ellements/standard_tools/web/browser_viewer.py +214 -0
- ellements/standard_tools/web/crawler.py +454 -0
- ellements/standard_tools/web/search.py +399 -0
- ellements/standard_tools/web/youtube.py +1297 -0
- ellements-0.2.0.dist-info/METADATA +368 -0
- ellements-0.2.0.dist-info/RECORD +130 -0
- ellements-0.2.0.dist-info/WHEEL +5 -0
- ellements-0.2.0.dist-info/entry_points.txt +2 -0
- ellements-0.2.0.dist-info/licenses/LICENSE +42 -0
- ellements-0.2.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,399 @@
|
|
|
1
|
+
"""Web search functionality using DDGS with backend fallback."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import asyncio
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
try:
|
|
9
|
+
from ddgs import DDGS
|
|
10
|
+
except ImportError:
|
|
11
|
+
from duckduckgo_search import DDGS
|
|
12
|
+
from ellements.core import ToolRegistry
|
|
13
|
+
from ellements.core.exceptions import LLMError
|
|
14
|
+
from pydantic import BaseModel, Field
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class SearchResult(BaseModel):
|
|
18
|
+
"""Represents a single search result."""
|
|
19
|
+
|
|
20
|
+
title: str = Field(description="Title of the search result")
|
|
21
|
+
url: str = Field(description="URL of the search result")
|
|
22
|
+
snippet: str = Field(description="Brief description/snippet of the result")
|
|
23
|
+
source: str = Field(default="google", description="Search backend source")
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class SearchResults(BaseModel):
|
|
27
|
+
"""Container for multiple search results."""
|
|
28
|
+
|
|
29
|
+
query: str = Field(description="The search query that was executed")
|
|
30
|
+
results: list[SearchResult] = Field(description="List of search results")
|
|
31
|
+
total_results: int = Field(description="Total number of results returned")
|
|
32
|
+
|
|
33
|
+
def __len__(self) -> int:
|
|
34
|
+
"""Return the number of results."""
|
|
35
|
+
return len(self.results)
|
|
36
|
+
|
|
37
|
+
def __iter__(self) -> Any:
|
|
38
|
+
"""Allow iteration over results."""
|
|
39
|
+
return iter(self.results)
|
|
40
|
+
|
|
41
|
+
def __getitem__(self, index: int) -> SearchResult:
|
|
42
|
+
"""Allow indexing of results."""
|
|
43
|
+
return self.results[index]
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class WebSearcher:
|
|
47
|
+
"""Web search client using DDGS."""
|
|
48
|
+
|
|
49
|
+
_TEXT_BACKEND_DEFAULTS = ("google", "duckduckgo", "bing", "auto", "html", "lite")
|
|
50
|
+
_NEWS_BACKEND_DEFAULTS = ("duckduckgo", "bing", "yahoo", "auto", "html", "lite")
|
|
51
|
+
|
|
52
|
+
def __init__(
|
|
53
|
+
self,
|
|
54
|
+
region: str = "us-en",
|
|
55
|
+
safe_search: str = "moderate",
|
|
56
|
+
timeout: int = 10,
|
|
57
|
+
max_results: int = 10,
|
|
58
|
+
**kwargs: Any,
|
|
59
|
+
) -> None:
|
|
60
|
+
"""Initialize the web searcher.
|
|
61
|
+
|
|
62
|
+
Args:
|
|
63
|
+
region: Region for search results (e.g., 'us-en', 'uk-en', 'de-de')
|
|
64
|
+
safe_search: Safe search setting ('strict', 'moderate', 'off')
|
|
65
|
+
timeout: Request timeout in seconds
|
|
66
|
+
max_results: Maximum number of results to return
|
|
67
|
+
**kwargs: Additional configuration for DDGS
|
|
68
|
+
"""
|
|
69
|
+
self.region = region
|
|
70
|
+
self.safe_search = safe_search
|
|
71
|
+
self.timeout = timeout
|
|
72
|
+
self.max_results = max_results
|
|
73
|
+
self.config = kwargs
|
|
74
|
+
|
|
75
|
+
def _backend_candidates(self, category: str, backend: str | None) -> list[str]:
|
|
76
|
+
if backend:
|
|
77
|
+
return [backend]
|
|
78
|
+
defaults = (
|
|
79
|
+
self._TEXT_BACKEND_DEFAULTS
|
|
80
|
+
if category == "text"
|
|
81
|
+
else self._NEWS_BACKEND_DEFAULTS
|
|
82
|
+
)
|
|
83
|
+
seen: set[str] = set()
|
|
84
|
+
candidates: list[str] = []
|
|
85
|
+
for candidate in defaults:
|
|
86
|
+
if candidate in seen:
|
|
87
|
+
continue
|
|
88
|
+
seen.add(candidate)
|
|
89
|
+
candidates.append(candidate)
|
|
90
|
+
return candidates
|
|
91
|
+
|
|
92
|
+
def _run_ddgs_query(
|
|
93
|
+
self,
|
|
94
|
+
ddgs: DDGS,
|
|
95
|
+
category: str,
|
|
96
|
+
query: str,
|
|
97
|
+
region: str,
|
|
98
|
+
safesearch: str,
|
|
99
|
+
timelimit: str | None,
|
|
100
|
+
max_results: int,
|
|
101
|
+
backend: str,
|
|
102
|
+
**kwargs: Any,
|
|
103
|
+
) -> list[dict[str, Any]]:
|
|
104
|
+
method = ddgs.text if category == "text" else ddgs.news
|
|
105
|
+
common_kwargs = {
|
|
106
|
+
"region": region,
|
|
107
|
+
"safesearch": safesearch,
|
|
108
|
+
"timelimit": timelimit,
|
|
109
|
+
"max_results": max_results,
|
|
110
|
+
"backend": backend,
|
|
111
|
+
**kwargs,
|
|
112
|
+
}
|
|
113
|
+
try:
|
|
114
|
+
return list(method(query, **common_kwargs))
|
|
115
|
+
except TypeError:
|
|
116
|
+
return list(method(keywords=query, **common_kwargs))
|
|
117
|
+
|
|
118
|
+
def search(
|
|
119
|
+
self,
|
|
120
|
+
query: str,
|
|
121
|
+
max_results: int | None = None,
|
|
122
|
+
region: str | None = None,
|
|
123
|
+
safe_search: str | None = None,
|
|
124
|
+
time_range: str | None = None,
|
|
125
|
+
backend: str | None = None,
|
|
126
|
+
**kwargs: Any,
|
|
127
|
+
) -> SearchResults:
|
|
128
|
+
"""Perform a web search.
|
|
129
|
+
|
|
130
|
+
Args:
|
|
131
|
+
query: Search query string
|
|
132
|
+
max_results: Maximum number of results (overrides default)
|
|
133
|
+
region: Region override
|
|
134
|
+
safe_search: Safe search override
|
|
135
|
+
time_range: Time range filter ('d', 'w', 'm', 'y' for day, week, month, year)
|
|
136
|
+
**kwargs: Additional search parameters
|
|
137
|
+
|
|
138
|
+
Returns:
|
|
139
|
+
SearchResults object containing the results
|
|
140
|
+
"""
|
|
141
|
+
try:
|
|
142
|
+
# Use provided parameters or fall back to instance defaults
|
|
143
|
+
search_region = region or self.region
|
|
144
|
+
search_safe = safe_search or self.safe_search
|
|
145
|
+
search_max = max_results or self.max_results
|
|
146
|
+
|
|
147
|
+
backend_candidates = self._backend_candidates("text", backend)
|
|
148
|
+
last_error: Exception | None = None
|
|
149
|
+
|
|
150
|
+
with DDGS(timeout=self.timeout, **self.config) as ddgs:
|
|
151
|
+
for candidate in backend_candidates:
|
|
152
|
+
try:
|
|
153
|
+
raw_results = self._run_ddgs_query(
|
|
154
|
+
ddgs,
|
|
155
|
+
"text",
|
|
156
|
+
query=query,
|
|
157
|
+
region=search_region,
|
|
158
|
+
safesearch=search_safe,
|
|
159
|
+
timelimit=time_range,
|
|
160
|
+
max_results=search_max,
|
|
161
|
+
backend=candidate,
|
|
162
|
+
**kwargs,
|
|
163
|
+
)
|
|
164
|
+
except Exception as exc:
|
|
165
|
+
last_error = exc
|
|
166
|
+
continue
|
|
167
|
+
|
|
168
|
+
results = []
|
|
169
|
+
for result in raw_results:
|
|
170
|
+
search_result = SearchResult(
|
|
171
|
+
title=result.get("title", ""),
|
|
172
|
+
url=result.get("href", ""),
|
|
173
|
+
snippet=result.get("body", ""),
|
|
174
|
+
source=candidate,
|
|
175
|
+
)
|
|
176
|
+
results.append(search_result)
|
|
177
|
+
|
|
178
|
+
return SearchResults(
|
|
179
|
+
query=query, results=results, total_results=len(results)
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
if last_error is not None:
|
|
183
|
+
raise last_error
|
|
184
|
+
return SearchResults(query=query, results=[], total_results=0)
|
|
185
|
+
|
|
186
|
+
except Exception as e:
|
|
187
|
+
raise LLMError(f"Web search failed: {e}") from e
|
|
188
|
+
|
|
189
|
+
async def search_async(
|
|
190
|
+
self,
|
|
191
|
+
query: str,
|
|
192
|
+
max_results: int | None = None,
|
|
193
|
+
region: str | None = None,
|
|
194
|
+
safe_search: str | None = None,
|
|
195
|
+
time_range: str | None = None,
|
|
196
|
+
**kwargs: Any,
|
|
197
|
+
) -> SearchResults:
|
|
198
|
+
"""Async version of search."""
|
|
199
|
+
# Run the synchronous search in a thread pool
|
|
200
|
+
loop = asyncio.get_event_loop()
|
|
201
|
+
return await loop.run_in_executor(
|
|
202
|
+
None,
|
|
203
|
+
lambda: self.search(
|
|
204
|
+
query=query,
|
|
205
|
+
max_results=max_results,
|
|
206
|
+
region=region,
|
|
207
|
+
safe_search=safe_search,
|
|
208
|
+
time_range=time_range,
|
|
209
|
+
**kwargs,
|
|
210
|
+
),
|
|
211
|
+
)
|
|
212
|
+
|
|
213
|
+
def search_news(
|
|
214
|
+
self,
|
|
215
|
+
query: str,
|
|
216
|
+
max_results: int | None = None,
|
|
217
|
+
region: str | None = None,
|
|
218
|
+
safe_search: str | None = None,
|
|
219
|
+
time_range: str | None = None,
|
|
220
|
+
backend: str | None = None,
|
|
221
|
+
**kwargs: Any,
|
|
222
|
+
) -> SearchResults:
|
|
223
|
+
"""Search for news articles.
|
|
224
|
+
|
|
225
|
+
Args:
|
|
226
|
+
query: Search query string
|
|
227
|
+
max_results: Maximum number of results
|
|
228
|
+
region: Region for search
|
|
229
|
+
safe_search: Safe search setting
|
|
230
|
+
time_range: Time range filter
|
|
231
|
+
**kwargs: Additional parameters
|
|
232
|
+
|
|
233
|
+
Returns:
|
|
234
|
+
SearchResults with news articles
|
|
235
|
+
"""
|
|
236
|
+
try:
|
|
237
|
+
search_region = region or self.region
|
|
238
|
+
search_safe = safe_search or self.safe_search
|
|
239
|
+
search_max = max_results or self.max_results
|
|
240
|
+
|
|
241
|
+
backend_candidates = self._backend_candidates("news", backend)
|
|
242
|
+
last_error: Exception | None = None
|
|
243
|
+
|
|
244
|
+
with DDGS(timeout=self.timeout, **self.config) as ddgs:
|
|
245
|
+
for candidate in backend_candidates:
|
|
246
|
+
try:
|
|
247
|
+
raw_results = self._run_ddgs_query(
|
|
248
|
+
ddgs,
|
|
249
|
+
"news",
|
|
250
|
+
query=query,
|
|
251
|
+
region=search_region,
|
|
252
|
+
safesearch=search_safe,
|
|
253
|
+
timelimit=time_range,
|
|
254
|
+
max_results=search_max,
|
|
255
|
+
backend=candidate,
|
|
256
|
+
**kwargs,
|
|
257
|
+
)
|
|
258
|
+
except Exception as exc:
|
|
259
|
+
last_error = exc
|
|
260
|
+
continue
|
|
261
|
+
|
|
262
|
+
results = []
|
|
263
|
+
for result in raw_results:
|
|
264
|
+
search_result = SearchResult(
|
|
265
|
+
title=result.get("title", ""),
|
|
266
|
+
url=result.get("url", ""),
|
|
267
|
+
snippet=result.get("body", ""),
|
|
268
|
+
source=f"{candidate}_news",
|
|
269
|
+
)
|
|
270
|
+
results.append(search_result)
|
|
271
|
+
|
|
272
|
+
return SearchResults(
|
|
273
|
+
query=query, results=results, total_results=len(results)
|
|
274
|
+
)
|
|
275
|
+
|
|
276
|
+
if last_error is not None:
|
|
277
|
+
raise last_error
|
|
278
|
+
return SearchResults(query=query, results=[], total_results=0)
|
|
279
|
+
|
|
280
|
+
except Exception as e:
|
|
281
|
+
raise LLMError(f"News search failed: {e}") from e
|
|
282
|
+
|
|
283
|
+
async def search_news_async(
|
|
284
|
+
self,
|
|
285
|
+
query: str,
|
|
286
|
+
max_results: int | None = None,
|
|
287
|
+
region: str | None = None,
|
|
288
|
+
safe_search: str | None = None,
|
|
289
|
+
time_range: str | None = None,
|
|
290
|
+
**kwargs: Any,
|
|
291
|
+
) -> SearchResults:
|
|
292
|
+
"""Async version of news search."""
|
|
293
|
+
loop = asyncio.get_event_loop()
|
|
294
|
+
return await loop.run_in_executor(
|
|
295
|
+
None,
|
|
296
|
+
lambda: self.search_news(
|
|
297
|
+
query=query,
|
|
298
|
+
max_results=max_results,
|
|
299
|
+
region=region,
|
|
300
|
+
safe_search=safe_search,
|
|
301
|
+
time_range=time_range,
|
|
302
|
+
**kwargs,
|
|
303
|
+
),
|
|
304
|
+
)
|
|
305
|
+
|
|
306
|
+
def quick_search(self, query: str, num_results: int = 5) -> list[str]:
|
|
307
|
+
"""Quick search returning just URLs.
|
|
308
|
+
|
|
309
|
+
Args:
|
|
310
|
+
query: Search query
|
|
311
|
+
num_results: Number of results to return
|
|
312
|
+
|
|
313
|
+
Returns:
|
|
314
|
+
List of URLs
|
|
315
|
+
"""
|
|
316
|
+
results = self.search(query, max_results=num_results)
|
|
317
|
+
return [result.url for result in results.results]
|
|
318
|
+
|
|
319
|
+
def search_and_summarize(self, query: str, max_results: int = 3) -> str:
|
|
320
|
+
"""Search and create a text summary of results.
|
|
321
|
+
|
|
322
|
+
Args:
|
|
323
|
+
query: Search query
|
|
324
|
+
max_results: Number of results to include
|
|
325
|
+
|
|
326
|
+
Returns:
|
|
327
|
+
Formatted text summary
|
|
328
|
+
"""
|
|
329
|
+
results = self.search(query, max_results=max_results)
|
|
330
|
+
|
|
331
|
+
summary_lines = [f"Search results for: {query}\n"]
|
|
332
|
+
|
|
333
|
+
for i, result in enumerate(results.results, 1):
|
|
334
|
+
summary_lines.append(f"{i}. {result.title}")
|
|
335
|
+
summary_lines.append(f" URL: {result.url}")
|
|
336
|
+
summary_lines.append(f" {result.snippet}\n")
|
|
337
|
+
|
|
338
|
+
return "\n".join(summary_lines)
|
|
339
|
+
|
|
340
|
+
|
|
341
|
+
# Agent Tools
|
|
342
|
+
# ---------------------------------------------------------------------------
|
|
343
|
+
|
|
344
|
+
|
|
345
|
+
def web_search_tools() -> ToolRegistry:
|
|
346
|
+
"""Create web search tools for agents.
|
|
347
|
+
|
|
348
|
+
These tools are returned as plain callables. Framework-specific backends
|
|
349
|
+
adapt them when needed.
|
|
350
|
+
|
|
351
|
+
Returns:
|
|
352
|
+
Dict of plain tool callables ready for adaptation by agent frameworks
|
|
353
|
+
|
|
354
|
+
Example:
|
|
355
|
+
>>> tools = web_search_tools()
|
|
356
|
+
>>> agent = AgentBuilder("WebResearcher").with_tools(tools).build()
|
|
357
|
+
"""
|
|
358
|
+
|
|
359
|
+
# Create searcher instance
|
|
360
|
+
searcher = WebSearcher()
|
|
361
|
+
|
|
362
|
+
def search_web(
|
|
363
|
+
query: str, max_results: int = 10, time_range: str | None = None
|
|
364
|
+
) -> SearchResults:
|
|
365
|
+
"""Search the web using DDGS (Google backend by default).
|
|
366
|
+
|
|
367
|
+
Args:
|
|
368
|
+
query: Search query string
|
|
369
|
+
max_results: Maximum number of results to return (default: 10)
|
|
370
|
+
time_range: Filter by time range - 'd' (day), 'w' (week), 'm' (month), 'y' (year)
|
|
371
|
+
|
|
372
|
+
Returns:
|
|
373
|
+
SearchResults object with web pages matching the query
|
|
374
|
+
"""
|
|
375
|
+
return searcher.search(query, max_results=max_results, time_range=time_range)
|
|
376
|
+
|
|
377
|
+
def search_news(
|
|
378
|
+
query: str, max_results: int = 10, time_range: str | None = None
|
|
379
|
+
) -> SearchResults:
|
|
380
|
+
"""Search for news articles using DDGS News backends.
|
|
381
|
+
|
|
382
|
+
Args:
|
|
383
|
+
query: Search query string
|
|
384
|
+
max_results: Maximum number of results to return (default: 10)
|
|
385
|
+
time_range: Filter by time range - 'd' (day), 'w' (week), 'm' (month), 'y' (year)
|
|
386
|
+
|
|
387
|
+
Returns:
|
|
388
|
+
SearchResults object with news articles matching the query
|
|
389
|
+
"""
|
|
390
|
+
return searcher.search_news(
|
|
391
|
+
query, max_results=max_results, time_range=time_range
|
|
392
|
+
)
|
|
393
|
+
|
|
394
|
+
return ToolRegistry.from_mapping(
|
|
395
|
+
{
|
|
396
|
+
"search_web": search_web,
|
|
397
|
+
"search_news": search_news,
|
|
398
|
+
}
|
|
399
|
+
)
|