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
|
@@ -13,6 +13,7 @@ from contextlib import contextmanager
|
|
|
13
13
|
|
|
14
14
|
from cachetools import LRUCache
|
|
15
15
|
from aiecs.utils.execution_utils import ExecutionUtils
|
|
16
|
+
from aiecs.utils.cache_provider import ICacheProvider, LRUCacheProvider
|
|
16
17
|
import re
|
|
17
18
|
from pydantic import BaseModel, ValidationError, ConfigDict
|
|
18
19
|
|
|
@@ -58,6 +59,10 @@ class ExecutorConfig(BaseModel):
|
|
|
58
59
|
retry_attempts (int): Number of retry attempts for transient errors.
|
|
59
60
|
retry_backoff (float): Backoff factor for retries.
|
|
60
61
|
timeout (int): Timeout for operations in seconds.
|
|
62
|
+
enable_dual_cache (bool): Enable dual-layer caching (L1: LRU + L2: Redis).
|
|
63
|
+
enable_redis_cache (bool): Enable Redis as L2 cache (requires enable_dual_cache=True).
|
|
64
|
+
redis_cache_ttl (int): Redis cache TTL in seconds (for L2 cache).
|
|
65
|
+
l1_cache_ttl (int): L1 cache TTL in seconds (for dual-layer cache).
|
|
61
66
|
"""
|
|
62
67
|
enable_cache: bool = True
|
|
63
68
|
cache_size: int = 100
|
|
@@ -73,6 +78,12 @@ class ExecutorConfig(BaseModel):
|
|
|
73
78
|
retry_backoff: float = 1.0
|
|
74
79
|
timeout: int = 30
|
|
75
80
|
|
|
81
|
+
# Dual-layer cache configuration
|
|
82
|
+
enable_dual_cache: bool = False
|
|
83
|
+
enable_redis_cache: bool = False
|
|
84
|
+
redis_cache_ttl: int = 86400 # 1 day
|
|
85
|
+
l1_cache_ttl: int = 300 # 5 minutes
|
|
86
|
+
|
|
76
87
|
model_config = ConfigDict(env_prefix="TOOL_EXECUTOR_")
|
|
77
88
|
|
|
78
89
|
# Metrics counter
|
|
@@ -157,6 +168,93 @@ def cache_result(ttl: Optional[int] = None) -> Callable:
|
|
|
157
168
|
return wrapper
|
|
158
169
|
return decorator
|
|
159
170
|
|
|
171
|
+
|
|
172
|
+
def cache_result_with_strategy(ttl_strategy: Optional[Union[int, Callable]] = None) -> Callable:
|
|
173
|
+
"""
|
|
174
|
+
Decorator to cache function results with flexible TTL strategy.
|
|
175
|
+
|
|
176
|
+
Supports multiple TTL strategy types:
|
|
177
|
+
1. Fixed TTL (int): Static TTL in seconds
|
|
178
|
+
2. Callable strategy: Function that calculates TTL based on result and context
|
|
179
|
+
3. None: Use default TTL from executor config
|
|
180
|
+
|
|
181
|
+
Args:
|
|
182
|
+
ttl_strategy: TTL strategy, can be:
|
|
183
|
+
- int: Fixed TTL in seconds
|
|
184
|
+
- Callable[[Any, tuple, dict], int]: Function(result, args, kwargs) -> ttl_seconds
|
|
185
|
+
- None: Use default TTL
|
|
186
|
+
|
|
187
|
+
Returns:
|
|
188
|
+
Callable: Decorated function with intelligent caching.
|
|
189
|
+
|
|
190
|
+
Example:
|
|
191
|
+
# Fixed TTL
|
|
192
|
+
@cache_result_with_strategy(ttl_strategy=3600)
|
|
193
|
+
def simple_operation(self, data):
|
|
194
|
+
return process(data)
|
|
195
|
+
|
|
196
|
+
# Dynamic TTL based on result
|
|
197
|
+
def calculate_ttl(result, args, kwargs):
|
|
198
|
+
if result.get('type') == 'static':
|
|
199
|
+
return 86400 # 1 day
|
|
200
|
+
return 3600 # 1 hour
|
|
201
|
+
|
|
202
|
+
@cache_result_with_strategy(ttl_strategy=calculate_ttl)
|
|
203
|
+
def smart_operation(self, query):
|
|
204
|
+
return search(query)
|
|
205
|
+
"""
|
|
206
|
+
def decorator(func: Callable) -> Callable:
|
|
207
|
+
@functools.wraps(func)
|
|
208
|
+
def wrapper(self, *args, **kwargs):
|
|
209
|
+
if not hasattr(self, '_executor') or not self._executor.config.enable_cache:
|
|
210
|
+
return func(self, *args, **kwargs)
|
|
211
|
+
|
|
212
|
+
# Generate cache key
|
|
213
|
+
cache_key = self._executor._get_cache_key(func.__name__, args, kwargs)
|
|
214
|
+
|
|
215
|
+
# Check cache
|
|
216
|
+
cached = self._executor._get_from_cache(cache_key)
|
|
217
|
+
if cached is not None:
|
|
218
|
+
logger.debug(f"Cache hit for {func.__name__}")
|
|
219
|
+
self._executor._metrics.record_cache_hit()
|
|
220
|
+
return cached
|
|
221
|
+
|
|
222
|
+
# Execute function
|
|
223
|
+
result = func(self, *args, **kwargs)
|
|
224
|
+
|
|
225
|
+
# Calculate TTL based on strategy
|
|
226
|
+
# Support both regular callables and lambdas that need self
|
|
227
|
+
if callable(ttl_strategy):
|
|
228
|
+
try:
|
|
229
|
+
# Try calling with self first (for lambda self, result, args, kwargs)
|
|
230
|
+
import inspect
|
|
231
|
+
sig = inspect.signature(ttl_strategy)
|
|
232
|
+
if len(sig.parameters) == 4: # self, result, args, kwargs
|
|
233
|
+
ttl = ttl_strategy(self, result, args, kwargs)
|
|
234
|
+
else: # result, args, kwargs
|
|
235
|
+
ttl = ttl_strategy(result, args, kwargs)
|
|
236
|
+
|
|
237
|
+
if not isinstance(ttl, int) or ttl < 0:
|
|
238
|
+
logger.warning(
|
|
239
|
+
f"TTL strategy returned invalid value: {ttl}. "
|
|
240
|
+
f"Expected positive integer. Using default TTL."
|
|
241
|
+
)
|
|
242
|
+
ttl = None
|
|
243
|
+
except Exception as e:
|
|
244
|
+
logger.error(f"Error calculating TTL from strategy: {e}. Using default TTL.")
|
|
245
|
+
ttl = None
|
|
246
|
+
else:
|
|
247
|
+
ttl = self._executor._calculate_ttl_from_strategy(
|
|
248
|
+
ttl_strategy, result, args, kwargs
|
|
249
|
+
)
|
|
250
|
+
|
|
251
|
+
# Cache with calculated TTL
|
|
252
|
+
self._executor._add_to_cache(cache_key, result, ttl)
|
|
253
|
+
return result
|
|
254
|
+
|
|
255
|
+
return wrapper
|
|
256
|
+
return decorator
|
|
257
|
+
|
|
160
258
|
def run_in_executor(func: Callable) -> Callable:
|
|
161
259
|
"""
|
|
162
260
|
Decorator to run a synchronous function in the thread pool executor.
|
|
@@ -240,12 +338,13 @@ class ToolExecutor:
|
|
|
240
338
|
executor = ToolExecutor(config={'max_workers': 8})
|
|
241
339
|
result = executor.execute(tool_instance, 'operation_name', param1='value')
|
|
242
340
|
"""
|
|
243
|
-
def __init__(self, config: Optional[Dict[str, Any]] = None):
|
|
341
|
+
def __init__(self, config: Optional[Dict[str, Any]] = None, cache_provider: Optional[ICacheProvider] = None):
|
|
244
342
|
"""
|
|
245
343
|
Initialize the executor with optional configuration.
|
|
246
344
|
|
|
247
345
|
Args:
|
|
248
346
|
config (Dict[str, Any], optional): Configuration overrides for ExecutorConfig.
|
|
347
|
+
cache_provider (ICacheProvider, optional): Custom cache provider. If None, uses default based on config.
|
|
249
348
|
|
|
250
349
|
Raises:
|
|
251
350
|
ValueError: If config is invalid.
|
|
@@ -265,6 +364,66 @@ class ToolExecutor:
|
|
|
265
364
|
retry_backoff=self.config.retry_backoff
|
|
266
365
|
)
|
|
267
366
|
|
|
367
|
+
# Support pluggable cache provider
|
|
368
|
+
if cache_provider is not None:
|
|
369
|
+
# User provided custom cache provider
|
|
370
|
+
self.cache_provider = cache_provider
|
|
371
|
+
logger.info(f"Using custom cache provider: {cache_provider.__class__.__name__}")
|
|
372
|
+
elif self.config.enable_dual_cache and self.config.enable_redis_cache:
|
|
373
|
+
# Enable dual-layer cache (L1: LRU + L2: Redis)
|
|
374
|
+
self.cache_provider = self._initialize_dual_cache()
|
|
375
|
+
else:
|
|
376
|
+
# Default: use LRUCacheProvider wrapping ExecutionUtils
|
|
377
|
+
self.cache_provider = LRUCacheProvider(self.execution_utils)
|
|
378
|
+
logger.debug("Using default LRUCacheProvider")
|
|
379
|
+
|
|
380
|
+
def _initialize_dual_cache(self) -> ICacheProvider:
|
|
381
|
+
"""
|
|
382
|
+
Initialize dual-layer cache (L1: LRU + L2: Redis).
|
|
383
|
+
|
|
384
|
+
Returns:
|
|
385
|
+
DualLayerCacheProvider instance or fallback to LRUCacheProvider
|
|
386
|
+
"""
|
|
387
|
+
try:
|
|
388
|
+
from aiecs.utils.cache_provider import DualLayerCacheProvider, RedisCacheProvider
|
|
389
|
+
|
|
390
|
+
# Create L1 cache (LRU)
|
|
391
|
+
l1_cache = LRUCacheProvider(self.execution_utils)
|
|
392
|
+
|
|
393
|
+
# Create L2 cache (Redis) - this requires async initialization
|
|
394
|
+
# We'll use a lazy initialization approach
|
|
395
|
+
try:
|
|
396
|
+
# Try to get global Redis client synchronously
|
|
397
|
+
# Note: This assumes Redis client is already initialized
|
|
398
|
+
from aiecs.infrastructure.persistence import redis_client
|
|
399
|
+
|
|
400
|
+
if redis_client is not None:
|
|
401
|
+
l2_cache = RedisCacheProvider(
|
|
402
|
+
redis_client,
|
|
403
|
+
prefix="tool_executor:",
|
|
404
|
+
default_ttl=self.config.redis_cache_ttl
|
|
405
|
+
)
|
|
406
|
+
|
|
407
|
+
dual_cache = DualLayerCacheProvider(
|
|
408
|
+
l1_provider=l1_cache,
|
|
409
|
+
l2_provider=l2_cache,
|
|
410
|
+
l1_ttl=self.config.l1_cache_ttl
|
|
411
|
+
)
|
|
412
|
+
|
|
413
|
+
logger.info("Dual-layer cache enabled (L1: LRU + L2: Redis)")
|
|
414
|
+
return dual_cache
|
|
415
|
+
else:
|
|
416
|
+
logger.warning("Redis client not initialized, falling back to LRU cache")
|
|
417
|
+
return l1_cache
|
|
418
|
+
|
|
419
|
+
except ImportError:
|
|
420
|
+
logger.warning("Redis client not available, falling back to LRU cache")
|
|
421
|
+
return l1_cache
|
|
422
|
+
|
|
423
|
+
except Exception as e:
|
|
424
|
+
logger.warning(f"Failed to initialize dual-layer cache: {e}, falling back to LRU")
|
|
425
|
+
return LRUCacheProvider(self.execution_utils)
|
|
426
|
+
|
|
268
427
|
def _get_cache_key(self, func_name: str, args: tuple, kwargs: Dict[str, Any]) -> str:
|
|
269
428
|
"""
|
|
270
429
|
Generate a context-aware cache key from function name, user ID, task ID, and arguments.
|
|
@@ -281,9 +440,70 @@ class ToolExecutor:
|
|
|
281
440
|
task_id = kwargs.get("task_id", "none")
|
|
282
441
|
return self.execution_utils.generate_cache_key(func_name, user_id, task_id, args, kwargs)
|
|
283
442
|
|
|
443
|
+
def _calculate_ttl_from_strategy(
|
|
444
|
+
self,
|
|
445
|
+
ttl_strategy: Optional[Union[int, Callable]],
|
|
446
|
+
result: Any,
|
|
447
|
+
args: tuple,
|
|
448
|
+
kwargs: Dict[str, Any]
|
|
449
|
+
) -> Optional[int]:
|
|
450
|
+
"""
|
|
451
|
+
Calculate TTL based on the provided strategy.
|
|
452
|
+
|
|
453
|
+
Supports multiple strategy types:
|
|
454
|
+
1. None: Use default TTL from config
|
|
455
|
+
2. int: Fixed TTL in seconds
|
|
456
|
+
3. Callable: Dynamic TTL calculation function
|
|
457
|
+
|
|
458
|
+
Args:
|
|
459
|
+
ttl_strategy: TTL strategy (None, int, or Callable)
|
|
460
|
+
result: Function execution result
|
|
461
|
+
args: Function positional arguments
|
|
462
|
+
kwargs: Function keyword arguments
|
|
463
|
+
|
|
464
|
+
Returns:
|
|
465
|
+
Optional[int]: Calculated TTL in seconds, or None for default
|
|
466
|
+
|
|
467
|
+
Example:
|
|
468
|
+
# Strategy function signature
|
|
469
|
+
def my_ttl_strategy(result: Any, args: tuple, kwargs: dict) -> int:
|
|
470
|
+
if result.get('type') == 'permanent':
|
|
471
|
+
return 86400 * 30 # 30 days
|
|
472
|
+
return 3600 # 1 hour
|
|
473
|
+
"""
|
|
474
|
+
# Case 1: No strategy - use default
|
|
475
|
+
if ttl_strategy is None:
|
|
476
|
+
return None
|
|
477
|
+
|
|
478
|
+
# Case 2: Fixed TTL (integer)
|
|
479
|
+
if isinstance(ttl_strategy, int):
|
|
480
|
+
return ttl_strategy
|
|
481
|
+
|
|
482
|
+
# Case 3: Callable strategy - dynamic calculation
|
|
483
|
+
if callable(ttl_strategy):
|
|
484
|
+
try:
|
|
485
|
+
calculated_ttl = ttl_strategy(result, args, kwargs)
|
|
486
|
+
if not isinstance(calculated_ttl, int) or calculated_ttl < 0:
|
|
487
|
+
logger.warning(
|
|
488
|
+
f"TTL strategy returned invalid value: {calculated_ttl}. "
|
|
489
|
+
f"Expected positive integer. Using default TTL."
|
|
490
|
+
)
|
|
491
|
+
return None
|
|
492
|
+
return calculated_ttl
|
|
493
|
+
except Exception as e:
|
|
494
|
+
logger.error(f"Error calculating TTL from strategy: {e}. Using default TTL.")
|
|
495
|
+
return None
|
|
496
|
+
|
|
497
|
+
# Invalid strategy type
|
|
498
|
+
logger.warning(
|
|
499
|
+
f"Invalid TTL strategy type: {type(ttl_strategy)}. "
|
|
500
|
+
f"Expected None, int, or Callable. Using default TTL."
|
|
501
|
+
)
|
|
502
|
+
return None
|
|
503
|
+
|
|
284
504
|
def _get_from_cache(self, cache_key: str) -> Optional[Any]:
|
|
285
505
|
"""
|
|
286
|
-
Get a result from cache if it exists and is not expired.
|
|
506
|
+
Get a result from cache if it exists and is not expired (synchronous).
|
|
287
507
|
|
|
288
508
|
Args:
|
|
289
509
|
cache_key (str): Cache key.
|
|
@@ -293,11 +513,44 @@ class ToolExecutor:
|
|
|
293
513
|
"""
|
|
294
514
|
if not self.config.enable_cache:
|
|
295
515
|
return None
|
|
296
|
-
return self.
|
|
516
|
+
return self.cache_provider.get(cache_key)
|
|
297
517
|
|
|
298
518
|
def _add_to_cache(self, cache_key: str, result: Any, ttl: Optional[int] = None) -> None:
|
|
299
519
|
"""
|
|
300
|
-
Add a result to the cache with optional TTL.
|
|
520
|
+
Add a result to the cache with optional TTL (synchronous).
|
|
521
|
+
|
|
522
|
+
Args:
|
|
523
|
+
cache_key (str): Cache key.
|
|
524
|
+
result (Any): Result to cache.
|
|
525
|
+
ttl (Optional[int]): Time-to-live in seconds.
|
|
526
|
+
"""
|
|
527
|
+
if not self.config.enable_cache:
|
|
528
|
+
return
|
|
529
|
+
self.cache_provider.set(cache_key, result, ttl)
|
|
530
|
+
|
|
531
|
+
async def _get_from_cache_async(self, cache_key: str) -> Optional[Any]:
|
|
532
|
+
"""
|
|
533
|
+
Get a result from cache if it exists and is not expired (asynchronous).
|
|
534
|
+
|
|
535
|
+
Args:
|
|
536
|
+
cache_key (str): Cache key.
|
|
537
|
+
|
|
538
|
+
Returns:
|
|
539
|
+
Optional[Any]: Cached result or None.
|
|
540
|
+
"""
|
|
541
|
+
if not self.config.enable_cache:
|
|
542
|
+
return None
|
|
543
|
+
|
|
544
|
+
# Use async interface if available
|
|
545
|
+
if hasattr(self.cache_provider, 'get_async'):
|
|
546
|
+
return await self.cache_provider.get_async(cache_key)
|
|
547
|
+
else:
|
|
548
|
+
# Fallback to sync interface
|
|
549
|
+
return self.cache_provider.get(cache_key)
|
|
550
|
+
|
|
551
|
+
async def _add_to_cache_async(self, cache_key: str, result: Any, ttl: Optional[int] = None) -> None:
|
|
552
|
+
"""
|
|
553
|
+
Add a result to the cache with optional TTL (asynchronous).
|
|
301
554
|
|
|
302
555
|
Args:
|
|
303
556
|
cache_key (str): Cache key.
|
|
@@ -306,7 +559,13 @@ class ToolExecutor:
|
|
|
306
559
|
"""
|
|
307
560
|
if not self.config.enable_cache:
|
|
308
561
|
return
|
|
309
|
-
|
|
562
|
+
|
|
563
|
+
# Use async interface if available
|
|
564
|
+
if hasattr(self.cache_provider, 'set_async'):
|
|
565
|
+
await self.cache_provider.set_async(cache_key, result, ttl)
|
|
566
|
+
else:
|
|
567
|
+
# Fallback to sync interface
|
|
568
|
+
self.cache_provider.set(cache_key, result, ttl)
|
|
310
569
|
|
|
311
570
|
def get_lock(self, resource_id: str) -> threading.Lock:
|
|
312
571
|
"""
|
|
@@ -443,13 +702,13 @@ class ToolExecutor:
|
|
|
443
702
|
for k, v in kwargs.items():
|
|
444
703
|
if isinstance(v, str) and re.search(r'(\bSELECT\b|\bINSERT\b|--|;|/\*)', v, re.IGNORECASE):
|
|
445
704
|
raise SecurityError(f"Input parameter '{k}' contains potentially malicious content")
|
|
446
|
-
# Use cache if enabled
|
|
705
|
+
# Use cache if enabled (async)
|
|
447
706
|
if self.config.enable_cache:
|
|
448
707
|
cache_key = self._get_cache_key(operation, (), kwargs)
|
|
449
|
-
cached_result = self.
|
|
708
|
+
cached_result = await self._get_from_cache_async(cache_key)
|
|
450
709
|
if cached_result is not None:
|
|
451
710
|
self._metrics.record_cache_hit()
|
|
452
|
-
logger.debug(f"Cache hit for {operation}")
|
|
711
|
+
logger.debug(f"Cache hit for {operation} (async)")
|
|
453
712
|
return cached_result
|
|
454
713
|
|
|
455
714
|
async def _execute():
|
|
@@ -462,9 +721,9 @@ class ToolExecutor:
|
|
|
462
721
|
if self.config.log_execution_time:
|
|
463
722
|
logger.info(f"{tool_instance.__class__.__name__}.{operation} executed in {time.time() - start_time:.4f} seconds")
|
|
464
723
|
|
|
465
|
-
# Cache result if enabled
|
|
724
|
+
# Cache result if enabled (async)
|
|
466
725
|
if self.config.enable_cache:
|
|
467
|
-
self.
|
|
726
|
+
await self._add_to_cache_async(cache_key, result)
|
|
468
727
|
return result
|
|
469
728
|
except Exception as e:
|
|
470
729
|
self._metrics.record_failure()
|
|
@@ -499,20 +758,31 @@ class ToolExecutor:
|
|
|
499
758
|
logger.error(f"Batch operation {operations[i]['op']} failed: {result}")
|
|
500
759
|
return results
|
|
501
760
|
|
|
502
|
-
# Singleton executor instance
|
|
761
|
+
# Singleton executor instance (for backward compatibility)
|
|
503
762
|
_default_executor = None
|
|
504
763
|
|
|
505
764
|
def get_executor(config: Optional[Dict[str, Any]] = None) -> ToolExecutor:
|
|
506
765
|
"""
|
|
507
|
-
Get or create
|
|
766
|
+
Get or create executor instance.
|
|
767
|
+
|
|
768
|
+
If config is provided, creates a new executor with that config.
|
|
769
|
+
If config is None, returns the default singleton executor.
|
|
508
770
|
|
|
509
771
|
Args:
|
|
510
772
|
config (Dict[str, Any], optional): Configuration overrides.
|
|
773
|
+
If provided, creates a new executor instance.
|
|
774
|
+
If None, returns the default singleton.
|
|
511
775
|
|
|
512
776
|
Returns:
|
|
513
|
-
ToolExecutor:
|
|
777
|
+
ToolExecutor: Executor instance.
|
|
514
778
|
"""
|
|
515
779
|
global _default_executor
|
|
780
|
+
|
|
781
|
+
# If config is provided, create a new executor with that config
|
|
782
|
+
if config is not None:
|
|
783
|
+
return ToolExecutor(config)
|
|
784
|
+
|
|
785
|
+
# Otherwise, return the default singleton
|
|
516
786
|
if _default_executor is None:
|
|
517
|
-
_default_executor = ToolExecutor(
|
|
787
|
+
_default_executor = ToolExecutor()
|
|
518
788
|
return _default_executor
|
aiecs/utils/__init__.py
CHANGED
|
@@ -5,16 +5,27 @@ This module provides utility functions including:
|
|
|
5
5
|
- Prompt loading functionality
|
|
6
6
|
- Token usage tracking
|
|
7
7
|
- Execution utilities
|
|
8
|
+
- Cache provider interfaces and implementations
|
|
8
9
|
"""
|
|
9
10
|
|
|
10
11
|
from .prompt_loader import get_prompt
|
|
11
12
|
from .token_usage_repository import TokenUsageRepository
|
|
12
13
|
from .execution_utils import ExecutionUtils
|
|
14
|
+
from .cache_provider import (
|
|
15
|
+
ICacheProvider,
|
|
16
|
+
LRUCacheProvider,
|
|
17
|
+
DualLayerCacheProvider,
|
|
18
|
+
RedisCacheProvider
|
|
19
|
+
)
|
|
13
20
|
|
|
14
21
|
__all__ = [
|
|
15
22
|
'get_prompt',
|
|
16
23
|
'TokenUsageRepository',
|
|
17
24
|
'ExecutionUtils',
|
|
25
|
+
'ICacheProvider',
|
|
26
|
+
'LRUCacheProvider',
|
|
27
|
+
'DualLayerCacheProvider',
|
|
28
|
+
'RedisCacheProvider',
|
|
18
29
|
]
|
|
19
30
|
|
|
20
31
|
# Version information
|