aiecs 1.0.8__py3-none-any.whl → 1.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.
Potentially problematic release.
This version of aiecs might be problematic. Click here for more details.
- aiecs/__init__.py +1 -1
- aiecs/aiecs_client.py +159 -1
- aiecs/config/config.py +6 -0
- aiecs/domain/__init__.py +95 -0
- aiecs/domain/community/__init__.py +159 -0
- aiecs/domain/community/agent_adapter.py +516 -0
- aiecs/domain/community/analytics.py +465 -0
- aiecs/domain/community/collaborative_workflow.py +99 -7
- aiecs/domain/community/communication_hub.py +649 -0
- aiecs/domain/community/community_builder.py +322 -0
- aiecs/domain/community/community_integration.py +365 -12
- aiecs/domain/community/community_manager.py +481 -5
- aiecs/domain/community/decision_engine.py +459 -13
- aiecs/domain/community/exceptions.py +238 -0
- aiecs/domain/community/models/__init__.py +36 -0
- aiecs/domain/community/resource_manager.py +1 -1
- aiecs/domain/community/shared_context_manager.py +621 -0
- aiecs/domain/context/__init__.py +24 -0
- aiecs/domain/context/context_engine.py +37 -33
- aiecs/main.py +20 -2
- aiecs/scripts/aid/VERSION_MANAGEMENT.md +97 -0
- aiecs/scripts/aid/__init__.py +15 -0
- aiecs/scripts/aid/version_manager.py +224 -0
- aiecs/scripts/dependance_check/__init__.py +18 -0
- aiecs/scripts/{download_nlp_data.py → dependance_check/download_nlp_data.py} +51 -8
- aiecs/scripts/dependance_patch/__init__.py +8 -0
- aiecs/scripts/dependance_patch/fix_weasel/__init__.py +12 -0
- aiecs/scripts/tools_develop/README.md +340 -0
- aiecs/scripts/tools_develop/__init__.py +16 -0
- aiecs/scripts/tools_develop/check_type_annotations.py +263 -0
- aiecs/scripts/tools_develop/validate_tool_schemas.py +346 -0
- aiecs/tools/__init__.py +53 -34
- aiecs/tools/docs/__init__.py +106 -0
- aiecs/tools/docs/ai_document_orchestrator.py +556 -0
- aiecs/tools/docs/ai_document_writer_orchestrator.py +2222 -0
- aiecs/tools/docs/content_insertion_tool.py +1234 -0
- aiecs/tools/docs/document_creator_tool.py +1179 -0
- aiecs/tools/docs/document_layout_tool.py +1105 -0
- aiecs/tools/docs/document_parser_tool.py +924 -0
- aiecs/tools/docs/document_writer_tool.py +1636 -0
- aiecs/tools/langchain_adapter.py +102 -51
- aiecs/tools/schema_generator.py +265 -0
- aiecs/tools/statistics/__init__.py +82 -0
- aiecs/tools/statistics/ai_data_analysis_orchestrator.py +581 -0
- aiecs/tools/statistics/ai_insight_generator_tool.py +473 -0
- aiecs/tools/statistics/ai_report_orchestrator_tool.py +629 -0
- aiecs/tools/statistics/data_loader_tool.py +518 -0
- aiecs/tools/statistics/data_profiler_tool.py +599 -0
- aiecs/tools/statistics/data_transformer_tool.py +531 -0
- aiecs/tools/statistics/data_visualizer_tool.py +460 -0
- aiecs/tools/statistics/model_trainer_tool.py +470 -0
- aiecs/tools/statistics/statistical_analyzer_tool.py +426 -0
- aiecs/tools/task_tools/chart_tool.py +2 -1
- aiecs/tools/task_tools/image_tool.py +43 -43
- aiecs/tools/task_tools/office_tool.py +48 -36
- aiecs/tools/task_tools/pandas_tool.py +37 -33
- aiecs/tools/task_tools/report_tool.py +67 -56
- aiecs/tools/task_tools/research_tool.py +32 -31
- aiecs/tools/task_tools/scraper_tool.py +53 -46
- aiecs/tools/task_tools/search_tool.py +1123 -0
- aiecs/tools/task_tools/stats_tool.py +20 -15
- {aiecs-1.0.8.dist-info → aiecs-1.2.0.dist-info}/METADATA +5 -1
- aiecs-1.2.0.dist-info/RECORD +135 -0
- aiecs-1.2.0.dist-info/entry_points.txt +10 -0
- aiecs/tools/task_tools/search_api.py +0 -7
- aiecs-1.0.8.dist-info/RECORD +0 -98
- aiecs-1.0.8.dist-info/entry_points.txt +0 -7
- /aiecs/scripts/{DEPENDENCY_SYSTEM_SUMMARY.md → dependance_check/DEPENDENCY_SYSTEM_SUMMARY.md} +0 -0
- /aiecs/scripts/{README_DEPENDENCY_CHECKER.md → dependance_check/README_DEPENDENCY_CHECKER.md} +0 -0
- /aiecs/scripts/{dependency_checker.py → dependance_check/dependency_checker.py} +0 -0
- /aiecs/scripts/{dependency_fixer.py → dependance_check/dependency_fixer.py} +0 -0
- /aiecs/scripts/{quick_dependency_check.py → dependance_check/quick_dependency_check.py} +0 -0
- /aiecs/scripts/{setup_nlp_data.sh → dependance_check/setup_nlp_data.sh} +0 -0
- /aiecs/scripts/{README_WEASEL_PATCH.md → dependance_patch/fix_weasel/README_WEASEL_PATCH.md} +0 -0
- /aiecs/scripts/{fix_weasel_validator.py → dependance_patch/fix_weasel/fix_weasel_validator.py} +0 -0
- /aiecs/scripts/{fix_weasel_validator.sh → dependance_patch/fix_weasel/fix_weasel_validator.sh} +0 -0
- /aiecs/scripts/{patch_weasel_library.sh → dependance_patch/fix_weasel/patch_weasel_library.sh} +0 -0
- /aiecs/scripts/{run_weasel_patch.sh → dependance_patch/fix_weasel/run_weasel_patch.sh} +0 -0
- {aiecs-1.0.8.dist-info → aiecs-1.2.0.dist-info}/WHEEL +0 -0
- {aiecs-1.0.8.dist-info → aiecs-1.2.0.dist-info}/licenses/LICENSE +0 -0
- {aiecs-1.0.8.dist-info → aiecs-1.2.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,346 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
工具 Schema 质量验证器
|
|
4
|
+
|
|
5
|
+
用于工具开发和维护,验证自动生成的 Schema 质量。
|
|
6
|
+
帮助开发者识别需要改进的文档字符串,提升 Schema 描述质量。
|
|
7
|
+
|
|
8
|
+
使用方法:
|
|
9
|
+
# 验证所有工具
|
|
10
|
+
aiecs tools validate-schemas
|
|
11
|
+
|
|
12
|
+
# 验证特定工具
|
|
13
|
+
aiecs tools validate-schemas pandas
|
|
14
|
+
|
|
15
|
+
# 显示详细的改进建议
|
|
16
|
+
aiecs tools validate-schemas pandas --verbose
|
|
17
|
+
|
|
18
|
+
# 显示示例 Schema
|
|
19
|
+
aiecs tools validate-schemas pandas --show-examples
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
import sys
|
|
23
|
+
import inspect
|
|
24
|
+
from typing import Dict, List, Any, Type, Optional, get_type_hints
|
|
25
|
+
from pydantic import BaseModel
|
|
26
|
+
|
|
27
|
+
# 确保可以导入 aiecs
|
|
28
|
+
import os
|
|
29
|
+
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(__file__)))))
|
|
30
|
+
|
|
31
|
+
from aiecs.tools import discover_tools, TOOL_CLASSES
|
|
32
|
+
from aiecs.tools.schema_generator import generate_schema_from_method
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class SchemaQualityMetrics:
|
|
36
|
+
"""Schema 质量指标"""
|
|
37
|
+
|
|
38
|
+
def __init__(self):
|
|
39
|
+
self.total_methods = 0
|
|
40
|
+
self.schemas_generated = 0
|
|
41
|
+
self.schemas_failed = 0
|
|
42
|
+
self.total_fields = 0
|
|
43
|
+
self.fields_with_meaningful_descriptions = 0
|
|
44
|
+
self.fields_with_types = 0
|
|
45
|
+
self.quality_issues = []
|
|
46
|
+
|
|
47
|
+
def add_method(self, has_schema: bool):
|
|
48
|
+
"""添加方法统计"""
|
|
49
|
+
self.total_methods += 1
|
|
50
|
+
if has_schema:
|
|
51
|
+
self.schemas_generated += 1
|
|
52
|
+
else:
|
|
53
|
+
self.schemas_failed += 1
|
|
54
|
+
|
|
55
|
+
def add_field(self, has_type: bool, has_meaningful_desc: bool):
|
|
56
|
+
"""添加字段统计"""
|
|
57
|
+
self.total_fields += 1
|
|
58
|
+
if has_type:
|
|
59
|
+
self.fields_with_types += 1
|
|
60
|
+
if has_meaningful_desc:
|
|
61
|
+
self.fields_with_meaningful_descriptions += 1
|
|
62
|
+
|
|
63
|
+
def add_issue(self, issue: str):
|
|
64
|
+
"""添加质量问题"""
|
|
65
|
+
self.quality_issues.append(issue)
|
|
66
|
+
|
|
67
|
+
def get_scores(self) -> Dict[str, float]:
|
|
68
|
+
"""计算质量分数"""
|
|
69
|
+
generation_rate = (self.schemas_generated / self.total_methods * 100) if self.total_methods > 0 else 0
|
|
70
|
+
description_rate = (self.fields_with_meaningful_descriptions / self.total_fields * 100) if self.total_fields > 0 else 0
|
|
71
|
+
type_coverage = (self.fields_with_types / self.total_fields * 100) if self.total_fields > 0 else 0
|
|
72
|
+
|
|
73
|
+
return {
|
|
74
|
+
'generation_rate': generation_rate,
|
|
75
|
+
'description_quality': description_rate,
|
|
76
|
+
'type_coverage': type_coverage,
|
|
77
|
+
'overall_score': (generation_rate + description_rate + type_coverage) / 3
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def validate_schema_quality(schema: Type[BaseModel], method: callable, method_name: str) -> List[str]:
|
|
82
|
+
"""
|
|
83
|
+
验证单个 Schema 的质量
|
|
84
|
+
|
|
85
|
+
Returns:
|
|
86
|
+
质量问题列表(改进建议)
|
|
87
|
+
"""
|
|
88
|
+
issues = []
|
|
89
|
+
|
|
90
|
+
# 1. 检查 Schema 描述
|
|
91
|
+
if not schema.__doc__ or schema.__doc__.strip() == f"Execute {method_name} operation":
|
|
92
|
+
issues.append(f"💡 在方法文档字符串的第一行添加有意义的描述")
|
|
93
|
+
|
|
94
|
+
# 2. 检查字段
|
|
95
|
+
if not schema.model_fields:
|
|
96
|
+
return issues
|
|
97
|
+
|
|
98
|
+
for field_name, field_info in schema.model_fields.items():
|
|
99
|
+
# 检查字段描述
|
|
100
|
+
description = field_info.description
|
|
101
|
+
if not description or description == f"Parameter {field_name}":
|
|
102
|
+
issues.append(f"💡 在文档字符串的 Args 部分为参数 '{field_name}' 添加描述")
|
|
103
|
+
|
|
104
|
+
return issues
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def analyze_tool_schemas(tool_name: str, tool_class: Type) -> Dict[str, Any]:
|
|
108
|
+
"""分析工具的 Schema 生成情况"""
|
|
109
|
+
|
|
110
|
+
metrics = SchemaQualityMetrics()
|
|
111
|
+
methods_info = []
|
|
112
|
+
|
|
113
|
+
for method_name in dir(tool_class):
|
|
114
|
+
# 跳过私有方法和特殊方法
|
|
115
|
+
if method_name.startswith('_'):
|
|
116
|
+
continue
|
|
117
|
+
|
|
118
|
+
# 跳过基类方法
|
|
119
|
+
if method_name in ['run', 'run_async', 'run_batch']:
|
|
120
|
+
continue
|
|
121
|
+
|
|
122
|
+
method = getattr(tool_class, method_name)
|
|
123
|
+
|
|
124
|
+
# 跳过非方法属性
|
|
125
|
+
if not callable(method) or isinstance(method, type):
|
|
126
|
+
continue
|
|
127
|
+
|
|
128
|
+
# 生成 Schema
|
|
129
|
+
schema = generate_schema_from_method(method, method_name)
|
|
130
|
+
|
|
131
|
+
method_info = {
|
|
132
|
+
'name': method_name,
|
|
133
|
+
'schema': schema,
|
|
134
|
+
'issues': []
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if schema:
|
|
138
|
+
metrics.add_method(True)
|
|
139
|
+
|
|
140
|
+
# 验证质量
|
|
141
|
+
issues = validate_schema_quality(schema, method, method_name)
|
|
142
|
+
method_info['issues'] = issues
|
|
143
|
+
|
|
144
|
+
# 统计字段
|
|
145
|
+
for field_name, field_info in schema.model_fields.items():
|
|
146
|
+
has_type = field_info.annotation is not None
|
|
147
|
+
has_meaningful_desc = (
|
|
148
|
+
field_info.description and
|
|
149
|
+
field_info.description != f"Parameter {field_name}"
|
|
150
|
+
)
|
|
151
|
+
metrics.add_field(has_type, has_meaningful_desc)
|
|
152
|
+
|
|
153
|
+
# 记录问题
|
|
154
|
+
for issue in issues:
|
|
155
|
+
metrics.add_issue(f"{tool_name}.{method_name}: {issue}")
|
|
156
|
+
else:
|
|
157
|
+
metrics.add_method(False)
|
|
158
|
+
method_info['issues'] = ['⚠️ 无法生成 Schema(可能是无参数方法)']
|
|
159
|
+
|
|
160
|
+
methods_info.append(method_info)
|
|
161
|
+
|
|
162
|
+
return {
|
|
163
|
+
'metrics': metrics,
|
|
164
|
+
'methods': methods_info
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def print_tool_report(tool_name: str, result: Dict, verbose: bool = False, show_examples: bool = False):
|
|
169
|
+
"""打印工具报告"""
|
|
170
|
+
|
|
171
|
+
metrics = result['metrics']
|
|
172
|
+
methods = result['methods']
|
|
173
|
+
scores = metrics.get_scores()
|
|
174
|
+
|
|
175
|
+
# 状态图标
|
|
176
|
+
overall = scores['overall_score']
|
|
177
|
+
if overall >= 90:
|
|
178
|
+
status = "✅"
|
|
179
|
+
grade = "A (优秀)"
|
|
180
|
+
elif overall >= 80:
|
|
181
|
+
status = "⚠️"
|
|
182
|
+
grade = "B (良好)"
|
|
183
|
+
elif overall >= 70:
|
|
184
|
+
status = "⚠️"
|
|
185
|
+
grade = "C (中等)"
|
|
186
|
+
else:
|
|
187
|
+
status = "❌"
|
|
188
|
+
grade = "D (需改进)"
|
|
189
|
+
|
|
190
|
+
print(f"\n{status} {tool_name}")
|
|
191
|
+
print(f" 方法数: {metrics.total_methods}")
|
|
192
|
+
print(f" 成功生成 Schema: {metrics.schemas_generated} ({scores['generation_rate']:.1f}%)")
|
|
193
|
+
print(f" 描述质量: {scores['description_quality']:.1f}%")
|
|
194
|
+
print(f" 综合评分: {scores['overall_score']:.1f}% ({grade})")
|
|
195
|
+
|
|
196
|
+
# 显示需要改进的方法
|
|
197
|
+
methods_with_issues = [m for m in methods if m['issues'] and m['schema']]
|
|
198
|
+
|
|
199
|
+
if methods_with_issues and (verbose or scores['description_quality'] < 80):
|
|
200
|
+
print(f"\n 需要改进的方法 ({len(methods_with_issues)} 个):")
|
|
201
|
+
|
|
202
|
+
for method_info in methods_with_issues[:5 if not verbose else None]:
|
|
203
|
+
print(f"\n {method_info['name']}:")
|
|
204
|
+
for issue in method_info['issues']:
|
|
205
|
+
print(f" {issue}")
|
|
206
|
+
|
|
207
|
+
if not verbose and len(methods_with_issues) > 5:
|
|
208
|
+
print(f"\n ... 还有 {len(methods_with_issues) - 5} 个方法需要改进")
|
|
209
|
+
print(f" 使用 --verbose 查看全部")
|
|
210
|
+
|
|
211
|
+
# 显示示例 Schema
|
|
212
|
+
if show_examples:
|
|
213
|
+
methods_with_schema = [m for m in methods if m['schema']]
|
|
214
|
+
if methods_with_schema:
|
|
215
|
+
print(f"\n 示例 Schema:")
|
|
216
|
+
for method_info in methods_with_schema[:2]:
|
|
217
|
+
schema = method_info['schema']
|
|
218
|
+
print(f"\n {method_info['name']} → {schema.__name__}")
|
|
219
|
+
print(f" 描述: {schema.__doc__}")
|
|
220
|
+
print(f" 字段:")
|
|
221
|
+
for field_name, field_info in list(schema.model_fields.items())[:3]:
|
|
222
|
+
required = "必需" if field_info.is_required() else "可选"
|
|
223
|
+
print(f" - {field_name}: {field_info.description} [{required}]")
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
def validate_schemas(tool_names: Optional[List[str]] = None, verbose: bool = False, show_examples: bool = False):
|
|
227
|
+
"""
|
|
228
|
+
验证工具的 Schema 质量
|
|
229
|
+
|
|
230
|
+
Args:
|
|
231
|
+
tool_names: 要验证的工具名称列表,None 表示验证所有工具
|
|
232
|
+
verbose: 是否显示详细信息
|
|
233
|
+
show_examples: 是否显示示例 Schema
|
|
234
|
+
"""
|
|
235
|
+
print("=" * 100)
|
|
236
|
+
print("工具 Schema 质量验证器")
|
|
237
|
+
print("=" * 100)
|
|
238
|
+
|
|
239
|
+
discover_tools()
|
|
240
|
+
|
|
241
|
+
# 确定要验证的工具
|
|
242
|
+
if tool_names:
|
|
243
|
+
tools_to_check = {}
|
|
244
|
+
for name in tool_names:
|
|
245
|
+
if name in TOOL_CLASSES:
|
|
246
|
+
tools_to_check[name] = TOOL_CLASSES[name]
|
|
247
|
+
else:
|
|
248
|
+
print(f"\n❌ 工具 '{name}' 不存在")
|
|
249
|
+
|
|
250
|
+
if not tools_to_check:
|
|
251
|
+
print("\n没有找到要验证的工具")
|
|
252
|
+
return
|
|
253
|
+
else:
|
|
254
|
+
tools_to_check = TOOL_CLASSES
|
|
255
|
+
|
|
256
|
+
# 验证每个工具
|
|
257
|
+
all_results = {}
|
|
258
|
+
for tool_name in sorted(tools_to_check.keys()):
|
|
259
|
+
tool_class = tools_to_check[tool_name]
|
|
260
|
+
result = analyze_tool_schemas(tool_name, tool_class)
|
|
261
|
+
all_results[tool_name] = result
|
|
262
|
+
|
|
263
|
+
print_tool_report(tool_name, result, verbose, show_examples)
|
|
264
|
+
|
|
265
|
+
# 总体统计
|
|
266
|
+
if len(all_results) > 1:
|
|
267
|
+
total_methods = sum(r['metrics'].total_methods for r in all_results.values())
|
|
268
|
+
total_generated = sum(r['metrics'].schemas_generated for r in all_results.values())
|
|
269
|
+
total_fields = sum(r['metrics'].total_fields for r in all_results.values())
|
|
270
|
+
total_meaningful = sum(r['metrics'].fields_with_meaningful_descriptions for r in all_results.values())
|
|
271
|
+
|
|
272
|
+
overall_generation = (total_generated / total_methods * 100) if total_methods > 0 else 0
|
|
273
|
+
overall_description = (total_meaningful / total_fields * 100) if total_fields > 0 else 0
|
|
274
|
+
|
|
275
|
+
print("\n" + "=" * 100)
|
|
276
|
+
print(f"总体统计:")
|
|
277
|
+
print(f" 方法数: {total_methods}")
|
|
278
|
+
print(f" Schema 生成率: {total_generated}/{total_methods} ({overall_generation:.1f}%)")
|
|
279
|
+
print(f" 描述质量: {overall_description:.1f}%")
|
|
280
|
+
print("=" * 100)
|
|
281
|
+
|
|
282
|
+
print("\n💡 改进建议:")
|
|
283
|
+
print(" 1. 在方法的文档字符串第一行添加简短描述")
|
|
284
|
+
print(" 2. 在 Args 部分为每个参数添加详细描述")
|
|
285
|
+
print(" 3. 使用 Google 或 NumPy 风格的文档字符串")
|
|
286
|
+
print("\n示例:")
|
|
287
|
+
print(' def filter(self, records: List[Dict], condition: str) -> List[Dict]:')
|
|
288
|
+
print(' """')
|
|
289
|
+
print(' Filter DataFrame based on a condition.')
|
|
290
|
+
print(' ')
|
|
291
|
+
print(' Args:')
|
|
292
|
+
print(' records: List of records to filter')
|
|
293
|
+
print(' condition: Filter condition (pandas query syntax)')
|
|
294
|
+
print(' """')
|
|
295
|
+
|
|
296
|
+
|
|
297
|
+
def main():
|
|
298
|
+
"""命令行入口"""
|
|
299
|
+
import argparse
|
|
300
|
+
|
|
301
|
+
parser = argparse.ArgumentParser(
|
|
302
|
+
description='验证工具 Schema 的生成质量',
|
|
303
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
304
|
+
epilog="""
|
|
305
|
+
示例:
|
|
306
|
+
# 验证所有工具
|
|
307
|
+
aiecs tools validate-schemas
|
|
308
|
+
|
|
309
|
+
# 验证特定工具
|
|
310
|
+
aiecs tools validate-schemas pandas
|
|
311
|
+
|
|
312
|
+
# 显示详细的改进建议
|
|
313
|
+
aiecs tools validate-schemas pandas --verbose
|
|
314
|
+
|
|
315
|
+
# 显示示例 Schema
|
|
316
|
+
aiecs tools validate-schemas pandas --show-examples
|
|
317
|
+
"""
|
|
318
|
+
)
|
|
319
|
+
|
|
320
|
+
parser.add_argument(
|
|
321
|
+
'tools',
|
|
322
|
+
nargs='*',
|
|
323
|
+
help='要验证的工具名称(不指定则验证所有工具)'
|
|
324
|
+
)
|
|
325
|
+
|
|
326
|
+
parser.add_argument(
|
|
327
|
+
'-v', '--verbose',
|
|
328
|
+
action='store_true',
|
|
329
|
+
help='显示详细的改进建议'
|
|
330
|
+
)
|
|
331
|
+
|
|
332
|
+
parser.add_argument(
|
|
333
|
+
'-e', '--show-examples',
|
|
334
|
+
action='store_true',
|
|
335
|
+
help='显示示例 Schema'
|
|
336
|
+
)
|
|
337
|
+
|
|
338
|
+
args = parser.parse_args()
|
|
339
|
+
|
|
340
|
+
tool_names = args.tools if args.tools else None
|
|
341
|
+
validate_schemas(tool_names, args.verbose, args.show_examples)
|
|
342
|
+
|
|
343
|
+
|
|
344
|
+
if __name__ == '__main__':
|
|
345
|
+
main()
|
|
346
|
+
|
aiecs/tools/__init__.py
CHANGED
|
@@ -11,25 +11,25 @@ from aiecs.tools.base_tool import BaseTool
|
|
|
11
11
|
|
|
12
12
|
logger = logging.getLogger(__name__)
|
|
13
13
|
|
|
14
|
-
#
|
|
14
|
+
# Global tool registry
|
|
15
15
|
TOOL_REGISTRY = {}
|
|
16
16
|
TOOL_CLASSES = {}
|
|
17
17
|
TOOL_CONFIGS = {}
|
|
18
18
|
|
|
19
19
|
def register_tool(name):
|
|
20
20
|
"""
|
|
21
|
-
|
|
21
|
+
Decorator for registering tool classes
|
|
22
22
|
|
|
23
23
|
Args:
|
|
24
|
-
name:
|
|
24
|
+
name: Tool name
|
|
25
25
|
|
|
26
26
|
Returns:
|
|
27
|
-
|
|
27
|
+
Decorated class
|
|
28
28
|
"""
|
|
29
29
|
def wrapper(cls):
|
|
30
|
-
#
|
|
30
|
+
# Store tool class but don't instantiate immediately
|
|
31
31
|
TOOL_CLASSES[name] = cls
|
|
32
|
-
#
|
|
32
|
+
# Backward compatibility: if class inherits from BaseTool, don't instantiate immediately
|
|
33
33
|
if not issubclass(cls, BaseTool):
|
|
34
34
|
TOOL_REGISTRY[name] = cls()
|
|
35
35
|
return cls
|
|
@@ -37,22 +37,29 @@ def register_tool(name):
|
|
|
37
37
|
|
|
38
38
|
def get_tool(name):
|
|
39
39
|
"""
|
|
40
|
-
|
|
40
|
+
Get tool instance
|
|
41
41
|
|
|
42
42
|
Args:
|
|
43
|
-
name:
|
|
43
|
+
name: Tool name
|
|
44
44
|
|
|
45
45
|
Returns:
|
|
46
|
-
|
|
46
|
+
Tool instance
|
|
47
47
|
|
|
48
48
|
Raises:
|
|
49
|
-
ValueError:
|
|
49
|
+
ValueError: If tool is not registered
|
|
50
50
|
"""
|
|
51
|
-
if
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
51
|
+
# Check if placeholder needs to be replaced or lazy instantiation is needed
|
|
52
|
+
if name in TOOL_CLASSES:
|
|
53
|
+
# If TOOL_REGISTRY contains placeholder or doesn't exist, instantiate real tool class
|
|
54
|
+
current_tool = TOOL_REGISTRY.get(name)
|
|
55
|
+
is_placeholder = getattr(current_tool, 'is_placeholder', False)
|
|
56
|
+
|
|
57
|
+
if current_tool is None or is_placeholder:
|
|
58
|
+
# Lazy instantiation of BaseTool subclasses, replace placeholder
|
|
59
|
+
tool_class = TOOL_CLASSES[name]
|
|
60
|
+
config = TOOL_CONFIGS.get(name, {})
|
|
61
|
+
TOOL_REGISTRY[name] = tool_class(config)
|
|
62
|
+
logger.debug(f"Instantiated tool '{name}' from class {tool_class.__name__}")
|
|
56
63
|
|
|
57
64
|
if name not in TOOL_REGISTRY:
|
|
58
65
|
raise ValueError(f"Tool '{name}' is not registered")
|
|
@@ -61,17 +68,17 @@ def get_tool(name):
|
|
|
61
68
|
|
|
62
69
|
def list_tools():
|
|
63
70
|
"""
|
|
64
|
-
|
|
71
|
+
List all registered tools
|
|
65
72
|
|
|
66
73
|
Returns:
|
|
67
|
-
|
|
74
|
+
List of tool information dictionaries
|
|
68
75
|
"""
|
|
69
76
|
tools = []
|
|
70
77
|
all_tool_names = list(set(list(TOOL_REGISTRY.keys()) + list(TOOL_CLASSES.keys())))
|
|
71
78
|
|
|
72
79
|
for tool_name in all_tool_names:
|
|
73
80
|
try:
|
|
74
|
-
#
|
|
81
|
+
# Prefer using information from existing instances
|
|
75
82
|
if tool_name in TOOL_REGISTRY:
|
|
76
83
|
tool_instance = TOOL_REGISTRY[tool_name]
|
|
77
84
|
tool_info = {
|
|
@@ -83,7 +90,7 @@ def list_tools():
|
|
|
83
90
|
"status": "loaded"
|
|
84
91
|
}
|
|
85
92
|
elif tool_name in TOOL_CLASSES:
|
|
86
|
-
#
|
|
93
|
+
# Get information from class definition but don't instantiate
|
|
87
94
|
tool_class = TOOL_CLASSES[tool_name]
|
|
88
95
|
tool_info = {
|
|
89
96
|
"name": tool_name,
|
|
@@ -100,7 +107,7 @@ def list_tools():
|
|
|
100
107
|
|
|
101
108
|
except Exception as e:
|
|
102
109
|
logger.warning(f"Failed to get info for tool {tool_name}: {e}")
|
|
103
|
-
#
|
|
110
|
+
# Provide basic information
|
|
104
111
|
tools.append({
|
|
105
112
|
"name": tool_name,
|
|
106
113
|
"description": f"{tool_name} (info unavailable)",
|
|
@@ -114,26 +121,26 @@ def list_tools():
|
|
|
114
121
|
|
|
115
122
|
def discover_tools(package_path: str = "aiecs.tools"):
|
|
116
123
|
"""
|
|
117
|
-
|
|
124
|
+
Discover and register all tools in the package
|
|
118
125
|
|
|
119
126
|
Args:
|
|
120
|
-
package_path:
|
|
127
|
+
package_path: Package path to search
|
|
121
128
|
"""
|
|
122
129
|
package = importlib.import_module(package_path)
|
|
123
130
|
package_dir = os.path.dirname(package.__file__)
|
|
124
131
|
|
|
125
132
|
for _, module_name, is_pkg in pkgutil.iter_modules([package_dir]):
|
|
126
133
|
if is_pkg:
|
|
127
|
-
#
|
|
134
|
+
# Recursively search for tools in subpackages
|
|
128
135
|
discover_tools(f"{package_path}.{module_name}")
|
|
129
136
|
else:
|
|
130
|
-
#
|
|
137
|
+
# Import module
|
|
131
138
|
try:
|
|
132
139
|
importlib.import_module(f"{package_path}.{module_name}")
|
|
133
140
|
except Exception as e:
|
|
134
141
|
logger.error(f"Error importing module {module_name}: {e}")
|
|
135
142
|
|
|
136
|
-
#
|
|
143
|
+
# Import base tool class for inheritance
|
|
137
144
|
from aiecs.tools.base_tool import BaseTool
|
|
138
145
|
|
|
139
146
|
# Lazy loading strategy: don't import all tools at package init
|
|
@@ -152,18 +159,30 @@ def _register_known_tools():
|
|
|
152
159
|
"""Register known tools without importing heavy dependencies"""
|
|
153
160
|
# Pre-register tool classes for discovery without importing modules
|
|
154
161
|
# This allows list_tools() to work before actual tool loading
|
|
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)
|
|
155
174
|
|
|
156
175
|
known_tools = [
|
|
157
|
-
("
|
|
158
|
-
("
|
|
159
|
-
("
|
|
160
|
-
("
|
|
161
|
-
("
|
|
162
|
-
("
|
|
163
|
-
("
|
|
164
|
-
("
|
|
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"),
|
|
165
184
|
("search_api", "Search API integration"),
|
|
166
|
-
("
|
|
185
|
+
("stats", "Statistical analysis and computation")
|
|
167
186
|
]
|
|
168
187
|
|
|
169
188
|
# Register as placeholder until actually loaded
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# python-middleware/aiecs/tools/docs/__init__.py
|
|
2
|
+
|
|
3
|
+
"""
|
|
4
|
+
Document Tools Module
|
|
5
|
+
|
|
6
|
+
This module contains specialized tools for document processing and analysis:
|
|
7
|
+
- document_parser_tool: Modern high-performance document parsing with AI
|
|
8
|
+
- ai_document_orchestrator: AI-powered document processing orchestrator
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
# Lazy import all document tools to avoid heavy dependencies at import time
|
|
12
|
+
import os
|
|
13
|
+
|
|
14
|
+
# Define available document tools for lazy loading
|
|
15
|
+
_AVAILABLE_DOC_TOOLS = [
|
|
16
|
+
'document_parser_tool',
|
|
17
|
+
'ai_document_orchestrator',
|
|
18
|
+
'document_writer_tool',
|
|
19
|
+
'ai_document_writer_orchestrator',
|
|
20
|
+
'document_creator_tool',
|
|
21
|
+
'document_layout_tool',
|
|
22
|
+
'content_insertion_tool'
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
# Track which tools have been loaded
|
|
26
|
+
_LOADED_DOC_TOOLS = set()
|
|
27
|
+
|
|
28
|
+
def _lazy_load_doc_tool(tool_name: str):
|
|
29
|
+
"""Lazy load a specific document tool module"""
|
|
30
|
+
if tool_name in _LOADED_DOC_TOOLS:
|
|
31
|
+
return
|
|
32
|
+
|
|
33
|
+
# Mark as loading to prevent infinite recursion
|
|
34
|
+
_LOADED_DOC_TOOLS.add(tool_name)
|
|
35
|
+
|
|
36
|
+
try:
|
|
37
|
+
if tool_name == 'document_parser_tool':
|
|
38
|
+
from . import document_parser_tool
|
|
39
|
+
globals()['document_parser_tool'] = document_parser_tool
|
|
40
|
+
elif tool_name == 'ai_document_orchestrator':
|
|
41
|
+
from . import ai_document_orchestrator
|
|
42
|
+
globals()['ai_document_orchestrator'] = ai_document_orchestrator
|
|
43
|
+
elif tool_name == 'document_writer_tool':
|
|
44
|
+
from . import document_writer_tool
|
|
45
|
+
globals()['document_writer_tool'] = document_writer_tool
|
|
46
|
+
elif tool_name == 'ai_document_writer_orchestrator':
|
|
47
|
+
from . import ai_document_writer_orchestrator
|
|
48
|
+
globals()['ai_document_writer_orchestrator'] = ai_document_writer_orchestrator
|
|
49
|
+
elif tool_name == 'document_creator_tool':
|
|
50
|
+
from . import document_creator_tool
|
|
51
|
+
globals()['document_creator_tool'] = document_creator_tool
|
|
52
|
+
elif tool_name == 'document_layout_tool':
|
|
53
|
+
from . import document_layout_tool
|
|
54
|
+
globals()['document_layout_tool'] = document_layout_tool
|
|
55
|
+
elif tool_name == 'content_insertion_tool':
|
|
56
|
+
from . import content_insertion_tool
|
|
57
|
+
globals()['content_insertion_tool'] = content_insertion_tool
|
|
58
|
+
|
|
59
|
+
except ImportError as e:
|
|
60
|
+
# Remove from loaded set if import failed
|
|
61
|
+
_LOADED_DOC_TOOLS.discard(tool_name)
|
|
62
|
+
print(f"Warning: Could not import {tool_name}: {e}")
|
|
63
|
+
|
|
64
|
+
def __getattr__(name: str):
|
|
65
|
+
"""
|
|
66
|
+
Lazy loading mechanism for document tools.
|
|
67
|
+
|
|
68
|
+
This allows importing tools like:
|
|
69
|
+
from aiecs.tools.docs import document_parser_tool
|
|
70
|
+
"""
|
|
71
|
+
if name in _AVAILABLE_DOC_TOOLS:
|
|
72
|
+
_lazy_load_doc_tool(name)
|
|
73
|
+
if name in globals():
|
|
74
|
+
return globals()[name]
|
|
75
|
+
|
|
76
|
+
raise AttributeError(f"module '{__name__}' has no attribute '{name}'")
|
|
77
|
+
|
|
78
|
+
def list_doc_tools():
|
|
79
|
+
"""List all available document tools"""
|
|
80
|
+
return _AVAILABLE_DOC_TOOLS.copy()
|
|
81
|
+
|
|
82
|
+
def load_all_doc_tools():
|
|
83
|
+
"""Load all available document tools"""
|
|
84
|
+
for tool_name in _AVAILABLE_DOC_TOOLS:
|
|
85
|
+
_lazy_load_doc_tool(tool_name)
|
|
86
|
+
|
|
87
|
+
# Auto-discovery of tool modules in this directory
|
|
88
|
+
def _discover_doc_tools():
|
|
89
|
+
"""Discover document tool modules in the current directory"""
|
|
90
|
+
current_dir = os.path.dirname(__file__)
|
|
91
|
+
if not current_dir:
|
|
92
|
+
return
|
|
93
|
+
|
|
94
|
+
for filename in os.listdir(current_dir):
|
|
95
|
+
if filename.endswith('_tool.py') and not filename.startswith('__'):
|
|
96
|
+
tool_name = filename[:-3] # Remove .py extension
|
|
97
|
+
if tool_name not in _AVAILABLE_DOC_TOOLS:
|
|
98
|
+
_AVAILABLE_DOC_TOOLS.append(tool_name)
|
|
99
|
+
|
|
100
|
+
# Discover tools on import
|
|
101
|
+
_discover_doc_tools()
|
|
102
|
+
|
|
103
|
+
__all__ = [
|
|
104
|
+
'list_doc_tools',
|
|
105
|
+
'load_all_doc_tools',
|
|
106
|
+
] + _AVAILABLE_DOC_TOOLS
|