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,279 @@
1
+ from datetime import datetime
2
+ from typing import Optional, Dict, Any
3
+ import logging
4
+ from ..infrastructure.persistence.redis_client import get_redis_client
5
+
6
+ logger = logging.getLogger(__name__)
7
+
8
+ class TokenUsageRepository:
9
+ """Encapsulates all Redis operations related to user token usage"""
10
+
11
+ def _get_key_for_current_period(self, user_id: str, cycle_start_date: Optional[str] = None) -> str:
12
+ """
13
+ Generate Redis key for current billing period
14
+
15
+ Args:
16
+ user_id: User ID
17
+ cycle_start_date: Cycle start date in YYYY-MM-DD format, defaults to current month if not provided
18
+
19
+ Returns:
20
+ Redis key string
21
+ """
22
+ if cycle_start_date:
23
+ # Use provided cycle start date
24
+ period = cycle_start_date
25
+ else:
26
+ # Use current month as default period
27
+ period = datetime.now().strftime("%Y-%m-%d")
28
+
29
+ return f"token_usage:{user_id}:{period}"
30
+
31
+ async def increment_prompt_tokens(self, user_id: str, prompt_tokens: int, cycle_start_date: Optional[str] = None):
32
+ """
33
+ Increment prompt token usage for specified user
34
+
35
+ Args:
36
+ user_id: User ID
37
+ prompt_tokens: Number of input tokens
38
+ cycle_start_date: Cycle start date
39
+ """
40
+ if not user_id or prompt_tokens <= 0:
41
+ return
42
+
43
+ redis_key = self._get_key_for_current_period(user_id, cycle_start_date)
44
+
45
+ try:
46
+ # Use HINCRBY for atomic increment
47
+ client = await get_redis_client()
48
+ await client.hincrby(redis_key, "prompt_tokens", prompt_tokens)
49
+ logger.info(f"[Repository] User '{user_id}' prompt tokens incremented by {prompt_tokens} in key '{redis_key}'.")
50
+ except Exception as e:
51
+ logger.error(f"Failed to increment prompt tokens for user {user_id}: {e}")
52
+ raise
53
+
54
+ async def increment_completion_tokens(self, user_id: str, completion_tokens: int, cycle_start_date: Optional[str] = None):
55
+ """
56
+ Increment completion token usage for specified user
57
+
58
+ Args:
59
+ user_id: User ID
60
+ completion_tokens: Number of output tokens
61
+ cycle_start_date: Cycle start date
62
+ """
63
+ if not user_id or completion_tokens <= 0:
64
+ return
65
+
66
+ redis_key = self._get_key_for_current_period(user_id, cycle_start_date)
67
+
68
+ try:
69
+ # Use HINCRBY for atomic increment
70
+ client = await get_redis_client()
71
+ await client.hincrby(redis_key, "completion_tokens", completion_tokens)
72
+ logger.info(f"[Repository] User '{user_id}' completion tokens incremented by {completion_tokens} in key '{redis_key}'.")
73
+ except Exception as e:
74
+ logger.error(f"Failed to increment completion tokens for user {user_id}: {e}")
75
+ raise
76
+
77
+ async def increment_total_usage(self, user_id: str, total_tokens: int, cycle_start_date: Optional[str] = None):
78
+ """
79
+ Increment total token usage for specified user
80
+
81
+ Args:
82
+ user_id: User ID
83
+ total_tokens: Total number of tokens
84
+ cycle_start_date: Cycle start date
85
+ """
86
+ if not user_id or total_tokens <= 0:
87
+ return
88
+
89
+ redis_key = self._get_key_for_current_period(user_id, cycle_start_date)
90
+
91
+ try:
92
+ # Use HINCRBY for atomic increment
93
+ client = await get_redis_client()
94
+ await client.hincrby(redis_key, "total_tokens", total_tokens)
95
+ logger.info(f"[Repository] User '{user_id}' total usage incremented by {total_tokens} tokens in key '{redis_key}'.")
96
+ except Exception as e:
97
+ logger.error(f"Failed to increment total tokens for user {user_id}: {e}")
98
+ raise
99
+
100
+ async def increment_detailed_usage(
101
+ self,
102
+ user_id: str,
103
+ prompt_tokens: int,
104
+ completion_tokens: int,
105
+ cycle_start_date: Optional[str] = None
106
+ ):
107
+ """
108
+ Increment both prompt and completion token usage for specified user
109
+
110
+ Args:
111
+ user_id: User ID
112
+ prompt_tokens: Number of input tokens
113
+ completion_tokens: Number of output tokens
114
+ cycle_start_date: Cycle start date
115
+ """
116
+ if not user_id or (prompt_tokens <= 0 and completion_tokens <= 0):
117
+ return
118
+
119
+ redis_key = self._get_key_for_current_period(user_id, cycle_start_date)
120
+
121
+ try:
122
+ # Batch update multiple fields
123
+ updates = {}
124
+ if prompt_tokens > 0:
125
+ updates["prompt_tokens"] = prompt_tokens
126
+ if completion_tokens > 0:
127
+ updates["completion_tokens"] = completion_tokens
128
+
129
+ # Calculate total token count
130
+ total_tokens = prompt_tokens + completion_tokens
131
+ if total_tokens > 0:
132
+ updates["total_tokens"] = total_tokens
133
+
134
+ # Use pipeline for batch operations
135
+ redis_client_instance = await get_redis_client()
136
+ client = await redis_client_instance.get_client()
137
+ pipe = client.pipeline()
138
+
139
+ for field, value in updates.items():
140
+ pipe.hincrby(redis_key, field, value)
141
+
142
+ await pipe.execute()
143
+
144
+ logger.info(f"[Repository] User '{user_id}' detailed usage updated: prompt={prompt_tokens}, completion={completion_tokens}, total={total_tokens} in key '{redis_key}'.")
145
+ except Exception as e:
146
+ logger.error(f"Failed to increment detailed usage for user {user_id}: {e}")
147
+ raise
148
+
149
+ async def get_usage_stats(self, user_id: str, cycle_start_date: Optional[str] = None) -> Dict[str, int]:
150
+ """
151
+ Get token usage statistics for specified user
152
+
153
+ Args:
154
+ user_id: User ID
155
+ cycle_start_date: Cycle start date
156
+
157
+ Returns:
158
+ Dictionary containing token usage statistics
159
+ """
160
+ if not user_id:
161
+ return {}
162
+
163
+ redis_key = self._get_key_for_current_period(user_id, cycle_start_date)
164
+
165
+ try:
166
+ client = await get_redis_client()
167
+ stats = await client.hgetall(redis_key)
168
+
169
+ # Convert to integer type
170
+ result = {}
171
+ for key, value in stats.items():
172
+ try:
173
+ result[key] = int(value) if value else 0
174
+ except (ValueError, TypeError):
175
+ result[key] = 0
176
+
177
+ # Ensure required fields exist
178
+ result.setdefault("prompt_tokens", 0)
179
+ result.setdefault("completion_tokens", 0)
180
+ result.setdefault("total_tokens", 0)
181
+
182
+ logger.debug(f"[Repository] Retrieved usage stats for user '{user_id}': {result}")
183
+ return result
184
+
185
+ except Exception as e:
186
+ logger.error(f"Failed to get usage stats for user {user_id}: {e}")
187
+ return {
188
+ "prompt_tokens": 0,
189
+ "completion_tokens": 0,
190
+ "total_tokens": 0
191
+ }
192
+
193
+ async def reset_usage(self, user_id: str, cycle_start_date: Optional[str] = None):
194
+ """
195
+ Reset token usage for specified user
196
+
197
+ Args:
198
+ user_id: User ID
199
+ cycle_start_date: Cycle start date
200
+ """
201
+ if not user_id:
202
+ return
203
+
204
+ redis_key = self._get_key_for_current_period(user_id, cycle_start_date)
205
+
206
+ try:
207
+ redis_client_instance = await get_redis_client()
208
+ client = await redis_client_instance.get_client()
209
+ await client.delete(redis_key)
210
+ logger.info(f"[Repository] Reset usage for user '{user_id}' in key '{redis_key}'.")
211
+ except Exception as e:
212
+ logger.error(f"Failed to reset usage for user {user_id}: {e}")
213
+ raise
214
+
215
+ async def set_usage_limit(self, user_id: str, limit: int, cycle_start_date: Optional[str] = None):
216
+ """
217
+ Set token usage limit for user
218
+
219
+ Args:
220
+ user_id: User ID
221
+ limit: Token usage limit
222
+ cycle_start_date: Cycle start date
223
+ """
224
+ if not user_id or limit <= 0:
225
+ return
226
+
227
+ redis_key = self._get_key_for_current_period(user_id, cycle_start_date)
228
+
229
+ try:
230
+ client = await get_redis_client()
231
+ await client.hset(redis_key, {"usage_limit": str(limit)})
232
+ logger.info(f"[Repository] Set usage limit {limit} for user '{user_id}' in key '{redis_key}'.")
233
+ except Exception as e:
234
+ logger.error(f"Failed to set usage limit for user {user_id}: {e}")
235
+ raise
236
+
237
+ async def check_usage_limit(self, user_id: str, cycle_start_date: Optional[str] = None) -> Dict[str, Any]:
238
+ """
239
+ Check if user has exceeded usage limit
240
+
241
+ Args:
242
+ user_id: User ID
243
+ cycle_start_date: Cycle start date
244
+
245
+ Returns:
246
+ Dictionary containing limit check results
247
+ """
248
+ if not user_id:
249
+ return {"exceeded": False, "current_usage": 0, "limit": 0, "remaining": 0}
250
+
251
+ try:
252
+ stats = await self.get_usage_stats(user_id, cycle_start_date)
253
+ current_usage = stats.get("total_tokens", 0)
254
+
255
+ redis_key = self._get_key_for_current_period(user_id, cycle_start_date)
256
+ client = await get_redis_client()
257
+ limit_str = await client.hget(redis_key, "usage_limit")
258
+ limit = int(limit_str) if limit_str else 0
259
+
260
+ exceeded = limit > 0 and current_usage >= limit
261
+ remaining = max(0, limit - current_usage) if limit > 0 else float('inf')
262
+
263
+ result = {
264
+ "exceeded": exceeded,
265
+ "current_usage": current_usage,
266
+ "limit": limit,
267
+ "remaining": remaining
268
+ }
269
+
270
+ logger.debug(f"[Repository] Usage limit check for user '{user_id}': {result}")
271
+ return result
272
+
273
+ except Exception as e:
274
+ logger.error(f"Failed to check usage limit for user {user_id}: {e}")
275
+ return {"exceeded": False, "current_usage": 0, "limit": 0, "remaining": 0}
276
+
277
+
278
+ # Create a singleton for global application use
279
+ token_usage_repo = TokenUsageRepository()
aiecs/ws/__init__.py ADDED
File without changes
@@ -0,0 +1,41 @@
1
+ import socketio
2
+ from aiecs.config.config import get_settings
3
+
4
+ settings = get_settings()
5
+ # In production, this should be set to specific origins
6
+ # For example: ["https://your-frontend-domain.com"]
7
+ allowed_origins = settings.cors_allowed_origins.split(",") if hasattr(settings, "cors_allowed_origins") else ["http://express-gateway:3001"]
8
+
9
+ # Allow all origins for development (more permissive)
10
+ # In production, you should use specific origins
11
+ # Explicitly set async_mode to 'asgi' for compatibility with uvicorn
12
+ sio = socketio.AsyncServer(cors_allowed_origins="*", async_mode='asgi')
13
+ # We no longer create a FastAPI app or combined ASGI app here
14
+ # The FastAPI app will be created in main.py and the Socket.IO server will be mounted there
15
+
16
+ # Store connected clients by user ID
17
+ connected_clients = {}
18
+
19
+ @sio.event
20
+ async def connect(sid, environ, auth=None):
21
+ print(f"Client connected: {sid}")
22
+
23
+ @sio.event
24
+ async def disconnect(sid):
25
+ print(f"Client disconnected: {sid}")
26
+ for user, socket_id in list(connected_clients.items()):
27
+ if socket_id == sid:
28
+ del connected_clients[user]
29
+
30
+ @sio.event
31
+ async def register(sid, data):
32
+ user_id = data.get("user_id")
33
+ if user_id:
34
+ connected_clients[user_id] = sid
35
+ print(f"Registered user {user_id} on SID {sid}")
36
+
37
+ # Send progress update to user
38
+ async def push_progress(user_id: str, data: dict):
39
+ sid = connected_clients.get(user_id)
40
+ if sid:
41
+ await sio.emit("progress", data, to=sid)