aiecs 1.2.1__py3-none-any.whl → 1.3.1__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.
- aiecs/__init__.py +1 -1
- aiecs/config/config.py +2 -1
- aiecs/llm/clients/vertex_client.py +5 -0
- aiecs/main.py +2 -2
- aiecs/scripts/tools_develop/README.md +111 -2
- aiecs/scripts/tools_develop/TOOL_AUTO_DISCOVERY.md +234 -0
- aiecs/scripts/tools_develop/validate_tool_schemas.py +80 -21
- aiecs/scripts/tools_develop/verify_tools.py +347 -0
- aiecs/tools/__init__.py +94 -30
- aiecs/tools/apisource/__init__.py +106 -0
- aiecs/tools/apisource/intelligence/__init__.py +20 -0
- aiecs/tools/apisource/intelligence/data_fusion.py +378 -0
- aiecs/tools/apisource/intelligence/query_analyzer.py +387 -0
- aiecs/tools/apisource/intelligence/search_enhancer.py +384 -0
- aiecs/tools/apisource/monitoring/__init__.py +12 -0
- aiecs/tools/apisource/monitoring/metrics.py +308 -0
- aiecs/tools/apisource/providers/__init__.py +114 -0
- aiecs/tools/apisource/providers/base.py +684 -0
- aiecs/tools/apisource/providers/census.py +412 -0
- aiecs/tools/apisource/providers/fred.py +575 -0
- aiecs/tools/apisource/providers/newsapi.py +402 -0
- aiecs/tools/apisource/providers/worldbank.py +346 -0
- aiecs/tools/apisource/reliability/__init__.py +14 -0
- aiecs/tools/apisource/reliability/error_handler.py +362 -0
- aiecs/tools/apisource/reliability/fallback_strategy.py +420 -0
- aiecs/tools/apisource/tool.py +814 -0
- aiecs/tools/apisource/utils/__init__.py +12 -0
- aiecs/tools/apisource/utils/validators.py +343 -0
- aiecs/tools/langchain_adapter.py +95 -17
- aiecs/tools/search_tool/__init__.py +102 -0
- aiecs/tools/search_tool/analyzers.py +583 -0
- aiecs/tools/search_tool/cache.py +280 -0
- aiecs/tools/search_tool/constants.py +127 -0
- aiecs/tools/search_tool/context.py +219 -0
- aiecs/tools/search_tool/core.py +773 -0
- aiecs/tools/search_tool/deduplicator.py +123 -0
- aiecs/tools/search_tool/error_handler.py +257 -0
- aiecs/tools/search_tool/metrics.py +375 -0
- aiecs/tools/search_tool/rate_limiter.py +177 -0
- aiecs/tools/search_tool/schemas.py +297 -0
- aiecs/tools/statistics/data_loader_tool.py +2 -2
- aiecs/tools/statistics/data_transformer_tool.py +1 -1
- aiecs/tools/task_tools/__init__.py +8 -8
- aiecs/tools/task_tools/report_tool.py +1 -1
- aiecs/tools/tool_executor/__init__.py +2 -0
- aiecs/tools/tool_executor/tool_executor.py +284 -14
- aiecs/utils/__init__.py +11 -0
- aiecs/utils/cache_provider.py +698 -0
- aiecs/utils/execution_utils.py +5 -5
- {aiecs-1.2.1.dist-info → aiecs-1.3.1.dist-info}/METADATA +1 -1
- {aiecs-1.2.1.dist-info → aiecs-1.3.1.dist-info}/RECORD +55 -23
- aiecs/tools/task_tools/search_tool.py +0 -1123
- {aiecs-1.2.1.dist-info → aiecs-1.3.1.dist-info}/WHEEL +0 -0
- {aiecs-1.2.1.dist-info → aiecs-1.3.1.dist-info}/entry_points.txt +0 -0
- {aiecs-1.2.1.dist-info → aiecs-1.3.1.dist-info}/licenses/LICENSE +0 -0
- {aiecs-1.2.1.dist-info → aiecs-1.3.1.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,347 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
快速验证脚本:展示 aiecs.tools 注册的所有工具和功能
|
|
4
|
+
|
|
5
|
+
使用方法:
|
|
6
|
+
poetry run python -m aiecs.scripts.tools_develop.verify_tools
|
|
7
|
+
|
|
8
|
+
功能:
|
|
9
|
+
1. 列出所有注册的工具(按类别分组)
|
|
10
|
+
2. 交互式选择工具查看详细功能
|
|
11
|
+
3. 实际加载指定工具,展示真实的原子功能
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
import sys
|
|
15
|
+
import os
|
|
16
|
+
import inspect
|
|
17
|
+
from typing import List, Dict
|
|
18
|
+
from collections import defaultdict
|
|
19
|
+
|
|
20
|
+
# 添加项目根目录到 Python 路径
|
|
21
|
+
current_dir = os.path.dirname(os.path.abspath(__file__))
|
|
22
|
+
project_root = os.path.abspath(os.path.join(current_dir, '../../../'))
|
|
23
|
+
sys.path.insert(0, project_root)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def get_tool_methods(tool_instance) -> List[str]:
|
|
27
|
+
"""获取工具实例的所有公共方法(原子功能)"""
|
|
28
|
+
methods = []
|
|
29
|
+
for name, method in inspect.getmembers(tool_instance, predicate=inspect.ismethod):
|
|
30
|
+
# 只获取公共方法,排除私有方法和特殊方法
|
|
31
|
+
if not name.startswith('_'):
|
|
32
|
+
methods.append(name)
|
|
33
|
+
return sorted(methods)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def display_tools_by_category(tools: List[Dict]):
|
|
37
|
+
"""按类别分组显示工具"""
|
|
38
|
+
# 按类别分组
|
|
39
|
+
categories = defaultdict(list)
|
|
40
|
+
for tool in tools:
|
|
41
|
+
category = tool.get('category', 'unknown')
|
|
42
|
+
categories[category].append(tool)
|
|
43
|
+
|
|
44
|
+
# 显示每个类别
|
|
45
|
+
category_names = {
|
|
46
|
+
'task': '任务工具',
|
|
47
|
+
'docs': '文档工具',
|
|
48
|
+
'statistics': '数据统计工具',
|
|
49
|
+
'unknown': '其他工具'
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
tool_index = 1
|
|
53
|
+
tool_map = {} # 用于存储序号到工具名的映射
|
|
54
|
+
|
|
55
|
+
for category in ['task', 'docs', 'statistics', 'unknown']:
|
|
56
|
+
if category not in categories:
|
|
57
|
+
continue
|
|
58
|
+
|
|
59
|
+
category_tools = categories[category]
|
|
60
|
+
category_display = category_names.get(category, category.upper())
|
|
61
|
+
|
|
62
|
+
print(f"\n{'=' * 80}")
|
|
63
|
+
print(f"📁 {category_display} ({len(category_tools)} 个)")
|
|
64
|
+
print(f"{'=' * 80}")
|
|
65
|
+
|
|
66
|
+
for tool in category_tools:
|
|
67
|
+
tool_name = tool['name']
|
|
68
|
+
tool_map[tool_index] = tool_name
|
|
69
|
+
|
|
70
|
+
print(f"\n[{tool_index}] {tool_name}")
|
|
71
|
+
print(f" 描述: {tool.get('description', '无描述')}")
|
|
72
|
+
print(f" 状态: {tool.get('status', '未知')}")
|
|
73
|
+
|
|
74
|
+
tool_index += 1
|
|
75
|
+
|
|
76
|
+
return tool_map
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def auto_discover_tool_modules():
|
|
80
|
+
"""自动发现工具模块映射,无需手动维护"""
|
|
81
|
+
import os
|
|
82
|
+
import re
|
|
83
|
+
|
|
84
|
+
tool_module_map = {}
|
|
85
|
+
|
|
86
|
+
# 扫描 aiecs/tools 目录
|
|
87
|
+
tools_dir = os.path.join(project_root, 'aiecs', 'tools')
|
|
88
|
+
|
|
89
|
+
# 定义工具目录
|
|
90
|
+
tool_dirs = {
|
|
91
|
+
'task_tools': 'aiecs.tools.task_tools',
|
|
92
|
+
'docs': 'aiecs.tools.docs',
|
|
93
|
+
'statistics': 'aiecs.tools.statistics',
|
|
94
|
+
'api_sources': 'aiecs.tools.api_sources', # 旧的 API sources (如果存在)
|
|
95
|
+
'apisource': 'aiecs.tools.apisource', # 新的 APISource Tool
|
|
96
|
+
'search_tool': 'aiecs.tools.search_tool',
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
for dir_name, package_name in tool_dirs.items():
|
|
100
|
+
dir_path = os.path.join(tools_dir, dir_name)
|
|
101
|
+
if not os.path.exists(dir_path):
|
|
102
|
+
continue
|
|
103
|
+
|
|
104
|
+
# Check if this is a package (has __init__.py) or a directory of modules
|
|
105
|
+
init_file = os.path.join(dir_path, '__init__.py')
|
|
106
|
+
files_to_scan = []
|
|
107
|
+
|
|
108
|
+
if os.path.isfile(init_file):
|
|
109
|
+
# For packages, scan __init__.py and use package name directly
|
|
110
|
+
files_to_scan.append(('__init__.py', init_file, package_name))
|
|
111
|
+
|
|
112
|
+
# 扫描目录中的所有其他 Python 文件
|
|
113
|
+
for filename in os.listdir(dir_path):
|
|
114
|
+
if filename.endswith('.py') and not filename.startswith('__'):
|
|
115
|
+
file_path = os.path.join(dir_path, filename)
|
|
116
|
+
module_name = filename[:-3] # 去掉 .py 扩展名
|
|
117
|
+
module_path = f"{package_name}.{module_name}"
|
|
118
|
+
files_to_scan.append((filename, file_path, module_path))
|
|
119
|
+
|
|
120
|
+
# Process all files
|
|
121
|
+
for filename, file_path, module_path in files_to_scan:
|
|
122
|
+
try:
|
|
123
|
+
with open(file_path, 'r', encoding='utf-8') as f:
|
|
124
|
+
content = f.read()
|
|
125
|
+
|
|
126
|
+
# 查找 @register_tool 装饰器 (两种模式)
|
|
127
|
+
# Pattern 1: @register_tool("name") decorator syntax
|
|
128
|
+
decorator_pattern = r'@register_tool\([\'"]([^\'"]+)[\'"]\)'
|
|
129
|
+
decorator_matches = re.findall(decorator_pattern, content)
|
|
130
|
+
|
|
131
|
+
# Pattern 2: register_tool("name")(ClassName) function call syntax
|
|
132
|
+
function_pattern = r'register_tool\([\'"]([^\'"]+)[\'"]\)\([A-Za-z_][A-Za-z0-9_]*\)'
|
|
133
|
+
function_matches = re.findall(function_pattern, content)
|
|
134
|
+
|
|
135
|
+
# Combine all matches
|
|
136
|
+
all_matches = list(set(decorator_matches + function_matches))
|
|
137
|
+
|
|
138
|
+
for tool_name in all_matches:
|
|
139
|
+
tool_module_map[tool_name] = {
|
|
140
|
+
'module_file': filename[:-3] if filename != '__init__.py' else '__init__',
|
|
141
|
+
'package': package_name,
|
|
142
|
+
'module_path': module_path,
|
|
143
|
+
'category': dir_name,
|
|
144
|
+
}
|
|
145
|
+
except Exception:
|
|
146
|
+
pass
|
|
147
|
+
|
|
148
|
+
return tool_module_map
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def load_and_inspect_tool(tool_name: str):
|
|
152
|
+
"""加载并检查指定工具的详细功能"""
|
|
153
|
+
from aiecs.tools import get_tool
|
|
154
|
+
import importlib
|
|
155
|
+
|
|
156
|
+
print(f"\n{'=' * 80}")
|
|
157
|
+
print(f"🔍 加载工具: {tool_name}")
|
|
158
|
+
print(f"{'=' * 80}")
|
|
159
|
+
|
|
160
|
+
try:
|
|
161
|
+
# 自动发现工具模块
|
|
162
|
+
print(f"\n⏳ 正在加载...")
|
|
163
|
+
|
|
164
|
+
tool_module_map = auto_discover_tool_modules()
|
|
165
|
+
|
|
166
|
+
# 如果找到了工具的模块信息,预加载模块
|
|
167
|
+
if tool_name in tool_module_map:
|
|
168
|
+
info = tool_module_map[tool_name]
|
|
169
|
+
module_path = info.get('module_path') or f"{info['package']}.{info['module_file']}"
|
|
170
|
+
|
|
171
|
+
try:
|
|
172
|
+
importlib.import_module(module_path)
|
|
173
|
+
print(f" 已触发 {module_path} 模块加载")
|
|
174
|
+
except Exception as e:
|
|
175
|
+
print(f" 警告: 无法预加载模块 ({e})")
|
|
176
|
+
else:
|
|
177
|
+
print(f" 未找到工具模块映射,尝试直接加载...")
|
|
178
|
+
|
|
179
|
+
# 获取工具实例
|
|
180
|
+
tool = get_tool(tool_name)
|
|
181
|
+
|
|
182
|
+
# 检查是否为占位符
|
|
183
|
+
is_placeholder = getattr(tool, 'is_placeholder', False)
|
|
184
|
+
|
|
185
|
+
if is_placeholder:
|
|
186
|
+
print(f"\n⚠️ 工具仍处于占位符状态")
|
|
187
|
+
print(f" 描述: {tool.description}")
|
|
188
|
+
print(f" 提示: 此工具需要在调用具体方法时才会完全实例化")
|
|
189
|
+
return
|
|
190
|
+
|
|
191
|
+
# 显示工具基本信息
|
|
192
|
+
print(f"\n✅ 工具已成功加载")
|
|
193
|
+
print(f" 类名: {tool.__class__.__name__}")
|
|
194
|
+
print(f" 模块: {tool.__class__.__module__}")
|
|
195
|
+
|
|
196
|
+
if hasattr(tool, 'description'):
|
|
197
|
+
print(f" 描述: {tool.description}")
|
|
198
|
+
|
|
199
|
+
if hasattr(tool, 'category'):
|
|
200
|
+
print(f" 类别: {tool.category}")
|
|
201
|
+
|
|
202
|
+
# 获取所有方法(原子功能)
|
|
203
|
+
methods = get_tool_methods(tool)
|
|
204
|
+
|
|
205
|
+
if not methods:
|
|
206
|
+
print(f"\n❌ 未发现公共方法")
|
|
207
|
+
return
|
|
208
|
+
|
|
209
|
+
print(f"\n📋 原子功能列表 (共 {len(methods)} 个方法):")
|
|
210
|
+
print(f"{'-' * 80}")
|
|
211
|
+
|
|
212
|
+
for i, method_name in enumerate(methods, 1):
|
|
213
|
+
try:
|
|
214
|
+
method = getattr(tool, method_name)
|
|
215
|
+
|
|
216
|
+
# 获取方法签名
|
|
217
|
+
sig = inspect.signature(method)
|
|
218
|
+
params = []
|
|
219
|
+
for param_name, param in sig.parameters.items():
|
|
220
|
+
if param_name == 'self':
|
|
221
|
+
continue
|
|
222
|
+
|
|
223
|
+
# 构建参数字符串
|
|
224
|
+
param_str = param_name
|
|
225
|
+
if param.annotation != inspect.Parameter.empty:
|
|
226
|
+
param_str += f": {param.annotation.__name__ if hasattr(param.annotation, '__name__') else str(param.annotation)}"
|
|
227
|
+
if param.default != inspect.Parameter.empty:
|
|
228
|
+
param_str += f" = {param.default!r}"
|
|
229
|
+
params.append(param_str)
|
|
230
|
+
|
|
231
|
+
# 获取返回类型
|
|
232
|
+
return_annotation = ""
|
|
233
|
+
if sig.return_annotation != inspect.Signature.empty:
|
|
234
|
+
return_type = sig.return_annotation
|
|
235
|
+
return_annotation = f" -> {return_type.__name__ if hasattr(return_type, '__name__') else str(return_type)}"
|
|
236
|
+
|
|
237
|
+
# 显示方法签名
|
|
238
|
+
print(f"\n [{i}] {method_name}({', '.join(params)}){return_annotation}")
|
|
239
|
+
|
|
240
|
+
# 获取文档字符串
|
|
241
|
+
if method.__doc__:
|
|
242
|
+
doc_lines = method.__doc__.strip().split('\n')
|
|
243
|
+
first_line = doc_lines[0].strip()
|
|
244
|
+
if first_line:
|
|
245
|
+
print(f" {first_line}")
|
|
246
|
+
|
|
247
|
+
except Exception as e:
|
|
248
|
+
print(f"\n [{i}] {method_name}")
|
|
249
|
+
print(f" (无法获取详细信息: {e})")
|
|
250
|
+
|
|
251
|
+
print(f"\n{'-' * 80}")
|
|
252
|
+
|
|
253
|
+
except Exception as e:
|
|
254
|
+
print(f"\n❌ 加载工具失败: {e}")
|
|
255
|
+
import traceback
|
|
256
|
+
traceback.print_exc()
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
def interactive_mode(tool_map: Dict[int, str]):
|
|
260
|
+
"""交互式模式"""
|
|
261
|
+
print(f"\n{'=' * 80}")
|
|
262
|
+
print("🎮 交互模式")
|
|
263
|
+
print(f"{'=' * 80}")
|
|
264
|
+
print("\n提示:")
|
|
265
|
+
print(" - 输入工具序号 (1-{}) 查看详细功能".format(len(tool_map)))
|
|
266
|
+
print(" - 输入工具名称查看详细功能")
|
|
267
|
+
print(" - 输入 'list' 重新显示工具列表")
|
|
268
|
+
print(" - 输入 'q' 或 'quit' 退出")
|
|
269
|
+
|
|
270
|
+
while True:
|
|
271
|
+
try:
|
|
272
|
+
user_input = input("\n👉 请选择工具 > ").strip()
|
|
273
|
+
|
|
274
|
+
if not user_input:
|
|
275
|
+
continue
|
|
276
|
+
|
|
277
|
+
if user_input.lower() in ['q', 'quit', 'exit']:
|
|
278
|
+
print("\n👋 再见!")
|
|
279
|
+
break
|
|
280
|
+
|
|
281
|
+
if user_input.lower() == 'list':
|
|
282
|
+
from aiecs.tools import list_tools
|
|
283
|
+
tools = list_tools()
|
|
284
|
+
display_tools_by_category(tools)
|
|
285
|
+
continue
|
|
286
|
+
|
|
287
|
+
# 尝试作为序号解析
|
|
288
|
+
tool_name = None
|
|
289
|
+
try:
|
|
290
|
+
index = int(user_input)
|
|
291
|
+
if index in tool_map:
|
|
292
|
+
tool_name = tool_map[index]
|
|
293
|
+
else:
|
|
294
|
+
print(f"❌ 无效的序号: {index}")
|
|
295
|
+
continue
|
|
296
|
+
except ValueError:
|
|
297
|
+
# 作为工具名称
|
|
298
|
+
tool_name = user_input
|
|
299
|
+
|
|
300
|
+
if tool_name:
|
|
301
|
+
load_and_inspect_tool(tool_name)
|
|
302
|
+
|
|
303
|
+
except KeyboardInterrupt:
|
|
304
|
+
print("\n\n👋 再见!")
|
|
305
|
+
break
|
|
306
|
+
except EOFError:
|
|
307
|
+
print("\n\n👋 再见!")
|
|
308
|
+
break
|
|
309
|
+
except Exception as e:
|
|
310
|
+
print(f"\n❌ 错误: {e}")
|
|
311
|
+
|
|
312
|
+
|
|
313
|
+
def main():
|
|
314
|
+
"""主函数:验证和展示所有注册的工具"""
|
|
315
|
+
print("=" * 80)
|
|
316
|
+
print("AIECS Tools 注册工具验证")
|
|
317
|
+
print("=" * 80)
|
|
318
|
+
|
|
319
|
+
try:
|
|
320
|
+
from aiecs.tools import list_tools
|
|
321
|
+
|
|
322
|
+
# 获取所有注册的工具
|
|
323
|
+
tools = list_tools()
|
|
324
|
+
|
|
325
|
+
print(f"\n发现 {len(tools)} 个注册的工具")
|
|
326
|
+
|
|
327
|
+
# 按类别显示工具
|
|
328
|
+
tool_map = display_tools_by_category(tools)
|
|
329
|
+
|
|
330
|
+
print(f"\n{'=' * 80}")
|
|
331
|
+
print(f"✅ 工具列表显示完成! 共 {len(tools)} 个工具")
|
|
332
|
+
print(f"{'=' * 80}")
|
|
333
|
+
|
|
334
|
+
# 进入交互模式
|
|
335
|
+
interactive_mode(tool_map)
|
|
336
|
+
|
|
337
|
+
except ImportError as e:
|
|
338
|
+
print(f"❌ 导入错误: {e}")
|
|
339
|
+
print("请确保在正确的项目目录中运行此脚本")
|
|
340
|
+
except Exception as e:
|
|
341
|
+
print(f"❌ 运行错误: {e}")
|
|
342
|
+
import traceback
|
|
343
|
+
traceback.print_exc()
|
|
344
|
+
|
|
345
|
+
|
|
346
|
+
if __name__ == "__main__":
|
|
347
|
+
main()
|
aiecs/tools/__init__.py
CHANGED
|
@@ -155,48 +155,112 @@ def _ensure_task_tools_available():
|
|
|
155
155
|
logger.error(f"Failed to import task_tools: {e}")
|
|
156
156
|
return False
|
|
157
157
|
|
|
158
|
+
def _auto_discover_tools():
|
|
159
|
+
"""Automatically discover all tools by scanning tool directories"""
|
|
160
|
+
import re
|
|
161
|
+
|
|
162
|
+
# Define tool directories and their categories
|
|
163
|
+
tool_dirs = [
|
|
164
|
+
('task_tools', 'task'),
|
|
165
|
+
('docs', 'docs'),
|
|
166
|
+
('statistics', 'statistics'),
|
|
167
|
+
('search_tool', 'task'), # Enhanced search tool
|
|
168
|
+
('api_sources', 'task'), # API data sources (legacy)
|
|
169
|
+
('apisource', 'task'), # API Source Tool (new modular version)
|
|
170
|
+
]
|
|
171
|
+
|
|
172
|
+
discovered_tools = []
|
|
173
|
+
|
|
174
|
+
for dir_name, category in tool_dirs:
|
|
175
|
+
dir_path = os.path.join(os.path.dirname(__file__), dir_name)
|
|
176
|
+
if not os.path.exists(dir_path):
|
|
177
|
+
continue
|
|
178
|
+
|
|
179
|
+
# Check if this is a package (has __init__.py) or a directory of modules
|
|
180
|
+
init_file = os.path.join(dir_path, '__init__.py')
|
|
181
|
+
files_to_scan = []
|
|
182
|
+
|
|
183
|
+
if os.path.isfile(init_file):
|
|
184
|
+
# Scan __init__.py for package-level registrations
|
|
185
|
+
files_to_scan.append(('__init__.py', init_file))
|
|
186
|
+
|
|
187
|
+
# Scan all other Python files in the directory
|
|
188
|
+
for filename in os.listdir(dir_path):
|
|
189
|
+
if filename.endswith('.py') and not filename.startswith('__'):
|
|
190
|
+
file_path = os.path.join(dir_path, filename)
|
|
191
|
+
files_to_scan.append((filename, file_path))
|
|
192
|
+
|
|
193
|
+
# Process all files
|
|
194
|
+
for filename, file_path in files_to_scan:
|
|
195
|
+
try:
|
|
196
|
+
with open(file_path, 'r', encoding='utf-8') as f:
|
|
197
|
+
content = f.read()
|
|
198
|
+
|
|
199
|
+
# Find @register_tool decorators (two patterns)
|
|
200
|
+
# Pattern 1: @register_tool("name") decorator syntax
|
|
201
|
+
decorator_pattern = r'@register_tool\([\'"]([^\'"]+)[\'"]\)'
|
|
202
|
+
decorator_matches = re.findall(decorator_pattern, content)
|
|
203
|
+
|
|
204
|
+
# Pattern 2: register_tool("name")(ClassName) function call syntax
|
|
205
|
+
function_pattern = r'register_tool\([\'"]([^\'"]+)[\'"]\)\([A-Za-z_][A-Za-z0-9_]*\)'
|
|
206
|
+
function_matches = re.findall(function_pattern, content)
|
|
207
|
+
|
|
208
|
+
# Combine all matches
|
|
209
|
+
all_matches = list(set(decorator_matches + function_matches))
|
|
210
|
+
|
|
211
|
+
for tool_name in all_matches:
|
|
212
|
+
# Try to extract description from class docstring or module docstring
|
|
213
|
+
description = f"{tool_name} tool"
|
|
214
|
+
|
|
215
|
+
# Method 1: Look for class definition after the decorator
|
|
216
|
+
class_pattern = rf'@register_tool\([\'"]({tool_name})[\'"]\)\s*class\s+\w+.*?"""(.*?)"""'
|
|
217
|
+
class_match = re.search(class_pattern, content, re.DOTALL)
|
|
218
|
+
if class_match:
|
|
219
|
+
doc = class_match.group(2).strip()
|
|
220
|
+
# Get first line of docstring
|
|
221
|
+
first_line = doc.split('\n')[0].strip()
|
|
222
|
+
if first_line and len(first_line) < 200:
|
|
223
|
+
description = first_line
|
|
224
|
+
|
|
225
|
+
# Method 2: For __init__.py files, try to extract from module docstring
|
|
226
|
+
if not class_match and filename == '__init__.py':
|
|
227
|
+
module_doc_pattern = r'^"""(.*?)"""'
|
|
228
|
+
module_doc_match = re.search(module_doc_pattern, content, re.DOTALL | re.MULTILINE)
|
|
229
|
+
if module_doc_match:
|
|
230
|
+
doc = module_doc_match.group(1).strip()
|
|
231
|
+
# Get first non-empty line
|
|
232
|
+
for line in doc.split('\n'):
|
|
233
|
+
line = line.strip()
|
|
234
|
+
if line and not line.startswith('#') and len(line) < 200:
|
|
235
|
+
description = line
|
|
236
|
+
break
|
|
237
|
+
|
|
238
|
+
discovered_tools.append((tool_name, description, category))
|
|
239
|
+
except Exception as e:
|
|
240
|
+
logger.debug(f"Error scanning {filename}: {e}")
|
|
241
|
+
|
|
242
|
+
return discovered_tools
|
|
243
|
+
|
|
158
244
|
def _register_known_tools():
|
|
159
245
|
"""Register known tools without importing heavy dependencies"""
|
|
160
|
-
#
|
|
161
|
-
|
|
162
|
-
#
|
|
163
|
-
# NOTE: Tool names must match the names used in @register_tool decorators:
|
|
164
|
-
# - chart (not chart_tool)
|
|
165
|
-
# - classifier (not classfire_tool)
|
|
166
|
-
# - image (not image_tool)
|
|
167
|
-
# - office (not office_tool)
|
|
168
|
-
# - pandas (not pandas_tool)
|
|
169
|
-
# - report (not report_tool)
|
|
170
|
-
# - research (not research_tool)
|
|
171
|
-
# - scraper (not scraper_tool)
|
|
172
|
-
# - search_api (same)
|
|
173
|
-
# - stats (not stats_tool)
|
|
246
|
+
# Automatically discover all tools
|
|
247
|
+
discovered_tools = _auto_discover_tools()
|
|
174
248
|
|
|
175
|
-
|
|
176
|
-
("chart", "Chart and visualization operations"),
|
|
177
|
-
("classifier", "Text classification and keyword extraction"),
|
|
178
|
-
("image", "Image processing and OCR operations"),
|
|
179
|
-
("office", "Office document processing"),
|
|
180
|
-
("pandas", "Data analysis and manipulation"),
|
|
181
|
-
("report", "Report generation and formatting"),
|
|
182
|
-
("research", "Research and information gathering"),
|
|
183
|
-
("scraper", "Web scraping and data extraction"),
|
|
184
|
-
("search_api", "Search API integration"),
|
|
185
|
-
("stats", "Statistical analysis and computation")
|
|
186
|
-
]
|
|
249
|
+
logger.info(f"Auto-discovered {len(discovered_tools)} tools")
|
|
187
250
|
|
|
188
251
|
# Register as placeholder until actually loaded
|
|
189
|
-
for
|
|
252
|
+
for tool_info in discovered_tools:
|
|
253
|
+
tool_name, description, category = tool_info
|
|
190
254
|
if tool_name not in TOOL_REGISTRY and tool_name not in TOOL_CLASSES:
|
|
191
255
|
# Create a placeholder class for discovery
|
|
192
256
|
class ToolPlaceholder:
|
|
193
|
-
def __init__(self, name, desc):
|
|
257
|
+
def __init__(self, name, desc, cat):
|
|
194
258
|
self.name = name
|
|
195
259
|
self.description = desc
|
|
196
|
-
self.category =
|
|
260
|
+
self.category = cat
|
|
197
261
|
self.is_placeholder = True
|
|
198
262
|
|
|
199
|
-
TOOL_REGISTRY[tool_name] = ToolPlaceholder(tool_name, description)
|
|
263
|
+
TOOL_REGISTRY[tool_name] = ToolPlaceholder(tool_name, description, category)
|
|
200
264
|
|
|
201
265
|
# Register known tools for discovery
|
|
202
266
|
_register_known_tools()
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
"""
|
|
2
|
+
APISource Tool - Unified API Data Source Interface
|
|
3
|
+
|
|
4
|
+
A comprehensive tool for querying external API data sources with advanced features:
|
|
5
|
+
- Multi-provider support (FRED, World Bank, News API, Census Bureau)
|
|
6
|
+
- Intelligent query understanding and parameter enhancement
|
|
7
|
+
- Cross-provider data fusion
|
|
8
|
+
- Automatic fallback and retry logic
|
|
9
|
+
- Advanced search with relevance ranking
|
|
10
|
+
- Comprehensive metrics and health monitoring
|
|
11
|
+
|
|
12
|
+
Usage:
|
|
13
|
+
from aiecs.tools.apisource import APISourceTool
|
|
14
|
+
|
|
15
|
+
tool = APISourceTool({
|
|
16
|
+
'fred_api_key': 'YOUR_KEY',
|
|
17
|
+
'enable_fallback': True,
|
|
18
|
+
'enable_query_enhancement': True
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
# Query with natural language
|
|
22
|
+
result = tool.query(
|
|
23
|
+
provider='fred',
|
|
24
|
+
operation='get_series_observations',
|
|
25
|
+
params={'series_id': 'GDP'},
|
|
26
|
+
query_text="Get GDP data for last 5 years"
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
# Multi-provider search with fusion
|
|
30
|
+
results = tool.search(
|
|
31
|
+
query="unemployment trends",
|
|
32
|
+
enable_fusion=True
|
|
33
|
+
)
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
from aiecs.tools.apisource.tool import (
|
|
37
|
+
APISourceTool,
|
|
38
|
+
APISourceError,
|
|
39
|
+
ProviderNotFoundError,
|
|
40
|
+
APIRateLimitError,
|
|
41
|
+
APIAuthenticationError
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
# Import providers for convenience
|
|
45
|
+
from aiecs.tools.apisource.providers import (
|
|
46
|
+
BaseAPIProvider,
|
|
47
|
+
get_provider,
|
|
48
|
+
list_providers,
|
|
49
|
+
PROVIDER_REGISTRY
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
# Import intelligence components
|
|
53
|
+
from aiecs.tools.apisource.intelligence import (
|
|
54
|
+
QueryIntentAnalyzer,
|
|
55
|
+
QueryEnhancer,
|
|
56
|
+
DataFusionEngine,
|
|
57
|
+
SearchEnhancer
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
# Import reliability components
|
|
61
|
+
from aiecs.tools.apisource.reliability import (
|
|
62
|
+
SmartErrorHandler,
|
|
63
|
+
FallbackStrategy
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
# Import monitoring components
|
|
67
|
+
from aiecs.tools.apisource.monitoring import DetailedMetrics
|
|
68
|
+
|
|
69
|
+
# Import utilities
|
|
70
|
+
from aiecs.tools.apisource.utils import DataValidator
|
|
71
|
+
|
|
72
|
+
__version__ = '2.0.0'
|
|
73
|
+
|
|
74
|
+
__all__ = [
|
|
75
|
+
# Main tool
|
|
76
|
+
'APISourceTool',
|
|
77
|
+
|
|
78
|
+
# Exceptions
|
|
79
|
+
'APISourceError',
|
|
80
|
+
'ProviderNotFoundError',
|
|
81
|
+
'APIRateLimitError',
|
|
82
|
+
'APIAuthenticationError',
|
|
83
|
+
|
|
84
|
+
# Providers
|
|
85
|
+
'BaseAPIProvider',
|
|
86
|
+
'get_provider',
|
|
87
|
+
'list_providers',
|
|
88
|
+
'PROVIDER_REGISTRY',
|
|
89
|
+
|
|
90
|
+
# Intelligence
|
|
91
|
+
'QueryIntentAnalyzer',
|
|
92
|
+
'QueryEnhancer',
|
|
93
|
+
'DataFusionEngine',
|
|
94
|
+
'SearchEnhancer',
|
|
95
|
+
|
|
96
|
+
# Reliability
|
|
97
|
+
'SmartErrorHandler',
|
|
98
|
+
'FallbackStrategy',
|
|
99
|
+
|
|
100
|
+
# Monitoring
|
|
101
|
+
'DetailedMetrics',
|
|
102
|
+
|
|
103
|
+
# Utils
|
|
104
|
+
'DataValidator'
|
|
105
|
+
]
|
|
106
|
+
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Intelligence Module
|
|
3
|
+
|
|
4
|
+
Contains query analysis, data fusion, and search enhancement components.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from aiecs.tools.apisource.intelligence.query_analyzer import (
|
|
8
|
+
QueryIntentAnalyzer,
|
|
9
|
+
QueryEnhancer
|
|
10
|
+
)
|
|
11
|
+
from aiecs.tools.apisource.intelligence.data_fusion import DataFusionEngine
|
|
12
|
+
from aiecs.tools.apisource.intelligence.search_enhancer import SearchEnhancer
|
|
13
|
+
|
|
14
|
+
__all__ = [
|
|
15
|
+
'QueryIntentAnalyzer',
|
|
16
|
+
'QueryEnhancer',
|
|
17
|
+
'DataFusionEngine',
|
|
18
|
+
'SearchEnhancer'
|
|
19
|
+
]
|
|
20
|
+
|