aiecs 1.0.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.

Files changed (90) hide show
  1. aiecs/__init__.py +75 -0
  2. aiecs/__main__.py +41 -0
  3. aiecs/aiecs_client.py +295 -0
  4. aiecs/application/__init__.py +10 -0
  5. aiecs/application/executors/__init__.py +10 -0
  6. aiecs/application/executors/operation_executor.py +341 -0
  7. aiecs/config/__init__.py +15 -0
  8. aiecs/config/config.py +117 -0
  9. aiecs/config/registry.py +19 -0
  10. aiecs/core/__init__.py +46 -0
  11. aiecs/core/interface/__init__.py +34 -0
  12. aiecs/core/interface/execution_interface.py +150 -0
  13. aiecs/core/interface/storage_interface.py +214 -0
  14. aiecs/domain/__init__.py +20 -0
  15. aiecs/domain/context/__init__.py +28 -0
  16. aiecs/domain/context/content_engine.py +982 -0
  17. aiecs/domain/context/conversation_models.py +306 -0
  18. aiecs/domain/execution/__init__.py +12 -0
  19. aiecs/domain/execution/model.py +49 -0
  20. aiecs/domain/task/__init__.py +13 -0
  21. aiecs/domain/task/dsl_processor.py +460 -0
  22. aiecs/domain/task/model.py +50 -0
  23. aiecs/domain/task/task_context.py +257 -0
  24. aiecs/infrastructure/__init__.py +26 -0
  25. aiecs/infrastructure/messaging/__init__.py +13 -0
  26. aiecs/infrastructure/messaging/celery_task_manager.py +341 -0
  27. aiecs/infrastructure/messaging/websocket_manager.py +289 -0
  28. aiecs/infrastructure/monitoring/__init__.py +12 -0
  29. aiecs/infrastructure/monitoring/executor_metrics.py +138 -0
  30. aiecs/infrastructure/monitoring/structured_logger.py +50 -0
  31. aiecs/infrastructure/monitoring/tracing_manager.py +376 -0
  32. aiecs/infrastructure/persistence/__init__.py +12 -0
  33. aiecs/infrastructure/persistence/database_manager.py +286 -0
  34. aiecs/infrastructure/persistence/file_storage.py +671 -0
  35. aiecs/infrastructure/persistence/redis_client.py +162 -0
  36. aiecs/llm/__init__.py +54 -0
  37. aiecs/llm/base_client.py +99 -0
  38. aiecs/llm/client_factory.py +339 -0
  39. aiecs/llm/custom_callbacks.py +228 -0
  40. aiecs/llm/openai_client.py +125 -0
  41. aiecs/llm/vertex_client.py +186 -0
  42. aiecs/llm/xai_client.py +184 -0
  43. aiecs/main.py +351 -0
  44. aiecs/scripts/DEPENDENCY_SYSTEM_SUMMARY.md +241 -0
  45. aiecs/scripts/README_DEPENDENCY_CHECKER.md +309 -0
  46. aiecs/scripts/README_WEASEL_PATCH.md +126 -0
  47. aiecs/scripts/__init__.py +3 -0
  48. aiecs/scripts/dependency_checker.py +825 -0
  49. aiecs/scripts/dependency_fixer.py +348 -0
  50. aiecs/scripts/download_nlp_data.py +348 -0
  51. aiecs/scripts/fix_weasel_validator.py +121 -0
  52. aiecs/scripts/fix_weasel_validator.sh +82 -0
  53. aiecs/scripts/patch_weasel_library.sh +188 -0
  54. aiecs/scripts/quick_dependency_check.py +269 -0
  55. aiecs/scripts/run_weasel_patch.sh +41 -0
  56. aiecs/scripts/setup_nlp_data.sh +217 -0
  57. aiecs/tasks/__init__.py +2 -0
  58. aiecs/tasks/worker.py +111 -0
  59. aiecs/tools/__init__.py +196 -0
  60. aiecs/tools/base_tool.py +202 -0
  61. aiecs/tools/langchain_adapter.py +361 -0
  62. aiecs/tools/task_tools/__init__.py +82 -0
  63. aiecs/tools/task_tools/chart_tool.py +704 -0
  64. aiecs/tools/task_tools/classfire_tool.py +901 -0
  65. aiecs/tools/task_tools/image_tool.py +397 -0
  66. aiecs/tools/task_tools/office_tool.py +600 -0
  67. aiecs/tools/task_tools/pandas_tool.py +565 -0
  68. aiecs/tools/task_tools/report_tool.py +499 -0
  69. aiecs/tools/task_tools/research_tool.py +363 -0
  70. aiecs/tools/task_tools/scraper_tool.py +548 -0
  71. aiecs/tools/task_tools/search_api.py +7 -0
  72. aiecs/tools/task_tools/stats_tool.py +513 -0
  73. aiecs/tools/temp_file_manager.py +126 -0
  74. aiecs/tools/tool_executor/__init__.py +35 -0
  75. aiecs/tools/tool_executor/tool_executor.py +518 -0
  76. aiecs/utils/LLM_output_structor.py +409 -0
  77. aiecs/utils/__init__.py +23 -0
  78. aiecs/utils/base_callback.py +50 -0
  79. aiecs/utils/execution_utils.py +158 -0
  80. aiecs/utils/logging.py +1 -0
  81. aiecs/utils/prompt_loader.py +13 -0
  82. aiecs/utils/token_usage_repository.py +279 -0
  83. aiecs/ws/__init__.py +0 -0
  84. aiecs/ws/socket_server.py +41 -0
  85. aiecs-1.0.0.dist-info/METADATA +610 -0
  86. aiecs-1.0.0.dist-info/RECORD +90 -0
  87. aiecs-1.0.0.dist-info/WHEEL +5 -0
  88. aiecs-1.0.0.dist-info/entry_points.txt +7 -0
  89. aiecs-1.0.0.dist-info/licenses/LICENSE +225 -0
  90. aiecs-1.0.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,41 @@
1
+ #!/bin/bash
2
+
3
+ # Script to run the weasel library patch using poetry
4
+ # This ensures we're working within the correct virtual environment
5
+
6
+ set -e
7
+
8
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
9
+ PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
10
+
11
+ echo "🔧 Running Weasel Library Patch via Poetry"
12
+ echo "=========================================="
13
+ echo "Project directory: $PROJECT_DIR"
14
+ echo ""
15
+
16
+ # Change to project directory
17
+ cd "$PROJECT_DIR"
18
+
19
+ # Check if pyproject.toml exists
20
+ if [ ! -f "pyproject.toml" ]; then
21
+ echo "❌ Error: pyproject.toml not found. Please run this script from the python-middleware directory"
22
+ exit 1
23
+ fi
24
+
25
+ # Check if poetry is available
26
+ if ! command -v poetry &> /dev/null; then
27
+ echo "❌ Error: Poetry is not installed or not in PATH"
28
+ echo "Please install poetry first: https://python-poetry.org/docs/#installation"
29
+ exit 1
30
+ fi
31
+
32
+ # Run the Python patch script using poetry
33
+ echo "🚀 Running patch script with poetry..."
34
+ poetry run python3 scripts/fix_weasel_validator.py
35
+
36
+ echo ""
37
+ echo "🎉 Patch execution completed!"
38
+ echo ""
39
+ echo "Next steps:"
40
+ echo "1. Try running your tests again with: poetry run pytest"
41
+ echo "2. If the issue persists, you may need to restart your development environment"
@@ -0,0 +1,217 @@
1
+ #!/bin/bash
2
+ # Setup NLP Data for AIECS ClassifierTool
3
+ # This script downloads required NLTK and spaCy data packages
4
+
5
+ set -e # Exit on any error
6
+
7
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
8
+ PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
9
+
10
+ echo "=================================================="
11
+ echo "AIECS NLP Data Setup"
12
+ echo "=================================================="
13
+ echo "This script will download required NLP data for:"
14
+ echo " - NLTK stopwords corpus"
15
+ echo " - spaCy English model (en_core_web_sm)"
16
+ echo " - spaCy Chinese model (zh_core_web_sm)"
17
+ echo "=================================================="
18
+ echo
19
+
20
+ # Function to check if a command exists
21
+ command_exists() {
22
+ command -v "$1" >/dev/null 2>&1
23
+ }
24
+
25
+ # Function to activate virtual environment if it exists
26
+ activate_venv() {
27
+ if [[ -n "$VIRTUAL_ENV" ]]; then
28
+ echo "✅ Virtual environment already active: $VIRTUAL_ENV"
29
+ return 0
30
+ fi
31
+
32
+ # Check for common virtual environment locations
33
+ local venv_paths=(
34
+ "$PROJECT_ROOT/venv"
35
+ "$PROJECT_ROOT/.venv"
36
+ "$PROJECT_ROOT/env"
37
+ "$PROJECT_ROOT/.env"
38
+ )
39
+
40
+ for venv_path in "${venv_paths[@]}"; do
41
+ if [[ -f "$venv_path/bin/activate" ]]; then
42
+ echo "📦 Activating virtual environment: $venv_path"
43
+ source "$venv_path/bin/activate"
44
+ return 0
45
+ fi
46
+ done
47
+
48
+ echo "⚠️ No virtual environment found. Using system Python."
49
+ return 1
50
+ }
51
+
52
+ # Function to check Python and packages
53
+ check_dependencies() {
54
+ echo "🔍 Checking dependencies..."
55
+
56
+ if ! command_exists python3; then
57
+ echo "❌ Python 3 is not installed or not in PATH"
58
+ exit 1
59
+ fi
60
+
61
+ local python_version=$(python3 --version 2>&1 | cut -d' ' -f2)
62
+ echo "✅ Python version: $python_version"
63
+
64
+ # Check if we're in the project directory and can import aiecs
65
+ cd "$PROJECT_ROOT"
66
+ if python3 -c "import aiecs" 2>/dev/null; then
67
+ echo "✅ AIECS package is available"
68
+ else
69
+ echo "⚠️ AIECS package not found. You may need to install it first:"
70
+ echo " pip install -e ."
71
+ fi
72
+ }
73
+
74
+ # Function to run the Python download script
75
+ run_download_script() {
76
+ echo
77
+ echo "🚀 Starting NLP data download..."
78
+ echo
79
+
80
+ cd "$PROJECT_ROOT"
81
+
82
+ # Try multiple ways to run the script
83
+ if python3 -m aiecs.scripts.download_nlp_data; then
84
+ echo "✅ NLP data download completed successfully!"
85
+ return 0
86
+ elif python3 aiecs/scripts/download_nlp_data.py; then
87
+ echo "✅ NLP data download completed successfully!"
88
+ return 0
89
+ else
90
+ echo "❌ Failed to run NLP data download script"
91
+ return 1
92
+ fi
93
+ }
94
+
95
+ # Function to verify installation
96
+ verify_installation() {
97
+ echo
98
+ echo "🔍 Verifying NLP data installation..."
99
+
100
+ # Test NLTK
101
+ if python3 -c "
102
+ import nltk
103
+ from nltk.corpus import stopwords
104
+ stopwords.words('english')
105
+ print('✅ NLTK stopwords data available')
106
+ " 2>/dev/null; then
107
+ echo "✅ NLTK verification passed"
108
+ else
109
+ echo "⚠️ NLTK verification failed"
110
+ fi
111
+
112
+ # Test spaCy English model
113
+ if python3 -c "
114
+ import spacy
115
+ nlp = spacy.load('en_core_web_sm')
116
+ doc = nlp('This is a test.')
117
+ print('✅ spaCy English model available')
118
+ " 2>/dev/null; then
119
+ echo "✅ spaCy English model verification passed"
120
+ else
121
+ echo "⚠️ spaCy English model verification failed"
122
+ fi
123
+
124
+ # Test spaCy Chinese model (optional)
125
+ if python3 -c "
126
+ import spacy
127
+ nlp = spacy.load('zh_core_web_sm')
128
+ doc = nlp('这是测试。')
129
+ print('✅ spaCy Chinese model available')
130
+ " 2>/dev/null; then
131
+ echo "✅ spaCy Chinese model verification passed"
132
+ else
133
+ echo "⚠️ spaCy Chinese model not available (optional)"
134
+ fi
135
+ }
136
+
137
+ # Function to display usage information
138
+ show_usage() {
139
+ echo "Usage: $0 [OPTIONS]"
140
+ echo
141
+ echo "Options:"
142
+ echo " -h, --help Show this help message"
143
+ echo " -v, --verify Only verify existing installations"
144
+ echo " --no-venv Skip virtual environment activation"
145
+ echo
146
+ echo "This script downloads required NLP data for AIECS ClassifierTool:"
147
+ echo " - NLTK stopwords corpus"
148
+ echo " - spaCy English model (en_core_web_sm)"
149
+ echo " - spaCy Chinese model (zh_core_web_sm, optional)"
150
+ echo
151
+ }
152
+
153
+ # Parse command line arguments
154
+ VERIFY_ONLY=false
155
+ SKIP_VENV=false
156
+
157
+ while [[ $# -gt 0 ]]; do
158
+ case $1 in
159
+ -h|--help)
160
+ show_usage
161
+ exit 0
162
+ ;;
163
+ -v|--verify)
164
+ VERIFY_ONLY=true
165
+ shift
166
+ ;;
167
+ --no-venv)
168
+ SKIP_VENV=true
169
+ shift
170
+ ;;
171
+ *)
172
+ echo "Unknown option: $1"
173
+ show_usage
174
+ exit 1
175
+ ;;
176
+ esac
177
+ done
178
+
179
+ # Main execution
180
+ main() {
181
+ echo "📍 Project root: $PROJECT_ROOT"
182
+ echo
183
+
184
+ # Activate virtual environment if available and not skipped
185
+ if [[ "$SKIP_VENV" != true ]]; then
186
+ activate_venv || true
187
+ fi
188
+
189
+ # Check dependencies
190
+ check_dependencies
191
+
192
+ if [[ "$VERIFY_ONLY" == true ]]; then
193
+ echo
194
+ echo "🔍 Running verification only..."
195
+ verify_installation
196
+ else
197
+ # Download NLP data
198
+ if run_download_script; then
199
+ verify_installation
200
+ echo
201
+ echo "🎉 NLP data setup completed successfully!"
202
+ echo "AIECS ClassifierTool is ready to use."
203
+ else
204
+ echo
205
+ echo "❌ NLP data setup failed. Please check the errors above."
206
+ exit 1
207
+ fi
208
+ fi
209
+
210
+ echo
211
+ echo "=================================================="
212
+ echo "Setup complete!"
213
+ echo "=================================================="
214
+ }
215
+
216
+ # Run main function
217
+ main "$@"
@@ -0,0 +1,2 @@
1
+ # This file makes the app/tasks directory a Python package
2
+ from .worker import celery_app
aiecs/tasks/worker.py ADDED
@@ -0,0 +1,111 @@
1
+ from celery import Celery
2
+ from aiecs.config.config import get_settings
3
+ from aiecs.domain.task.task_context import build_context
4
+ from aiecs.config.registry import get_ai_service
5
+ from aiecs.ws.socket_server import push_progress
6
+ import logging
7
+ import json
8
+ from enum import Enum
9
+ from typing import Dict, Any, Optional
10
+
11
+ logger = logging.getLogger(__name__)
12
+
13
+ settings = get_settings()
14
+ celery_app = Celery("ai_worker", broker=settings.celery_broker_url)
15
+
16
+ # Configure Celery
17
+ celery_app.conf.update(
18
+ task_serializer='json',
19
+ accept_content=['json'],
20
+ result_serializer='json',
21
+ timezone='UTC',
22
+ enable_utc=True,
23
+ task_queues={
24
+ 'fast_tasks': {'exchange': 'fast_tasks', 'routing_key': 'fast_tasks'},
25
+ 'heavy_tasks': {'exchange': 'heavy_tasks', 'routing_key': 'heavy_tasks'}
26
+ },
27
+ task_routes={
28
+ 'aiecs.tasks.worker.execute_task': {'queue': 'fast_tasks'},
29
+ 'aiecs.tasks.worker.execute_heavy_task': {'queue': 'heavy_tasks'}
30
+ }
31
+ )
32
+
33
+ from aiecs.domain.execution.model import TaskStatus
34
+
35
+ @celery_app.task(bind=True, name="aiecs.tasks.worker.execute_task")
36
+ def execute_task(self, task_name: str, user_id: str, task_id: str, step: int,
37
+ mode: str, service: str, input_data: Dict[str, Any], context: Dict[str, Any]):
38
+ """
39
+ Execute a fast task from the service executor queue.
40
+ This task is used for operations that should complete quickly.
41
+ """
42
+ logger.info(f"Executing fast task: {task_name} for user {user_id}, task {task_id}, step {step}")
43
+ return _execute_service_task(self, task_name, user_id, task_id, step, mode, service, input_data, context)
44
+
45
+ @celery_app.task(bind=True, name="aiecs.tasks.worker.execute_heavy_task")
46
+ def execute_heavy_task(self, task_name: str, user_id: str, task_id: str, step: int,
47
+ mode: str, service: str, input_data: Dict[str, Any], context: Dict[str, Any]):
48
+ """
49
+ Execute a heavy task from the service executor queue.
50
+ This task is used for operations that may take longer to complete.
51
+ """
52
+ logger.info(f"Executing heavy task: {task_name} for user {user_id}, task {task_id}, step {step}")
53
+ return _execute_service_task(self, task_name, user_id, task_id, step, mode, service, input_data, context)
54
+
55
+ def _execute_service_task(self, task_name: str, user_id: str, task_id: str, step: int,
56
+ mode: str, service: str, input_data: Dict[str, Any], context: Dict[str, Any]):
57
+ """
58
+ Common implementation for executing both fast and heavy tasks.
59
+ This function handles the actual task execution logic.
60
+ """
61
+ try:
62
+ # 1. Push started status
63
+ push_progress(user_id, {
64
+ "status": TaskStatus.RUNNING.value,
65
+ "step": step,
66
+ "task": task_name,
67
+ "message": f"Executing task: {task_name}"
68
+ })
69
+
70
+ # 2. Get the service instance
71
+ service_cls = get_ai_service(mode, service)
72
+ service_instance = service_cls()
73
+
74
+ # 3. Execute the task
75
+ if hasattr(service_instance, task_name) and callable(getattr(service_instance, task_name)):
76
+ method = getattr(service_instance, task_name)
77
+ result = method(input_data, context)
78
+ else:
79
+ # Fallback to a generic execution method if the specific task method doesn't exist
80
+ result = service_instance.execute_task(task_name, input_data, context)
81
+
82
+ # 4. Push completed status
83
+ push_progress(user_id, {
84
+ "status": TaskStatus.COMPLETED.value,
85
+ "step": step,
86
+ "task": task_name,
87
+ "result": result,
88
+ "message": f"Completed task: {task_name}"
89
+ })
90
+
91
+ return {
92
+ "status": TaskStatus.COMPLETED.value,
93
+ "task": task_name,
94
+ "result": result
95
+ }
96
+ except Exception as e:
97
+ logger.error(f"Error executing task {task_name}: {str(e)}", exc_info=True)
98
+ # Push error status
99
+ push_progress(user_id, {
100
+ "status": TaskStatus.FAILED.value,
101
+ "step": step,
102
+ "task": task_name,
103
+ "error": str(e),
104
+ "message": f"Failed to execute task: {task_name}"
105
+ })
106
+
107
+ return {
108
+ "status": TaskStatus.FAILED.value,
109
+ "task": task_name,
110
+ "error": str(e)
111
+ }
@@ -0,0 +1,196 @@
1
+ # python-middleware/app/tools/__init__.py
2
+
3
+ import importlib
4
+ import inspect
5
+ import logging
6
+ import os
7
+ import pkgutil
8
+ from typing import Any, Dict, List, Optional, Type
9
+
10
+ from aiecs.tools.base_tool import BaseTool
11
+
12
+ logger = logging.getLogger(__name__)
13
+
14
+ # 全局工具注册表
15
+ TOOL_REGISTRY = {}
16
+ TOOL_CLASSES = {}
17
+ TOOL_CONFIGS = {}
18
+
19
+ def register_tool(name):
20
+ """
21
+ 装饰器,用于注册工具类
22
+
23
+ Args:
24
+ name: 工具名称
25
+
26
+ Returns:
27
+ 装饰后的类
28
+ """
29
+ def wrapper(cls):
30
+ # 存储工具类,但不立即实例化
31
+ TOOL_CLASSES[name] = cls
32
+ # 兼容旧版本:如果类继承自BaseTool,则不立即实例化
33
+ if not issubclass(cls, BaseTool):
34
+ TOOL_REGISTRY[name] = cls()
35
+ return cls
36
+ return wrapper
37
+
38
+ def get_tool(name):
39
+ """
40
+ 获取工具实例
41
+
42
+ Args:
43
+ name: 工具名称
44
+
45
+ Returns:
46
+ 工具实例
47
+
48
+ Raises:
49
+ ValueError: 如果工具未注册
50
+ """
51
+ if name not in TOOL_REGISTRY and name in TOOL_CLASSES:
52
+ # 延迟实例化BaseTool子类
53
+ tool_class = TOOL_CLASSES[name]
54
+ config = TOOL_CONFIGS.get(name, {})
55
+ TOOL_REGISTRY[name] = tool_class(config)
56
+
57
+ if name not in TOOL_REGISTRY:
58
+ raise ValueError(f"Tool '{name}' is not registered")
59
+
60
+ return TOOL_REGISTRY[name]
61
+
62
+ def list_tools():
63
+ """
64
+ 列出所有已注册的工具
65
+
66
+ Returns:
67
+ 工具信息字典列表
68
+ """
69
+ tools = []
70
+ all_tool_names = list(set(list(TOOL_REGISTRY.keys()) + list(TOOL_CLASSES.keys())))
71
+
72
+ for tool_name in all_tool_names:
73
+ try:
74
+ # 优先使用已有实例的信息
75
+ if tool_name in TOOL_REGISTRY:
76
+ tool_instance = TOOL_REGISTRY[tool_name]
77
+ tool_info = {
78
+ "name": tool_name,
79
+ "description": getattr(tool_instance, 'description', f'{tool_name} tool'),
80
+ "category": getattr(tool_instance, 'category', 'general'),
81
+ "class_name": tool_instance.__class__.__name__,
82
+ "module": tool_instance.__class__.__module__,
83
+ "status": "loaded"
84
+ }
85
+ elif tool_name in TOOL_CLASSES:
86
+ # 从类定义获取信息,但不实例化
87
+ tool_class = TOOL_CLASSES[tool_name]
88
+ tool_info = {
89
+ "name": tool_name,
90
+ "description": getattr(tool_class, 'description', f'{tool_name} tool'),
91
+ "category": getattr(tool_class, 'category', 'general'),
92
+ "class_name": tool_class.__name__,
93
+ "module": tool_class.__module__,
94
+ "status": "available"
95
+ }
96
+ else:
97
+ continue
98
+
99
+ tools.append(tool_info)
100
+
101
+ except Exception as e:
102
+ logger.warning(f"Failed to get info for tool {tool_name}: {e}")
103
+ # 提供基本信息
104
+ tools.append({
105
+ "name": tool_name,
106
+ "description": f"{tool_name} (info unavailable)",
107
+ "category": "unknown",
108
+ "class_name": "Unknown",
109
+ "module": "unknown",
110
+ "status": "error"
111
+ })
112
+
113
+ return tools
114
+
115
+ def discover_tools(package_path: str = "aiecs.tools"):
116
+ """
117
+ 发现并注册包中的所有工具
118
+
119
+ Args:
120
+ package_path: 要搜索的包路径
121
+ """
122
+ package = importlib.import_module(package_path)
123
+ package_dir = os.path.dirname(package.__file__)
124
+
125
+ for _, module_name, is_pkg in pkgutil.iter_modules([package_dir]):
126
+ if is_pkg:
127
+ # 递归搜索子包中的工具
128
+ discover_tools(f"{package_path}.{module_name}")
129
+ else:
130
+ # 导入模块
131
+ try:
132
+ importlib.import_module(f"{package_path}.{module_name}")
133
+ except Exception as e:
134
+ logger.error(f"Error importing module {module_name}: {e}")
135
+
136
+ # 导入基础工具类供继承使用
137
+ from aiecs.tools.base_tool import BaseTool
138
+
139
+ # Lazy loading strategy: don't import all tools at package init
140
+ # Tools will be loaded on-demand when requested
141
+
142
+ def _ensure_task_tools_available():
143
+ """Ensure task_tools module is available for lazy loading"""
144
+ try:
145
+ from . import task_tools
146
+ return True
147
+ except ImportError as e:
148
+ logger.error(f"Failed to import task_tools: {e}")
149
+ return False
150
+
151
+ def _register_known_tools():
152
+ """Register known tools without importing heavy dependencies"""
153
+ # Pre-register tool classes for discovery without importing modules
154
+ # This allows list_tools() to work before actual tool loading
155
+
156
+ known_tools = [
157
+ ("chart_tool", "Chart and visualization operations"),
158
+ ("classfire_tool", "Text classification and keyword extraction"),
159
+ ("image_tool", "Image processing and OCR operations"),
160
+ ("office_tool", "Office document processing"),
161
+ ("pandas_tool", "Data analysis and manipulation"),
162
+ ("report_tool", "Report generation and formatting"),
163
+ ("research_tool", "Research and information gathering"),
164
+ ("scraper_tool", "Web scraping and data extraction"),
165
+ ("search_api", "Search API integration"),
166
+ ("stats_tool", "Statistical analysis and computation")
167
+ ]
168
+
169
+ # Register as placeholder until actually loaded
170
+ for tool_name, description in known_tools:
171
+ if tool_name not in TOOL_REGISTRY and tool_name not in TOOL_CLASSES:
172
+ # Create a placeholder class for discovery
173
+ class ToolPlaceholder:
174
+ def __init__(self, name, desc):
175
+ self.name = name
176
+ self.description = desc
177
+ self.category = "task"
178
+ self.is_placeholder = True
179
+
180
+ TOOL_REGISTRY[tool_name] = ToolPlaceholder(tool_name, description)
181
+
182
+ # Register known tools for discovery
183
+ _register_known_tools()
184
+
185
+ try:
186
+ from . import db_api
187
+ except ImportError:
188
+ pass
189
+
190
+ try:
191
+ from . import vector_search
192
+ except ImportError:
193
+ pass
194
+
195
+ # Don't auto-discover tools at import time for performance
196
+ # Tools will be discovered when explicitly requested via discover_tools() call