contextsynapse 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.
- contextsynapse/__init__.py +107 -0
- contextsynapse/__main__.py +9 -0
- contextsynapse/a2a/__init__.py +28 -0
- contextsynapse/a2a/client.py +120 -0
- contextsynapse/a2a/discovery.py +128 -0
- contextsynapse/a2a/handlers.py +164 -0
- contextsynapse/a2a/models.py +246 -0
- contextsynapse/a2a/streaming.py +92 -0
- contextsynapse/a2a/task_manager.py +275 -0
- contextsynapse/adapters/__init__.py +98 -0
- contextsynapse/adapters/_base.py +298 -0
- contextsynapse/adapters/autogen/__init__.py +4 -0
- contextsynapse/adapters/autogen/tools.py +34 -0
- contextsynapse/adapters/crewai/__init__.py +4 -0
- contextsynapse/adapters/crewai/tools.py +63 -0
- contextsynapse/adapters/langchain/__init__.py +19 -0
- contextsynapse/adapters/langchain/message_history.py +17 -0
- contextsynapse/adapters/langchain/retriever.py +158 -0
- contextsynapse/adapters/langchain/tools.py +43 -0
- contextsynapse/adapters/langgraph/__init__.py +21 -0
- contextsynapse/adapters/langgraph/checkpoint.py +223 -0
- contextsynapse/adapters/langgraph/context_tools.py +341 -0
- contextsynapse/adapters/langgraph/message_history.py +138 -0
- contextsynapse/adapters/langgraph/tools.py +43 -0
- contextsynapse/adapters/llamaindex/__init__.py +4 -0
- contextsynapse/adapters/llamaindex/tools.py +64 -0
- contextsynapse/adapters/openai/__init__.py +24 -0
- contextsynapse/adapters/openai/tools.py +206 -0
- contextsynapse/adapters/pydantic_ai/__init__.py +4 -0
- contextsynapse/adapters/pydantic_ai/tools.py +36 -0
- contextsynapse/adapters/swarm/__init__.py +4 -0
- contextsynapse/adapters/swarm/tools.py +44 -0
- contextsynapse/agents/__init__.py +6 -0
- contextsynapse/agents/auto_capture.py +80 -0
- contextsynapse/agents/dispatcher.py +299 -0
- contextsynapse/agents/notifications.py +112 -0
- contextsynapse/aiql/__init__.py +71 -0
- contextsynapse/aiql/cache.py +341 -0
- contextsynapse/aiql/compiler/__init__.py +62 -0
- contextsynapse/aiql/compiler/optimizer.py +126 -0
- contextsynapse/aiql/compiler/planner.py +366 -0
- contextsynapse/aiql/compiler/statistics.py +235 -0
- contextsynapse/aiql/compiler/validator.py +425 -0
- contextsynapse/aiql/engine/__init__.py +11 -0
- contextsynapse/aiql/engine/chunk_storage.py +2584 -0
- contextsynapse/aiql/engine/executor.py +7737 -0
- contextsynapse/aiql/engine/extraction_integration.py +442 -0
- contextsynapse/aiql/engine/graph_builder.py +405 -0
- contextsynapse/aiql/engine/node_creation_from_normalized.py +691 -0
- contextsynapse/aiql/engine/performance_monitor.py +289 -0
- contextsynapse/aiql/engine/postprocessing.py +365 -0
- contextsynapse/aiql/engine/ray_stage_executors.py +918 -0
- contextsynapse/aiql/engine/redis_cache.py +106 -0
- contextsynapse/aiql/engine/streaming_json.py +366 -0
- contextsynapse/aiql/engine/streaming_parquet.py +568 -0
- contextsynapse/aiql/engine/streaming_ray_chunking.py +419 -0
- contextsynapse/aiql/grammar/__init__.py +14 -0
- contextsynapse/aiql/grammar/aiql_grammar.py +1822 -0
- contextsynapse/aiql/grammar/base.py +71 -0
- contextsynapse/aiql/parser/__init__.py +46 -0
- contextsynapse/aiql/parser/aiql_parser.py +11330 -0
- contextsynapse/aiql/parser/base_parser.py +47 -0
- contextsynapse/api/__init__.py +5 -0
- contextsynapse/api/a2a_router.py +233 -0
- contextsynapse/api/admin_router.py +140 -0
- contextsynapse/api/agent_worker_router.py +978 -0
- contextsynapse/api/algorithms_router.py +317 -0
- contextsynapse/api/api.py +4144 -0
- contextsynapse/api/audit.py +204 -0
- contextsynapse/api/auth.py +441 -0
- contextsynapse/api/auth_router.py +537 -0
- contextsynapse/api/billing.py +261 -0
- contextsynapse/api/billing_router.py +275 -0
- contextsynapse/api/boundary_router.py +452 -0
- contextsynapse/api/cognition_router.py +116 -0
- contextsynapse/api/connector_router.py +237 -0
- contextsynapse/api/context_router.py +1619 -0
- contextsynapse/api/dashboard_router.py +11367 -0
- contextsynapse/api/errors.py +183 -0
- contextsynapse/api/events.py +88 -0
- contextsynapse/api/experiment_bootstrap.py +321 -0
- contextsynapse/api/experiment_orchestrator.py +1078 -0
- contextsynapse/api/experiment_router.py +2089 -0
- contextsynapse/api/feature_gate.py +89 -0
- contextsynapse/api/federation_router.py +108 -0
- contextsynapse/api/integrations.py +567 -0
- contextsynapse/api/integrations_router.py +461 -0
- contextsynapse/api/intelligence_router.py +1028 -0
- contextsynapse/api/metering.py +155 -0
- contextsynapse/api/metrics.py +112 -0
- contextsynapse/api/models.py +38 -0
- contextsynapse/api/monitoring_router.py +174 -0
- contextsynapse/api/pipeline_router.py +1437 -0
- contextsynapse/api/playground_conn.py +116 -0
- contextsynapse/api/projects_router.py +131 -0
- contextsynapse/api/queries_router.py +282 -0
- contextsynapse/api/quota.py +95 -0
- contextsynapse/api/rate_limit.py +64 -0
- contextsynapse/api/search_router.py +261 -0
- contextsynapse/api/shield_router.py +447 -0
- contextsynapse/api/tenant_guard.py +103 -0
- contextsynapse/api/tenant_quotas.py +231 -0
- contextsynapse/api/tenants.py +305 -0
- contextsynapse/api/users.py +646 -0
- contextsynapse/api/v1.py +167 -0
- contextsynapse/api/vertical_builder_router.py +387 -0
- contextsynapse/benchmark/__init__.py +15 -0
- contextsynapse/benchmark/__main__.py +64 -0
- contextsynapse/benchmark/agents.py +338 -0
- contextsynapse/benchmark/live_ab.py +338 -0
- contextsynapse/benchmark/reporter.py +149 -0
- contextsynapse/benchmark/runner.py +264 -0
- contextsynapse/benchmark/savings.py +266 -0
- contextsynapse/benchmark/token_counter.py +212 -0
- contextsynapse/benchmark/workloads.py +289 -0
- contextsynapse/buffer/__init__.py +154 -0
- contextsynapse/buffer/base.py +257 -0
- contextsynapse/buffer/duckdb_buffer.py +246 -0
- contextsynapse/buffer/file_buffer.py +229 -0
- contextsynapse/buffer/lmdb_buffer.py +286 -0
- contextsynapse/buffer/local_buffer.py +154 -0
- contextsynapse/buffer/redis_buffer.py +211 -0
- contextsynapse/buffer/sqlite_buffer.py +266 -0
- contextsynapse/cache.py +2 -0
- contextsynapse/cli/__init__.py +40 -0
- contextsynapse/cli/cli.py +475 -0
- contextsynapse/cli/config_cli.py +177 -0
- contextsynapse/cli/scaffold.py +465 -0
- contextsynapse/cognition/__init__.py +31 -0
- contextsynapse/cognition/derivation.py +55 -0
- contextsynapse/cognition/events.py +135 -0
- contextsynapse/cognition/feedback.py +52 -0
- contextsynapse/cognition/hallucination.py +90 -0
- contextsynapse/cognition/invalidation.py +70 -0
- contextsynapse/cognition/read_tracker.py +50 -0
- contextsynapse/collaboration.py +2 -0
- contextsynapse/comparison.py +2 -0
- contextsynapse/config/__init__.py +10 -0
- contextsynapse/config/connectors/amfi_nav.yaml +18 -0
- contextsynapse/config/connectors/auto_sector.yaml +20 -0
- contextsynapse/config/connectors/banking_sector.yaml +20 -0
- contextsynapse/config/connectors/energy_sector.yaml +20 -0
- contextsynapse/config/connectors/fmcg_sector.yaml +20 -0
- contextsynapse/config/connectors/nsdl_fpi.yaml +9 -0
- contextsynapse/config/connectors/nse_price.yaml +61 -0
- contextsynapse/config/connectors/pharma_sector.yaml +20 -0
- contextsynapse/config/connectors/rbi_dbie.yaml +12 -0
- contextsynapse/config/context_presets/ai_research.json +25 -0
- contextsynapse/config/context_presets/chatgpt_conversations.json +19 -0
- contextsynapse/config/context_presets/claude_conversations.json +19 -0
- contextsynapse/config/context_presets/legal_compliance.json +21 -0
- contextsynapse/config/context_presets/meeting_notes.json +21 -0
- contextsynapse/config/context_presets/slack_engineering.json +21 -0
- contextsynapse/config/context_presets/tech_climate.json +24 -0
- contextsynapse/config/context_presets/toi_politics.json +25 -0
- contextsynapse/config/database_config.py +453 -0
- contextsynapse/config/deployment.py +221 -0
- contextsynapse/config/gpt_action_schema.json +164 -0
- contextsynapse/config/indicators/india_macro.yaml +213 -0
- contextsynapse/config/logging_config.py +88 -0
- contextsynapse/config/mode_config.py +223 -0
- contextsynapse/config/schemas/api_spec.yaml +48 -0
- contextsynapse/config/schemas/architecture_doc.yaml +51 -0
- contextsynapse/config/schemas/chat_history.yaml +90 -0
- contextsynapse/config/schemas/chatgpt_conversation.yaml +101 -0
- contextsynapse/config/schemas/claude_conversation.yaml +148 -0
- contextsynapse/config/schemas/contract.yaml +48 -0
- contextsynapse/config/schemas/conversation.yaml +167 -0
- contextsynapse/config/schemas/database.yaml +74 -0
- contextsynapse/config/schemas/decision.yaml +72 -0
- contextsynapse/config/schemas/ecommerce.yaml +56 -0
- contextsynapse/config/schemas/education.yaml +42 -0
- contextsynapse/config/schemas/healthcare.yaml +133 -0
- contextsynapse/config/schemas/hr_document.yaml +48 -0
- contextsynapse/config/schemas/invoice.yaml +40 -0
- contextsynapse/config/schemas/knowledge_base.yaml +75 -0
- contextsynapse/config/schemas/legal.yaml +42 -0
- contextsynapse/config/schemas/marketing_content.yaml +48 -0
- contextsynapse/config/schemas/meeting_notes.yaml +51 -0
- contextsynapse/config/schemas/news_article.yaml +61 -0
- contextsynapse/config/schemas/real_estate.yaml +49 -0
- contextsynapse/config/schemas/requirements_doc.yaml +48 -0
- contextsynapse/config/schemas/research_paper.yaml +56 -0
- contextsynapse/config/schemas/resume.yaml +49 -0
- contextsynapse/config/schemas/retail.yaml +42 -0
- contextsynapse/config/schemas/rule_meta.yaml +173 -0
- contextsynapse/config/schemas/rules.yaml +72 -0
- contextsynapse/config/schemas/screener_meta.yaml +236 -0
- contextsynapse/config/schemas/sdlc.yaml +103 -0
- contextsynapse/config/schemas/session_graph.yaml +172 -0
- contextsynapse/config/schemas/support_ticket.yaml +48 -0
- contextsynapse/config/schemas/web.yaml +64 -0
- contextsynapse/config/sensors/earnings_calendar.yaml +52 -0
- contextsynapse/config/sensors/global_risk.yaml +53 -0
- contextsynapse/config/sensors/government_policy.yaml +54 -0
- contextsynapse/config/sensors/supply_chain.yaml +50 -0
- contextsynapse/config/sensors/templates/behavioral.yaml +56 -0
- contextsynapse/config/sensors/templates/equity_sentiment.yaml +48 -0
- contextsynapse/config/sensors/templates/global_macro.yaml +47 -0
- contextsynapse/config/sensors/templates/market_data.yaml +118 -0
- contextsynapse/config/sensors/templates/sector_technical.yaml +47 -0
- contextsynapse/config/sensors/weather.yaml +52 -0
- contextsynapse/config/storage_strategy.py +202 -0
- contextsynapse/config/templates/competitive_intel.yaml +39 -0
- contextsynapse/config/templates/geopolitical_risk.yaml +46 -0
- contextsynapse/config/templates/market_analysis.yaml +58 -0
- contextsynapse/connectors/__init__.py +15 -0
- contextsynapse/connectors/api_router.py +121 -0
- contextsynapse/connectors/base.py +357 -0
- contextsynapse/connectors/financial/audio.py +170 -0
- contextsynapse/connectors/financial/youtube.py +272 -0
- contextsynapse/connectors/github.py +164 -0
- contextsynapse/connectors/jira.py +120 -0
- contextsynapse/connectors/kafka_connector.py +124 -0
- contextsynapse/connectors/pipeline_connector.py +747 -0
- contextsynapse/connectors/registry.py +116 -0
- contextsynapse/connectors/scheduler.py +87 -0
- contextsynapse/connectors/universal.py +360 -0
- contextsynapse/context/__init__.py +96 -0
- contextsynapse/context/__main__.py +4 -0
- contextsynapse/context/acl.py +125 -0
- contextsynapse/context/agent_card.py +201 -0
- contextsynapse/context/agent_memory.py +814 -0
- contextsynapse/context/agents.py +472 -0
- contextsynapse/context/agents_redis.py +391 -0
- contextsynapse/context/assembled.py +204 -0
- contextsynapse/context/attribution.py +113 -0
- contextsynapse/context/blob.py +184 -0
- contextsynapse/context/boundaries.py +187 -0
- contextsynapse/context/boundary.py +507 -0
- contextsynapse/context/bundle.py +155 -0
- contextsynapse/context/cli.py +519 -0
- contextsynapse/context/collaboration.py +165 -0
- contextsynapse/context/compiler.py +604 -0
- contextsynapse/context/composite.py +536 -0
- contextsynapse/context/compression.py +147 -0
- contextsynapse/context/context_manager.py +888 -0
- contextsynapse/context/context_schema.py +90 -0
- contextsynapse/context/context_schema.yaml +105 -0
- contextsynapse/context/context_units.py +891 -0
- contextsynapse/context/conversation.py +488 -0
- contextsynapse/context/dedup.py +207 -0
- contextsynapse/context/document_processor.py +698 -0
- contextsynapse/context/embedding_hooks.py +338 -0
- contextsynapse/context/execution_schema.py +274 -0
- contextsynapse/context/fan_out.py +138 -0
- contextsynapse/context/frozen.py +80 -0
- contextsynapse/context/gravity.py +227 -0
- contextsynapse/context/hub.py +1401 -0
- contextsynapse/context/ingest.py +391 -0
- contextsynapse/context/injection.py +226 -0
- contextsynapse/context/interaction_graphifier.py +562 -0
- contextsynapse/context/layers.py +574 -0
- contextsynapse/context/projection.py +1195 -0
- contextsynapse/context/promotion.py +634 -0
- contextsynapse/context/proof.py +131 -0
- contextsynapse/context/propagation.py +577 -0
- contextsynapse/context/pubsub.py +135 -0
- contextsynapse/context/quality.py +1054 -0
- contextsynapse/context/quality_tracker.py +270 -0
- contextsynapse/context/scoping.py +370 -0
- contextsynapse/context/seed.py +992 -0
- contextsynapse/context/session.py +1142 -0
- contextsynapse/context/session_graph.py +593 -0
- contextsynapse/context/session_memory.py +309 -0
- contextsynapse/context/session_resolver.py +111 -0
- contextsynapse/context/skill_learning.py +237 -0
- contextsynapse/context/smart_budget.py +129 -0
- contextsynapse/context/source_policy.py +242 -0
- contextsynapse/context/store_factory.py +53 -0
- contextsynapse/context/sync.py +648 -0
- contextsynapse/context/token.py +236 -0
- contextsynapse/context/vector_integration.py +297 -0
- contextsynapse/context/webhooks.py +194 -0
- contextsynapse/context/working_memory.py +224 -0
- contextsynapse/core/__init__.py +18 -0
- contextsynapse/core/archiver.py +237 -0
- contextsynapse/core/change_stream.py +282 -0
- contextsynapse/core/checkpoint.py +310 -0
- contextsynapse/core/cloud_storage.py +387 -0
- contextsynapse/core/context_state.py +651 -0
- contextsynapse/core/cron.py +324 -0
- contextsynapse/core/datasource.py +643 -0
- contextsynapse/core/db.py +289 -0
- contextsynapse/core/federation.py +283 -0
- contextsynapse/core/graph_coordinator.py +201 -0
- contextsynapse/core/graph_intelligence.py +388 -0
- contextsynapse/core/graph_structures.py +106 -0
- contextsynapse/core/graph_sync.py +157 -0
- contextsynapse/core/hybrid_graph_storage.py +2998 -0
- contextsynapse/core/lifecycle.py +691 -0
- contextsynapse/core/multiworker.py +378 -0
- contextsynapse/core/pruning.py +312 -0
- contextsynapse/core/redis_registry.py +524 -0
- contextsynapse/core/registry.py +1043 -0
- contextsynapse/core/registry_factory.py +51 -0
- contextsynapse/core/registry_metadata.py +294 -0
- contextsynapse/core/replay.py +354 -0
- contextsynapse/core/replication.py +339 -0
- contextsynapse/core/time_travel.py +260 -0
- contextsynapse/core/write_behind.py +152 -0
- contextsynapse/core/write_context.py +39 -0
- contextsynapse/db/__init__.py +1 -0
- contextsynapse/db/postgres.py +267 -0
- contextsynapse/db/profile.py +236 -0
- contextsynapse/db/rules.py +328 -0
- contextsynapse/demo/__init__.py +0 -0
- contextsynapse/demo/report.py +147 -0
- contextsynapse/demo/runner.py +520 -0
- contextsynapse/demo/scenario.py +165 -0
- contextsynapse/demo/session_demo.py +280 -0
- contextsynapse/demo/session_report.py +143 -0
- contextsynapse/demo/universal_runner.py +257 -0
- contextsynapse/engine/__init__.py +15 -0
- contextsynapse/engine/core.py +555 -0
- contextsynapse/extraction/__init__.py +69 -0
- contextsynapse/extraction/cli.py +274 -0
- contextsynapse/extraction/config.py +107 -0
- contextsynapse/extraction/examples/example_usage.py +118 -0
- contextsynapse/extraction/examples/sample_manifest.json +53 -0
- contextsynapse/extraction/examples/sample_normalized.json +185 -0
- contextsynapse/extraction/extract_engine.py +440 -0
- contextsynapse/extraction/extractors/__init__.py +37 -0
- contextsynapse/extraction/extractors/chat_export_extractor.py +189 -0
- contextsynapse/extraction/extractors/csv_extractor.py +83 -0
- contextsynapse/extraction/extractors/docx_extractor.py +129 -0
- contextsynapse/extraction/extractors/excel_extractor.py +152 -0
- contextsynapse/extraction/extractors/html_extractor.py +146 -0
- contextsynapse/extraction/extractors/pdf_extractor.py +635 -0
- contextsynapse/extraction/extractors/pdf_extractor_layoutparser.py +408 -0
- contextsynapse/extraction/extractors/register_extractors.py +63 -0
- contextsynapse/extraction/extractors/text_extractor.py +65 -0
- contextsynapse/extraction/extractors/txt_extractor.py +120 -0
- contextsynapse/extraction/extractors/website_extractor.py +246 -0
- contextsynapse/extraction/fact_extractor.py +236 -0
- contextsynapse/extraction/file_manager.py +353 -0
- contextsynapse/extraction/hierarchy.py +239 -0
- contextsynapse/extraction/id_generator.py +450 -0
- contextsynapse/extraction/layout_detector.py +409 -0
- contextsynapse/extraction/llm_entity_extractor.py +301 -0
- contextsynapse/extraction/normalized_store.py +480 -0
- contextsynapse/extraction/normalizer.py +594 -0
- contextsynapse/extraction/parquet_writer.py +598 -0
- contextsynapse/extraction/post_processors/__init__.py +40 -0
- contextsynapse/extraction/post_processors/column_reorganizer.py +507 -0
- contextsynapse/extraction/ray_normalization.py +233 -0
- contextsynapse/extraction/ray_runner.py +207 -0
- contextsynapse/extraction/redis_schema_registry.py +222 -0
- contextsynapse/extraction/registry.py +204 -0
- contextsynapse/extraction/schema_loader.py +663 -0
- contextsynapse/extraction/schema_registry.py +230 -0
- contextsynapse/extraction/semantic_normalizer.py +245 -0
- contextsynapse/extraction/version_manager.py +314 -0
- contextsynapse/gateway/__init__.py +24 -0
- contextsynapse/gateway/action_emitter.py +212 -0
- contextsynapse/gateway/cost_tracker.py +508 -0
- contextsynapse/gateway/gateway.py +377 -0
- contextsynapse/gateway/policy.py +303 -0
- contextsynapse/governance/__init__.py +13 -0
- contextsynapse/governance/layer.py +513 -0
- contextsynapse/ingestion/__init__.py +15 -0
- contextsynapse/ingestion/amplifier.py +288 -0
- contextsynapse/ingestion/async_ingest.py +463 -0
- contextsynapse/ingestion/chunker.py +303 -0
- contextsynapse/ingestion/chunking.py +58 -0
- contextsynapse/ingestion/cleaner.py +190 -0
- contextsynapse/ingestion/cleanse.py +252 -0
- contextsynapse/ingestion/connectors/__init__.py +38 -0
- contextsynapse/ingestion/connectors/base.py +300 -0
- contextsynapse/ingestion/connectors/chatgpt_memory.py +247 -0
- contextsynapse/ingestion/connectors/claude_memory.py +274 -0
- contextsynapse/ingestion/connectors/sap_ingestor.py +208 -0
- contextsynapse/ingestion/content_detector.py +80 -0
- contextsynapse/ingestion/content_monitor.py +502 -0
- contextsynapse/ingestion/dedup.py +106 -0
- contextsynapse/ingestion/feed.py +640 -0
- contextsynapse/ingestion/filters.py +461 -0
- contextsynapse/ingestion/graph_builder.py +705 -0
- contextsynapse/ingestion/input_classifier.py +307 -0
- contextsynapse/ingestion/job_manager.py +571 -0
- contextsynapse/ingestion/llm_extractor.py +610 -0
- contextsynapse/ingestion/orchestrator.py +116 -0
- contextsynapse/ingestion/parsers/__init__.py +19 -0
- contextsynapse/ingestion/parsers/graph_parser_registry.py +103 -0
- contextsynapse/ingestion/parsers/graphml_parser.py +141 -0
- contextsynapse/ingestion/parsers/jsonld_parser.py +171 -0
- contextsynapse/ingestion/parsers/rdf_parser.py +150 -0
- contextsynapse/ingestion/pipeline_context.py +228 -0
- contextsynapse/ingestion/pipeline_store.py +1195 -0
- contextsynapse/ingestion/queue.py +133 -0
- contextsynapse/ingestion/reprocess.py +179 -0
- contextsynapse/ingestion/scenario_router.py +224 -0
- contextsynapse/ingestion/schema_extractor.py +759 -0
- contextsynapse/ingestion/schema_validator.py +274 -0
- contextsynapse/ingestion/sdlc_ingest.py +87 -0
- contextsynapse/ingestion/smart_ingest.py +2451 -0
- contextsynapse/ingestion/source_connector.py +171 -0
- contextsynapse/ingestion/stage_executor.py +2095 -0
- contextsynapse/ingestion/stages/__init__.py +9 -0
- contextsynapse/ingestion/stages/tabular_to_graph.py +289 -0
- contextsynapse/ingestion/strategies.py +386 -0
- contextsynapse/ingestion/universal/__init__.py +4 -0
- contextsynapse/ingestion/universal/_operator_registry.py +59 -0
- contextsynapse/ingestion/universal/ingest_content.py +32 -0
- contextsynapse/ingestion/universal/operators/__init__.py +1 -0
- contextsynapse/ingestion/universal/operators/base.py +19 -0
- contextsynapse/ingestion/universal/operators/build_edges.py +75 -0
- contextsynapse/ingestion/universal/operators/cluster_topics.py +397 -0
- contextsynapse/ingestion/universal/operators/deduplicate.py +45 -0
- contextsynapse/ingestion/universal/operators/detect_signals.py +90 -0
- contextsynapse/ingestion/universal/operators/embed.py +80 -0
- contextsynapse/ingestion/universal/operators/extract_entities.py +253 -0
- contextsynapse/ingestion/universal/operators/extract_preferences.py +117 -0
- contextsynapse/ingestion/universal/operators/filter_content.py +69 -0
- contextsynapse/ingestion/universal/operators/index_bm25.py +52 -0
- contextsynapse/ingestion/universal/operators/infer_domains.py +69 -0
- contextsynapse/ingestion/universal/operators/link_cross_reference.py +210 -0
- contextsynapse/ingestion/universal/operators/parse_records.py +66 -0
- contextsynapse/ingestion/universal/operators/resolve_entities.py +261 -0
- contextsynapse/ingestion/universal/operators/scanners/__init__.py +16 -0
- contextsynapse/ingestion/universal/operators/scanners/edges.py +173 -0
- contextsynapse/ingestion/universal/operators/scanners/git.py +187 -0
- contextsynapse/ingestion/universal/operators/scanners/github.py +135 -0
- contextsynapse/ingestion/universal/operators/scanners/github_api.py +509 -0
- contextsynapse/ingestion/universal/operators/scanners/helpers.py +67 -0
- contextsynapse/ingestion/universal/operators/scanners/jira.py +65 -0
- contextsynapse/ingestion/universal/operators/scanners/llm_enrichment.py +82 -0
- contextsynapse/ingestion/universal/operators/scanners/repo.py +149 -0
- contextsynapse/ingestion/universal/operators/sdlc_scan.py +213 -0
- contextsynapse/ingestion/universal/operators/spec_scanner.py +149 -0
- contextsynapse/ingestion/universal/operators/store_documents.py +72 -0
- contextsynapse/ingestion/universal/operators/synthesize_cu.py +180 -0
- contextsynapse/ingestion/universal/operators/validate_gate.py +65 -0
- contextsynapse/ingestion/universal/parsers/__init__.py +5 -0
- contextsynapse/ingestion/universal/parsers/record_parser.py +203 -0
- contextsynapse/ingestion/universal/parsers/turn_parser.py +329 -0
- contextsynapse/ingestion/universal/shared_signals.py +161 -0
- contextsynapse/ingestion/universal/stage_executor.py +219 -0
- contextsynapse/ingestion/web_crawler.py +265 -0
- contextsynapse/intelligence/__init__.py +53 -0
- contextsynapse/intelligence/alerts.py +198 -0
- contextsynapse/intelligence/collector.py +655 -0
- contextsynapse/intelligence/comparison.py +198 -0
- contextsynapse/intelligence/config.py +56 -0
- contextsynapse/intelligence/conflict_detector.py +182 -0
- contextsynapse/intelligence/context_gaps.py +192 -0
- contextsynapse/intelligence/context_radar.py +135 -0
- contextsynapse/intelligence/correlation_engine.py +416 -0
- contextsynapse/intelligence/correlation_template.py +120 -0
- contextsynapse/intelligence/dedup.py +296 -0
- contextsynapse/intelligence/event_bus.py +91 -0
- contextsynapse/intelligence/feedback_loop.py +101 -0
- contextsynapse/intelligence/freshness.py +307 -0
- contextsynapse/intelligence/fundamental_feed.py +551 -0
- contextsynapse/intelligence/geo_data.py +170 -0
- contextsynapse/intelligence/geo_reference.py +112 -0
- contextsynapse/intelligence/graph_fusion.py +465 -0
- contextsynapse/intelligence/heuristics.py +176 -0
- contextsynapse/intelligence/impact_tracker.py +336 -0
- contextsynapse/intelligence/macro_indicators.py +319 -0
- contextsynapse/intelligence/ocr.py +104 -0
- contextsynapse/intelligence/persistence.py +267 -0
- contextsynapse/intelligence/pipeline_health.py +130 -0
- contextsynapse/intelligence/price_feed.py +221 -0
- contextsynapse/intelligence/rate_limiter.py +130 -0
- contextsynapse/intelligence/reactive.py +411 -0
- contextsynapse/intelligence/runtime_context.py +390 -0
- contextsynapse/intelligence/sentiment_decay.py +426 -0
- contextsynapse/intelligence/session.py +277 -0
- contextsynapse/intelligence/signal_hierarchy.py +438 -0
- contextsynapse/intelligence/signals.py +185 -0
- contextsynapse/intelligence/source_watcher.py +132 -0
- contextsynapse/intelligence/table_enricher.py +256 -0
- contextsynapse/intelligence/tagger.py +319 -0
- contextsynapse/intelligence/timeseries.py +209 -0
- contextsynapse/intelligence/tracker.py +318 -0
- contextsynapse/intelligence/watchdog.py +392 -0
- contextsynapse/intelligence/watchdog_manager.py +263 -0
- contextsynapse/intelligence/webhook_receiver.py +165 -0
- contextsynapse/intelligence/weight_learner.py +234 -0
- contextsynapse/llm/__init__.py +8 -0
- contextsynapse/llm/client.py +348 -0
- contextsynapse/llm/rate_limiter.py +166 -0
- contextsynapse/logging_config.py +2 -0
- contextsynapse/marketplace/__init__.py +7 -0
- contextsynapse/marketplace/registry.py +173 -0
- contextsynapse/mcp/__init__.py +11 -0
- contextsynapse/mcp/__main__.py +4 -0
- contextsynapse/mcp/auth_middleware.py +212 -0
- contextsynapse/mcp/connection_pool.py +128 -0
- contextsynapse/mcp/server.py +986 -0
- contextsynapse/mcp/session_context.py +67 -0
- contextsynapse/memory/__init__.py +34 -0
- contextsynapse/memory/engine.py +475 -0
- contextsynapse/memory/temporal.py +327 -0
- contextsynapse/metadata/__init__.py +12 -0
- contextsynapse/metadata/metadata_db.py +272 -0
- contextsynapse/metadata/metadata_query_tool.py +300 -0
- contextsynapse/metadata/metadata_tracker.py +425 -0
- contextsynapse/models/__init__.py +27 -0
- contextsynapse/models/embedding_service.py +360 -0
- contextsynapse/models/model_registry.py +228 -0
- contextsynapse/multiworker.py +2 -0
- contextsynapse/pipelines/__init__.py +1 -0
- contextsynapse/pipelines/executor.py +851 -0
- contextsynapse/pipelines/filters.py +189 -0
- contextsynapse/pipelines/models.py +176 -0
- contextsynapse/pipelines/scheduler.py +268 -0
- contextsynapse/playground/__init__.py +1 -0
- contextsynapse/playground/templates.py +85 -0
- contextsynapse/plugins/__init__.py +43 -0
- contextsynapse/plugins/api.py +66 -0
- contextsynapse/plugins/base.py +350 -0
- contextsynapse/plugins/domain_interface.py +508 -0
- contextsynapse/plugins/domain_loader.py +241 -0
- contextsynapse/plugins/loader.py +148 -0
- contextsynapse/plugins/registry.py +265 -0
- contextsynapse/project/__init__.py +11 -0
- contextsynapse/project/code_context.py +807 -0
- contextsynapse/project/connectors/__init__.py +4 -0
- contextsynapse/project/connectors/github_connector.py +113 -0
- contextsynapse/project/connectors/jira_connector.py +160 -0
- contextsynapse/project/graph.py +1023 -0
- contextsynapse/project/project_context.py +545 -0
- contextsynapse/project/scanner_registry.py +61 -0
- contextsynapse/project/schema_manager.py +322 -0
- contextsynapse/project/schemas/knowledge.yaml +59 -0
- contextsynapse/project/schemas/sdlc.yaml +369 -0
- contextsynapse/project/sdlc_schema.py +172 -0
- contextsynapse/project/spec_parser.py +85 -0
- contextsynapse/project/task_lock.py +181 -0
- contextsynapse/project/task_stream.py +203 -0
- contextsynapse/project/team_tools.py +537 -0
- contextsynapse/py.typed +0 -0
- contextsynapse/realtime/__init__.py +6 -0
- contextsynapse/realtime/collaboration.py +147 -0
- contextsynapse/rules/__init__.py +10 -0
- contextsynapse/rules/engine.py +116 -0
- contextsynapse/rules/evaluators/__init__.py +6 -0
- contextsynapse/rules/evaluators/aggregate.py +205 -0
- contextsynapse/rules/evaluators/custom.py +170 -0
- contextsynapse/rules/evaluators/pre_action.py +125 -0
- contextsynapse/rules/evaluators/temporal.py +129 -0
- contextsynapse/rules/loader.py +52 -0
- contextsynapse/rules/models.py +124 -0
- contextsynapse/rules/universal.py +393 -0
- contextsynapse/scheduler.py +229 -0
- contextsynapse/schema/__init__.py +42 -0
- contextsynapse/schema/compiler.py +252 -0
- contextsynapse/schema/composer.py +158 -0
- contextsynapse/schema/dedup.py +73 -0
- contextsynapse/schema/derivation.py +172 -0
- contextsynapse/schema/sdl.py +326 -0
- contextsynapse/schema/validation_gate.py +124 -0
- contextsynapse/sdk/__init__.py +63 -0
- contextsynapse/sdk/agent.py +214 -0
- contextsynapse/sdk/client.py +269 -0
- contextsynapse/sdk/exceptions.py +23 -0
- contextsynapse/sdk/models.py +78 -0
- contextsynapse/sdk/session.py +408 -0
- contextsynapse/sdk/tracked.py +218 -0
- contextsynapse/sdk/worker.py +266 -0
- contextsynapse/sdk/workspace_client.py +98 -0
- contextsynapse/sdk/ws.py +41 -0
- contextsynapse/search/__init__.py +20 -0
- contextsynapse/search/embedding_cache.py +140 -0
- contextsynapse/search/enhanced_search.py +152 -0
- contextsynapse/search/fulltext.py +130 -0
- contextsynapse/search/graph_search.py +634 -0
- contextsynapse/search/lmdb_index.py +1218 -0
- contextsynapse/search/nl_to_aiql.py +199 -0
- contextsynapse/search/rag.py +1346 -0
- contextsynapse/search/rag_cache.py +167 -0
- contextsynapse/search/reasoning_chain.py +231 -0
- contextsynapse/search/redis_search.py +207 -0
- contextsynapse/search/retrieval_quality.py +314 -0
- contextsynapse/search/semantic_query.py +455 -0
- contextsynapse/search/text_resolver.py +159 -0
- contextsynapse/search/whoosh_search.py +373 -0
- contextsynapse/security/__init__.py +17 -0
- contextsynapse/security/audit.py +297 -0
- contextsynapse/security/audit_logger.py +97 -0
- contextsynapse/security/audit_trail.py +270 -0
- contextsynapse/security/auth_unified.py +284 -0
- contextsynapse/security/auto_tagger.py +230 -0
- contextsynapse/security/data_security.py +218 -0
- contextsynapse/security/encryption.py +207 -0
- contextsynapse/security/identity.py +447 -0
- contextsynapse/security/jwt_identity.py +59 -0
- contextsynapse/security/middleware.py +336 -0
- contextsynapse/security/pii.py +233 -0
- contextsynapse/security/rbac.py +96 -0
- contextsynapse/security/rls.py +205 -0
- contextsynapse/security/sanitize.py +233 -0
- contextsynapse/security/scoped_encryption.py +131 -0
- contextsynapse/security/tenant.py +401 -0
- contextsynapse/shield/__init__.py +21 -0
- contextsynapse/shield/anomaly.py +56 -0
- contextsynapse/shield/permissions.py +99 -0
- contextsynapse/shield/profile.py +103 -0
- contextsynapse/shield/shield.py +108 -0
- contextsynapse/shield/trust_engine.py +69 -0
- contextsynapse/skills/__init__.py +14 -0
- contextsynapse/skills/engine.py +233 -0
- contextsynapse/skills/loader.py +59 -0
- contextsynapse/skills/models.py +139 -0
- contextsynapse/storage/__init__.py +7 -0
- contextsynapse/storage/cache.py +208 -0
- contextsynapse/storage/columnar_store_v2.py +178 -0
- contextsynapse/storage/csr_graph_storage.py +587 -0
- contextsynapse/storage/csr_store.py +388 -0
- contextsynapse/storage/document_store.py +491 -0
- contextsynapse/storage/hnsw_index.py +294 -0
- contextsynapse/storage/hybrid_store_v2.py +278 -0
- contextsynapse/storage/lmdb_graph_storage.py +366 -0
- contextsynapse/storage/migration.py +279 -0
- contextsynapse/storage/namespace_store.py +346 -0
- contextsynapse/storage/pure_graph_storage.py +185 -0
- contextsynapse/storage/redis_graph_adapter.py +186 -0
- contextsynapse/storage/redis_graph_storage.py +375 -0
- contextsynapse/storage/router/__init__.py +29 -0
- contextsynapse/storage/router/duckdb_store.py +398 -0
- contextsynapse/storage/router/factory.py +83 -0
- contextsynapse/storage/router/interface.py +125 -0
- contextsynapse/storage/router/postgres_store.py +339 -0
- contextsynapse/storage/router/resolver.py +192 -0
- contextsynapse/storage/router/router.py +300 -0
- contextsynapse/storage/single_file_storage.py +391 -0
- contextsynapse/storage/storage_manager.py +214 -0
- contextsynapse/storage/storage_query_optimizer.py +314 -0
- contextsynapse/storage/wal.py +359 -0
- contextsynapse/temporal/__init__.py +0 -0
- contextsynapse/temporal/storage.py +106 -0
- contextsynapse/tests/__init__.py +11 -0
- contextsynapse/tests/regression/__init__.py +1 -0
- contextsynapse/tests/regression/add_metadata.py +395 -0
- contextsynapse/tests/regression/add_skip_flags.py +85 -0
- contextsynapse/tests/regression/cases/test_basic_create_node.yaml +20 -0
- contextsynapse/tests/regression/cases/test_basic_select.yaml +21 -0
- contextsynapse/tests/regression/cases/test_blockchain_audit_trail.yaml +37 -0
- contextsynapse/tests/regression/cases/test_blockchain_audit_trail_filtered.yaml +39 -0
- contextsynapse/tests/regression/cases/test_blockchain_blocks_range.yaml +39 -0
- contextsynapse/tests/regression/cases/test_blockchain_get_block.yaml +35 -0
- contextsynapse/tests/regression/cases/test_blockchain_latest_block.yaml +35 -0
- contextsynapse/tests/regression/cases/test_blockchain_length.yaml +37 -0
- contextsynapse/tests/regression/cases/test_blockchain_merkle_root.yaml +37 -0
- contextsynapse/tests/regression/cases/test_blockchain_verify.yaml +35 -0
- contextsynapse/tests/regression/cases/test_blockchain_verify_block.yaml +35 -0
- contextsynapse/tests/regression/cases/test_create_edge.yaml +28 -0
- contextsynapse/tests/regression/cases/test_delete_edge_basic.yaml +29 -0
- contextsynapse/tests/regression/cases/test_delete_node_basic.yaml +29 -0
- contextsynapse/tests/regression/cases/test_evaluation_mrr.yaml +40 -0
- contextsynapse/tests/regression/cases/test_evaluation_ndcg.yaml +40 -0
- contextsynapse/tests/regression/cases/test_evaluation_precision.yaml +40 -0
- contextsynapse/tests/regression/cases/test_evaluation_recall.yaml +40 -0
- contextsynapse/tests/regression/cases/test_group_by_basic.yaml +56 -0
- contextsynapse/tests/regression/cases/test_match_node_basic.yaml +29 -0
- contextsynapse/tests/regression/cases/test_match_node_with_order_limit.yaml +31 -0
- contextsynapse/tests/regression/cases/test_namespace_switch.yaml +18 -0
- contextsynapse/tests/regression/cases/test_pipeline_cascaded.yaml +61 -0
- contextsynapse/tests/regression/cases/test_pipeline_fixed_size_chunking.yaml +48 -0
- contextsynapse/tests/regression/cases/test_pipeline_gpt4_chunking.yaml +54 -0
- contextsynapse/tests/regression/cases/test_pipeline_hybrid_indexing.yaml +57 -0
- contextsynapse/tests/regression/cases/test_pipeline_large_embedding.yaml +52 -0
- contextsynapse/tests/regression/cases/test_pipeline_paragraph_chunking.yaml +49 -0
- contextsynapse/tests/regression/cases/test_pipeline_recursive_chunking.yaml +47 -0
- contextsynapse/tests/regression/cases/test_pipeline_run_pipeline.yaml +56 -0
- contextsynapse/tests/regression/cases/test_pipeline_section_based_chunking.yaml +53 -0
- contextsynapse/tests/regression/cases/test_pipeline_semantic_chunking.yaml +51 -0
- contextsynapse/tests/regression/cases/test_pipeline_with_entity_extraction.yaml +57 -0
- contextsynapse/tests/regression/cases/test_pipeline_with_relationships.yaml +62 -0
- contextsynapse/tests/regression/cases/test_rag_evaluation.yaml +38 -0
- contextsynapse/tests/regression/cases/test_rag_graph_bfs_with_eval.yaml +48 -0
- contextsynapse/tests/regression/cases/test_rag_graph_centrality.yaml +40 -0
- contextsynapse/tests/regression/cases/test_rag_graph_centrality_eval.yaml +35 -0
- contextsynapse/tests/regression/cases/test_rag_graph_community.yaml +40 -0
- contextsynapse/tests/regression/cases/test_rag_graph_community_eval.yaml +35 -0
- contextsynapse/tests/regression/cases/test_rag_graph_dfs_with_eval.yaml +35 -0
- contextsynapse/tests/regression/cases/test_rag_graph_multi_hop_eval.yaml +35 -0
- contextsynapse/tests/regression/cases/test_rag_graph_path.yaml +40 -0
- contextsynapse/tests/regression/cases/test_rag_graph_search_basic.yaml +38 -0
- contextsynapse/tests/regression/cases/test_rag_graph_search_bfs.yaml +52 -0
- contextsynapse/tests/regression/cases/test_rag_graph_search_dfs.yaml +41 -0
- contextsynapse/tests/regression/cases/test_rag_graph_search_multi_hop.yaml +43 -0
- contextsynapse/tests/regression/cases/test_rag_graph_search_shortest_path.yaml +41 -0
- contextsynapse/tests/regression/cases/test_rag_graph_search_weighted.yaml +43 -0
- contextsynapse/tests/regression/cases/test_rag_graph_shortest_path_eval.yaml +35 -0
- contextsynapse/tests/regression/cases/test_rag_graph_traversal.yaml +30 -0
- contextsynapse/tests/regression/cases/test_rag_graph_weighted_eval.yaml +35 -0
- contextsynapse/tests/regression/cases/test_rag_hybrid_search.yaml +33 -0
- contextsynapse/tests/regression/cases/test_rag_hybrid_with_prompt_embedding.yaml +44 -0
- contextsynapse/tests/regression/cases/test_rag_keyword_search.yaml +32 -0
- contextsynapse/tests/regression/cases/test_rag_multi_hop_graph.yaml +32 -0
- contextsynapse/tests/regression/cases/test_rag_path_based.yaml +34 -0
- contextsynapse/tests/regression/cases/test_rag_profile_based.yaml +32 -0
- contextsynapse/tests/regression/cases/test_rag_vector_search.yaml +32 -0
- contextsynapse/tests/regression/cases/test_rag_weighted_graph.yaml +34 -0
- contextsynapse/tests/regression/cases/test_rag_weighted_hybrid.yaml +31 -0
- contextsynapse/tests/regression/cases/test_semantic_boundary_chunking.yaml +57 -0
- contextsynapse/tests/regression/cases/test_semantic_boundary_detection.yaml +33 -0
- contextsynapse/tests/regression/cases/test_semantic_hash_function.yaml +30 -0
- contextsynapse/tests/regression/cases/test_semantic_hash_with_model.yaml +31 -0
- contextsynapse/tests/regression/cases/test_tcs_ingestion_fixed_size.yaml +44 -0
- contextsynapse/tests/regression/cases/test_tcs_ingestion_recursive.yaml +46 -0
- contextsynapse/tests/regression/cases/test_tcs_ingestion_section.yaml +46 -0
- contextsynapse/tests/regression/cases/test_tcs_ingestion_semantic.yaml +44 -0
- contextsynapse/tests/regression/cases/test_time_travel_as_of.yaml +35 -0
- contextsynapse/tests/regression/cases/test_time_travel_at_commit.yaml +33 -0
- contextsynapse/tests/regression/cases/test_time_travel_at_timestamp.yaml +33 -0
- contextsynapse/tests/regression/cases/test_time_travel_for_system_time_all.yaml +37 -0
- contextsynapse/tests/regression/cases/test_time_travel_versions_between.yaml +39 -0
- contextsynapse/tests/regression/cases/test_traverse_basic.yaml +31 -0
- contextsynapse/tests/regression/cases/test_traverse_max_depth.yaml +34 -0
- contextsynapse/tests/regression/cases/test_update_edge_basic.yaml +29 -0
- contextsynapse/tests/regression/evaluation_metrics.py +227 -0
- contextsynapse/tests/regression/regression_runner.py +1081 -0
- contextsynapse/tests/regression/setup_regression_data.py +212 -0
- contextsynapse/tests/regression/test_basic_only.py +84 -0
- contextsynapse/tests/regression/test_namespace_creation.py +29 -0
- contextsynapse/tests/regression/test_single_fix.py +61 -0
- contextsynapse/tests/regression/test_single_query.py +20 -0
- contextsynapse/tests/regression/validate_test_syntax.py +179 -0
- contextsynapse/tests/test_advanced_features.py +347 -0
- contextsynapse/tests/test_aiql_summary.py +283 -0
- contextsynapse/tests/test_all_aiql_queries.py +948 -0
- contextsynapse/tests/test_all_create_queries.py +213 -0
- contextsynapse/tests/test_api_vs_native.py +280 -0
- contextsynapse/tests/test_chunking_dedup.py +199 -0
- contextsynapse/tests/test_connectors.py +171 -0
- contextsynapse/tests/test_edges_and_match.py +202 -0
- contextsynapse/tests/test_extraction_pipeline_integration.py +581 -0
- contextsynapse/tests/test_name_property.py +224 -0
- contextsynapse/tests/test_node_edge_metadata.py +250 -0
- contextsynapse/tests/test_playground_conn.py +114 -0
- contextsynapse/tests/test_query_cache.py +213 -0
- contextsynapse/tests/test_source_policy_auth.py +348 -0
- contextsynapse/tests/test_temporal_and_domain.py +224 -0
- contextsynapse/tests/test_time_travel_e2e.py +571 -0
- contextsynapse/tests/test_traversal_and_aggregation.py +275 -0
- contextsynapse/tools/__init__.py +36 -0
- contextsynapse/tools/a2a_tools.py +106 -0
- contextsynapse/tools/cognition_tools.py +118 -0
- contextsynapse/tools/context.py +905 -0
- contextsynapse/tools/formatting.py +92 -0
- contextsynapse/tools/gateway_tools.py +186 -0
- contextsynapse/tools/graph.py +1700 -0
- contextsynapse/tools/intelligence.py +235 -0
- contextsynapse/tools/memory.py +118 -0
- contextsynapse/tools/registry.py +373 -0
- contextsynapse/tools/shield_tools.py +159 -0
- contextsynapse/tools/tasks.py +341 -0
- contextsynapse/tools/workspace_tools.py +124 -0
- contextsynapse/utils/__init__.py +15 -0
- contextsynapse/utils/config_loader.py +198 -0
- contextsynapse/utils/env_loader.py +156 -0
- contextsynapse/utils/file_utils.py +288 -0
- contextsynapse/utils/logging_optimizer.py +119 -0
- contextsynapse/vector/__init__.py +30 -0
- contextsynapse/vector/chroma_vector_store.py +152 -0
- contextsynapse/vector/custom_vector_store.py +537 -0
- contextsynapse/vector/faiss_vector_store.py +296 -0
- contextsynapse/vector/qdrant_vector_store.py +163 -0
- contextsynapse/vector/vector_db.py +953 -0
- contextsynapse/vector/vector_db_manager.py +197 -0
- contextsynapse/workspace/__init__.py +27 -0
- contextsynapse/workspace/base.py +94 -0
- contextsynapse/workspace/git.py +228 -0
- contextsynapse/workspace/github.py +78 -0
- contextsynapse/workspace/local.py +73 -0
- contextsynapse/workspace/tools.py +222 -0
- contextsynapse-1.0.0.dist-info/METADATA +847 -0
- contextsynapse-1.0.0.dist-info/RECORD +776 -0
- contextsynapse-1.0.0.dist-info/WHEEL +5 -0
- contextsynapse-1.0.0.dist-info/entry_points.txt +4 -0
- contextsynapse-1.0.0.dist-info/licenses/LICENSE +200 -0
- contextsynapse-1.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,918 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Ray Remote Functions for Pipeline Stages
|
|
3
|
+
|
|
4
|
+
Distributed execution of pipeline stages using Ray.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import logging
|
|
8
|
+
from typing import Dict, List, Any, Optional
|
|
9
|
+
import json
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
import time
|
|
12
|
+
|
|
13
|
+
logger = logging.getLogger(__name__)
|
|
14
|
+
|
|
15
|
+
# Try to import Ray
|
|
16
|
+
try:
|
|
17
|
+
import ray
|
|
18
|
+
RAY_AVAILABLE = True
|
|
19
|
+
except ImportError:
|
|
20
|
+
RAY_AVAILABLE = False
|
|
21
|
+
logger.warning("Ray not available. Install with: pip install ray")
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def _init_ray_if_needed() -> bool:
|
|
25
|
+
"""
|
|
26
|
+
Initialize Ray if not already initialized with optimized configuration.
|
|
27
|
+
|
|
28
|
+
Uses configuration from ray.yaml if available, otherwise uses sensible defaults.
|
|
29
|
+
"""
|
|
30
|
+
if not RAY_AVAILABLE:
|
|
31
|
+
return False
|
|
32
|
+
|
|
33
|
+
if not ray.is_initialized():
|
|
34
|
+
try:
|
|
35
|
+
# Try to load configuration
|
|
36
|
+
ray_config = None
|
|
37
|
+
try:
|
|
38
|
+
from ...ray.ray_config import RayConfigLoader
|
|
39
|
+
ray_config = RayConfigLoader.load_config()
|
|
40
|
+
except:
|
|
41
|
+
pass
|
|
42
|
+
|
|
43
|
+
# Configure Ray with optimal settings for chunking
|
|
44
|
+
init_kwargs = {
|
|
45
|
+
"ignore_reinit_error": True,
|
|
46
|
+
"num_cpus": None, # Use all available CPUs
|
|
47
|
+
"object_store_memory": 2 * 1024 * 1024 * 1024, # 2GB object store
|
|
48
|
+
"include_dashboard": False, # Disable dashboard for performance
|
|
49
|
+
"log_to_driver": False, # Reduce logging overhead
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
# Override with config if available
|
|
53
|
+
if ray_config:
|
|
54
|
+
if hasattr(ray_config, 'cluster') and ray_config.cluster:
|
|
55
|
+
if ray_config.cluster.get('address') and ray_config.cluster['address'] != 'auto':
|
|
56
|
+
init_kwargs['address'] = ray_config.cluster['address']
|
|
57
|
+
|
|
58
|
+
if hasattr(ray_config, 'resources') and ray_config.resources:
|
|
59
|
+
if ray_config.resources.get('object_store_memory_mb'):
|
|
60
|
+
init_kwargs['object_store_memory'] = ray_config.resources['object_store_memory_mb'] * 1024 * 1024
|
|
61
|
+
|
|
62
|
+
ray.init(**init_kwargs)
|
|
63
|
+
logger.info(f"Ray initialized for distributed pipeline execution (CPUs: {ray.cluster_resources().get('CPU', 'unknown')})")
|
|
64
|
+
return True
|
|
65
|
+
except Exception as e:
|
|
66
|
+
logger.error(f"Failed to initialize Ray: {e}")
|
|
67
|
+
return False
|
|
68
|
+
|
|
69
|
+
return True
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
if RAY_AVAILABLE:
|
|
73
|
+
@ray.remote(num_cpus=2, memory=2048 * 1024 * 1024) # 2 CPUs, 2GB memory
|
|
74
|
+
def remote_chunk_page(
|
|
75
|
+
page_data: Dict[str, Any],
|
|
76
|
+
document_id: str,
|
|
77
|
+
normalized_version: str,
|
|
78
|
+
chunking_strategy: str,
|
|
79
|
+
chunk_size: int,
|
|
80
|
+
overlap: int,
|
|
81
|
+
namespace: str,
|
|
82
|
+
base_dir: str,
|
|
83
|
+
chunking_params: Optional[Dict[str, Any]] = None,
|
|
84
|
+
normalized_doc_snapshot: Optional[Dict[str, Any]] = None
|
|
85
|
+
) -> List[Dict[str, Any]]:
|
|
86
|
+
"""
|
|
87
|
+
Remote function to chunk a single page with full strategy support.
|
|
88
|
+
|
|
89
|
+
Args:
|
|
90
|
+
page_data: Page data dictionary
|
|
91
|
+
document_id: Document identifier
|
|
92
|
+
normalized_version: Normalized document version
|
|
93
|
+
chunking_strategy: Chunking strategy name
|
|
94
|
+
chunk_size: Chunk size in characters
|
|
95
|
+
overlap: Overlap between chunks
|
|
96
|
+
namespace: Namespace name
|
|
97
|
+
base_dir: Base directory for storage
|
|
98
|
+
chunking_params: Additional chunking parameters (for advanced strategies)
|
|
99
|
+
normalized_doc_snapshot: Snapshot of normalized document (for strategies that need it)
|
|
100
|
+
"""
|
|
101
|
+
try:
|
|
102
|
+
from ..engine.chunk_storage import ChunkStorage
|
|
103
|
+
|
|
104
|
+
chunk_storage = ChunkStorage(namespace=namespace, base_dir=base_dir)
|
|
105
|
+
page_no = page_data.get('page_no')
|
|
106
|
+
page_text = page_data.get('flat_text', '')
|
|
107
|
+
page = page_data # Full page data for strategies that need it
|
|
108
|
+
|
|
109
|
+
context = {
|
|
110
|
+
'document_id': document_id,
|
|
111
|
+
'page_no': page_no,
|
|
112
|
+
'strategy': chunking_strategy,
|
|
113
|
+
'namespace': namespace
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
logger.debug(f"[remote_chunk_page] Starting chunking for page {page_no} (text_length={len(page_text) if page_text else 0})")
|
|
117
|
+
|
|
118
|
+
if not page_text:
|
|
119
|
+
logger.warning(f"[remote_chunk_page] Page {page_no} has no text, returning empty chunks")
|
|
120
|
+
return []
|
|
121
|
+
|
|
122
|
+
chunking_params = chunking_params or {}
|
|
123
|
+
|
|
124
|
+
chunk_start_time = time.time()
|
|
125
|
+
try:
|
|
126
|
+
# Chunk the page text - support all strategies
|
|
127
|
+
if chunking_strategy == "semantic":
|
|
128
|
+
page_chunks = chunk_storage._chunk_semantic(page_text, chunk_size, overlap)
|
|
129
|
+
elif chunking_strategy == "paragraph":
|
|
130
|
+
page_chunks = chunk_storage._chunk_paragraph(page_text, overlap)
|
|
131
|
+
elif chunking_strategy == "recursive":
|
|
132
|
+
page_chunks = chunk_storage._chunk_recursive(page_text, chunk_size, overlap)
|
|
133
|
+
elif chunking_strategy == "sliding_window":
|
|
134
|
+
step_size = chunking_params.get('step_size', chunk_size - overlap)
|
|
135
|
+
page_chunks = chunk_storage._chunk_sliding_window(page_text, chunk_size, overlap, step_size=step_size)
|
|
136
|
+
elif chunking_strategy == "section_based":
|
|
137
|
+
section_markers = chunking_params.get('section_markers', ["##", "###", "Chapter", "Section", "Part", "#"])
|
|
138
|
+
preserve_hierarchy = chunking_params.get('preserve_hierarchy', True)
|
|
139
|
+
page_chunks = chunk_storage._chunk_section_based(page, chunk_size, overlap,
|
|
140
|
+
section_markers=section_markers,
|
|
141
|
+
preserve_hierarchy=preserve_hierarchy)
|
|
142
|
+
elif chunking_strategy == "table_aware":
|
|
143
|
+
# Need normalized doc for table positions
|
|
144
|
+
if normalized_doc_snapshot:
|
|
145
|
+
extract_tables = chunking_params.get('extract_tables', True)
|
|
146
|
+
preserve_table_context = chunking_params.get('preserve_table_context', True)
|
|
147
|
+
table_chunk_size = chunking_params.get('table_chunk_size', 300)
|
|
148
|
+
context_before = chunking_params.get('context_before', 200)
|
|
149
|
+
context_after = chunking_params.get('context_after', 200)
|
|
150
|
+
page_chunks = chunk_storage._chunk_table_aware(page, normalized_doc_snapshot, chunk_size, overlap,
|
|
151
|
+
extract_tables=extract_tables,
|
|
152
|
+
preserve_table_context=preserve_table_context,
|
|
153
|
+
table_chunk_size=table_chunk_size,
|
|
154
|
+
context_before=context_before,
|
|
155
|
+
context_after=context_after)
|
|
156
|
+
else:
|
|
157
|
+
# Fallback to semantic if no normalized doc
|
|
158
|
+
page_chunks = chunk_storage._chunk_semantic(page_text, chunk_size, overlap)
|
|
159
|
+
elif chunking_strategy == "smart":
|
|
160
|
+
# Need normalized doc for smart detection
|
|
161
|
+
if normalized_doc_snapshot:
|
|
162
|
+
document_type_detection = chunking_params.get('document_type_detection', True)
|
|
163
|
+
fallback_strategy = chunking_params.get('fallback_strategy', 'fixed')
|
|
164
|
+
page_chunks = chunk_storage._chunk_smart(page, normalized_doc_snapshot, chunk_size, overlap,
|
|
165
|
+
document_type_detection=document_type_detection,
|
|
166
|
+
fallback_strategy=fallback_strategy)
|
|
167
|
+
else:
|
|
168
|
+
# Fallback to semantic if no normalized doc
|
|
169
|
+
page_chunks = chunk_storage._chunk_semantic(page_text, chunk_size, overlap)
|
|
170
|
+
elif chunking_strategy == "token_based":
|
|
171
|
+
max_tokens = chunking_params.get('max_tokens', 512)
|
|
172
|
+
overlap_tokens = chunking_params.get('overlap_tokens', 50)
|
|
173
|
+
tokenizer = chunking_params.get('tokenizer', 'tiktoken')
|
|
174
|
+
model = chunking_params.get('model', 'gpt-3.5-turbo')
|
|
175
|
+
page_chunks = chunk_storage._chunk_token_based(page_text, max_tokens, overlap_tokens, tokenizer, model)
|
|
176
|
+
elif chunking_strategy == "semantic_similarity":
|
|
177
|
+
similarity_threshold = chunking_params.get('similarity_threshold', 0.7)
|
|
178
|
+
max_chunk_size = chunking_params.get('max_chunk_size', chunk_size)
|
|
179
|
+
min_chunk_size = chunking_params.get('min_chunk_size', 200)
|
|
180
|
+
embedding_model = chunking_params.get('embedding_model', 'sentence-transformers')
|
|
181
|
+
page_chunks = chunk_storage._chunk_semantic_similarity(page_text, similarity_threshold, max_chunk_size, min_chunk_size, embedding_model)
|
|
182
|
+
elif chunking_strategy == "topic_aware":
|
|
183
|
+
num_topics = chunking_params.get('num_topics', 10)
|
|
184
|
+
min_chunk_size = chunking_params.get('min_chunk_size', 300)
|
|
185
|
+
topic_model = chunking_params.get('topic_model', 'simple')
|
|
186
|
+
page_chunks = chunk_storage._chunk_topic_aware(page_text, num_topics, min_chunk_size, topic_model)
|
|
187
|
+
elif chunking_strategy == "qa_aware":
|
|
188
|
+
question_length = chunking_params.get('question_length', 50)
|
|
189
|
+
context_size = chunking_params.get('context_size', 500)
|
|
190
|
+
answer_markers = chunking_params.get('answer_markers', None)
|
|
191
|
+
page_chunks = chunk_storage._chunk_qa_aware(page_text, question_length, context_size, answer_markers)
|
|
192
|
+
elif chunking_strategy == "code_aware":
|
|
193
|
+
preserve_code_blocks = chunking_params.get('preserve_code_blocks', True)
|
|
194
|
+
code_context = chunking_params.get('code_context', 200)
|
|
195
|
+
min_code_block_size = chunking_params.get('min_code_block_size', 50)
|
|
196
|
+
page_chunks = chunk_storage._chunk_code_aware(page, chunk_size, overlap, preserve_code_blocks, code_context, min_code_block_size)
|
|
197
|
+
elif chunking_strategy == "image_aware":
|
|
198
|
+
if normalized_doc_snapshot:
|
|
199
|
+
include_captions = chunking_params.get('include_captions', True)
|
|
200
|
+
caption_context = chunking_params.get('caption_context', 300)
|
|
201
|
+
image_chunk_size = chunking_params.get('image_chunk_size', 200)
|
|
202
|
+
page_chunks = chunk_storage._chunk_image_aware(page, normalized_doc_snapshot, chunk_size, overlap, include_captions, caption_context, image_chunk_size)
|
|
203
|
+
else:
|
|
204
|
+
page_chunks = chunk_storage._chunk_semantic(page_text, chunk_size, overlap)
|
|
205
|
+
elif chunking_strategy == "adaptive":
|
|
206
|
+
base_chunk_size = chunking_params.get('base_chunk_size', chunk_size)
|
|
207
|
+
complexity_threshold = chunking_params.get('complexity_threshold', 0.5)
|
|
208
|
+
density_factor = chunking_params.get('density_factor', 1.2)
|
|
209
|
+
page_chunks = chunk_storage._chunk_adaptive(page_text, base_chunk_size, overlap, complexity_threshold, density_factor)
|
|
210
|
+
elif chunking_strategy == "entity_aware":
|
|
211
|
+
entity_types = chunking_params.get('entity_types', None)
|
|
212
|
+
page_chunks = chunk_storage._chunk_entity_aware(page_text, chunk_size, overlap, entity_types)
|
|
213
|
+
elif chunking_strategy == "multilang":
|
|
214
|
+
per_language_chunking = chunking_params.get('per_language_chunking', True)
|
|
215
|
+
page_chunks = chunk_storage._chunk_multilang(page_text, chunk_size, overlap, per_language_chunking)
|
|
216
|
+
elif chunking_strategy == "citation_aware":
|
|
217
|
+
citation_patterns = chunking_params.get('citation_patterns', None)
|
|
218
|
+
citation_context = chunking_params.get('citation_context', 300)
|
|
219
|
+
page_chunks = chunk_storage._chunk_citation_aware(page_text, chunk_size, overlap, citation_patterns, citation_context)
|
|
220
|
+
elif chunking_strategy == "dialogue_aware":
|
|
221
|
+
speaker_detection = chunking_params.get('speaker_detection', True)
|
|
222
|
+
preserve_turns = chunking_params.get('preserve_turns', True)
|
|
223
|
+
page_chunks = chunk_storage._chunk_dialogue_aware(page_text, chunk_size, overlap, speaker_detection, preserve_turns)
|
|
224
|
+
elif chunking_strategy == "formula_aware":
|
|
225
|
+
formula_patterns = chunking_params.get('formula_patterns', None)
|
|
226
|
+
formula_context = chunking_params.get('formula_context', 200)
|
|
227
|
+
page_chunks = chunk_storage._chunk_formula_aware(page_text, chunk_size, overlap, formula_patterns, formula_context)
|
|
228
|
+
elif chunking_strategy == "list_aware":
|
|
229
|
+
preserve_lists = chunking_params.get('preserve_lists', True)
|
|
230
|
+
list_context = chunking_params.get('list_context', 200)
|
|
231
|
+
page_chunks = chunk_storage._chunk_list_aware(page, chunk_size, overlap, preserve_lists, list_context)
|
|
232
|
+
else: # fixed (default)
|
|
233
|
+
page_chunks = chunk_storage._chunk_fixed(page_text, chunk_size, overlap)
|
|
234
|
+
|
|
235
|
+
chunk_time = time.time() - chunk_start_time
|
|
236
|
+
logger.debug(f"[remote_chunk_page] Page {page_no}: Created {len(page_chunks)} chunks in {chunk_time:.3f}s using {chunking_strategy}")
|
|
237
|
+
except Exception as chunk_err:
|
|
238
|
+
chunk_time = time.time() - chunk_start_time
|
|
239
|
+
logger.error(f"[remote_chunk_page] Error chunking page {page_no} with {chunking_strategy}: {chunk_err} (after {chunk_time:.3f}s)")
|
|
240
|
+
import traceback
|
|
241
|
+
logger.error(f"[remote_chunk_page] Traceback:\n{traceback.format_exc()}")
|
|
242
|
+
return []
|
|
243
|
+
|
|
244
|
+
# Create chunk properties
|
|
245
|
+
chunks = []
|
|
246
|
+
# Validate chunks don't exceed page boundaries
|
|
247
|
+
page_text_length = len(page_text)
|
|
248
|
+
validation_start = time.time()
|
|
249
|
+
|
|
250
|
+
chunks_validated = 0
|
|
251
|
+
chunks_skipped = 0
|
|
252
|
+
for chunk_idx, (chunk_text, start, end) in enumerate(page_chunks):
|
|
253
|
+
# Validate chunk boundaries are within page
|
|
254
|
+
if start < 0:
|
|
255
|
+
logger.warning(f"[remote_chunk_page] Page {page_no}, chunk {chunk_idx}: negative start ({start}), clamping to 0")
|
|
256
|
+
start = 0
|
|
257
|
+
|
|
258
|
+
if end > page_text_length:
|
|
259
|
+
logger.warning(f"[remote_chunk_page] Page {page_no}, chunk {chunk_idx}: exceeds boundary (end={end}, page_length={page_text_length}), clamping")
|
|
260
|
+
end = page_text_length
|
|
261
|
+
# Truncate chunk text to match
|
|
262
|
+
chunk_text = chunk_text[:end - start]
|
|
263
|
+
|
|
264
|
+
if start >= end:
|
|
265
|
+
logger.warning(f"[remote_chunk_page] Page {page_no}, chunk {chunk_idx}: invalid boundaries (start={start}, end={end}), skipping")
|
|
266
|
+
chunks_skipped += 1
|
|
267
|
+
continue
|
|
268
|
+
|
|
269
|
+
chunks_validated += 1
|
|
270
|
+
|
|
271
|
+
from contextsynapse.extraction.id_generator import IDGenerator
|
|
272
|
+
chunk_id = IDGenerator.generate_chunk_id(
|
|
273
|
+
document_id=document_id,
|
|
274
|
+
page_no=page_no,
|
|
275
|
+
chunk_index=chunk_idx,
|
|
276
|
+
chunking_strategy=chunking_strategy,
|
|
277
|
+
strategy_version="v1",
|
|
278
|
+
namespace=None
|
|
279
|
+
)
|
|
280
|
+
chunk_props = chunk_storage.create_chunk_with_pointer(
|
|
281
|
+
chunk_id=chunk_id,
|
|
282
|
+
content=chunk_text,
|
|
283
|
+
document_id=document_id,
|
|
284
|
+
normalized_version=normalized_version,
|
|
285
|
+
page_no=page_no,
|
|
286
|
+
start_char=start,
|
|
287
|
+
end_char=end,
|
|
288
|
+
chunk_index=chunk_idx,
|
|
289
|
+
chunking_strategy=chunking_strategy,
|
|
290
|
+
strategy_version="v1",
|
|
291
|
+
chunking_params={
|
|
292
|
+
"chunk_size": chunk_size,
|
|
293
|
+
"overlap": overlap,
|
|
294
|
+
**chunking_params
|
|
295
|
+
},
|
|
296
|
+
document_node_id=None, # Will be set later
|
|
297
|
+
metadata={
|
|
298
|
+
"page_boundary_validated": True, # Flag to indicate validation
|
|
299
|
+
}
|
|
300
|
+
)
|
|
301
|
+
chunks.append(chunk_props)
|
|
302
|
+
|
|
303
|
+
validation_time = time.time() - validation_start
|
|
304
|
+
total_time = time.time() - chunk_start_time
|
|
305
|
+
logger.info(f"[remote_chunk_page] Page {page_no} complete: {chunks_validated} chunks created, {chunks_skipped} skipped "
|
|
306
|
+
f"(validation: {validation_time:.3f}s, total: {total_time:.3f}s)")
|
|
307
|
+
|
|
308
|
+
return chunks
|
|
309
|
+
except Exception as e:
|
|
310
|
+
logger.error(f"[remote_chunk_page] Fatal error for page {page_no}: {e}")
|
|
311
|
+
import traceback
|
|
312
|
+
logger.error(f"[remote_chunk_page] Full traceback:\n{traceback.format_exc()}")
|
|
313
|
+
return []
|
|
314
|
+
|
|
315
|
+
@ray.remote(num_cpus=1, num_gpus=0.5, memory=4096 * 1024 * 1024) # 1 CPU, 0.5 GPU, 4GB memory
|
|
316
|
+
def remote_embed_chunk(
|
|
317
|
+
chunk_id: str,
|
|
318
|
+
content: str,
|
|
319
|
+
model: str,
|
|
320
|
+
dimensions: int,
|
|
321
|
+
embedding_field: str
|
|
322
|
+
) -> Dict[str, Any]:
|
|
323
|
+
"""Remote function to generate embedding for a single chunk."""
|
|
324
|
+
try:
|
|
325
|
+
from ...llm.openai_embedding_service import OpenAIEmbeddingService
|
|
326
|
+
|
|
327
|
+
embedding_service = OpenAIEmbeddingService()
|
|
328
|
+
embedding = embedding_service.generate_embedding(content)
|
|
329
|
+
|
|
330
|
+
return {
|
|
331
|
+
"chunk_id": chunk_id,
|
|
332
|
+
"embedding": embedding,
|
|
333
|
+
"embedding_field": embedding_field
|
|
334
|
+
}
|
|
335
|
+
except Exception as e:
|
|
336
|
+
logger.error(f"Error in remote_embed_chunk for {chunk_id}: {e}")
|
|
337
|
+
return {
|
|
338
|
+
"chunk_id": chunk_id,
|
|
339
|
+
"embedding": None,
|
|
340
|
+
"error": str(e)
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
@ray.remote(num_cpus=2, memory=2048 * 1024 * 1024) # 2 CPUs, 2GB memory
|
|
344
|
+
def remote_extract_entities_from_chunk(
|
|
345
|
+
chunk_id: str,
|
|
346
|
+
content: str,
|
|
347
|
+
llm_model: str,
|
|
348
|
+
prompt: str
|
|
349
|
+
) -> Dict[str, Any]:
|
|
350
|
+
"""Remote function to extract entities from a single chunk."""
|
|
351
|
+
try:
|
|
352
|
+
from ...llm.universal_llm import AIContextDBUniversalLLM
|
|
353
|
+
|
|
354
|
+
llm_manager = AIContextDBUniversalLLM()
|
|
355
|
+
|
|
356
|
+
# Prepare prompt
|
|
357
|
+
full_prompt = f"{prompt}\n\nText:\n{content[:2000]}" # Limit content size
|
|
358
|
+
|
|
359
|
+
# Call LLM
|
|
360
|
+
result = llm_manager.generate(
|
|
361
|
+
model_name=llm_model,
|
|
362
|
+
prompt=full_prompt,
|
|
363
|
+
temperature=0.3,
|
|
364
|
+
max_tokens=2000
|
|
365
|
+
)
|
|
366
|
+
|
|
367
|
+
if not result.get('success'):
|
|
368
|
+
return {
|
|
369
|
+
"chunk_id": chunk_id,
|
|
370
|
+
"entities": [],
|
|
371
|
+
"error": result.get('error', 'Unknown error')
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
response_text = result.get('response', '')
|
|
375
|
+
|
|
376
|
+
# Parse entities from response
|
|
377
|
+
try:
|
|
378
|
+
entities_data = json.loads(response_text)
|
|
379
|
+
if not isinstance(entities_data, list):
|
|
380
|
+
entities_data = [entities_data]
|
|
381
|
+
except:
|
|
382
|
+
# Fallback: try to extract entities from text response
|
|
383
|
+
import re
|
|
384
|
+
entities_data = []
|
|
385
|
+
patterns = [
|
|
386
|
+
r'(\w+):\s*([^(]+)\s*\(([^)]+)\)',
|
|
387
|
+
r'Name:\s*([^,]+),\s*Type:\s*([^,]+)',
|
|
388
|
+
]
|
|
389
|
+
for pattern in patterns:
|
|
390
|
+
matches = re.findall(pattern, response_text)
|
|
391
|
+
for match in matches:
|
|
392
|
+
if len(match) >= 2:
|
|
393
|
+
entities_data.append({
|
|
394
|
+
'name': match[1] if len(match) > 1 else match[0],
|
|
395
|
+
'type': match[2] if len(match) > 2 else 'Entity'
|
|
396
|
+
})
|
|
397
|
+
|
|
398
|
+
return {
|
|
399
|
+
"chunk_id": chunk_id,
|
|
400
|
+
"entities": entities_data,
|
|
401
|
+
"error": None
|
|
402
|
+
}
|
|
403
|
+
except Exception as e:
|
|
404
|
+
logger.error(f"Error in remote_extract_entities_from_chunk for {chunk_id}: {e}")
|
|
405
|
+
return {
|
|
406
|
+
"chunk_id": chunk_id,
|
|
407
|
+
"entities": [],
|
|
408
|
+
"error": str(e)
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
|
|
412
|
+
def chunk_pages_parallel(
|
|
413
|
+
pages: List[Dict[str, Any]],
|
|
414
|
+
document_id: str,
|
|
415
|
+
normalized_version: str,
|
|
416
|
+
chunking_strategy: str,
|
|
417
|
+
chunk_size: int,
|
|
418
|
+
overlap: int,
|
|
419
|
+
namespace: str,
|
|
420
|
+
base_dir: str,
|
|
421
|
+
use_ray: bool = True,
|
|
422
|
+
chunking_params: Optional[Dict[str, Any]] = None,
|
|
423
|
+
normalized_doc: Optional[Dict[str, Any]] = None,
|
|
424
|
+
max_concurrent: Optional[int] = None
|
|
425
|
+
) -> List[Dict[str, Any]]:
|
|
426
|
+
"""
|
|
427
|
+
Chunk multiple pages in parallel using Ray with optimized batching.
|
|
428
|
+
|
|
429
|
+
Args:
|
|
430
|
+
pages: List of page data dictionaries
|
|
431
|
+
document_id: Document identifier
|
|
432
|
+
normalized_version: Normalized document version
|
|
433
|
+
chunking_strategy: Chunking strategy (all strategies supported)
|
|
434
|
+
chunk_size: Chunk size in characters
|
|
435
|
+
overlap: Overlap between chunks
|
|
436
|
+
namespace: Namespace name
|
|
437
|
+
base_dir: Base directory for storage
|
|
438
|
+
use_ray: Whether to use Ray for parallel processing
|
|
439
|
+
chunking_params: Additional chunking parameters for advanced strategies
|
|
440
|
+
normalized_doc: Full normalized document (for strategies that need it)
|
|
441
|
+
max_concurrent: Maximum concurrent Ray tasks (None = auto-detect)
|
|
442
|
+
|
|
443
|
+
Returns:
|
|
444
|
+
List of chunk property dictionaries
|
|
445
|
+
"""
|
|
446
|
+
if not use_ray or not RAY_AVAILABLE or not _init_ray_if_needed():
|
|
447
|
+
# Fallback to sequential processing
|
|
448
|
+
from ..engine.chunk_storage import ChunkStorage
|
|
449
|
+
chunk_storage = ChunkStorage(namespace=namespace, base_dir=base_dir)
|
|
450
|
+
|
|
451
|
+
all_chunks = []
|
|
452
|
+
for page in pages:
|
|
453
|
+
page_no = page.get('page_no')
|
|
454
|
+
page_text = page.get('flat_text', '')
|
|
455
|
+
|
|
456
|
+
if not page_text:
|
|
457
|
+
continue
|
|
458
|
+
|
|
459
|
+
chunking_params = chunking_params or {}
|
|
460
|
+
|
|
461
|
+
# Support all strategies in fallback mode
|
|
462
|
+
if chunking_strategy == "semantic":
|
|
463
|
+
page_chunks = chunk_storage._chunk_semantic(page_text, chunk_size, overlap)
|
|
464
|
+
elif chunking_strategy == "paragraph":
|
|
465
|
+
page_chunks = chunk_storage._chunk_paragraph(page_text, overlap)
|
|
466
|
+
elif chunking_strategy == "recursive":
|
|
467
|
+
page_chunks = chunk_storage._chunk_recursive(page_text, chunk_size, overlap)
|
|
468
|
+
elif chunking_strategy == "sliding_window":
|
|
469
|
+
step_size = chunking_params.get('step_size', chunk_size - overlap)
|
|
470
|
+
page_chunks = chunk_storage._chunk_sliding_window(page_text, chunk_size, overlap, step_size=step_size)
|
|
471
|
+
elif chunking_strategy == "section_based":
|
|
472
|
+
section_markers = chunking_params.get('section_markers', ["##", "###", "Chapter", "Section", "Part", "#"])
|
|
473
|
+
preserve_hierarchy = chunking_params.get('preserve_hierarchy', True)
|
|
474
|
+
page_chunks = chunk_storage._chunk_section_based(page, chunk_size, overlap,
|
|
475
|
+
section_markers=section_markers,
|
|
476
|
+
preserve_hierarchy=preserve_hierarchy)
|
|
477
|
+
elif chunking_strategy == "table_aware" and normalized_doc:
|
|
478
|
+
extract_tables = chunking_params.get('extract_tables', True)
|
|
479
|
+
preserve_table_context = chunking_params.get('preserve_table_context', True)
|
|
480
|
+
table_chunk_size = chunking_params.get('table_chunk_size', 300)
|
|
481
|
+
context_before = chunking_params.get('context_before', 200)
|
|
482
|
+
context_after = chunking_params.get('context_after', 200)
|
|
483
|
+
page_chunks = chunk_storage._chunk_table_aware(page, normalized_doc, chunk_size, overlap,
|
|
484
|
+
extract_tables=extract_tables,
|
|
485
|
+
preserve_table_context=preserve_table_context,
|
|
486
|
+
table_chunk_size=table_chunk_size,
|
|
487
|
+
context_before=context_before,
|
|
488
|
+
context_after=context_after)
|
|
489
|
+
elif chunking_strategy == "smart" and normalized_doc:
|
|
490
|
+
document_type_detection = chunking_params.get('document_type_detection', True)
|
|
491
|
+
fallback_strategy = chunking_params.get('fallback_strategy', 'fixed')
|
|
492
|
+
page_chunks = chunk_storage._chunk_smart(page, normalized_doc, chunk_size, overlap,
|
|
493
|
+
document_type_detection=document_type_detection,
|
|
494
|
+
fallback_strategy=fallback_strategy)
|
|
495
|
+
elif chunking_strategy == "token_based":
|
|
496
|
+
max_tokens = chunking_params.get('max_tokens', 512)
|
|
497
|
+
overlap_tokens = chunking_params.get('overlap_tokens', 50)
|
|
498
|
+
tokenizer = chunking_params.get('tokenizer', 'tiktoken')
|
|
499
|
+
model = chunking_params.get('model', 'gpt-3.5-turbo')
|
|
500
|
+
page_chunks = chunk_storage._chunk_token_based(page_text, max_tokens, overlap_tokens, tokenizer, model)
|
|
501
|
+
elif chunking_strategy == "semantic_similarity":
|
|
502
|
+
similarity_threshold = chunking_params.get('similarity_threshold', 0.7)
|
|
503
|
+
max_chunk_size = chunking_params.get('max_chunk_size', chunk_size)
|
|
504
|
+
min_chunk_size = chunking_params.get('min_chunk_size', 200)
|
|
505
|
+
embedding_model = chunking_params.get('embedding_model', 'sentence-transformers')
|
|
506
|
+
page_chunks = chunk_storage._chunk_semantic_similarity(page_text, similarity_threshold, max_chunk_size, min_chunk_size, embedding_model)
|
|
507
|
+
elif chunking_strategy == "topic_aware":
|
|
508
|
+
num_topics = chunking_params.get('num_topics', 10)
|
|
509
|
+
min_chunk_size = chunking_params.get('min_chunk_size', 300)
|
|
510
|
+
topic_model = chunking_params.get('topic_model', 'simple')
|
|
511
|
+
page_chunks = chunk_storage._chunk_topic_aware(page_text, num_topics, min_chunk_size, topic_model)
|
|
512
|
+
elif chunking_strategy == "qa_aware":
|
|
513
|
+
question_length = chunking_params.get('question_length', 50)
|
|
514
|
+
context_size = chunking_params.get('context_size', 500)
|
|
515
|
+
answer_markers = chunking_params.get('answer_markers', None)
|
|
516
|
+
page_chunks = chunk_storage._chunk_qa_aware(page_text, question_length, context_size, answer_markers)
|
|
517
|
+
elif chunking_strategy == "code_aware" and normalized_doc:
|
|
518
|
+
preserve_code_blocks = chunking_params.get('preserve_code_blocks', True)
|
|
519
|
+
code_context = chunking_params.get('code_context', 200)
|
|
520
|
+
min_code_block_size = chunking_params.get('min_code_block_size', 50)
|
|
521
|
+
page_chunks = chunk_storage._chunk_code_aware(page, chunk_size, overlap, preserve_code_blocks, code_context, min_code_block_size)
|
|
522
|
+
elif chunking_strategy == "image_aware" and normalized_doc:
|
|
523
|
+
include_captions = chunking_params.get('include_captions', True)
|
|
524
|
+
caption_context = chunking_params.get('caption_context', 300)
|
|
525
|
+
image_chunk_size = chunking_params.get('image_chunk_size', 200)
|
|
526
|
+
page_chunks = chunk_storage._chunk_image_aware(page, normalized_doc, chunk_size, overlap, include_captions, caption_context, image_chunk_size)
|
|
527
|
+
elif chunking_strategy == "adaptive":
|
|
528
|
+
base_chunk_size = chunking_params.get('base_chunk_size', chunk_size)
|
|
529
|
+
complexity_threshold = chunking_params.get('complexity_threshold', 0.5)
|
|
530
|
+
density_factor = chunking_params.get('density_factor', 1.2)
|
|
531
|
+
page_chunks = chunk_storage._chunk_adaptive(page_text, base_chunk_size, overlap, complexity_threshold, density_factor)
|
|
532
|
+
elif chunking_strategy == "entity_aware":
|
|
533
|
+
entity_types = chunking_params.get('entity_types', None)
|
|
534
|
+
page_chunks = chunk_storage._chunk_entity_aware(page_text, chunk_size, overlap, entity_types)
|
|
535
|
+
elif chunking_strategy == "multilang":
|
|
536
|
+
per_language_chunking = chunking_params.get('per_language_chunking', True)
|
|
537
|
+
page_chunks = chunk_storage._chunk_multilang(page_text, chunk_size, overlap, per_language_chunking)
|
|
538
|
+
elif chunking_strategy == "citation_aware":
|
|
539
|
+
citation_patterns = chunking_params.get('citation_patterns', None)
|
|
540
|
+
citation_context = chunking_params.get('citation_context', 300)
|
|
541
|
+
page_chunks = chunk_storage._chunk_citation_aware(page_text, chunk_size, overlap, citation_patterns, citation_context)
|
|
542
|
+
elif chunking_strategy == "dialogue_aware":
|
|
543
|
+
speaker_detection = chunking_params.get('speaker_detection', True)
|
|
544
|
+
preserve_turns = chunking_params.get('preserve_turns', True)
|
|
545
|
+
page_chunks = chunk_storage._chunk_dialogue_aware(page_text, chunk_size, overlap, speaker_detection, preserve_turns)
|
|
546
|
+
elif chunking_strategy == "formula_aware":
|
|
547
|
+
formula_patterns = chunking_params.get('formula_patterns', None)
|
|
548
|
+
formula_context = chunking_params.get('formula_context', 200)
|
|
549
|
+
page_chunks = chunk_storage._chunk_formula_aware(page_text, chunk_size, overlap, formula_patterns, formula_context)
|
|
550
|
+
elif chunking_strategy == "list_aware" and normalized_doc:
|
|
551
|
+
preserve_lists = chunking_params.get('preserve_lists', True)
|
|
552
|
+
list_context = chunking_params.get('list_context', 200)
|
|
553
|
+
page_chunks = chunk_storage._chunk_list_aware(page, chunk_size, overlap, preserve_lists, list_context)
|
|
554
|
+
else:
|
|
555
|
+
page_chunks = chunk_storage._chunk_fixed(page_text, chunk_size, overlap)
|
|
556
|
+
|
|
557
|
+
for chunk_idx, (chunk_text, start, end) in enumerate(page_chunks):
|
|
558
|
+
from contextsynapse.extraction.id_generator import IDGenerator
|
|
559
|
+
chunk_id = IDGenerator.generate_chunk_id(
|
|
560
|
+
document_id=document_id,
|
|
561
|
+
page_no=page_no,
|
|
562
|
+
chunk_index=chunk_idx,
|
|
563
|
+
chunking_strategy=chunking_strategy,
|
|
564
|
+
strategy_version="v1",
|
|
565
|
+
namespace=None
|
|
566
|
+
)
|
|
567
|
+
chunk_props = chunk_storage.create_chunk_with_pointer(
|
|
568
|
+
chunk_id=chunk_id,
|
|
569
|
+
content=chunk_text,
|
|
570
|
+
document_id=document_id,
|
|
571
|
+
normalized_version=normalized_version,
|
|
572
|
+
page_no=page_no,
|
|
573
|
+
start_char=start,
|
|
574
|
+
end_char=end,
|
|
575
|
+
chunk_index=chunk_idx,
|
|
576
|
+
chunking_strategy=chunking_strategy,
|
|
577
|
+
strategy_version="v1",
|
|
578
|
+
chunking_params={"chunk_size": chunk_size, "overlap": overlap, **(chunking_params or {})},
|
|
579
|
+
document_node_id=None
|
|
580
|
+
)
|
|
581
|
+
all_chunks.append(chunk_props)
|
|
582
|
+
|
|
583
|
+
return all_chunks
|
|
584
|
+
|
|
585
|
+
# Use Ray for parallel processing with optimized batching
|
|
586
|
+
parallel_start = time.time()
|
|
587
|
+
logger.info(f"[chunk_pages_parallel] Starting parallel chunking: {len(pages)} pages, strategy={chunking_strategy}, "
|
|
588
|
+
f"chunk_size={chunk_size}, overlap={overlap}, namespace={namespace}")
|
|
589
|
+
|
|
590
|
+
# Validate pages are unique to prevent duplicate processing
|
|
591
|
+
page_numbers = [p.get('page_no') for p in pages if p.get('page_no') is not None]
|
|
592
|
+
unique_pages = len(set(page_numbers))
|
|
593
|
+
if unique_pages < len(page_numbers):
|
|
594
|
+
logger.warning(f"[chunk_pages_parallel] Duplicate page numbers detected! {len(page_numbers)} pages but only {unique_pages} unique. Deduplicating...")
|
|
595
|
+
# Deduplicate pages by page_no (keep first occurrence)
|
|
596
|
+
seen_pages = set()
|
|
597
|
+
deduplicated_pages = []
|
|
598
|
+
for page in pages:
|
|
599
|
+
page_no = page.get('page_no')
|
|
600
|
+
if page_no is not None and page_no not in seen_pages:
|
|
601
|
+
seen_pages.add(page_no)
|
|
602
|
+
deduplicated_pages.append(page)
|
|
603
|
+
elif page_no is None:
|
|
604
|
+
# Keep pages without page_no (shouldn't happen, but be safe)
|
|
605
|
+
deduplicated_pages.append(page)
|
|
606
|
+
pages = deduplicated_pages
|
|
607
|
+
logger.info(f"[chunk_pages_parallel] Deduplicated to {len(pages)} unique pages")
|
|
608
|
+
|
|
609
|
+
# Determine if we need normalized doc snapshot
|
|
610
|
+
needs_normalized_doc = chunking_strategy in ["table_aware", "smart", "image_aware", "code_aware", "list_aware"]
|
|
611
|
+
normalized_doc_snapshot = None
|
|
612
|
+
if needs_normalized_doc and normalized_doc:
|
|
613
|
+
# Create a lightweight snapshot (only what's needed)
|
|
614
|
+
# OPTIMIZATION: Only include tables/images metadata, not full page data
|
|
615
|
+
content = normalized_doc.get("content", {})
|
|
616
|
+
normalized_doc_snapshot = {
|
|
617
|
+
"document_id": normalized_doc.get("document_id"),
|
|
618
|
+
"content": {
|
|
619
|
+
"tables": content.get("tables", []),
|
|
620
|
+
"images": content.get("images", []) if chunking_strategy == "image_aware" else []
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
logger.debug(f"[chunk_pages_parallel] Created optimized normalized_doc snapshot: "
|
|
624
|
+
f"{len(normalized_doc_snapshot['content'].get('tables', []))} tables, "
|
|
625
|
+
f"{len(normalized_doc_snapshot['content'].get('images', []))} images")
|
|
626
|
+
|
|
627
|
+
# Optimize batch size based on strategy and available resources
|
|
628
|
+
if max_concurrent is None:
|
|
629
|
+
# Auto-detect based on available CPUs
|
|
630
|
+
try:
|
|
631
|
+
available_cpus = ray.cluster_resources().get("CPU", 4)
|
|
632
|
+
# Use 80% of available CPUs, but cap at reasonable limit
|
|
633
|
+
max_concurrent = min(int(available_cpus * 0.8), len(pages), 20)
|
|
634
|
+
except:
|
|
635
|
+
max_concurrent = min(len(pages), 10) # Conservative default
|
|
636
|
+
|
|
637
|
+
# OPTIMIZED: Process in batches with streaming results to prevent memory accumulation
|
|
638
|
+
# Use smaller batches and process results as they arrive
|
|
639
|
+
all_chunks = []
|
|
640
|
+
chunk_id_set = set() # Track chunk IDs to detect duplicates
|
|
641
|
+
duplicate_count = 0
|
|
642
|
+
batch_size = max_concurrent
|
|
643
|
+
|
|
644
|
+
import time
|
|
645
|
+
import gc
|
|
646
|
+
parallel_start_time = time.time()
|
|
647
|
+
total_batches = (len(pages) + batch_size - 1) // batch_size
|
|
648
|
+
|
|
649
|
+
print(f"[CHUNK] Ray parallel processing: {len(pages)} pages, {max_concurrent} concurrent workers, {total_batches} batches", flush=True)
|
|
650
|
+
print(f"[CHUNK] OPTIMIZED: Processing results incrementally to prevent memory buildup", flush=True)
|
|
651
|
+
|
|
652
|
+
for batch_start in range(0, len(pages), batch_size):
|
|
653
|
+
batch_end = min(batch_start + batch_size, len(pages))
|
|
654
|
+
batch_pages = pages[batch_start:batch_end]
|
|
655
|
+
batch_num = batch_start // batch_size + 1
|
|
656
|
+
|
|
657
|
+
batch_start_time = time.time()
|
|
658
|
+
logger.info(f"[chunk_pages_parallel] Batch {batch_num}/{total_batches}: Processing pages {batch_start+1}-{batch_end} of {len(pages)}")
|
|
659
|
+
print(f"[CHUNK] Ray batch {batch_num}/{total_batches}: Processing pages {batch_start+1}-{batch_end} of {len(pages)}", flush=True)
|
|
660
|
+
|
|
661
|
+
# Create futures for this batch
|
|
662
|
+
futures = [
|
|
663
|
+
remote_chunk_page.remote(
|
|
664
|
+
page, document_id, normalized_version, chunking_strategy,
|
|
665
|
+
chunk_size, overlap, namespace, base_dir,
|
|
666
|
+
chunking_params=chunking_params,
|
|
667
|
+
normalized_doc_snapshot=normalized_doc_snapshot
|
|
668
|
+
)
|
|
669
|
+
for page in batch_pages
|
|
670
|
+
]
|
|
671
|
+
|
|
672
|
+
# OPTIMIZED: Process results as they complete using ray.wait() for streaming
|
|
673
|
+
# This allows processing chunks incrementally instead of waiting for entire batch
|
|
674
|
+
batch_results = []
|
|
675
|
+
remaining_futures = futures.copy()
|
|
676
|
+
|
|
677
|
+
while remaining_futures:
|
|
678
|
+
# Wait for at least one future to complete (timeout=1s to avoid blocking too long)
|
|
679
|
+
ready, remaining_futures = ray.wait(remaining_futures, num_returns=1, timeout=1.0)
|
|
680
|
+
|
|
681
|
+
for future in ready:
|
|
682
|
+
try:
|
|
683
|
+
page_chunks = ray.get(future)
|
|
684
|
+
batch_results.append(page_chunks)
|
|
685
|
+
except Exception as page_err:
|
|
686
|
+
logger.warning(f"[chunk_pages_parallel] Page chunking failed: {page_err}")
|
|
687
|
+
batch_results.append([]) # Continue with empty result
|
|
688
|
+
|
|
689
|
+
batch_time = time.time() - batch_start_time
|
|
690
|
+
batch_chunks = sum(len(page_chunks) for page_chunks in batch_results)
|
|
691
|
+
logger.info(f"[chunk_pages_parallel] Batch {batch_num} completed: {batch_chunks} chunks in {batch_time:.2f}s "
|
|
692
|
+
f"({batch_chunks/batch_time:.1f} chunks/sec)")
|
|
693
|
+
|
|
694
|
+
# OPTIMIZED: Process chunks from batch immediately and clear from memory
|
|
695
|
+
# This prevents accumulating all chunks in memory
|
|
696
|
+
batch_chunks_processed = 0
|
|
697
|
+
for page_chunks in batch_results:
|
|
698
|
+
for chunk in page_chunks:
|
|
699
|
+
chunk_id = chunk.get('chunk_id') or chunk.get('id')
|
|
700
|
+
if not chunk_id:
|
|
701
|
+
# Try to extract from source if available
|
|
702
|
+
source = chunk.get('source', {})
|
|
703
|
+
if isinstance(source, dict):
|
|
704
|
+
chunk_id = source.get('chunk_id')
|
|
705
|
+
if not chunk_id:
|
|
706
|
+
chunk_id = f"chunk_{len(all_chunks) + batch_chunks_processed}"
|
|
707
|
+
|
|
708
|
+
if chunk_id in chunk_id_set:
|
|
709
|
+
duplicate_count += 1
|
|
710
|
+
logger.warning(f"[chunk_pages_parallel] Duplicate chunk ID detected: {chunk_id}. Skipping duplicate.")
|
|
711
|
+
continue
|
|
712
|
+
|
|
713
|
+
chunk_id_set.add(chunk_id)
|
|
714
|
+
all_chunks.append(chunk)
|
|
715
|
+
batch_chunks_processed += 1
|
|
716
|
+
|
|
717
|
+
# Clear batch results from memory after processing
|
|
718
|
+
del batch_results
|
|
719
|
+
del futures
|
|
720
|
+
gc.collect() # Force garbage collection after each batch
|
|
721
|
+
|
|
722
|
+
elapsed = time.time() - parallel_start_time
|
|
723
|
+
remaining = len(pages) - batch_end
|
|
724
|
+
estimated_remaining = (remaining / len(batch_pages)) * batch_time if len(batch_pages) > 0 else 0
|
|
725
|
+
progress_pct = (batch_end / len(pages)) * 100
|
|
726
|
+
|
|
727
|
+
print(f"[CHUNK] Ray batch {batch_num} complete: {batch_chunks} chunks in {batch_time:.2f}s | Total: {len(all_chunks)} chunks | Progress: {progress_pct:.1f}% | Est. remaining: {estimated_remaining:.1f}s", flush=True)
|
|
728
|
+
|
|
729
|
+
logger.debug(f"[chunk_pages_parallel] Batch {batch_num} complete: {len(all_chunks)} unique chunks so far "
|
|
730
|
+
f"(skipped {duplicate_count} duplicates, progress: {progress_pct:.1f}%)")
|
|
731
|
+
|
|
732
|
+
total_time = time.time() - parallel_start_time
|
|
733
|
+
if duplicate_count > 0:
|
|
734
|
+
logger.warning(f"[chunk_pages_parallel] Detected and skipped {duplicate_count} duplicate chunks during parallel processing")
|
|
735
|
+
|
|
736
|
+
chunks_per_sec = len(all_chunks) / total_time if total_time > 0 else 0
|
|
737
|
+
print(f"[CHUNK] Ray parallel processing complete: {len(all_chunks)} unique chunks in {total_time:.2f}s ({chunks_per_sec:.1f} chunks/sec)", flush=True)
|
|
738
|
+
logger.info(f"[chunk_pages_parallel] Parallel processing complete: {len(all_chunks)} unique chunks in {total_time:.2f}s "
|
|
739
|
+
f"({chunks_per_sec:.1f} chunks/sec, {len(pages)/total_time:.1f} pages/sec)")
|
|
740
|
+
|
|
741
|
+
# Final cleanup
|
|
742
|
+
gc.collect()
|
|
743
|
+
return all_chunks
|
|
744
|
+
|
|
745
|
+
|
|
746
|
+
def embed_chunks_parallel(
|
|
747
|
+
chunks: List[Dict[str, Any]],
|
|
748
|
+
model: str = "text-embedding-3-large",
|
|
749
|
+
dimensions: int = 3072,
|
|
750
|
+
embedding_field: str = "embedding",
|
|
751
|
+
use_ray: bool = True
|
|
752
|
+
) -> Dict[str, List[float]]:
|
|
753
|
+
"""
|
|
754
|
+
Generate embeddings for multiple chunks in parallel using Ray.
|
|
755
|
+
|
|
756
|
+
Args:
|
|
757
|
+
chunks: List of chunk dictionaries with 'id' and 'content'
|
|
758
|
+
model: Embedding model name
|
|
759
|
+
dimensions: Embedding dimensions
|
|
760
|
+
embedding_field: Field name to store embedding
|
|
761
|
+
use_ray: Whether to use Ray for parallel processing
|
|
762
|
+
|
|
763
|
+
Returns:
|
|
764
|
+
Dictionary mapping chunk_id to embedding vector
|
|
765
|
+
"""
|
|
766
|
+
if not chunks:
|
|
767
|
+
return {}
|
|
768
|
+
|
|
769
|
+
if not use_ray or not RAY_AVAILABLE or not _init_ray_if_needed():
|
|
770
|
+
# Fallback to sequential processing
|
|
771
|
+
try:
|
|
772
|
+
from ...llm.openai_embedding_service import OpenAIEmbeddingService
|
|
773
|
+
embedding_service = OpenAIEmbeddingService()
|
|
774
|
+
except ImportError:
|
|
775
|
+
logger.warning("OpenAI embedding service not available")
|
|
776
|
+
return {}
|
|
777
|
+
|
|
778
|
+
embeddings = {}
|
|
779
|
+
for chunk in chunks:
|
|
780
|
+
chunk_id = chunk.get("id", "")
|
|
781
|
+
content = chunk.get("content", "")
|
|
782
|
+
if content:
|
|
783
|
+
try:
|
|
784
|
+
embedding = embedding_service.generate_embedding(content)
|
|
785
|
+
embeddings[chunk_id] = embedding
|
|
786
|
+
except Exception as e:
|
|
787
|
+
logger.warning(f"Failed to embed chunk {chunk_id}: {e}")
|
|
788
|
+
|
|
789
|
+
return embeddings
|
|
790
|
+
|
|
791
|
+
# Use Ray for parallel processing
|
|
792
|
+
logger.info(f"Generating embeddings for {len(chunks)} chunks in parallel using Ray")
|
|
793
|
+
futures = [
|
|
794
|
+
remote_embed_chunk.remote(
|
|
795
|
+
chunk.get("id", ""),
|
|
796
|
+
chunk.get("content", ""),
|
|
797
|
+
model,
|
|
798
|
+
dimensions,
|
|
799
|
+
embedding_field
|
|
800
|
+
)
|
|
801
|
+
for chunk in chunks
|
|
802
|
+
]
|
|
803
|
+
|
|
804
|
+
results = ray.get(futures)
|
|
805
|
+
embeddings = {}
|
|
806
|
+
for result in results:
|
|
807
|
+
if result.get("embedding"):
|
|
808
|
+
embeddings[result["chunk_id"]] = result["embedding"]
|
|
809
|
+
|
|
810
|
+
logger.info(f"Generated {len(embeddings)} embeddings in parallel")
|
|
811
|
+
return embeddings
|
|
812
|
+
|
|
813
|
+
|
|
814
|
+
def extract_entities_parallel(
|
|
815
|
+
chunks: List[Dict[str, Any]],
|
|
816
|
+
llm_model: str = "gpt-4",
|
|
817
|
+
prompt: str = "Extract all named entities from the text.",
|
|
818
|
+
use_ray: bool = True
|
|
819
|
+
) -> Dict[str, List[Dict[str, Any]]]:
|
|
820
|
+
"""
|
|
821
|
+
Extract entities from multiple chunks in parallel using Ray.
|
|
822
|
+
|
|
823
|
+
Args:
|
|
824
|
+
chunks: List of chunk dictionaries with 'id' and 'content'
|
|
825
|
+
llm_model: LLM model name
|
|
826
|
+
prompt: Prompt for entity extraction
|
|
827
|
+
use_ray: Whether to use Ray for parallel processing
|
|
828
|
+
|
|
829
|
+
Returns:
|
|
830
|
+
Dictionary mapping chunk_id to list of entities
|
|
831
|
+
"""
|
|
832
|
+
if not chunks:
|
|
833
|
+
return {}
|
|
834
|
+
|
|
835
|
+
if not use_ray or not RAY_AVAILABLE or not _init_ray_if_needed():
|
|
836
|
+
# Fallback to sequential processing
|
|
837
|
+
try:
|
|
838
|
+
from ...llm.universal_llm import AIContextDBUniversalLLM
|
|
839
|
+
llm_manager = AIContextDBUniversalLLM()
|
|
840
|
+
except ImportError:
|
|
841
|
+
logger.warning("LLM module not available for entity extraction")
|
|
842
|
+
return {}
|
|
843
|
+
|
|
844
|
+
all_entities = {}
|
|
845
|
+
for chunk in chunks:
|
|
846
|
+
chunk_id = chunk.get("id", "")
|
|
847
|
+
content = chunk.get("content", "")
|
|
848
|
+
if not content:
|
|
849
|
+
continue
|
|
850
|
+
|
|
851
|
+
try:
|
|
852
|
+
full_prompt = f"{prompt}\n\nText:\n{content[:2000]}"
|
|
853
|
+
result = llm_manager.generate(
|
|
854
|
+
model_name=llm_model,
|
|
855
|
+
prompt=full_prompt,
|
|
856
|
+
temperature=0.3,
|
|
857
|
+
max_tokens=2000
|
|
858
|
+
)
|
|
859
|
+
|
|
860
|
+
if result.get('success'):
|
|
861
|
+
response_text = result.get('response', '')
|
|
862
|
+
try:
|
|
863
|
+
entities_data = json.loads(response_text)
|
|
864
|
+
if not isinstance(entities_data, list):
|
|
865
|
+
entities_data = [entities_data]
|
|
866
|
+
except:
|
|
867
|
+
entities_data = _parse_entities_from_text(response_text)
|
|
868
|
+
all_entities[chunk_id] = entities_data
|
|
869
|
+
except Exception as e:
|
|
870
|
+
logger.warning(f"Failed to extract entities from chunk {chunk_id}: {e}")
|
|
871
|
+
|
|
872
|
+
return all_entities
|
|
873
|
+
|
|
874
|
+
# Use Ray for parallel processing
|
|
875
|
+
logger.info(f"Extracting entities from {len(chunks)} chunks in parallel using Ray")
|
|
876
|
+
futures = [
|
|
877
|
+
remote_extract_entities_from_chunk.remote(
|
|
878
|
+
chunk.get("id", ""),
|
|
879
|
+
chunk.get("content", ""),
|
|
880
|
+
llm_model,
|
|
881
|
+
prompt
|
|
882
|
+
)
|
|
883
|
+
for chunk in chunks
|
|
884
|
+
]
|
|
885
|
+
|
|
886
|
+
results = ray.get(futures)
|
|
887
|
+
all_entities = {}
|
|
888
|
+
for result in results:
|
|
889
|
+
if result.get("entities"):
|
|
890
|
+
all_entities[result["chunk_id"]] = result["entities"]
|
|
891
|
+
|
|
892
|
+
logger.info(f"Extracted entities from {len(all_entities)} chunks in parallel")
|
|
893
|
+
return all_entities
|
|
894
|
+
|
|
895
|
+
|
|
896
|
+
def _parse_entities_from_text_fallback(text: str) -> List[Dict[str, Any]]:
|
|
897
|
+
"""Parse entities from text response (fallback method)."""
|
|
898
|
+
entities = []
|
|
899
|
+
import re
|
|
900
|
+
patterns = [
|
|
901
|
+
r'(\w+):\s*([^(]+)\s*\(([^)]+)\)',
|
|
902
|
+
r'Name:\s*([^,]+),\s*Type:\s*([^,]+)',
|
|
903
|
+
]
|
|
904
|
+
for pattern in patterns:
|
|
905
|
+
matches = re.findall(pattern, text)
|
|
906
|
+
for match in matches:
|
|
907
|
+
if len(match) >= 2:
|
|
908
|
+
entities.append({
|
|
909
|
+
'name': match[1] if len(match) > 1 else match[0],
|
|
910
|
+
'type': match[2] if len(match) > 2 else 'Entity'
|
|
911
|
+
})
|
|
912
|
+
return entities
|
|
913
|
+
|
|
914
|
+
|
|
915
|
+
def _parse_entities_from_text(text: str) -> List[Dict[str, Any]]:
|
|
916
|
+
"""Parse entities from text response (fallback method)."""
|
|
917
|
+
return _parse_entities_from_text_fallback(text)
|
|
918
|
+
|