aiecs 1.0.1__py3-none-any.whl → 1.7.17__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 +13 -16
- aiecs/__main__.py +7 -7
- aiecs/aiecs_client.py +269 -75
- aiecs/application/executors/operation_executor.py +79 -54
- aiecs/application/knowledge_graph/__init__.py +7 -0
- aiecs/application/knowledge_graph/builder/__init__.py +37 -0
- aiecs/application/knowledge_graph/builder/data_quality.py +302 -0
- aiecs/application/knowledge_graph/builder/data_reshaping.py +293 -0
- aiecs/application/knowledge_graph/builder/document_builder.py +369 -0
- aiecs/application/knowledge_graph/builder/graph_builder.py +490 -0
- aiecs/application/knowledge_graph/builder/import_optimizer.py +396 -0
- aiecs/application/knowledge_graph/builder/schema_inference.py +462 -0
- aiecs/application/knowledge_graph/builder/schema_mapping.py +563 -0
- aiecs/application/knowledge_graph/builder/structured_pipeline.py +1384 -0
- aiecs/application/knowledge_graph/builder/text_chunker.py +317 -0
- aiecs/application/knowledge_graph/extractors/__init__.py +27 -0
- aiecs/application/knowledge_graph/extractors/base.py +98 -0
- aiecs/application/knowledge_graph/extractors/llm_entity_extractor.py +422 -0
- aiecs/application/knowledge_graph/extractors/llm_relation_extractor.py +347 -0
- aiecs/application/knowledge_graph/extractors/ner_entity_extractor.py +241 -0
- aiecs/application/knowledge_graph/fusion/__init__.py +78 -0
- aiecs/application/knowledge_graph/fusion/ab_testing.py +395 -0
- aiecs/application/knowledge_graph/fusion/abbreviation_expander.py +327 -0
- aiecs/application/knowledge_graph/fusion/alias_index.py +597 -0
- aiecs/application/knowledge_graph/fusion/alias_matcher.py +384 -0
- aiecs/application/knowledge_graph/fusion/cache_coordinator.py +343 -0
- aiecs/application/knowledge_graph/fusion/entity_deduplicator.py +433 -0
- aiecs/application/knowledge_graph/fusion/entity_linker.py +511 -0
- aiecs/application/knowledge_graph/fusion/evaluation_dataset.py +240 -0
- aiecs/application/knowledge_graph/fusion/knowledge_fusion.py +632 -0
- aiecs/application/knowledge_graph/fusion/matching_config.py +489 -0
- aiecs/application/knowledge_graph/fusion/name_normalizer.py +352 -0
- aiecs/application/knowledge_graph/fusion/relation_deduplicator.py +183 -0
- aiecs/application/knowledge_graph/fusion/semantic_name_matcher.py +464 -0
- aiecs/application/knowledge_graph/fusion/similarity_pipeline.py +534 -0
- aiecs/application/knowledge_graph/pattern_matching/__init__.py +21 -0
- aiecs/application/knowledge_graph/pattern_matching/pattern_matcher.py +342 -0
- aiecs/application/knowledge_graph/pattern_matching/query_executor.py +366 -0
- aiecs/application/knowledge_graph/profiling/__init__.py +12 -0
- aiecs/application/knowledge_graph/profiling/query_plan_visualizer.py +195 -0
- aiecs/application/knowledge_graph/profiling/query_profiler.py +223 -0
- aiecs/application/knowledge_graph/reasoning/__init__.py +27 -0
- aiecs/application/knowledge_graph/reasoning/evidence_synthesis.py +341 -0
- aiecs/application/knowledge_graph/reasoning/inference_engine.py +500 -0
- aiecs/application/knowledge_graph/reasoning/logic_form_parser.py +163 -0
- aiecs/application/knowledge_graph/reasoning/logic_parser/__init__.py +79 -0
- aiecs/application/knowledge_graph/reasoning/logic_parser/ast_builder.py +513 -0
- aiecs/application/knowledge_graph/reasoning/logic_parser/ast_nodes.py +913 -0
- aiecs/application/knowledge_graph/reasoning/logic_parser/ast_validator.py +866 -0
- aiecs/application/knowledge_graph/reasoning/logic_parser/error_handler.py +475 -0
- aiecs/application/knowledge_graph/reasoning/logic_parser/parser.py +396 -0
- aiecs/application/knowledge_graph/reasoning/logic_parser/query_context.py +208 -0
- aiecs/application/knowledge_graph/reasoning/logic_query_integration.py +170 -0
- aiecs/application/knowledge_graph/reasoning/query_planner.py +855 -0
- aiecs/application/knowledge_graph/reasoning/reasoning_engine.py +518 -0
- aiecs/application/knowledge_graph/retrieval/__init__.py +27 -0
- aiecs/application/knowledge_graph/retrieval/query_intent_classifier.py +211 -0
- aiecs/application/knowledge_graph/retrieval/retrieval_strategies.py +592 -0
- aiecs/application/knowledge_graph/retrieval/strategy_types.py +23 -0
- aiecs/application/knowledge_graph/search/__init__.py +59 -0
- aiecs/application/knowledge_graph/search/hybrid_search.py +457 -0
- aiecs/application/knowledge_graph/search/reranker.py +293 -0
- aiecs/application/knowledge_graph/search/reranker_strategies.py +535 -0
- aiecs/application/knowledge_graph/search/text_similarity.py +392 -0
- aiecs/application/knowledge_graph/traversal/__init__.py +15 -0
- aiecs/application/knowledge_graph/traversal/enhanced_traversal.py +305 -0
- aiecs/application/knowledge_graph/traversal/path_scorer.py +271 -0
- aiecs/application/knowledge_graph/validators/__init__.py +13 -0
- aiecs/application/knowledge_graph/validators/relation_validator.py +239 -0
- aiecs/application/knowledge_graph/visualization/__init__.py +11 -0
- aiecs/application/knowledge_graph/visualization/graph_visualizer.py +313 -0
- aiecs/common/__init__.py +9 -0
- aiecs/common/knowledge_graph/__init__.py +17 -0
- aiecs/common/knowledge_graph/runnable.py +471 -0
- aiecs/config/__init__.py +20 -5
- aiecs/config/config.py +762 -31
- aiecs/config/graph_config.py +131 -0
- aiecs/config/tool_config.py +435 -0
- aiecs/core/__init__.py +29 -13
- aiecs/core/interface/__init__.py +2 -2
- aiecs/core/interface/execution_interface.py +22 -22
- aiecs/core/interface/storage_interface.py +37 -88
- aiecs/core/registry/__init__.py +31 -0
- aiecs/core/registry/service_registry.py +92 -0
- aiecs/domain/__init__.py +270 -1
- aiecs/domain/agent/__init__.py +191 -0
- aiecs/domain/agent/base_agent.py +3949 -0
- aiecs/domain/agent/exceptions.py +99 -0
- aiecs/domain/agent/graph_aware_mixin.py +569 -0
- aiecs/domain/agent/hybrid_agent.py +1731 -0
- aiecs/domain/agent/integration/__init__.py +29 -0
- aiecs/domain/agent/integration/context_compressor.py +216 -0
- aiecs/domain/agent/integration/context_engine_adapter.py +587 -0
- aiecs/domain/agent/integration/protocols.py +281 -0
- aiecs/domain/agent/integration/retry_policy.py +218 -0
- aiecs/domain/agent/integration/role_config.py +213 -0
- aiecs/domain/agent/knowledge_aware_agent.py +1892 -0
- aiecs/domain/agent/lifecycle.py +291 -0
- aiecs/domain/agent/llm_agent.py +692 -0
- aiecs/domain/agent/memory/__init__.py +12 -0
- aiecs/domain/agent/memory/conversation.py +1124 -0
- aiecs/domain/agent/migration/__init__.py +14 -0
- aiecs/domain/agent/migration/conversion.py +163 -0
- aiecs/domain/agent/migration/legacy_wrapper.py +86 -0
- aiecs/domain/agent/models.py +894 -0
- aiecs/domain/agent/observability.py +479 -0
- aiecs/domain/agent/persistence.py +449 -0
- aiecs/domain/agent/prompts/__init__.py +29 -0
- aiecs/domain/agent/prompts/builder.py +159 -0
- aiecs/domain/agent/prompts/formatters.py +187 -0
- aiecs/domain/agent/prompts/template.py +255 -0
- aiecs/domain/agent/registry.py +253 -0
- aiecs/domain/agent/tool_agent.py +444 -0
- aiecs/domain/agent/tools/__init__.py +15 -0
- aiecs/domain/agent/tools/schema_generator.py +377 -0
- aiecs/domain/community/__init__.py +155 -0
- aiecs/domain/community/agent_adapter.py +469 -0
- aiecs/domain/community/analytics.py +432 -0
- aiecs/domain/community/collaborative_workflow.py +648 -0
- aiecs/domain/community/communication_hub.py +634 -0
- aiecs/domain/community/community_builder.py +320 -0
- aiecs/domain/community/community_integration.py +796 -0
- aiecs/domain/community/community_manager.py +803 -0
- aiecs/domain/community/decision_engine.py +849 -0
- aiecs/domain/community/exceptions.py +231 -0
- aiecs/domain/community/models/__init__.py +33 -0
- aiecs/domain/community/models/community_models.py +234 -0
- aiecs/domain/community/resource_manager.py +461 -0
- aiecs/domain/community/shared_context_manager.py +589 -0
- aiecs/domain/context/__init__.py +40 -10
- aiecs/domain/context/context_engine.py +1910 -0
- aiecs/domain/context/conversation_models.py +87 -53
- aiecs/domain/context/graph_memory.py +582 -0
- aiecs/domain/execution/model.py +12 -4
- aiecs/domain/knowledge_graph/__init__.py +19 -0
- aiecs/domain/knowledge_graph/models/__init__.py +52 -0
- aiecs/domain/knowledge_graph/models/entity.py +148 -0
- aiecs/domain/knowledge_graph/models/evidence.py +178 -0
- aiecs/domain/knowledge_graph/models/inference_rule.py +184 -0
- aiecs/domain/knowledge_graph/models/path.py +171 -0
- aiecs/domain/knowledge_graph/models/path_pattern.py +171 -0
- aiecs/domain/knowledge_graph/models/query.py +261 -0
- aiecs/domain/knowledge_graph/models/query_plan.py +181 -0
- aiecs/domain/knowledge_graph/models/relation.py +202 -0
- aiecs/domain/knowledge_graph/schema/__init__.py +23 -0
- aiecs/domain/knowledge_graph/schema/entity_type.py +131 -0
- aiecs/domain/knowledge_graph/schema/graph_schema.py +253 -0
- aiecs/domain/knowledge_graph/schema/property_schema.py +143 -0
- aiecs/domain/knowledge_graph/schema/relation_type.py +163 -0
- aiecs/domain/knowledge_graph/schema/schema_manager.py +691 -0
- aiecs/domain/knowledge_graph/schema/type_enums.py +209 -0
- aiecs/domain/task/dsl_processor.py +172 -56
- aiecs/domain/task/model.py +20 -8
- aiecs/domain/task/task_context.py +27 -24
- aiecs/infrastructure/__init__.py +0 -2
- aiecs/infrastructure/graph_storage/__init__.py +11 -0
- aiecs/infrastructure/graph_storage/base.py +837 -0
- aiecs/infrastructure/graph_storage/batch_operations.py +458 -0
- aiecs/infrastructure/graph_storage/cache.py +424 -0
- aiecs/infrastructure/graph_storage/distributed.py +223 -0
- aiecs/infrastructure/graph_storage/error_handling.py +380 -0
- aiecs/infrastructure/graph_storage/graceful_degradation.py +294 -0
- aiecs/infrastructure/graph_storage/health_checks.py +378 -0
- aiecs/infrastructure/graph_storage/in_memory.py +1197 -0
- aiecs/infrastructure/graph_storage/index_optimization.py +446 -0
- aiecs/infrastructure/graph_storage/lazy_loading.py +431 -0
- aiecs/infrastructure/graph_storage/metrics.py +344 -0
- aiecs/infrastructure/graph_storage/migration.py +400 -0
- aiecs/infrastructure/graph_storage/pagination.py +483 -0
- aiecs/infrastructure/graph_storage/performance_monitoring.py +456 -0
- aiecs/infrastructure/graph_storage/postgres.py +1563 -0
- aiecs/infrastructure/graph_storage/property_storage.py +353 -0
- aiecs/infrastructure/graph_storage/protocols.py +76 -0
- aiecs/infrastructure/graph_storage/query_optimizer.py +642 -0
- aiecs/infrastructure/graph_storage/schema_cache.py +290 -0
- aiecs/infrastructure/graph_storage/sqlite.py +1373 -0
- aiecs/infrastructure/graph_storage/streaming.py +487 -0
- aiecs/infrastructure/graph_storage/tenant.py +412 -0
- aiecs/infrastructure/messaging/celery_task_manager.py +92 -54
- aiecs/infrastructure/messaging/websocket_manager.py +51 -35
- aiecs/infrastructure/monitoring/__init__.py +22 -0
- aiecs/infrastructure/monitoring/executor_metrics.py +45 -11
- aiecs/infrastructure/monitoring/global_metrics_manager.py +212 -0
- aiecs/infrastructure/monitoring/structured_logger.py +3 -7
- aiecs/infrastructure/monitoring/tracing_manager.py +63 -35
- aiecs/infrastructure/persistence/__init__.py +14 -1
- aiecs/infrastructure/persistence/context_engine_client.py +184 -0
- aiecs/infrastructure/persistence/database_manager.py +67 -43
- aiecs/infrastructure/persistence/file_storage.py +180 -103
- aiecs/infrastructure/persistence/redis_client.py +74 -21
- aiecs/llm/__init__.py +73 -25
- aiecs/llm/callbacks/__init__.py +11 -0
- aiecs/llm/{custom_callbacks.py → callbacks/custom_callbacks.py} +26 -19
- aiecs/llm/client_factory.py +230 -37
- aiecs/llm/client_resolver.py +155 -0
- aiecs/llm/clients/__init__.py +38 -0
- aiecs/llm/clients/base_client.py +328 -0
- aiecs/llm/clients/google_function_calling_mixin.py +415 -0
- aiecs/llm/clients/googleai_client.py +314 -0
- aiecs/llm/clients/openai_client.py +158 -0
- aiecs/llm/clients/openai_compatible_mixin.py +367 -0
- aiecs/llm/clients/vertex_client.py +1186 -0
- aiecs/llm/clients/xai_client.py +201 -0
- aiecs/llm/config/__init__.py +51 -0
- aiecs/llm/config/config_loader.py +272 -0
- aiecs/llm/config/config_validator.py +206 -0
- aiecs/llm/config/model_config.py +143 -0
- aiecs/llm/protocols.py +149 -0
- aiecs/llm/utils/__init__.py +10 -0
- aiecs/llm/utils/validate_config.py +89 -0
- aiecs/main.py +140 -121
- aiecs/scripts/aid/VERSION_MANAGEMENT.md +138 -0
- aiecs/scripts/aid/__init__.py +19 -0
- aiecs/scripts/aid/module_checker.py +499 -0
- aiecs/scripts/aid/version_manager.py +235 -0
- aiecs/scripts/{DEPENDENCY_SYSTEM_SUMMARY.md → dependance_check/DEPENDENCY_SYSTEM_SUMMARY.md} +1 -0
- aiecs/scripts/{README_DEPENDENCY_CHECKER.md → dependance_check/README_DEPENDENCY_CHECKER.md} +1 -0
- aiecs/scripts/dependance_check/__init__.py +15 -0
- aiecs/scripts/dependance_check/dependency_checker.py +1835 -0
- aiecs/scripts/{dependency_fixer.py → dependance_check/dependency_fixer.py} +192 -90
- aiecs/scripts/{download_nlp_data.py → dependance_check/download_nlp_data.py} +203 -71
- aiecs/scripts/dependance_patch/__init__.py +7 -0
- aiecs/scripts/dependance_patch/fix_weasel/__init__.py +11 -0
- aiecs/scripts/{fix_weasel_validator.py → dependance_patch/fix_weasel/fix_weasel_validator.py} +21 -14
- aiecs/scripts/{patch_weasel_library.sh → dependance_patch/fix_weasel/patch_weasel_library.sh} +1 -1
- aiecs/scripts/knowledge_graph/__init__.py +3 -0
- aiecs/scripts/knowledge_graph/run_threshold_experiments.py +212 -0
- aiecs/scripts/migrations/multi_tenancy/README.md +142 -0
- aiecs/scripts/tools_develop/README.md +671 -0
- aiecs/scripts/tools_develop/README_CONFIG_CHECKER.md +273 -0
- aiecs/scripts/tools_develop/TOOLS_CONFIG_GUIDE.md +1287 -0
- aiecs/scripts/tools_develop/TOOL_AUTO_DISCOVERY.md +234 -0
- aiecs/scripts/tools_develop/__init__.py +21 -0
- aiecs/scripts/tools_develop/check_all_tools_config.py +548 -0
- aiecs/scripts/tools_develop/check_type_annotations.py +257 -0
- aiecs/scripts/tools_develop/pre-commit-schema-coverage.sh +66 -0
- aiecs/scripts/tools_develop/schema_coverage.py +511 -0
- aiecs/scripts/tools_develop/validate_tool_schemas.py +475 -0
- aiecs/scripts/tools_develop/verify_executor_config_fix.py +98 -0
- aiecs/scripts/tools_develop/verify_tools.py +352 -0
- aiecs/tasks/__init__.py +0 -1
- aiecs/tasks/worker.py +115 -47
- aiecs/tools/__init__.py +194 -72
- aiecs/tools/apisource/__init__.py +99 -0
- aiecs/tools/apisource/intelligence/__init__.py +19 -0
- aiecs/tools/apisource/intelligence/data_fusion.py +632 -0
- aiecs/tools/apisource/intelligence/query_analyzer.py +417 -0
- aiecs/tools/apisource/intelligence/search_enhancer.py +385 -0
- aiecs/tools/apisource/monitoring/__init__.py +9 -0
- aiecs/tools/apisource/monitoring/metrics.py +330 -0
- aiecs/tools/apisource/providers/__init__.py +112 -0
- aiecs/tools/apisource/providers/base.py +671 -0
- aiecs/tools/apisource/providers/census.py +397 -0
- aiecs/tools/apisource/providers/fred.py +535 -0
- aiecs/tools/apisource/providers/newsapi.py +409 -0
- aiecs/tools/apisource/providers/worldbank.py +352 -0
- aiecs/tools/apisource/reliability/__init__.py +12 -0
- aiecs/tools/apisource/reliability/error_handler.py +363 -0
- aiecs/tools/apisource/reliability/fallback_strategy.py +376 -0
- aiecs/tools/apisource/tool.py +832 -0
- aiecs/tools/apisource/utils/__init__.py +9 -0
- aiecs/tools/apisource/utils/validators.py +334 -0
- aiecs/tools/base_tool.py +415 -21
- aiecs/tools/docs/__init__.py +121 -0
- aiecs/tools/docs/ai_document_orchestrator.py +607 -0
- aiecs/tools/docs/ai_document_writer_orchestrator.py +2350 -0
- aiecs/tools/docs/content_insertion_tool.py +1320 -0
- aiecs/tools/docs/document_creator_tool.py +1464 -0
- aiecs/tools/docs/document_layout_tool.py +1160 -0
- aiecs/tools/docs/document_parser_tool.py +1016 -0
- aiecs/tools/docs/document_writer_tool.py +2008 -0
- aiecs/tools/knowledge_graph/__init__.py +17 -0
- aiecs/tools/knowledge_graph/graph_reasoning_tool.py +807 -0
- aiecs/tools/knowledge_graph/graph_search_tool.py +944 -0
- aiecs/tools/knowledge_graph/kg_builder_tool.py +524 -0
- aiecs/tools/langchain_adapter.py +300 -138
- aiecs/tools/schema_generator.py +455 -0
- aiecs/tools/search_tool/__init__.py +100 -0
- aiecs/tools/search_tool/analyzers.py +581 -0
- aiecs/tools/search_tool/cache.py +264 -0
- aiecs/tools/search_tool/constants.py +128 -0
- aiecs/tools/search_tool/context.py +224 -0
- aiecs/tools/search_tool/core.py +778 -0
- aiecs/tools/search_tool/deduplicator.py +119 -0
- aiecs/tools/search_tool/error_handler.py +242 -0
- aiecs/tools/search_tool/metrics.py +343 -0
- aiecs/tools/search_tool/rate_limiter.py +172 -0
- aiecs/tools/search_tool/schemas.py +275 -0
- aiecs/tools/statistics/__init__.py +80 -0
- aiecs/tools/statistics/ai_data_analysis_orchestrator.py +646 -0
- aiecs/tools/statistics/ai_insight_generator_tool.py +508 -0
- aiecs/tools/statistics/ai_report_orchestrator_tool.py +684 -0
- aiecs/tools/statistics/data_loader_tool.py +555 -0
- aiecs/tools/statistics/data_profiler_tool.py +638 -0
- aiecs/tools/statistics/data_transformer_tool.py +580 -0
- aiecs/tools/statistics/data_visualizer_tool.py +498 -0
- aiecs/tools/statistics/model_trainer_tool.py +507 -0
- aiecs/tools/statistics/statistical_analyzer_tool.py +472 -0
- aiecs/tools/task_tools/__init__.py +49 -36
- aiecs/tools/task_tools/chart_tool.py +200 -184
- aiecs/tools/task_tools/classfire_tool.py +268 -267
- aiecs/tools/task_tools/image_tool.py +220 -141
- aiecs/tools/task_tools/office_tool.py +226 -146
- aiecs/tools/task_tools/pandas_tool.py +477 -121
- aiecs/tools/task_tools/report_tool.py +390 -142
- aiecs/tools/task_tools/research_tool.py +149 -79
- aiecs/tools/task_tools/scraper_tool.py +339 -145
- aiecs/tools/task_tools/stats_tool.py +448 -209
- aiecs/tools/temp_file_manager.py +26 -24
- aiecs/tools/tool_executor/__init__.py +18 -16
- aiecs/tools/tool_executor/tool_executor.py +364 -52
- aiecs/utils/LLM_output_structor.py +74 -48
- aiecs/utils/__init__.py +14 -3
- aiecs/utils/base_callback.py +0 -3
- aiecs/utils/cache_provider.py +696 -0
- aiecs/utils/execution_utils.py +50 -31
- aiecs/utils/prompt_loader.py +1 -0
- aiecs/utils/token_usage_repository.py +37 -11
- aiecs/ws/socket_server.py +14 -4
- {aiecs-1.0.1.dist-info → aiecs-1.7.17.dist-info}/METADATA +52 -15
- aiecs-1.7.17.dist-info/RECORD +337 -0
- aiecs-1.7.17.dist-info/entry_points.txt +13 -0
- aiecs/config/registry.py +0 -19
- aiecs/domain/context/content_engine.py +0 -982
- aiecs/llm/base_client.py +0 -99
- aiecs/llm/openai_client.py +0 -125
- aiecs/llm/vertex_client.py +0 -186
- aiecs/llm/xai_client.py +0 -184
- aiecs/scripts/dependency_checker.py +0 -857
- aiecs/scripts/quick_dependency_check.py +0 -269
- aiecs/tools/task_tools/search_api.py +0 -7
- aiecs-1.0.1.dist-info/RECORD +0 -90
- aiecs-1.0.1.dist-info/entry_points.txt +0 -7
- /aiecs/scripts/{setup_nlp_data.sh → dependance_check/setup_nlp_data.sh} +0 -0
- /aiecs/scripts/{README_WEASEL_PATCH.md → dependance_patch/fix_weasel/README_WEASEL_PATCH.md} +0 -0
- /aiecs/scripts/{fix_weasel_validator.sh → dependance_patch/fix_weasel/fix_weasel_validator.sh} +0 -0
- /aiecs/scripts/{run_weasel_patch.sh → dependance_patch/fix_weasel/run_weasel_patch.sh} +0 -0
- {aiecs-1.0.1.dist-info → aiecs-1.7.17.dist-info}/WHEEL +0 -0
- {aiecs-1.0.1.dist-info → aiecs-1.7.17.dist-info}/licenses/LICENSE +0 -0
- {aiecs-1.0.1.dist-info → aiecs-1.7.17.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,396 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Logic Query Parser
|
|
3
|
+
|
|
4
|
+
This module provides the LogicQueryParser class for parsing Logic Query DSL
|
|
5
|
+
into Abstract Syntax Trees (AST).
|
|
6
|
+
|
|
7
|
+
The parser uses Lark for syntax parsing and implements two-phase error handling:
|
|
8
|
+
- Phase 1: Syntax parsing (fatal errors from Lark)
|
|
9
|
+
- Phase 2: Semantic validation (accumulated errors from AST)
|
|
10
|
+
|
|
11
|
+
Phase: 2.4 - Logic Query Parser
|
|
12
|
+
Version: 1.0
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from pathlib import Path
|
|
16
|
+
from typing import Union, List, Any, Optional, Dict
|
|
17
|
+
from functools import lru_cache
|
|
18
|
+
|
|
19
|
+
try:
|
|
20
|
+
from lark import (
|
|
21
|
+
Lark,
|
|
22
|
+
LarkError,
|
|
23
|
+
UnexpectedInput,
|
|
24
|
+
UnexpectedToken,
|
|
25
|
+
UnexpectedCharacters,
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
LARK_AVAILABLE = True
|
|
29
|
+
except ImportError:
|
|
30
|
+
LARK_AVAILABLE = False
|
|
31
|
+
from typing import Any, TYPE_CHECKING
|
|
32
|
+
if TYPE_CHECKING:
|
|
33
|
+
Lark: Any # type: ignore[assignment,no-redef]
|
|
34
|
+
LarkError: Any # type: ignore[assignment,no-redef]
|
|
35
|
+
UnexpectedInput: Any # type: ignore[assignment,no-redef]
|
|
36
|
+
UnexpectedToken: Any # type: ignore[assignment,no-redef]
|
|
37
|
+
UnexpectedCharacters: Any # type: ignore[assignment,no-redef]
|
|
38
|
+
else:
|
|
39
|
+
Lark = None # type: ignore[assignment]
|
|
40
|
+
LarkError = Exception # type: ignore[assignment]
|
|
41
|
+
UnexpectedInput = Exception # type: ignore[assignment]
|
|
42
|
+
UnexpectedToken = Exception # type: ignore[assignment]
|
|
43
|
+
UnexpectedCharacters = Exception # type: ignore[assignment]
|
|
44
|
+
|
|
45
|
+
# AST node types imported for type hints in docstrings
|
|
46
|
+
# from .ast_nodes import ASTNode, QueryNode, FindNode, TraversalNode, FilterNode
|
|
47
|
+
from .query_context import QueryContext
|
|
48
|
+
from .ast_builder import ASTBuilder
|
|
49
|
+
from .error_handler import ParserError, ErrorHandler
|
|
50
|
+
|
|
51
|
+
# Import QueryPlan models
|
|
52
|
+
try:
|
|
53
|
+
from aiecs.domain.knowledge_graph.models.query_plan import QueryPlan
|
|
54
|
+
|
|
55
|
+
QUERY_PLAN_AVAILABLE = True
|
|
56
|
+
except ImportError:
|
|
57
|
+
QUERY_PLAN_AVAILABLE = False
|
|
58
|
+
from typing import TYPE_CHECKING
|
|
59
|
+
if TYPE_CHECKING:
|
|
60
|
+
QueryPlan: Any # type: ignore[assignment,no-redef]
|
|
61
|
+
else:
|
|
62
|
+
QueryPlan = None # type: ignore[assignment]
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
class LogicQueryParser:
|
|
66
|
+
"""
|
|
67
|
+
Logic Query DSL Parser
|
|
68
|
+
|
|
69
|
+
Parses Logic Query DSL strings into Abstract Syntax Trees (AST).
|
|
70
|
+
|
|
71
|
+
This class handles:
|
|
72
|
+
1. Syntax parsing (via Lark)
|
|
73
|
+
2. AST building (via Transformer - to be implemented in Task 2.2)
|
|
74
|
+
3. Two-phase error handling (syntax vs semantic)
|
|
75
|
+
|
|
76
|
+
**Thread Safety**: This class is thread-safe. Each parse() call creates
|
|
77
|
+
a fresh QueryContext internally.
|
|
78
|
+
|
|
79
|
+
Example:
|
|
80
|
+
```python
|
|
81
|
+
schema = SchemaManager()
|
|
82
|
+
parser = LogicQueryParser(schema)
|
|
83
|
+
|
|
84
|
+
# Parse a query
|
|
85
|
+
result = parser.parse("Find(Person) WHERE age > 30")
|
|
86
|
+
|
|
87
|
+
if isinstance(result, list):
|
|
88
|
+
# Errors occurred
|
|
89
|
+
for error in result:
|
|
90
|
+
print(f"Error at line {error.line}: {error.message}")
|
|
91
|
+
else:
|
|
92
|
+
# Success - got AST
|
|
93
|
+
ast = result
|
|
94
|
+
print(f"Parsed: {ast}")
|
|
95
|
+
```
|
|
96
|
+
"""
|
|
97
|
+
|
|
98
|
+
def __init__(self, schema: Any = None):
|
|
99
|
+
"""
|
|
100
|
+
Initialize the parser
|
|
101
|
+
|
|
102
|
+
Args:
|
|
103
|
+
schema: SchemaManager instance for validation (optional for now)
|
|
104
|
+
|
|
105
|
+
Raises:
|
|
106
|
+
ImportError: If lark-parser is not installed
|
|
107
|
+
"""
|
|
108
|
+
if not LARK_AVAILABLE:
|
|
109
|
+
raise ImportError("lark-parser is required for LogicQueryParser. " "Install with: pip install lark-parser")
|
|
110
|
+
|
|
111
|
+
self.schema = schema
|
|
112
|
+
|
|
113
|
+
# Load grammar from file
|
|
114
|
+
grammar_path = Path(__file__).parent / "grammar.lark"
|
|
115
|
+
with open(grammar_path, "r") as f:
|
|
116
|
+
grammar_text = f.read()
|
|
117
|
+
|
|
118
|
+
# Create Lark parser with LALR algorithm
|
|
119
|
+
self.lark_parser = Lark(
|
|
120
|
+
grammar_text,
|
|
121
|
+
start="query",
|
|
122
|
+
parser="lalr",
|
|
123
|
+
propagate_positions=True, # Track line/column info
|
|
124
|
+
maybe_placeholders=False,
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
# Create AST builder
|
|
128
|
+
self.ast_builder = ASTBuilder()
|
|
129
|
+
|
|
130
|
+
# Create error handler
|
|
131
|
+
self.error_handler = ErrorHandler()
|
|
132
|
+
|
|
133
|
+
def parse(self, query: str) -> Union[Any, List[ParserError]]:
|
|
134
|
+
"""
|
|
135
|
+
Parse a query string into an AST
|
|
136
|
+
|
|
137
|
+
This method implements two-phase error handling:
|
|
138
|
+
- Phase 1: Syntax parsing (fatal - stops at first error)
|
|
139
|
+
- Phase 2: Semantic validation (accumulated - returns all errors)
|
|
140
|
+
|
|
141
|
+
Args:
|
|
142
|
+
query: Query string to parse
|
|
143
|
+
|
|
144
|
+
Returns:
|
|
145
|
+
- AST node if successful
|
|
146
|
+
- List of ParserError if errors occurred
|
|
147
|
+
|
|
148
|
+
Example:
|
|
149
|
+
```python
|
|
150
|
+
result = parser.parse("Find(Person) WHERE age > 30")
|
|
151
|
+
if isinstance(result, list):
|
|
152
|
+
print("Errors:", result)
|
|
153
|
+
else:
|
|
154
|
+
print("AST:", result)
|
|
155
|
+
```
|
|
156
|
+
"""
|
|
157
|
+
# Phase 1: Syntax parsing (fatal errors)
|
|
158
|
+
try:
|
|
159
|
+
parse_tree = self.lark_parser.parse(query)
|
|
160
|
+
|
|
161
|
+
# Transform parse tree to AST
|
|
162
|
+
ast = self.ast_builder.transform(parse_tree)
|
|
163
|
+
|
|
164
|
+
# Phase 2: Semantic validation (if schema is available)
|
|
165
|
+
if self.schema is not None:
|
|
166
|
+
validation_errors = ast.validate(self.schema)
|
|
167
|
+
if validation_errors:
|
|
168
|
+
# Convert validation errors to parser errors
|
|
169
|
+
return [self.error_handler.from_validation_error(err, query) for err in validation_errors]
|
|
170
|
+
|
|
171
|
+
return ast
|
|
172
|
+
|
|
173
|
+
except (UnexpectedInput, UnexpectedToken, UnexpectedCharacters) as e:
|
|
174
|
+
# Lark syntax error
|
|
175
|
+
return [self.error_handler.from_lark_error(e, query)]
|
|
176
|
+
except LarkError as e:
|
|
177
|
+
# Other Lark errors
|
|
178
|
+
return [self.error_handler.from_lark_error(e, query)]
|
|
179
|
+
except Exception as e:
|
|
180
|
+
# Unexpected errors
|
|
181
|
+
return [
|
|
182
|
+
ParserError(
|
|
183
|
+
line=1,
|
|
184
|
+
column=1,
|
|
185
|
+
message=f"Unexpected error: {str(e)}",
|
|
186
|
+
phase="syntax",
|
|
187
|
+
)
|
|
188
|
+
]
|
|
189
|
+
|
|
190
|
+
def parse_to_query_plan(self, query: str) -> Any:
|
|
191
|
+
"""
|
|
192
|
+
Parse query string and convert to QueryPlan
|
|
193
|
+
|
|
194
|
+
This is the main entry point for parsing and converting queries to executable plans.
|
|
195
|
+
It performs:
|
|
196
|
+
1. Syntax parsing (Lark)
|
|
197
|
+
2. AST building (Transformer)
|
|
198
|
+
3. Semantic validation (if schema available)
|
|
199
|
+
4. QueryPlan conversion
|
|
200
|
+
|
|
201
|
+
Args:
|
|
202
|
+
query: Query string in Logic Query DSL
|
|
203
|
+
|
|
204
|
+
Returns:
|
|
205
|
+
QueryPlan object if successful, or List[ParserError] if errors occurred
|
|
206
|
+
|
|
207
|
+
Example:
|
|
208
|
+
```python
|
|
209
|
+
parser = LogicQueryParser(schema=schema_manager)
|
|
210
|
+
result = parser.parse_to_query_plan("Find(Person) WHERE age > 30")
|
|
211
|
+
|
|
212
|
+
if isinstance(result, list):
|
|
213
|
+
# Errors occurred
|
|
214
|
+
for error in result:
|
|
215
|
+
print(f"Error at line {error.line}: {error.message}")
|
|
216
|
+
else:
|
|
217
|
+
# Success - result is a QueryPlan
|
|
218
|
+
for step in result.steps:
|
|
219
|
+
print(f"Step {step.step_id}: {step.description}")
|
|
220
|
+
```
|
|
221
|
+
"""
|
|
222
|
+
if not QUERY_PLAN_AVAILABLE:
|
|
223
|
+
return [
|
|
224
|
+
ParserError(
|
|
225
|
+
line=1,
|
|
226
|
+
column=1,
|
|
227
|
+
message="QueryPlan models not available. Cannot convert to query plan.",
|
|
228
|
+
phase="conversion",
|
|
229
|
+
)
|
|
230
|
+
]
|
|
231
|
+
|
|
232
|
+
# Step 1: Parse to AST
|
|
233
|
+
ast_result = self.parse(query)
|
|
234
|
+
|
|
235
|
+
# Check for errors
|
|
236
|
+
if isinstance(ast_result, list):
|
|
237
|
+
# Errors occurred during parsing/validation
|
|
238
|
+
return ast_result
|
|
239
|
+
|
|
240
|
+
# Step 2: Create fresh QueryContext for this request (thread-safe)
|
|
241
|
+
context = QueryContext(schema=self.schema)
|
|
242
|
+
|
|
243
|
+
# Step 3: Convert AST to QueryPlan
|
|
244
|
+
try:
|
|
245
|
+
query_plan = ast_result.to_query_plan(context, original_query=query)
|
|
246
|
+
return query_plan
|
|
247
|
+
except Exception as e:
|
|
248
|
+
# Conversion error
|
|
249
|
+
return [
|
|
250
|
+
ParserError(
|
|
251
|
+
line=1,
|
|
252
|
+
column=1,
|
|
253
|
+
message=f"Failed to convert to query plan: {str(e)}",
|
|
254
|
+
phase="conversion",
|
|
255
|
+
)
|
|
256
|
+
]
|
|
257
|
+
|
|
258
|
+
def parse_tree_to_string(self, parse_tree: Any) -> str:
|
|
259
|
+
"""
|
|
260
|
+
Convert parse tree to string representation
|
|
261
|
+
|
|
262
|
+
Args:
|
|
263
|
+
parse_tree: Lark parse tree
|
|
264
|
+
|
|
265
|
+
Returns:
|
|
266
|
+
String representation of the parse tree
|
|
267
|
+
"""
|
|
268
|
+
if parse_tree is None:
|
|
269
|
+
return "None"
|
|
270
|
+
return parse_tree.pretty()
|
|
271
|
+
|
|
272
|
+
# ========================================================================
|
|
273
|
+
# Multi-Query Support (Batch Processing)
|
|
274
|
+
# ========================================================================
|
|
275
|
+
|
|
276
|
+
def parse_batch(self, queries: List[str]) -> List[Any]:
|
|
277
|
+
"""
|
|
278
|
+
Parse multiple queries in batch
|
|
279
|
+
|
|
280
|
+
This method parses multiple queries independently and returns their
|
|
281
|
+
AST representations. Each query is parsed with its own context.
|
|
282
|
+
|
|
283
|
+
Args:
|
|
284
|
+
queries: List of query strings to parse
|
|
285
|
+
|
|
286
|
+
Returns:
|
|
287
|
+
List of results (QueryNode or List[ParserError] for each query)
|
|
288
|
+
|
|
289
|
+
Example:
|
|
290
|
+
```python
|
|
291
|
+
parser = LogicQueryParser(schema=schema_manager)
|
|
292
|
+
results = parser.parse_batch([
|
|
293
|
+
"Find(Person) WHERE age > 30",
|
|
294
|
+
"Find(Paper) WHERE year == 2023"
|
|
295
|
+
])
|
|
296
|
+
|
|
297
|
+
for i, result in enumerate(results):
|
|
298
|
+
if isinstance(result, list):
|
|
299
|
+
print(f"Query {i+1} errors: {result}")
|
|
300
|
+
else:
|
|
301
|
+
print(f"Query {i+1} success: {result}")
|
|
302
|
+
```
|
|
303
|
+
"""
|
|
304
|
+
results = []
|
|
305
|
+
for query in queries:
|
|
306
|
+
result = self.parse(query)
|
|
307
|
+
results.append(result)
|
|
308
|
+
return results
|
|
309
|
+
|
|
310
|
+
def parse_batch_to_query_plans(self, queries: List[str]) -> List[Any]:
|
|
311
|
+
"""
|
|
312
|
+
Parse multiple queries and convert to QueryPlans in batch
|
|
313
|
+
|
|
314
|
+
This method parses multiple queries and converts them to QueryPlan
|
|
315
|
+
objects. Each query is processed independently with its own context.
|
|
316
|
+
|
|
317
|
+
Args:
|
|
318
|
+
queries: List of query strings to parse
|
|
319
|
+
|
|
320
|
+
Returns:
|
|
321
|
+
List of results (QueryPlan or List[ParserError] for each query)
|
|
322
|
+
|
|
323
|
+
Example:
|
|
324
|
+
```python
|
|
325
|
+
parser = LogicQueryParser(schema=schema_manager)
|
|
326
|
+
plans = parser.parse_batch_to_query_plans([
|
|
327
|
+
"Find(Person) WHERE age > 30",
|
|
328
|
+
"Find(Paper) WHERE year == 2023"
|
|
329
|
+
])
|
|
330
|
+
|
|
331
|
+
for i, plan in enumerate(plans):
|
|
332
|
+
if isinstance(plan, list):
|
|
333
|
+
print(f"Query {i+1} errors: {plan}")
|
|
334
|
+
else:
|
|
335
|
+
print(f"Query {i+1} plan: {plan.plan_id}")
|
|
336
|
+
```
|
|
337
|
+
"""
|
|
338
|
+
results = []
|
|
339
|
+
for query in queries:
|
|
340
|
+
result = self.parse_to_query_plan(query)
|
|
341
|
+
results.append(result)
|
|
342
|
+
return results
|
|
343
|
+
|
|
344
|
+
def parse_batch_with_ids(self, queries: Dict[str, str]) -> Dict[str, Any]:
|
|
345
|
+
"""
|
|
346
|
+
Parse multiple queries with custom IDs
|
|
347
|
+
|
|
348
|
+
This method parses multiple queries and returns results keyed by
|
|
349
|
+
custom IDs. Useful for tracking which result corresponds to which query.
|
|
350
|
+
|
|
351
|
+
Args:
|
|
352
|
+
queries: Dictionary mapping query IDs to query strings
|
|
353
|
+
|
|
354
|
+
Returns:
|
|
355
|
+
Dictionary mapping query IDs to results (QueryNode or List[ParserError])
|
|
356
|
+
|
|
357
|
+
Example:
|
|
358
|
+
```python
|
|
359
|
+
parser = LogicQueryParser(schema=schema_manager)
|
|
360
|
+
results = parser.parse_batch_with_ids({
|
|
361
|
+
"find_people": "Find(Person) WHERE age > 30",
|
|
362
|
+
"find_papers": "Find(Paper) WHERE year == 2023"
|
|
363
|
+
})
|
|
364
|
+
|
|
365
|
+
if isinstance(results["find_people"], list):
|
|
366
|
+
print(f"Errors: {results['find_people']}")
|
|
367
|
+
else:
|
|
368
|
+
print(f"Success: {results['find_people']}")
|
|
369
|
+
```
|
|
370
|
+
"""
|
|
371
|
+
results = {}
|
|
372
|
+
for query_id, query in queries.items():
|
|
373
|
+
result = self.parse(query)
|
|
374
|
+
results[query_id] = result
|
|
375
|
+
return results
|
|
376
|
+
|
|
377
|
+
|
|
378
|
+
# Cached parser instance for performance
|
|
379
|
+
@lru_cache(maxsize=1)
|
|
380
|
+
def get_cached_parser(schema_id: Optional[int] = None) -> LogicQueryParser:
|
|
381
|
+
"""
|
|
382
|
+
Get a cached parser instance
|
|
383
|
+
|
|
384
|
+
This is an optional optimization for repeated parsing.
|
|
385
|
+
|
|
386
|
+
Args:
|
|
387
|
+
schema_id: Optional schema identifier for cache key
|
|
388
|
+
|
|
389
|
+
Returns:
|
|
390
|
+
Cached LogicQueryParser instance
|
|
391
|
+
|
|
392
|
+
Note:
|
|
393
|
+
This is optional (Task 2.1.6). The cache is based on schema_id.
|
|
394
|
+
If schema changes, use a different schema_id to get a new parser.
|
|
395
|
+
"""
|
|
396
|
+
return LogicQueryParser(schema=None)
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Query Context for Logic Query Parser
|
|
3
|
+
|
|
4
|
+
This module provides the QueryContext class for managing state during query parsing
|
|
5
|
+
and conversion. The context is used to track variables, errors, and query steps.
|
|
6
|
+
|
|
7
|
+
**IMPORTANT**: QueryContext is NOT thread-safe. Create a new instance for each parse request.
|
|
8
|
+
|
|
9
|
+
Phase: 2.4 - Logic Query Parser
|
|
10
|
+
Version: 1.0
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from dataclasses import dataclass, field
|
|
14
|
+
from typing import Any, Dict, List
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class VariableRedefinitionError(Exception):
|
|
18
|
+
"""Raised when attempting to redefine an already-bound variable"""
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class UndefinedVariableError(Exception):
|
|
22
|
+
"""Raised when attempting to resolve an undefined variable"""
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@dataclass
|
|
26
|
+
class QueryContext:
|
|
27
|
+
"""
|
|
28
|
+
Context for query parsing and conversion
|
|
29
|
+
|
|
30
|
+
This class manages state during query parsing, including:
|
|
31
|
+
- Schema reference for validation
|
|
32
|
+
- Variable bindings for multi-step queries
|
|
33
|
+
- Error accumulation for validation
|
|
34
|
+
- Query steps for multi-step query construction
|
|
35
|
+
|
|
36
|
+
**Thread Safety**: This class contains mutable state and is NOT thread-safe.
|
|
37
|
+
Create a NEW QueryContext instance for EACH parse request.
|
|
38
|
+
|
|
39
|
+
**Lifecycle**:
|
|
40
|
+
1. Create: `context = QueryContext(schema)`
|
|
41
|
+
2. Use: `ast.to_query_plan(context)` or `ast.validate(schema)`
|
|
42
|
+
3. Discard: Context should not be reused for another query
|
|
43
|
+
|
|
44
|
+
**Concurrency Pattern**:
|
|
45
|
+
```python
|
|
46
|
+
# ✅ CORRECT: New context per request
|
|
47
|
+
def parse_concurrent(query: str):
|
|
48
|
+
context = QueryContext(schema) # Fresh instance
|
|
49
|
+
return parser.parse(query, context)
|
|
50
|
+
|
|
51
|
+
# ❌ WRONG: Shared context across requests
|
|
52
|
+
shared_context = QueryContext(schema) # DON'T DO THIS
|
|
53
|
+
def parse_concurrent(query: str):
|
|
54
|
+
return parser.parse(query, shared_context) # Race condition!
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Attributes:
|
|
58
|
+
schema: Schema manager for validation (immutable reference)
|
|
59
|
+
variables: Variable bindings for multi-step queries (mutable)
|
|
60
|
+
query_steps: Accumulated query steps (mutable)
|
|
61
|
+
errors: Accumulated validation errors (mutable)
|
|
62
|
+
|
|
63
|
+
Example:
|
|
64
|
+
```python
|
|
65
|
+
schema = SchemaManager()
|
|
66
|
+
context = QueryContext(schema)
|
|
67
|
+
|
|
68
|
+
# Bind a variable
|
|
69
|
+
context.bind_variable("person_id", "123")
|
|
70
|
+
|
|
71
|
+
# Resolve a variable
|
|
72
|
+
person_id = context.resolve_variable("person_id")
|
|
73
|
+
|
|
74
|
+
# Add an error
|
|
75
|
+
context.add_error(ParserError(...))
|
|
76
|
+
|
|
77
|
+
# Clear context for reuse (not recommended)
|
|
78
|
+
context.clear()
|
|
79
|
+
```
|
|
80
|
+
"""
|
|
81
|
+
|
|
82
|
+
# SchemaManager instance (type hint as Any to avoid circular import)
|
|
83
|
+
schema: Any
|
|
84
|
+
variables: Dict[str, Any] = field(default_factory=dict)
|
|
85
|
+
query_steps: List[Any] = field(default_factory=list) # List[QueryStep]
|
|
86
|
+
errors: List[Any] = field(default_factory=list) # List[ParserError]
|
|
87
|
+
|
|
88
|
+
def bind_variable(self, name: str, value: Any) -> None:
|
|
89
|
+
"""
|
|
90
|
+
Bind a variable to a value
|
|
91
|
+
|
|
92
|
+
Variables are used in multi-step queries to reference results from
|
|
93
|
+
previous steps.
|
|
94
|
+
|
|
95
|
+
Args:
|
|
96
|
+
name: Variable name (must be unique)
|
|
97
|
+
value: Value to bind to the variable
|
|
98
|
+
|
|
99
|
+
Raises:
|
|
100
|
+
VariableRedefinitionError: If variable is already bound
|
|
101
|
+
|
|
102
|
+
Example:
|
|
103
|
+
```python
|
|
104
|
+
context.bind_variable("person_id", "123")
|
|
105
|
+
context.bind_variable("person_id", "456") # Raises error
|
|
106
|
+
```
|
|
107
|
+
"""
|
|
108
|
+
if name in self.variables:
|
|
109
|
+
raise VariableRedefinitionError(f"Variable '{name}' is already defined with value: {self.variables[name]}")
|
|
110
|
+
self.variables[name] = value
|
|
111
|
+
|
|
112
|
+
def resolve_variable(self, name: str) -> Any:
|
|
113
|
+
"""
|
|
114
|
+
Resolve a variable to its value
|
|
115
|
+
|
|
116
|
+
Args:
|
|
117
|
+
name: Variable name to resolve
|
|
118
|
+
|
|
119
|
+
Returns:
|
|
120
|
+
Value bound to the variable
|
|
121
|
+
|
|
122
|
+
Raises:
|
|
123
|
+
UndefinedVariableError: If variable is not bound
|
|
124
|
+
|
|
125
|
+
Example:
|
|
126
|
+
```python
|
|
127
|
+
context.bind_variable("person_id", "123")
|
|
128
|
+
value = context.resolve_variable("person_id") # Returns "123"
|
|
129
|
+
value = context.resolve_variable("unknown") # Raises error
|
|
130
|
+
```
|
|
131
|
+
"""
|
|
132
|
+
if name not in self.variables:
|
|
133
|
+
raise UndefinedVariableError(f"Variable '{name}' is not defined. Available variables: {list(self.variables.keys())}")
|
|
134
|
+
return self.variables[name]
|
|
135
|
+
|
|
136
|
+
def add_error(self, error: Any) -> None:
|
|
137
|
+
"""
|
|
138
|
+
Add a validation or parsing error to the context
|
|
139
|
+
|
|
140
|
+
Errors are accumulated during parsing and validation so that
|
|
141
|
+
multiple errors can be reported at once.
|
|
142
|
+
|
|
143
|
+
Args:
|
|
144
|
+
error: ParserError or ValidationError instance
|
|
145
|
+
|
|
146
|
+
Example:
|
|
147
|
+
```python
|
|
148
|
+
error = ParserError(line=1, column=10, message="Invalid syntax")
|
|
149
|
+
context.add_error(error)
|
|
150
|
+
```
|
|
151
|
+
"""
|
|
152
|
+
self.errors.append(error)
|
|
153
|
+
|
|
154
|
+
def clear(self) -> None:
|
|
155
|
+
"""
|
|
156
|
+
Clear all mutable state in the context
|
|
157
|
+
|
|
158
|
+
This method resets variables, query steps, and errors.
|
|
159
|
+
|
|
160
|
+
**WARNING**: Reusing a context is NOT recommended. Create a new
|
|
161
|
+
context for each parse request instead.
|
|
162
|
+
|
|
163
|
+
Example:
|
|
164
|
+
```python
|
|
165
|
+
context.clear() # Reset all state
|
|
166
|
+
```
|
|
167
|
+
"""
|
|
168
|
+
self.variables.clear()
|
|
169
|
+
self.query_steps.clear()
|
|
170
|
+
self.errors.clear()
|
|
171
|
+
|
|
172
|
+
def has_variable(self, name: str) -> bool:
|
|
173
|
+
"""
|
|
174
|
+
Check if a variable is bound
|
|
175
|
+
|
|
176
|
+
Args:
|
|
177
|
+
name: Variable name to check
|
|
178
|
+
|
|
179
|
+
Returns:
|
|
180
|
+
True if variable is bound, False otherwise
|
|
181
|
+
|
|
182
|
+
Example:
|
|
183
|
+
```python
|
|
184
|
+
context.bind_variable("person_id", "123")
|
|
185
|
+
assert context.has_variable("person_id") == True
|
|
186
|
+
assert context.has_variable("unknown") == False
|
|
187
|
+
```
|
|
188
|
+
"""
|
|
189
|
+
return name in self.variables
|
|
190
|
+
|
|
191
|
+
def has_errors(self) -> bool:
|
|
192
|
+
"""
|
|
193
|
+
Check if any errors have been accumulated
|
|
194
|
+
|
|
195
|
+
Returns:
|
|
196
|
+
True if errors exist, False otherwise
|
|
197
|
+
|
|
198
|
+
Example:
|
|
199
|
+
```python
|
|
200
|
+
if context.has_errors():
|
|
201
|
+
print(f"Found {len(context.errors)} errors")
|
|
202
|
+
```
|
|
203
|
+
"""
|
|
204
|
+
return len(self.errors) > 0
|
|
205
|
+
|
|
206
|
+
def __repr__(self) -> str:
|
|
207
|
+
"""String representation for debugging"""
|
|
208
|
+
return f"QueryContext(" f"variables={len(self.variables)}, " f"steps={len(self.query_steps)}, " f"errors={len(self.errors)})"
|