aiecs 1.2.2__py3-none-any.whl → 1.3.3__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.

Potentially problematic release.


This version of aiecs might be problematic. Click here for more details.

Files changed (55) hide show
  1. aiecs/__init__.py +1 -1
  2. aiecs/llm/clients/vertex_client.py +22 -2
  3. aiecs/main.py +2 -2
  4. aiecs/scripts/tools_develop/README.md +111 -2
  5. aiecs/scripts/tools_develop/TOOL_AUTO_DISCOVERY.md +234 -0
  6. aiecs/scripts/tools_develop/validate_tool_schemas.py +80 -21
  7. aiecs/scripts/tools_develop/verify_tools.py +347 -0
  8. aiecs/tools/__init__.py +94 -30
  9. aiecs/tools/apisource/__init__.py +106 -0
  10. aiecs/tools/apisource/intelligence/__init__.py +20 -0
  11. aiecs/tools/apisource/intelligence/data_fusion.py +378 -0
  12. aiecs/tools/apisource/intelligence/query_analyzer.py +387 -0
  13. aiecs/tools/apisource/intelligence/search_enhancer.py +384 -0
  14. aiecs/tools/apisource/monitoring/__init__.py +12 -0
  15. aiecs/tools/apisource/monitoring/metrics.py +308 -0
  16. aiecs/tools/apisource/providers/__init__.py +114 -0
  17. aiecs/tools/apisource/providers/base.py +684 -0
  18. aiecs/tools/apisource/providers/census.py +412 -0
  19. aiecs/tools/apisource/providers/fred.py +575 -0
  20. aiecs/tools/apisource/providers/newsapi.py +402 -0
  21. aiecs/tools/apisource/providers/worldbank.py +346 -0
  22. aiecs/tools/apisource/reliability/__init__.py +14 -0
  23. aiecs/tools/apisource/reliability/error_handler.py +362 -0
  24. aiecs/tools/apisource/reliability/fallback_strategy.py +420 -0
  25. aiecs/tools/apisource/tool.py +814 -0
  26. aiecs/tools/apisource/utils/__init__.py +12 -0
  27. aiecs/tools/apisource/utils/validators.py +343 -0
  28. aiecs/tools/langchain_adapter.py +95 -17
  29. aiecs/tools/search_tool/__init__.py +102 -0
  30. aiecs/tools/search_tool/analyzers.py +583 -0
  31. aiecs/tools/search_tool/cache.py +280 -0
  32. aiecs/tools/search_tool/constants.py +127 -0
  33. aiecs/tools/search_tool/context.py +219 -0
  34. aiecs/tools/search_tool/core.py +773 -0
  35. aiecs/tools/search_tool/deduplicator.py +123 -0
  36. aiecs/tools/search_tool/error_handler.py +257 -0
  37. aiecs/tools/search_tool/metrics.py +375 -0
  38. aiecs/tools/search_tool/rate_limiter.py +177 -0
  39. aiecs/tools/search_tool/schemas.py +297 -0
  40. aiecs/tools/statistics/data_loader_tool.py +2 -2
  41. aiecs/tools/statistics/data_transformer_tool.py +1 -1
  42. aiecs/tools/task_tools/__init__.py +8 -8
  43. aiecs/tools/task_tools/report_tool.py +1 -1
  44. aiecs/tools/tool_executor/__init__.py +2 -0
  45. aiecs/tools/tool_executor/tool_executor.py +284 -14
  46. aiecs/utils/__init__.py +11 -0
  47. aiecs/utils/cache_provider.py +698 -0
  48. aiecs/utils/execution_utils.py +5 -5
  49. {aiecs-1.2.2.dist-info → aiecs-1.3.3.dist-info}/METADATA +1 -1
  50. {aiecs-1.2.2.dist-info → aiecs-1.3.3.dist-info}/RECORD +54 -22
  51. aiecs/tools/task_tools/search_tool.py +0 -1123
  52. {aiecs-1.2.2.dist-info → aiecs-1.3.3.dist-info}/WHEEL +0 -0
  53. {aiecs-1.2.2.dist-info → aiecs-1.3.3.dist-info}/entry_points.txt +0 -0
  54. {aiecs-1.2.2.dist-info → aiecs-1.3.3.dist-info}/licenses/LICENSE +0 -0
  55. {aiecs-1.2.2.dist-info → aiecs-1.3.3.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,402 @@
1
+ """
2
+ News API Provider
3
+
4
+ Provides access to news articles from various sources worldwide.
5
+ Supports headline retrieval, article search, and source listing.
6
+
7
+ API Documentation: https://newsapi.org/docs
8
+ """
9
+
10
+ import logging
11
+ from datetime import datetime, timedelta
12
+ from typing import Any, Dict, List, Optional, Tuple
13
+
14
+ from aiecs.tools.apisource.providers.base import BaseAPIProvider, expose_operation
15
+
16
+ logger = logging.getLogger(__name__)
17
+
18
+ # Optional HTTP client
19
+ try:
20
+ import requests
21
+ REQUESTS_AVAILABLE = True
22
+ except ImportError:
23
+ REQUESTS_AVAILABLE = False
24
+
25
+
26
+ class NewsAPIProvider(BaseAPIProvider):
27
+ """
28
+ News API provider for accessing news articles and headlines.
29
+
30
+ Provides access to:
31
+ - Top headlines from various sources
32
+ - Article search by keywords
33
+ - News sources listing
34
+ - Filtering by country, language, category
35
+ """
36
+
37
+ BASE_URL = "https://newsapi.org/v2"
38
+
39
+ @property
40
+ def name(self) -> str:
41
+ return "newsapi"
42
+
43
+ @property
44
+ def description(self) -> str:
45
+ return "News API for accessing news articles, headlines, and sources worldwide"
46
+
47
+ @property
48
+ def supported_operations(self) -> List[str]:
49
+ return [
50
+ 'get_top_headlines',
51
+ 'search_everything',
52
+ 'get_sources'
53
+ ]
54
+
55
+ def validate_params(self, operation: str, params: Dict[str, Any]) -> Tuple[bool, Optional[str]]:
56
+ """Validate parameters for News API operations"""
57
+
58
+ if operation == 'get_top_headlines':
59
+ # At least one of these is required
60
+ if not any(k in params for k in ['q', 'country', 'category', 'sources']):
61
+ return False, "At least one of q, country, category, or sources is required"
62
+
63
+ elif operation == 'search_everything':
64
+ if 'q' not in params:
65
+ return False, "Missing required parameter: q (search query)"
66
+
67
+ return True, None
68
+
69
+ # Exposed operations for AI agent visibility
70
+
71
+ @expose_operation(
72
+ operation_name='get_top_headlines',
73
+ description='Get top news headlines from various sources with optional filtering'
74
+ )
75
+ def get_top_headlines(
76
+ self,
77
+ q: Optional[str] = None,
78
+ country: Optional[str] = None,
79
+ category: Optional[str] = None,
80
+ sources: Optional[str] = None,
81
+ page_size: Optional[int] = None
82
+ ) -> Dict[str, Any]:
83
+ """
84
+ Get top headlines.
85
+
86
+ Args:
87
+ q: Keywords or phrases to search for in article title and body
88
+ country: 2-letter ISO country code (e.g., 'us', 'gb', 'cn')
89
+ category: Category (business, entertainment, general, health, science, sports, technology)
90
+ sources: Comma-separated news source IDs
91
+ page_size: Number of results to return (max 100)
92
+
93
+ Returns:
94
+ Dictionary containing news articles and metadata
95
+ """
96
+ params = {}
97
+ if q:
98
+ params['q'] = q
99
+ if country:
100
+ params['country'] = country
101
+ if category:
102
+ params['category'] = category
103
+ if sources:
104
+ params['sources'] = sources
105
+ if page_size:
106
+ params['page_size'] = page_size
107
+
108
+ return self.execute('get_top_headlines', params)
109
+
110
+ @expose_operation(
111
+ operation_name='search_everything',
112
+ description='Search through millions of articles from news sources and blogs'
113
+ )
114
+ def search_everything(
115
+ self,
116
+ q: str,
117
+ from_date: Optional[str] = None,
118
+ to_date: Optional[str] = None,
119
+ language: Optional[str] = None,
120
+ sort_by: Optional[str] = None,
121
+ page_size: Optional[int] = None
122
+ ) -> Dict[str, Any]:
123
+ """
124
+ Search all articles.
125
+
126
+ Args:
127
+ q: Keywords or phrases to search for
128
+ from_date: Start date (YYYY-MM-DD or ISO 8601)
129
+ to_date: End date (YYYY-MM-DD or ISO 8601)
130
+ language: 2-letter ISO language code (e.g., 'en', 'es', 'fr')
131
+ sort_by: Sort order (relevancy, popularity, publishedAt)
132
+ page_size: Number of results to return (max 100)
133
+
134
+ Returns:
135
+ Dictionary containing search results and metadata
136
+ """
137
+ params = {'q': q}
138
+ if from_date:
139
+ params['from'] = from_date
140
+ if to_date:
141
+ params['to'] = to_date
142
+ if language:
143
+ params['language'] = language
144
+ if sort_by:
145
+ params['sortBy'] = sort_by
146
+ if page_size:
147
+ params['pageSize'] = page_size
148
+
149
+ return self.execute('search_everything', params)
150
+
151
+ @expose_operation(
152
+ operation_name='get_sources',
153
+ description='Get the list of available news sources'
154
+ )
155
+ def get_sources(
156
+ self,
157
+ category: Optional[str] = None,
158
+ language: Optional[str] = None,
159
+ country: Optional[str] = None
160
+ ) -> Dict[str, Any]:
161
+ """
162
+ Get available news sources.
163
+
164
+ Args:
165
+ category: Filter by category
166
+ language: Filter by language (2-letter ISO code)
167
+ country: Filter by country (2-letter ISO code)
168
+
169
+ Returns:
170
+ Dictionary containing list of news sources
171
+ """
172
+ params = {}
173
+ if category:
174
+ params['category'] = category
175
+ if language:
176
+ params['language'] = language
177
+ if country:
178
+ params['country'] = country
179
+
180
+ return self.execute('get_sources', params)
181
+
182
+ def fetch(self, operation: str, params: Dict[str, Any]) -> Dict[str, Any]:
183
+ """Fetch data from News API"""
184
+
185
+ if not REQUESTS_AVAILABLE:
186
+ raise ImportError("requests library is required for News API provider")
187
+
188
+ # Get API key
189
+ api_key = self._get_api_key('NEWSAPI_API_KEY')
190
+ if not api_key:
191
+ raise ValueError(
192
+ "News API key not found. Set NEWSAPI_API_KEY environment variable or "
193
+ "provide 'api_key' in config. Get your key at https://newsapi.org"
194
+ )
195
+
196
+ headers = {'X-Api-Key': api_key}
197
+ timeout = self.config.get('timeout', 30)
198
+
199
+ # Build endpoint based on operation
200
+ if operation == 'get_top_headlines':
201
+ endpoint = f"{self.BASE_URL}/top-headlines"
202
+ query_params = {}
203
+
204
+ # Optional parameters
205
+ if 'q' in params:
206
+ query_params['q'] = params['q']
207
+ if 'country' in params:
208
+ query_params['country'] = params['country']
209
+ if 'category' in params:
210
+ query_params['category'] = params['category']
211
+ if 'sources' in params:
212
+ query_params['sources'] = params['sources']
213
+ if 'page_size' in params:
214
+ query_params['pageSize'] = params['page_size']
215
+ if 'page' in params:
216
+ query_params['page'] = params['page']
217
+
218
+ elif operation == 'search_everything':
219
+ endpoint = f"{self.BASE_URL}/everything"
220
+ query_params = {
221
+ 'q': params['q']
222
+ }
223
+
224
+ # Optional parameters
225
+ if 'from_date' in params:
226
+ query_params['from'] = params['from_date']
227
+ elif 'days_back' in params:
228
+ # Convenience parameter: go back N days
229
+ from_date = datetime.now() - timedelta(days=params['days_back'])
230
+ query_params['from'] = from_date.strftime('%Y-%m-%d')
231
+
232
+ if 'to_date' in params:
233
+ query_params['to'] = params['to_date']
234
+ if 'language' in params:
235
+ query_params['language'] = params['language']
236
+ if 'sort_by' in params:
237
+ query_params['sortBy'] = params['sort_by']
238
+ if 'page_size' in params:
239
+ query_params['pageSize'] = params['page_size']
240
+ if 'page' in params:
241
+ query_params['page'] = params['page']
242
+
243
+ elif operation == 'get_sources':
244
+ endpoint = f"{self.BASE_URL}/top-headlines/sources"
245
+ query_params = {}
246
+
247
+ # Optional parameters
248
+ if 'country' in params:
249
+ query_params['country'] = params['country']
250
+ if 'language' in params:
251
+ query_params['language'] = params['language']
252
+ if 'category' in params:
253
+ query_params['category'] = params['category']
254
+
255
+ else:
256
+ raise ValueError(f"Unknown operation: {operation}")
257
+
258
+ # Make API request
259
+ try:
260
+ response = requests.get(
261
+ endpoint,
262
+ params=query_params,
263
+ headers=headers,
264
+ timeout=timeout
265
+ )
266
+ response.raise_for_status()
267
+
268
+ data = response.json()
269
+
270
+ # Check API response status
271
+ if data.get('status') != 'ok':
272
+ raise Exception(f"News API error: {data.get('message', 'Unknown error')}")
273
+
274
+ # Extract relevant data
275
+ if operation == 'get_sources':
276
+ result_data = data.get('sources', [])
277
+ else:
278
+ result_data = {
279
+ 'articles': data.get('articles', []),
280
+ 'total_results': data.get('totalResults', 0)
281
+ }
282
+
283
+ return self._format_response(
284
+ operation=operation,
285
+ data=result_data,
286
+ source=f"News API - {endpoint}"
287
+ )
288
+
289
+ except requests.exceptions.RequestException as e:
290
+ self.logger.error(f"News API request failed: {e}")
291
+ raise Exception(f"News API request failed: {str(e)}")
292
+
293
+ def get_operation_schema(self, operation: str) -> Optional[Dict[str, Any]]:
294
+ """Get detailed schema for News API operations"""
295
+
296
+ schemas = {
297
+ 'get_top_headlines': {
298
+ 'description': 'Get top news headlines',
299
+ 'parameters': {
300
+ 'q': {
301
+ 'type': 'string',
302
+ 'required': False,
303
+ 'description': 'Keywords or phrases to search for',
304
+ 'examples': ['bitcoin', 'climate change', 'technology']
305
+ },
306
+ 'country': {
307
+ 'type': 'string',
308
+ 'required': False,
309
+ 'description': '2-letter ISO country code',
310
+ 'examples': ['us', 'gb', 'cn', 'jp']
311
+ },
312
+ 'category': {
313
+ 'type': 'string',
314
+ 'required': False,
315
+ 'description': 'News category',
316
+ 'examples': ['business', 'entertainment', 'health', 'science', 'sports', 'technology']
317
+ },
318
+ 'sources': {
319
+ 'type': 'string',
320
+ 'required': False,
321
+ 'description': 'Comma-separated news source IDs',
322
+ 'examples': ['bbc-news', 'cnn', 'the-verge']
323
+ },
324
+ 'page_size': {
325
+ 'type': 'integer',
326
+ 'required': False,
327
+ 'description': 'Number of results (max 100)',
328
+ 'examples': [10, 20, 50],
329
+ 'default': 20
330
+ }
331
+ }
332
+ },
333
+ 'search_everything': {
334
+ 'description': 'Search all news articles',
335
+ 'parameters': {
336
+ 'q': {
337
+ 'type': 'string',
338
+ 'required': True,
339
+ 'description': 'Keywords or phrases to search for',
340
+ 'examples': ['artificial intelligence', 'climate summit', 'stock market']
341
+ },
342
+ 'from_date': {
343
+ 'type': 'string',
344
+ 'required': False,
345
+ 'description': 'Start date (YYYY-MM-DD)',
346
+ 'examples': ['2024-01-01', '2024-10-01']
347
+ },
348
+ 'to_date': {
349
+ 'type': 'string',
350
+ 'required': False,
351
+ 'description': 'End date (YYYY-MM-DD)',
352
+ 'examples': ['2024-12-31', '2024-10-17']
353
+ },
354
+ 'language': {
355
+ 'type': 'string',
356
+ 'required': False,
357
+ 'description': '2-letter ISO language code',
358
+ 'examples': ['en', 'es', 'fr', 'de']
359
+ },
360
+ 'sort_by': {
361
+ 'type': 'string',
362
+ 'required': False,
363
+ 'description': 'Sort order',
364
+ 'examples': ['relevancy', 'popularity', 'publishedAt'],
365
+ 'default': 'publishedAt'
366
+ },
367
+ 'page_size': {
368
+ 'type': 'integer',
369
+ 'required': False,
370
+ 'description': 'Number of results (max 100)',
371
+ 'examples': [10, 20, 50],
372
+ 'default': 20
373
+ }
374
+ }
375
+ },
376
+ 'get_sources': {
377
+ 'description': 'Get available news sources',
378
+ 'parameters': {
379
+ 'category': {
380
+ 'type': 'string',
381
+ 'required': False,
382
+ 'description': 'Filter by category',
383
+ 'examples': ['business', 'technology', 'sports']
384
+ },
385
+ 'language': {
386
+ 'type': 'string',
387
+ 'required': False,
388
+ 'description': 'Filter by language (2-letter ISO code)',
389
+ 'examples': ['en', 'es', 'fr']
390
+ },
391
+ 'country': {
392
+ 'type': 'string',
393
+ 'required': False,
394
+ 'description': 'Filter by country (2-letter ISO code)',
395
+ 'examples': ['us', 'gb', 'cn']
396
+ }
397
+ }
398
+ }
399
+ }
400
+
401
+ return schemas.get(operation)
402
+