aiecs 1.5.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.
Files changed (302) hide show
  1. aiecs/__init__.py +72 -0
  2. aiecs/__main__.py +41 -0
  3. aiecs/aiecs_client.py +469 -0
  4. aiecs/application/__init__.py +10 -0
  5. aiecs/application/executors/__init__.py +10 -0
  6. aiecs/application/executors/operation_executor.py +363 -0
  7. aiecs/application/knowledge_graph/__init__.py +7 -0
  8. aiecs/application/knowledge_graph/builder/__init__.py +37 -0
  9. aiecs/application/knowledge_graph/builder/document_builder.py +375 -0
  10. aiecs/application/knowledge_graph/builder/graph_builder.py +356 -0
  11. aiecs/application/knowledge_graph/builder/schema_mapping.py +531 -0
  12. aiecs/application/knowledge_graph/builder/structured_pipeline.py +443 -0
  13. aiecs/application/knowledge_graph/builder/text_chunker.py +319 -0
  14. aiecs/application/knowledge_graph/extractors/__init__.py +27 -0
  15. aiecs/application/knowledge_graph/extractors/base.py +100 -0
  16. aiecs/application/knowledge_graph/extractors/llm_entity_extractor.py +327 -0
  17. aiecs/application/knowledge_graph/extractors/llm_relation_extractor.py +349 -0
  18. aiecs/application/knowledge_graph/extractors/ner_entity_extractor.py +244 -0
  19. aiecs/application/knowledge_graph/fusion/__init__.py +23 -0
  20. aiecs/application/knowledge_graph/fusion/entity_deduplicator.py +387 -0
  21. aiecs/application/knowledge_graph/fusion/entity_linker.py +343 -0
  22. aiecs/application/knowledge_graph/fusion/knowledge_fusion.py +580 -0
  23. aiecs/application/knowledge_graph/fusion/relation_deduplicator.py +189 -0
  24. aiecs/application/knowledge_graph/pattern_matching/__init__.py +21 -0
  25. aiecs/application/knowledge_graph/pattern_matching/pattern_matcher.py +344 -0
  26. aiecs/application/knowledge_graph/pattern_matching/query_executor.py +378 -0
  27. aiecs/application/knowledge_graph/profiling/__init__.py +12 -0
  28. aiecs/application/knowledge_graph/profiling/query_plan_visualizer.py +199 -0
  29. aiecs/application/knowledge_graph/profiling/query_profiler.py +223 -0
  30. aiecs/application/knowledge_graph/reasoning/__init__.py +27 -0
  31. aiecs/application/knowledge_graph/reasoning/evidence_synthesis.py +347 -0
  32. aiecs/application/knowledge_graph/reasoning/inference_engine.py +504 -0
  33. aiecs/application/knowledge_graph/reasoning/logic_form_parser.py +167 -0
  34. aiecs/application/knowledge_graph/reasoning/logic_parser/__init__.py +79 -0
  35. aiecs/application/knowledge_graph/reasoning/logic_parser/ast_builder.py +513 -0
  36. aiecs/application/knowledge_graph/reasoning/logic_parser/ast_nodes.py +630 -0
  37. aiecs/application/knowledge_graph/reasoning/logic_parser/ast_validator.py +654 -0
  38. aiecs/application/knowledge_graph/reasoning/logic_parser/error_handler.py +477 -0
  39. aiecs/application/knowledge_graph/reasoning/logic_parser/parser.py +390 -0
  40. aiecs/application/knowledge_graph/reasoning/logic_parser/query_context.py +217 -0
  41. aiecs/application/knowledge_graph/reasoning/logic_query_integration.py +169 -0
  42. aiecs/application/knowledge_graph/reasoning/query_planner.py +872 -0
  43. aiecs/application/knowledge_graph/reasoning/reasoning_engine.py +554 -0
  44. aiecs/application/knowledge_graph/retrieval/__init__.py +19 -0
  45. aiecs/application/knowledge_graph/retrieval/retrieval_strategies.py +596 -0
  46. aiecs/application/knowledge_graph/search/__init__.py +59 -0
  47. aiecs/application/knowledge_graph/search/hybrid_search.py +423 -0
  48. aiecs/application/knowledge_graph/search/reranker.py +295 -0
  49. aiecs/application/knowledge_graph/search/reranker_strategies.py +553 -0
  50. aiecs/application/knowledge_graph/search/text_similarity.py +398 -0
  51. aiecs/application/knowledge_graph/traversal/__init__.py +15 -0
  52. aiecs/application/knowledge_graph/traversal/enhanced_traversal.py +329 -0
  53. aiecs/application/knowledge_graph/traversal/path_scorer.py +269 -0
  54. aiecs/application/knowledge_graph/validators/__init__.py +13 -0
  55. aiecs/application/knowledge_graph/validators/relation_validator.py +189 -0
  56. aiecs/application/knowledge_graph/visualization/__init__.py +11 -0
  57. aiecs/application/knowledge_graph/visualization/graph_visualizer.py +321 -0
  58. aiecs/common/__init__.py +9 -0
  59. aiecs/common/knowledge_graph/__init__.py +17 -0
  60. aiecs/common/knowledge_graph/runnable.py +484 -0
  61. aiecs/config/__init__.py +16 -0
  62. aiecs/config/config.py +498 -0
  63. aiecs/config/graph_config.py +137 -0
  64. aiecs/config/registry.py +23 -0
  65. aiecs/core/__init__.py +46 -0
  66. aiecs/core/interface/__init__.py +34 -0
  67. aiecs/core/interface/execution_interface.py +152 -0
  68. aiecs/core/interface/storage_interface.py +171 -0
  69. aiecs/domain/__init__.py +289 -0
  70. aiecs/domain/agent/__init__.py +189 -0
  71. aiecs/domain/agent/base_agent.py +697 -0
  72. aiecs/domain/agent/exceptions.py +103 -0
  73. aiecs/domain/agent/graph_aware_mixin.py +559 -0
  74. aiecs/domain/agent/hybrid_agent.py +490 -0
  75. aiecs/domain/agent/integration/__init__.py +26 -0
  76. aiecs/domain/agent/integration/context_compressor.py +222 -0
  77. aiecs/domain/agent/integration/context_engine_adapter.py +252 -0
  78. aiecs/domain/agent/integration/retry_policy.py +219 -0
  79. aiecs/domain/agent/integration/role_config.py +213 -0
  80. aiecs/domain/agent/knowledge_aware_agent.py +646 -0
  81. aiecs/domain/agent/lifecycle.py +296 -0
  82. aiecs/domain/agent/llm_agent.py +300 -0
  83. aiecs/domain/agent/memory/__init__.py +12 -0
  84. aiecs/domain/agent/memory/conversation.py +197 -0
  85. aiecs/domain/agent/migration/__init__.py +14 -0
  86. aiecs/domain/agent/migration/conversion.py +160 -0
  87. aiecs/domain/agent/migration/legacy_wrapper.py +90 -0
  88. aiecs/domain/agent/models.py +317 -0
  89. aiecs/domain/agent/observability.py +407 -0
  90. aiecs/domain/agent/persistence.py +289 -0
  91. aiecs/domain/agent/prompts/__init__.py +29 -0
  92. aiecs/domain/agent/prompts/builder.py +161 -0
  93. aiecs/domain/agent/prompts/formatters.py +189 -0
  94. aiecs/domain/agent/prompts/template.py +255 -0
  95. aiecs/domain/agent/registry.py +260 -0
  96. aiecs/domain/agent/tool_agent.py +257 -0
  97. aiecs/domain/agent/tools/__init__.py +12 -0
  98. aiecs/domain/agent/tools/schema_generator.py +221 -0
  99. aiecs/domain/community/__init__.py +155 -0
  100. aiecs/domain/community/agent_adapter.py +477 -0
  101. aiecs/domain/community/analytics.py +481 -0
  102. aiecs/domain/community/collaborative_workflow.py +642 -0
  103. aiecs/domain/community/communication_hub.py +645 -0
  104. aiecs/domain/community/community_builder.py +320 -0
  105. aiecs/domain/community/community_integration.py +800 -0
  106. aiecs/domain/community/community_manager.py +813 -0
  107. aiecs/domain/community/decision_engine.py +879 -0
  108. aiecs/domain/community/exceptions.py +225 -0
  109. aiecs/domain/community/models/__init__.py +33 -0
  110. aiecs/domain/community/models/community_models.py +268 -0
  111. aiecs/domain/community/resource_manager.py +457 -0
  112. aiecs/domain/community/shared_context_manager.py +603 -0
  113. aiecs/domain/context/__init__.py +58 -0
  114. aiecs/domain/context/context_engine.py +989 -0
  115. aiecs/domain/context/conversation_models.py +354 -0
  116. aiecs/domain/context/graph_memory.py +467 -0
  117. aiecs/domain/execution/__init__.py +12 -0
  118. aiecs/domain/execution/model.py +57 -0
  119. aiecs/domain/knowledge_graph/__init__.py +19 -0
  120. aiecs/domain/knowledge_graph/models/__init__.py +52 -0
  121. aiecs/domain/knowledge_graph/models/entity.py +130 -0
  122. aiecs/domain/knowledge_graph/models/evidence.py +194 -0
  123. aiecs/domain/knowledge_graph/models/inference_rule.py +186 -0
  124. aiecs/domain/knowledge_graph/models/path.py +179 -0
  125. aiecs/domain/knowledge_graph/models/path_pattern.py +173 -0
  126. aiecs/domain/knowledge_graph/models/query.py +272 -0
  127. aiecs/domain/knowledge_graph/models/query_plan.py +187 -0
  128. aiecs/domain/knowledge_graph/models/relation.py +136 -0
  129. aiecs/domain/knowledge_graph/schema/__init__.py +23 -0
  130. aiecs/domain/knowledge_graph/schema/entity_type.py +135 -0
  131. aiecs/domain/knowledge_graph/schema/graph_schema.py +271 -0
  132. aiecs/domain/knowledge_graph/schema/property_schema.py +155 -0
  133. aiecs/domain/knowledge_graph/schema/relation_type.py +171 -0
  134. aiecs/domain/knowledge_graph/schema/schema_manager.py +496 -0
  135. aiecs/domain/knowledge_graph/schema/type_enums.py +205 -0
  136. aiecs/domain/task/__init__.py +13 -0
  137. aiecs/domain/task/dsl_processor.py +613 -0
  138. aiecs/domain/task/model.py +62 -0
  139. aiecs/domain/task/task_context.py +268 -0
  140. aiecs/infrastructure/__init__.py +24 -0
  141. aiecs/infrastructure/graph_storage/__init__.py +11 -0
  142. aiecs/infrastructure/graph_storage/base.py +601 -0
  143. aiecs/infrastructure/graph_storage/batch_operations.py +449 -0
  144. aiecs/infrastructure/graph_storage/cache.py +429 -0
  145. aiecs/infrastructure/graph_storage/distributed.py +226 -0
  146. aiecs/infrastructure/graph_storage/error_handling.py +390 -0
  147. aiecs/infrastructure/graph_storage/graceful_degradation.py +306 -0
  148. aiecs/infrastructure/graph_storage/health_checks.py +378 -0
  149. aiecs/infrastructure/graph_storage/in_memory.py +514 -0
  150. aiecs/infrastructure/graph_storage/index_optimization.py +483 -0
  151. aiecs/infrastructure/graph_storage/lazy_loading.py +410 -0
  152. aiecs/infrastructure/graph_storage/metrics.py +357 -0
  153. aiecs/infrastructure/graph_storage/migration.py +413 -0
  154. aiecs/infrastructure/graph_storage/pagination.py +471 -0
  155. aiecs/infrastructure/graph_storage/performance_monitoring.py +466 -0
  156. aiecs/infrastructure/graph_storage/postgres.py +871 -0
  157. aiecs/infrastructure/graph_storage/query_optimizer.py +635 -0
  158. aiecs/infrastructure/graph_storage/schema_cache.py +290 -0
  159. aiecs/infrastructure/graph_storage/sqlite.py +623 -0
  160. aiecs/infrastructure/graph_storage/streaming.py +495 -0
  161. aiecs/infrastructure/messaging/__init__.py +13 -0
  162. aiecs/infrastructure/messaging/celery_task_manager.py +383 -0
  163. aiecs/infrastructure/messaging/websocket_manager.py +298 -0
  164. aiecs/infrastructure/monitoring/__init__.py +34 -0
  165. aiecs/infrastructure/monitoring/executor_metrics.py +174 -0
  166. aiecs/infrastructure/monitoring/global_metrics_manager.py +213 -0
  167. aiecs/infrastructure/monitoring/structured_logger.py +48 -0
  168. aiecs/infrastructure/monitoring/tracing_manager.py +410 -0
  169. aiecs/infrastructure/persistence/__init__.py +24 -0
  170. aiecs/infrastructure/persistence/context_engine_client.py +187 -0
  171. aiecs/infrastructure/persistence/database_manager.py +333 -0
  172. aiecs/infrastructure/persistence/file_storage.py +754 -0
  173. aiecs/infrastructure/persistence/redis_client.py +220 -0
  174. aiecs/llm/__init__.py +86 -0
  175. aiecs/llm/callbacks/__init__.py +11 -0
  176. aiecs/llm/callbacks/custom_callbacks.py +264 -0
  177. aiecs/llm/client_factory.py +420 -0
  178. aiecs/llm/clients/__init__.py +33 -0
  179. aiecs/llm/clients/base_client.py +193 -0
  180. aiecs/llm/clients/googleai_client.py +181 -0
  181. aiecs/llm/clients/openai_client.py +131 -0
  182. aiecs/llm/clients/vertex_client.py +437 -0
  183. aiecs/llm/clients/xai_client.py +184 -0
  184. aiecs/llm/config/__init__.py +51 -0
  185. aiecs/llm/config/config_loader.py +275 -0
  186. aiecs/llm/config/config_validator.py +236 -0
  187. aiecs/llm/config/model_config.py +151 -0
  188. aiecs/llm/utils/__init__.py +10 -0
  189. aiecs/llm/utils/validate_config.py +91 -0
  190. aiecs/main.py +363 -0
  191. aiecs/scripts/__init__.py +3 -0
  192. aiecs/scripts/aid/VERSION_MANAGEMENT.md +97 -0
  193. aiecs/scripts/aid/__init__.py +19 -0
  194. aiecs/scripts/aid/version_manager.py +215 -0
  195. aiecs/scripts/dependance_check/DEPENDENCY_SYSTEM_SUMMARY.md +242 -0
  196. aiecs/scripts/dependance_check/README_DEPENDENCY_CHECKER.md +310 -0
  197. aiecs/scripts/dependance_check/__init__.py +17 -0
  198. aiecs/scripts/dependance_check/dependency_checker.py +938 -0
  199. aiecs/scripts/dependance_check/dependency_fixer.py +391 -0
  200. aiecs/scripts/dependance_check/download_nlp_data.py +396 -0
  201. aiecs/scripts/dependance_check/quick_dependency_check.py +270 -0
  202. aiecs/scripts/dependance_check/setup_nlp_data.sh +217 -0
  203. aiecs/scripts/dependance_patch/__init__.py +7 -0
  204. aiecs/scripts/dependance_patch/fix_weasel/README_WEASEL_PATCH.md +126 -0
  205. aiecs/scripts/dependance_patch/fix_weasel/__init__.py +11 -0
  206. aiecs/scripts/dependance_patch/fix_weasel/fix_weasel_validator.py +128 -0
  207. aiecs/scripts/dependance_patch/fix_weasel/fix_weasel_validator.sh +82 -0
  208. aiecs/scripts/dependance_patch/fix_weasel/patch_weasel_library.sh +188 -0
  209. aiecs/scripts/dependance_patch/fix_weasel/run_weasel_patch.sh +41 -0
  210. aiecs/scripts/tools_develop/README.md +449 -0
  211. aiecs/scripts/tools_develop/TOOL_AUTO_DISCOVERY.md +234 -0
  212. aiecs/scripts/tools_develop/__init__.py +21 -0
  213. aiecs/scripts/tools_develop/check_type_annotations.py +259 -0
  214. aiecs/scripts/tools_develop/validate_tool_schemas.py +422 -0
  215. aiecs/scripts/tools_develop/verify_tools.py +356 -0
  216. aiecs/tasks/__init__.py +1 -0
  217. aiecs/tasks/worker.py +172 -0
  218. aiecs/tools/__init__.py +299 -0
  219. aiecs/tools/apisource/__init__.py +99 -0
  220. aiecs/tools/apisource/intelligence/__init__.py +19 -0
  221. aiecs/tools/apisource/intelligence/data_fusion.py +381 -0
  222. aiecs/tools/apisource/intelligence/query_analyzer.py +413 -0
  223. aiecs/tools/apisource/intelligence/search_enhancer.py +388 -0
  224. aiecs/tools/apisource/monitoring/__init__.py +9 -0
  225. aiecs/tools/apisource/monitoring/metrics.py +303 -0
  226. aiecs/tools/apisource/providers/__init__.py +115 -0
  227. aiecs/tools/apisource/providers/base.py +664 -0
  228. aiecs/tools/apisource/providers/census.py +401 -0
  229. aiecs/tools/apisource/providers/fred.py +564 -0
  230. aiecs/tools/apisource/providers/newsapi.py +412 -0
  231. aiecs/tools/apisource/providers/worldbank.py +357 -0
  232. aiecs/tools/apisource/reliability/__init__.py +12 -0
  233. aiecs/tools/apisource/reliability/error_handler.py +375 -0
  234. aiecs/tools/apisource/reliability/fallback_strategy.py +391 -0
  235. aiecs/tools/apisource/tool.py +850 -0
  236. aiecs/tools/apisource/utils/__init__.py +9 -0
  237. aiecs/tools/apisource/utils/validators.py +338 -0
  238. aiecs/tools/base_tool.py +201 -0
  239. aiecs/tools/docs/__init__.py +121 -0
  240. aiecs/tools/docs/ai_document_orchestrator.py +599 -0
  241. aiecs/tools/docs/ai_document_writer_orchestrator.py +2403 -0
  242. aiecs/tools/docs/content_insertion_tool.py +1333 -0
  243. aiecs/tools/docs/document_creator_tool.py +1317 -0
  244. aiecs/tools/docs/document_layout_tool.py +1166 -0
  245. aiecs/tools/docs/document_parser_tool.py +994 -0
  246. aiecs/tools/docs/document_writer_tool.py +1818 -0
  247. aiecs/tools/knowledge_graph/__init__.py +17 -0
  248. aiecs/tools/knowledge_graph/graph_reasoning_tool.py +734 -0
  249. aiecs/tools/knowledge_graph/graph_search_tool.py +923 -0
  250. aiecs/tools/knowledge_graph/kg_builder_tool.py +476 -0
  251. aiecs/tools/langchain_adapter.py +542 -0
  252. aiecs/tools/schema_generator.py +275 -0
  253. aiecs/tools/search_tool/__init__.py +100 -0
  254. aiecs/tools/search_tool/analyzers.py +589 -0
  255. aiecs/tools/search_tool/cache.py +260 -0
  256. aiecs/tools/search_tool/constants.py +128 -0
  257. aiecs/tools/search_tool/context.py +216 -0
  258. aiecs/tools/search_tool/core.py +749 -0
  259. aiecs/tools/search_tool/deduplicator.py +123 -0
  260. aiecs/tools/search_tool/error_handler.py +271 -0
  261. aiecs/tools/search_tool/metrics.py +371 -0
  262. aiecs/tools/search_tool/rate_limiter.py +178 -0
  263. aiecs/tools/search_tool/schemas.py +277 -0
  264. aiecs/tools/statistics/__init__.py +80 -0
  265. aiecs/tools/statistics/ai_data_analysis_orchestrator.py +643 -0
  266. aiecs/tools/statistics/ai_insight_generator_tool.py +505 -0
  267. aiecs/tools/statistics/ai_report_orchestrator_tool.py +694 -0
  268. aiecs/tools/statistics/data_loader_tool.py +564 -0
  269. aiecs/tools/statistics/data_profiler_tool.py +658 -0
  270. aiecs/tools/statistics/data_transformer_tool.py +573 -0
  271. aiecs/tools/statistics/data_visualizer_tool.py +495 -0
  272. aiecs/tools/statistics/model_trainer_tool.py +487 -0
  273. aiecs/tools/statistics/statistical_analyzer_tool.py +459 -0
  274. aiecs/tools/task_tools/__init__.py +86 -0
  275. aiecs/tools/task_tools/chart_tool.py +732 -0
  276. aiecs/tools/task_tools/classfire_tool.py +922 -0
  277. aiecs/tools/task_tools/image_tool.py +447 -0
  278. aiecs/tools/task_tools/office_tool.py +684 -0
  279. aiecs/tools/task_tools/pandas_tool.py +635 -0
  280. aiecs/tools/task_tools/report_tool.py +635 -0
  281. aiecs/tools/task_tools/research_tool.py +392 -0
  282. aiecs/tools/task_tools/scraper_tool.py +715 -0
  283. aiecs/tools/task_tools/stats_tool.py +688 -0
  284. aiecs/tools/temp_file_manager.py +130 -0
  285. aiecs/tools/tool_executor/__init__.py +37 -0
  286. aiecs/tools/tool_executor/tool_executor.py +881 -0
  287. aiecs/utils/LLM_output_structor.py +445 -0
  288. aiecs/utils/__init__.py +34 -0
  289. aiecs/utils/base_callback.py +47 -0
  290. aiecs/utils/cache_provider.py +695 -0
  291. aiecs/utils/execution_utils.py +184 -0
  292. aiecs/utils/logging.py +1 -0
  293. aiecs/utils/prompt_loader.py +14 -0
  294. aiecs/utils/token_usage_repository.py +323 -0
  295. aiecs/ws/__init__.py +0 -0
  296. aiecs/ws/socket_server.py +52 -0
  297. aiecs-1.5.1.dist-info/METADATA +608 -0
  298. aiecs-1.5.1.dist-info/RECORD +302 -0
  299. aiecs-1.5.1.dist-info/WHEEL +5 -0
  300. aiecs-1.5.1.dist-info/entry_points.txt +10 -0
  301. aiecs-1.5.1.dist-info/licenses/LICENSE +225 -0
  302. aiecs-1.5.1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,275 @@
1
+ """
2
+ Configuration loader for LLM models.
3
+
4
+ This module provides a singleton configuration loader that loads and manages
5
+ LLM model configurations from YAML files with support for hot-reloading.
6
+ """
7
+
8
+ import logging
9
+ import os
10
+ from pathlib import Path
11
+ from typing import Optional
12
+ import yaml
13
+ from threading import Lock
14
+
15
+ from aiecs.llm.config.model_config import (
16
+ LLMModelsConfig,
17
+ ProviderConfig,
18
+ ModelConfig,
19
+ )
20
+
21
+ logger = logging.getLogger(__name__)
22
+
23
+
24
+ class LLMConfigLoader:
25
+ """
26
+ Singleton configuration loader for LLM models.
27
+
28
+ Supports:
29
+ - Loading configuration from YAML files
30
+ - Hot-reloading (manual refresh)
31
+ - Thread-safe access
32
+ - Caching for performance
33
+ """
34
+
35
+ _instance: Optional["LLMConfigLoader"] = None
36
+ _lock = Lock()
37
+ _config_lock = Lock()
38
+
39
+ def __new__(cls):
40
+ """Ensure singleton instance"""
41
+ if cls._instance is None:
42
+ with cls._lock:
43
+ if cls._instance is None:
44
+ cls._instance = super().__new__(cls)
45
+ cls._instance._initialized = False
46
+ return cls._instance
47
+
48
+ def __init__(self):
49
+ """Initialize the configuration loader"""
50
+ if self._initialized:
51
+ return
52
+
53
+ self._config: Optional[LLMModelsConfig] = None
54
+ self._config_path: Optional[Path] = None
55
+ self._initialized = True
56
+ logger.info("LLMConfigLoader initialized")
57
+
58
+ def _find_config_file(self) -> Path:
59
+ """
60
+ Find the configuration file.
61
+
62
+ Search order:
63
+ 1. Settings llm_models_config_path
64
+ 2. Environment variable LLM_MODELS_CONFIG
65
+ 3. aiecs/config/llm_models.yaml
66
+ 4. config/llm_models.yaml
67
+ """
68
+ # Check settings first
69
+ try:
70
+ from aiecs.config.config import get_settings
71
+
72
+ settings = get_settings()
73
+ if settings.llm_models_config_path:
74
+ path = Path(settings.llm_models_config_path)
75
+ if path.exists():
76
+ logger.info(f"Using LLM config from settings: {path}")
77
+ return path
78
+ else:
79
+ logger.warning(f"Settings llm_models_config_path does not exist: {path}")
80
+ except Exception as e:
81
+ logger.debug(f"Could not load settings: {e}")
82
+
83
+ # Check environment variable
84
+ env_path = os.environ.get("LLM_MODELS_CONFIG")
85
+ if env_path:
86
+ path = Path(env_path)
87
+ if path.exists():
88
+ logger.info(f"Using LLM config from environment: {path}")
89
+ return path
90
+ else:
91
+ logger.warning(f"LLM_MODELS_CONFIG path does not exist: {path}")
92
+
93
+ # Check standard locations
94
+ current_dir = Path(__file__).parent.parent # aiecs/
95
+
96
+ # Try aiecs/config/llm_models.yaml
97
+ config_path1 = current_dir / "config" / "llm_models.yaml"
98
+ if config_path1.exists():
99
+ logger.info(f"Using LLM config from: {config_path1}")
100
+ return config_path1
101
+
102
+ # Try config/llm_models.yaml (relative to project root)
103
+ config_path2 = current_dir.parent / "config" / "llm_models.yaml"
104
+ if config_path2.exists():
105
+ logger.info(f"Using LLM config from: {config_path2}")
106
+ return config_path2
107
+
108
+ # Default to the first path even if it doesn't exist
109
+ logger.warning(f"LLM config file not found, using default path: {config_path1}")
110
+ return config_path1
111
+
112
+ def load_config(self, config_path: Optional[Path] = None) -> LLMModelsConfig:
113
+ """
114
+ Load configuration from YAML file.
115
+
116
+ Args:
117
+ config_path: Optional path to configuration file. If not provided,
118
+ will search in standard locations.
119
+
120
+ Returns:
121
+ LLMModelsConfig: Loaded and validated configuration
122
+
123
+ Raises:
124
+ FileNotFoundError: If config file doesn't exist
125
+ ValueError: If config file is invalid
126
+ """
127
+ with self._config_lock:
128
+ if config_path is None:
129
+ config_path = self._find_config_file()
130
+ else:
131
+ config_path = Path(config_path)
132
+
133
+ if not config_path.exists():
134
+ raise FileNotFoundError(
135
+ f"LLM models configuration file not found: {config_path}\n"
136
+ f"Please create the configuration file or set LLM_MODELS_CONFIG environment variable."
137
+ )
138
+
139
+ try:
140
+ with open(config_path, "r", encoding="utf-8") as f:
141
+ config_data = yaml.safe_load(f)
142
+
143
+ if not config_data:
144
+ raise ValueError("Configuration file is empty")
145
+
146
+ # Validate and parse using Pydantic
147
+ self._config = LLMModelsConfig(**config_data)
148
+ self._config_path = config_path
149
+
150
+ logger.info(
151
+ f"Loaded LLM configuration from {config_path}: "
152
+ f"{len(self._config.providers)} providers, "
153
+ f"{sum(len(p.models) for p in self._config.providers.values())} models"
154
+ )
155
+
156
+ return self._config
157
+
158
+ except yaml.YAMLError as e:
159
+ raise ValueError(f"Invalid YAML in configuration file: {e}")
160
+ except Exception as e:
161
+ raise ValueError(f"Failed to load configuration: {e}")
162
+
163
+ def reload_config(self) -> LLMModelsConfig:
164
+ """
165
+ Reload configuration from the current config file.
166
+
167
+ This supports the hybrid loading mode - configuration is loaded at startup
168
+ but can be manually refreshed without restarting the application.
169
+
170
+ Returns:
171
+ LLMModelsConfig: Reloaded configuration
172
+ """
173
+ logger.info("Reloading LLM configuration...")
174
+ return self.load_config(self._config_path)
175
+
176
+ def get_config(self) -> LLMModelsConfig:
177
+ """
178
+ Get the current configuration.
179
+
180
+ Loads configuration on first access if not already loaded.
181
+
182
+ Returns:
183
+ LLMModelsConfig: Current configuration
184
+ """
185
+ if self._config is None:
186
+ self.load_config()
187
+ return self._config
188
+
189
+ def get_provider_config(self, provider_name: str) -> Optional[ProviderConfig]:
190
+ """
191
+ Get configuration for a specific provider.
192
+
193
+ Args:
194
+ provider_name: Name of the provider (e.g., "Vertex", "OpenAI")
195
+
196
+ Returns:
197
+ ProviderConfig if found, None otherwise
198
+ """
199
+ config = self.get_config()
200
+ return config.get_provider_config(provider_name)
201
+
202
+ def get_model_config(self, provider_name: str, model_name: str) -> Optional[ModelConfig]:
203
+ """
204
+ Get configuration for a specific model.
205
+
206
+ Args:
207
+ provider_name: Name of the provider
208
+ model_name: Name of the model (or alias)
209
+
210
+ Returns:
211
+ ModelConfig if found, None otherwise
212
+ """
213
+ config = self.get_config()
214
+ return config.get_model_config(provider_name, model_name)
215
+
216
+ def get_default_model(self, provider_name: str) -> Optional[str]:
217
+ """
218
+ Get the default model name for a provider.
219
+
220
+ Args:
221
+ provider_name: Name of the provider
222
+
223
+ Returns:
224
+ Default model name if found, None otherwise
225
+ """
226
+ provider_config = self.get_provider_config(provider_name)
227
+ if provider_config:
228
+ return provider_config.default_model
229
+ return None
230
+
231
+ def is_loaded(self) -> bool:
232
+ """Check if configuration has been loaded"""
233
+ return self._config is not None
234
+
235
+ def get_config_path(self) -> Optional[Path]:
236
+ """Get the path to the current configuration file"""
237
+ return self._config_path
238
+
239
+
240
+ # Global singleton instance
241
+ _loader = LLMConfigLoader()
242
+
243
+
244
+ def get_llm_config_loader() -> LLMConfigLoader:
245
+ """
246
+ Get the global LLM configuration loader instance.
247
+
248
+ Returns:
249
+ LLMConfigLoader: Global singleton instance
250
+ """
251
+ return _loader
252
+
253
+
254
+ def get_llm_config() -> LLMModelsConfig:
255
+ """
256
+ Get the current LLM configuration.
257
+
258
+ Convenience function that returns the configuration from the global loader.
259
+
260
+ Returns:
261
+ LLMModelsConfig: Current configuration
262
+ """
263
+ return _loader.get_config()
264
+
265
+
266
+ def reload_llm_config() -> LLMModelsConfig:
267
+ """
268
+ Reload the LLM configuration.
269
+
270
+ Convenience function that reloads the configuration in the global loader.
271
+
272
+ Returns:
273
+ LLMModelsConfig: Reloaded configuration
274
+ """
275
+ return _loader.reload_config()
@@ -0,0 +1,236 @@
1
+ """
2
+ Configuration validation utilities for LLM models.
3
+
4
+ This module provides validation functions to ensure configuration integrity
5
+ and provide helpful error messages.
6
+ """
7
+
8
+ import logging
9
+ from typing import List, Tuple
10
+
11
+ from aiecs.llm.config.model_config import (
12
+ LLMModelsConfig,
13
+ ProviderConfig,
14
+ ModelConfig,
15
+ ModelCostConfig,
16
+ )
17
+
18
+ logger = logging.getLogger(__name__)
19
+
20
+
21
+ class ConfigValidationError(Exception):
22
+ """Raised when configuration validation fails"""
23
+
24
+
25
+ def validate_cost_config(cost_config: ModelCostConfig, model_name: str) -> List[str]:
26
+ """
27
+ Validate cost configuration.
28
+
29
+ Args:
30
+ cost_config: Cost configuration to validate
31
+ model_name: Name of the model (for error messages)
32
+
33
+ Returns:
34
+ List of warning messages (empty if valid)
35
+ """
36
+ warnings = []
37
+
38
+ if cost_config.input < 0:
39
+ raise ConfigValidationError(
40
+ f"Model '{model_name}': Input cost must be non-negative, got {cost_config.input}"
41
+ )
42
+
43
+ if cost_config.output < 0:
44
+ raise ConfigValidationError(
45
+ f"Model '{model_name}': Output cost must be non-negative, got {cost_config.output}"
46
+ )
47
+
48
+ # Warn if costs are zero (might be intentional for free tiers or unknown
49
+ # pricing)
50
+ if cost_config.input == 0 and cost_config.output == 0:
51
+ warnings.append(f"Model '{model_name}': Both input and output costs are 0")
52
+
53
+ return warnings
54
+
55
+
56
+ def validate_model_config(model_config: ModelConfig) -> List[str]:
57
+ """
58
+ Validate a single model configuration.
59
+
60
+ Args:
61
+ model_config: Model configuration to validate
62
+
63
+ Returns:
64
+ List of warning messages (empty if valid)
65
+
66
+ Raises:
67
+ ConfigValidationError: If validation fails
68
+ """
69
+ warnings = []
70
+
71
+ # Validate name
72
+ if not model_config.name or not model_config.name.strip():
73
+ raise ConfigValidationError("Model name cannot be empty")
74
+
75
+ # Validate costs
76
+ cost_warnings = validate_cost_config(model_config.costs, model_config.name)
77
+ warnings.extend(cost_warnings)
78
+
79
+ # Validate capabilities
80
+ if model_config.capabilities.max_tokens <= 0:
81
+ raise ConfigValidationError(
82
+ f"Model '{model_config.name}': max_tokens must be positive, "
83
+ f"got {model_config.capabilities.max_tokens}"
84
+ )
85
+
86
+ if model_config.capabilities.context_window <= 0:
87
+ raise ConfigValidationError(
88
+ f"Model '{model_config.name}': context_window must be positive, "
89
+ f"got {model_config.capabilities.context_window}"
90
+ )
91
+
92
+ # Validate default params
93
+ if not (0.0 <= model_config.default_params.temperature <= 2.0):
94
+ raise ConfigValidationError(
95
+ f"Model '{model_config.name}': temperature must be between 0.0 and 2.0, "
96
+ f"got {model_config.default_params.temperature}"
97
+ )
98
+
99
+ if model_config.default_params.max_tokens <= 0:
100
+ raise ConfigValidationError(
101
+ f"Model '{model_config.name}': default max_tokens must be positive, "
102
+ f"got {model_config.default_params.max_tokens}"
103
+ )
104
+
105
+ # Warn if default max_tokens exceeds capability max_tokens
106
+ if model_config.default_params.max_tokens > model_config.capabilities.max_tokens:
107
+ warnings.append(
108
+ f"Model '{model_config.name}': default max_tokens ({model_config.default_params.max_tokens}) "
109
+ f"exceeds capability max_tokens ({model_config.capabilities.max_tokens})"
110
+ )
111
+
112
+ return warnings
113
+
114
+
115
+ def validate_provider_config(provider_config: ProviderConfig) -> List[str]:
116
+ """
117
+ Validate a provider configuration.
118
+
119
+ Args:
120
+ provider_config: Provider configuration to validate
121
+
122
+ Returns:
123
+ List of warning messages (empty if valid)
124
+
125
+ Raises:
126
+ ConfigValidationError: If validation fails
127
+ """
128
+ warnings = []
129
+
130
+ # Validate provider name
131
+ if not provider_config.provider_name or not provider_config.provider_name.strip():
132
+ raise ConfigValidationError("Provider name cannot be empty")
133
+
134
+ # Validate models list
135
+ if not provider_config.models:
136
+ raise ConfigValidationError(
137
+ f"Provider '{provider_config.provider_name}': Must have at least one model"
138
+ )
139
+
140
+ # Validate default model exists
141
+ model_names = provider_config.get_model_names()
142
+ if provider_config.default_model not in model_names:
143
+ raise ConfigValidationError(
144
+ f"Provider '{provider_config.provider_name}': Default model '{provider_config.default_model}' "
145
+ f"not found in models list: {model_names}"
146
+ )
147
+
148
+ # Validate each model
149
+ for model in provider_config.models:
150
+ model_warnings = validate_model_config(model)
151
+ warnings.extend(model_warnings)
152
+
153
+ # Check for duplicate model names
154
+ if len(model_names) != len(set(model_names)):
155
+ duplicates = [name for name in model_names if model_names.count(name) > 1]
156
+ raise ConfigValidationError(
157
+ f"Provider '{provider_config.provider_name}': Duplicate model names found: {set(duplicates)}"
158
+ )
159
+
160
+ # Validate model mappings if present
161
+ if provider_config.model_mappings:
162
+ for alias, target in provider_config.model_mappings.items():
163
+ if target not in model_names:
164
+ raise ConfigValidationError(
165
+ f"Provider '{provider_config.provider_name}': Model mapping alias '{alias}' "
166
+ f"points to non-existent model '{target}'. Available models: {model_names}"
167
+ )
168
+
169
+ # Warn if alias is the same as target
170
+ if alias == target:
171
+ warnings.append(
172
+ f"Provider '{provider_config.provider_name}': Model mapping has redundant entry "
173
+ f"'{alias}' -> '{target}'"
174
+ )
175
+
176
+ return warnings
177
+
178
+
179
+ def validate_llm_config(config: LLMModelsConfig) -> Tuple[bool, List[str]]:
180
+ """
181
+ Validate the entire LLM configuration.
182
+
183
+ Args:
184
+ config: Complete LLM configuration to validate
185
+
186
+ Returns:
187
+ Tuple of (is_valid, warnings_list)
188
+
189
+ Raises:
190
+ ConfigValidationError: If validation fails critically
191
+ """
192
+ warnings = []
193
+
194
+ # Validate providers exist
195
+ if not config.providers:
196
+ raise ConfigValidationError("Configuration must have at least one provider")
197
+
198
+ # Validate each provider
199
+ for provider_name, provider_config in config.providers.items():
200
+ try:
201
+ provider_warnings = validate_provider_config(provider_config)
202
+ warnings.extend(provider_warnings)
203
+ except ConfigValidationError as e:
204
+ raise ConfigValidationError(f"Provider '{provider_name}': {e}")
205
+
206
+ # Log warnings
207
+ if warnings:
208
+ logger.warning(f"Configuration validation completed with {len(warnings)} warnings:")
209
+ for warning in warnings:
210
+ logger.warning(f" - {warning}")
211
+ else:
212
+ logger.info("Configuration validation completed successfully with no warnings")
213
+
214
+ return True, warnings
215
+
216
+
217
+ def validate_config_file(config_path: str) -> Tuple[bool, List[str]]:
218
+ """
219
+ Validate a configuration file.
220
+
221
+ Args:
222
+ config_path: Path to the configuration file
223
+
224
+ Returns:
225
+ Tuple of (is_valid, warnings_list)
226
+
227
+ Raises:
228
+ ConfigValidationError: If validation fails
229
+ FileNotFoundError: If file doesn't exist
230
+ """
231
+ from aiecs.llm.config.config_loader import LLMConfigLoader
232
+
233
+ loader = LLMConfigLoader()
234
+ config = loader.load_config(config_path)
235
+
236
+ return validate_llm_config(config)
@@ -0,0 +1,151 @@
1
+ """
2
+ Pydantic models for LLM configuration management.
3
+
4
+ This module defines the configuration schema for all LLM providers and models,
5
+ enabling centralized, type-safe configuration management.
6
+ """
7
+
8
+ from typing import Dict, Optional, List
9
+ from pydantic import BaseModel, Field, field_validator
10
+
11
+
12
+ class ModelCostConfig(BaseModel):
13
+ """Token cost configuration for a model"""
14
+
15
+ input: float = Field(ge=0, description="Cost per 1K input tokens in USD")
16
+ output: float = Field(ge=0, description="Cost per 1K output tokens in USD")
17
+
18
+ @field_validator("input", "output")
19
+ @classmethod
20
+ def validate_positive(cls, v: float) -> float:
21
+ """Ensure costs are non-negative"""
22
+ if v < 0:
23
+ raise ValueError("Cost must be non-negative")
24
+ return v
25
+
26
+
27
+ class ModelCapabilities(BaseModel):
28
+ """Capabilities and limits for a model"""
29
+
30
+ streaming: bool = Field(default=True, description="Whether the model supports streaming")
31
+ vision: bool = Field(
32
+ default=False,
33
+ description="Whether the model supports vision/image input",
34
+ )
35
+ function_calling: bool = Field(
36
+ default=False,
37
+ description="Whether the model supports function calling",
38
+ )
39
+ max_tokens: int = Field(default=8192, ge=1, description="Maximum output tokens")
40
+ context_window: int = Field(default=128000, ge=1, description="Maximum context window size")
41
+
42
+
43
+ class ModelDefaultParams(BaseModel):
44
+ """Default parameters for model inference"""
45
+
46
+ temperature: float = Field(default=0.7, ge=0.0, le=2.0, description="Default temperature")
47
+ max_tokens: int = Field(default=8192, ge=1, description="Default max output tokens")
48
+ top_p: float = Field(default=0.95, ge=0.0, le=1.0, description="Default top_p")
49
+ top_k: int = Field(default=40, ge=0, description="Default top_k")
50
+
51
+
52
+ class ModelConfig(BaseModel):
53
+ """Complete configuration for a single model"""
54
+
55
+ name: str = Field(description="Model identifier")
56
+ display_name: Optional[str] = Field(default=None, description="Human-readable model name")
57
+ costs: ModelCostConfig = Field(description="Token cost configuration")
58
+ capabilities: ModelCapabilities = Field(
59
+ default_factory=ModelCapabilities, description="Model capabilities"
60
+ )
61
+ default_params: ModelDefaultParams = Field(
62
+ default_factory=ModelDefaultParams, description="Default parameters"
63
+ )
64
+ description: Optional[str] = Field(default=None, description="Model description")
65
+
66
+ def __init__(self, **data):
67
+ super().__init__(**data)
68
+ # Set display_name to name if not provided
69
+ if self.display_name is None:
70
+ self.display_name = self.name
71
+
72
+
73
+ class ProviderConfig(BaseModel):
74
+ """Configuration for a single LLM provider"""
75
+
76
+ provider_name: str = Field(description="Provider identifier")
77
+ default_model: str = Field(description="Default model for this provider")
78
+ models: List[ModelConfig] = Field(description="List of available models")
79
+ model_mappings: Optional[Dict[str, str]] = Field(
80
+ default=None,
81
+ description="Model name aliases (e.g., 'Grok 4' -> 'grok-4')",
82
+ )
83
+
84
+ @field_validator("models")
85
+ @classmethod
86
+ def validate_models_not_empty(cls, v: List[ModelConfig]) -> List[ModelConfig]:
87
+ """Ensure at least one model is configured"""
88
+ if not v:
89
+ raise ValueError("Provider must have at least one model configured")
90
+ return v
91
+
92
+ def get_model_config(self, model_name: str) -> Optional[ModelConfig]:
93
+ """Get configuration for a specific model"""
94
+ # First, check if this is an alias
95
+ if self.model_mappings and model_name in self.model_mappings:
96
+ model_name = self.model_mappings[model_name]
97
+
98
+ # Find the model configuration
99
+ for model in self.models:
100
+ if model.name == model_name:
101
+ return model
102
+ return None
103
+
104
+ def get_model_names(self) -> List[str]:
105
+ """Get list of all model names"""
106
+ return [model.name for model in self.models]
107
+
108
+ def get_all_model_names_with_aliases(self) -> List[str]:
109
+ """Get list of all model names including aliases"""
110
+ names = self.get_model_names()
111
+ if self.model_mappings:
112
+ names.extend(list(self.model_mappings.keys()))
113
+ return names
114
+
115
+
116
+ class LLMModelsConfig(BaseModel):
117
+ """Root configuration containing all providers"""
118
+
119
+ providers: Dict[str, ProviderConfig] = Field(
120
+ description="Provider configurations keyed by provider name"
121
+ )
122
+
123
+ @field_validator("providers")
124
+ @classmethod
125
+ def validate_providers_not_empty(
126
+ cls, v: Dict[str, ProviderConfig]
127
+ ) -> Dict[str, ProviderConfig]:
128
+ """Ensure at least one provider is configured"""
129
+ if not v:
130
+ raise ValueError("At least one provider must be configured")
131
+ return v
132
+
133
+ def get_provider_config(self, provider_name: str) -> Optional[ProviderConfig]:
134
+ """Get configuration for a specific provider"""
135
+ # Normalize provider name (case-insensitive lookup)
136
+ provider_name_lower = provider_name.lower()
137
+ for key, config in self.providers.items():
138
+ if key.lower() == provider_name_lower:
139
+ return config
140
+ return None
141
+
142
+ def get_model_config(self, provider_name: str, model_name: str) -> Optional[ModelConfig]:
143
+ """Get configuration for a specific model from a provider"""
144
+ provider_config = self.get_provider_config(provider_name)
145
+ if provider_config:
146
+ return provider_config.get_model_config(model_name)
147
+ return None
148
+
149
+ def get_provider_names(self) -> List[str]:
150
+ """Get list of all provider names"""
151
+ return list(self.providers.keys())
@@ -0,0 +1,10 @@
1
+ """
2
+ LLM Utilities.
3
+
4
+ This package contains utility functions and scripts for LLM operations.
5
+ """
6
+
7
+ # Validation script is executable and doesn't need to be imported
8
+ # It can be run as: python -m aiecs.llm.utils.validate_config
9
+
10
+ __all__ = []