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,1028 @@
|
|
|
1
|
+
"""Intelligence API Router — manage collection plans, hierarchy, watchdog.
|
|
2
|
+
|
|
3
|
+
Endpoints:
|
|
4
|
+
GET /intelligence/plans — List all collection plans
|
|
5
|
+
GET /intelligence/plans/{name} — Get a plan's details + status
|
|
6
|
+
POST /intelligence/plans — Create/update a collection plan
|
|
7
|
+
DELETE /intelligence/plans/{name} — Delete a plan
|
|
8
|
+
POST /intelligence/plans/{name}/run — Run all pipelines for a plan NOW
|
|
9
|
+
POST /intelligence/plans/{name}/run/{pipeline} — Run one pipeline
|
|
10
|
+
|
|
11
|
+
GET /intelligence/hierarchy — Get signal hierarchy
|
|
12
|
+
POST /intelligence/hierarchy/link — Add a link
|
|
13
|
+
POST /intelligence/hierarchy/lateral — Add a lateral link
|
|
14
|
+
|
|
15
|
+
GET /intelligence/watchdog/status — Watchdog status + recent alerts
|
|
16
|
+
POST /intelligence/watchdog/start — Start watchdog
|
|
17
|
+
POST /intelligence/watchdog/stop — Stop watchdog
|
|
18
|
+
POST /intelligence/watchdog/check — Run one watchdog check cycle
|
|
19
|
+
|
|
20
|
+
GET /intelligence/signals — Recent signals + propagation log
|
|
21
|
+
GET /intelligence/impact — Impact tracker stats + training data
|
|
22
|
+
"""
|
|
23
|
+
from __future__ import annotations
|
|
24
|
+
|
|
25
|
+
import logging
|
|
26
|
+
from typing import Any, Dict
|
|
27
|
+
|
|
28
|
+
from fastapi import APIRouter, Body, Depends, HTTPException, Query
|
|
29
|
+
|
|
30
|
+
logger = logging.getLogger(__name__)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def create_intelligence_router(
|
|
34
|
+
context_manager,
|
|
35
|
+
graph_registry=None,
|
|
36
|
+
user_auth=None,
|
|
37
|
+
):
|
|
38
|
+
"""Create the intelligence API router with injected dependencies."""
|
|
39
|
+
|
|
40
|
+
router = APIRouter(prefix="/intelligence", tags=["intelligence"])
|
|
41
|
+
|
|
42
|
+
# Lazy-init singletons
|
|
43
|
+
_collector = None
|
|
44
|
+
_hierarchy = None
|
|
45
|
+
_reactive = None
|
|
46
|
+
_watchdog = None
|
|
47
|
+
_impact_tracker = None
|
|
48
|
+
_persistence = None
|
|
49
|
+
|
|
50
|
+
def _get_persistence():
|
|
51
|
+
nonlocal _persistence
|
|
52
|
+
if _persistence is None:
|
|
53
|
+
from ..intelligence.persistence import IntelligencePersistence
|
|
54
|
+
_persistence = IntelligencePersistence(context_manager)
|
|
55
|
+
return _persistence
|
|
56
|
+
|
|
57
|
+
_rate_limiter_instance = None
|
|
58
|
+
_health_monitor_instance = None
|
|
59
|
+
|
|
60
|
+
def _get_rate_limiter():
|
|
61
|
+
nonlocal _rate_limiter_instance
|
|
62
|
+
if _rate_limiter_instance is None:
|
|
63
|
+
from ..intelligence.rate_limiter import PipelineRateLimiter
|
|
64
|
+
_rate_limiter_instance = PipelineRateLimiter()
|
|
65
|
+
return _rate_limiter_instance
|
|
66
|
+
|
|
67
|
+
def _get_health_monitor():
|
|
68
|
+
nonlocal _health_monitor_instance
|
|
69
|
+
if _health_monitor_instance is None:
|
|
70
|
+
from ..intelligence.pipeline_health import PipelineHealthMonitor
|
|
71
|
+
_health_monitor_instance = PipelineHealthMonitor(
|
|
72
|
+
alert_dispatcher=_get_alert_dispatcher(),
|
|
73
|
+
)
|
|
74
|
+
return _health_monitor_instance
|
|
75
|
+
|
|
76
|
+
def _get_collector():
|
|
77
|
+
nonlocal _collector
|
|
78
|
+
if _collector is None:
|
|
79
|
+
from ..intelligence.collector import ContextCollector
|
|
80
|
+
_collector = ContextCollector(
|
|
81
|
+
rate_limiter=_get_rate_limiter(),
|
|
82
|
+
health_monitor=_get_health_monitor(),
|
|
83
|
+
)
|
|
84
|
+
# Load persisted plans
|
|
85
|
+
store = _get_persistence()
|
|
86
|
+
for plan in store.load_all_plans():
|
|
87
|
+
_collector.register(plan)
|
|
88
|
+
logger.info("[INTEL-API] Loaded %d collection plans", len(_collector.list_plans()))
|
|
89
|
+
return _collector
|
|
90
|
+
|
|
91
|
+
def _get_hierarchy():
|
|
92
|
+
nonlocal _hierarchy
|
|
93
|
+
if _hierarchy is None:
|
|
94
|
+
store = _get_persistence()
|
|
95
|
+
_hierarchy = store.load_hierarchy()
|
|
96
|
+
if not _hierarchy:
|
|
97
|
+
from ..intelligence.signal_hierarchy import SignalHierarchy
|
|
98
|
+
_hierarchy = SignalHierarchy()
|
|
99
|
+
return _hierarchy
|
|
100
|
+
|
|
101
|
+
def _get_reactive():
|
|
102
|
+
nonlocal _reactive
|
|
103
|
+
if _reactive is None:
|
|
104
|
+
from ..intelligence.reactive import ReactiveController
|
|
105
|
+
_reactive = ReactiveController(alert_dispatcher=_get_alert_dispatcher())
|
|
106
|
+
store = _get_persistence()
|
|
107
|
+
if not store.load_reactive_rules(_reactive):
|
|
108
|
+
_reactive.add_default_rules()
|
|
109
|
+
return _reactive
|
|
110
|
+
|
|
111
|
+
def _get_watchdog():
|
|
112
|
+
nonlocal _watchdog
|
|
113
|
+
if _watchdog is None:
|
|
114
|
+
from ..intelligence.watchdog import BreakingNewsWatchdog
|
|
115
|
+
from ..intelligence.impact_tracker import ImpactTracker
|
|
116
|
+
_watchdog = BreakingNewsWatchdog(
|
|
117
|
+
hierarchy=_get_hierarchy(),
|
|
118
|
+
collector=_get_collector(),
|
|
119
|
+
reactive=_get_reactive(),
|
|
120
|
+
impact_tracker=_get_impact_tracker(),
|
|
121
|
+
)
|
|
122
|
+
store = _get_persistence()
|
|
123
|
+
store.load_watchdog_config(_watchdog)
|
|
124
|
+
return _watchdog
|
|
125
|
+
|
|
126
|
+
def _get_impact_tracker():
|
|
127
|
+
nonlocal _impact_tracker
|
|
128
|
+
if _impact_tracker is None:
|
|
129
|
+
from ..intelligence.impact_tracker import ImpactTracker
|
|
130
|
+
_impact_tracker = ImpactTracker()
|
|
131
|
+
return _impact_tracker
|
|
132
|
+
|
|
133
|
+
# ═══════════════════════════════════════════════════════════════
|
|
134
|
+
# COLLECTION PLANS
|
|
135
|
+
# ═══════════════════════════════════════════════════════════════
|
|
136
|
+
|
|
137
|
+
@router.get("/plans")
|
|
138
|
+
async def list_plans():
|
|
139
|
+
"""List all registered collection plans."""
|
|
140
|
+
collector = _get_collector()
|
|
141
|
+
plans = []
|
|
142
|
+
for name in collector.list_plans():
|
|
143
|
+
plan = collector.get_plan(name)
|
|
144
|
+
status = collector.status(name)
|
|
145
|
+
plans.append({
|
|
146
|
+
"name": name,
|
|
147
|
+
"description": plan.context_description,
|
|
148
|
+
"tags": plan.tags,
|
|
149
|
+
"template": plan.template,
|
|
150
|
+
"pipeline_count": len(plan.pipelines),
|
|
151
|
+
"status": status,
|
|
152
|
+
})
|
|
153
|
+
return {"plans": plans, "count": len(plans)}
|
|
154
|
+
|
|
155
|
+
@router.get("/plans/{name}")
|
|
156
|
+
async def get_plan(name: str):
|
|
157
|
+
"""Get a collection plan's details and pipeline status."""
|
|
158
|
+
collector = _get_collector()
|
|
159
|
+
plan = collector.get_plan(name)
|
|
160
|
+
if not plan:
|
|
161
|
+
raise HTTPException(404, f"Plan '{name}' not found")
|
|
162
|
+
return {
|
|
163
|
+
"plan": plan.to_dict(),
|
|
164
|
+
"status": collector.status(name),
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
@router.post("/plans")
|
|
168
|
+
async def create_plan(body: dict = Body(...)):
|
|
169
|
+
"""Create or update a collection plan.
|
|
170
|
+
|
|
171
|
+
Body:
|
|
172
|
+
{
|
|
173
|
+
"context_name": "TCS Intelligence",
|
|
174
|
+
"context_description": "Track TCS...",
|
|
175
|
+
"tags": ["tcs", "it-services"],
|
|
176
|
+
"template": "market_analysis",
|
|
177
|
+
"pipelines": [
|
|
178
|
+
{
|
|
179
|
+
"name": "news",
|
|
180
|
+
"source_type": "rss",
|
|
181
|
+
"sources": ["https://..."],
|
|
182
|
+
"interval": "30m",
|
|
183
|
+
"strategy": "news_article",
|
|
184
|
+
"filter": {
|
|
185
|
+
"topics": ["TCS", "Tata Consultancy"],
|
|
186
|
+
"exclude": ["cricket"],
|
|
187
|
+
"semantic_query": "TCS business performance"
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
]
|
|
191
|
+
}
|
|
192
|
+
"""
|
|
193
|
+
from ..intelligence.collector import CollectionPlan
|
|
194
|
+
try:
|
|
195
|
+
plan = CollectionPlan.from_dict(body)
|
|
196
|
+
except Exception as exc:
|
|
197
|
+
raise HTTPException(400, f"Invalid plan: {exc}")
|
|
198
|
+
|
|
199
|
+
collector = _get_collector()
|
|
200
|
+
collector.register(plan)
|
|
201
|
+
|
|
202
|
+
# Persist
|
|
203
|
+
store = _get_persistence()
|
|
204
|
+
store.save_collection_plan(plan)
|
|
205
|
+
|
|
206
|
+
return {
|
|
207
|
+
"status": "created",
|
|
208
|
+
"name": plan.context_name,
|
|
209
|
+
"pipelines": len(plan.pipelines),
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
@router.delete("/plans/{name}")
|
|
213
|
+
async def delete_plan(name: str):
|
|
214
|
+
"""Remove a collection plan."""
|
|
215
|
+
collector = _get_collector()
|
|
216
|
+
if name not in collector.list_plans():
|
|
217
|
+
raise HTTPException(404, f"Plan '{name}' not found")
|
|
218
|
+
del collector._plans[name]
|
|
219
|
+
return {"status": "deleted", "name": name}
|
|
220
|
+
|
|
221
|
+
@router.post("/plans/{name}/run")
|
|
222
|
+
async def run_plan(name: str):
|
|
223
|
+
"""Run all pipelines for a plan immediately."""
|
|
224
|
+
collector = _get_collector()
|
|
225
|
+
plan = collector.get_plan(name)
|
|
226
|
+
if not plan:
|
|
227
|
+
raise HTTPException(404, f"Plan '{name}' not found")
|
|
228
|
+
|
|
229
|
+
result = collector.run_all(name)
|
|
230
|
+
return result.to_dict()
|
|
231
|
+
|
|
232
|
+
@router.post("/plans/{name}/run/{pipeline}")
|
|
233
|
+
async def run_pipeline(name: str, pipeline: str):
|
|
234
|
+
"""Run a single pipeline within a plan."""
|
|
235
|
+
collector = _get_collector()
|
|
236
|
+
plan = collector.get_plan(name)
|
|
237
|
+
if not plan:
|
|
238
|
+
raise HTTPException(404, f"Plan '{name}' not found")
|
|
239
|
+
|
|
240
|
+
result = collector.run_pipeline(name, pipeline)
|
|
241
|
+
return result
|
|
242
|
+
|
|
243
|
+
# ═══════════════════════════════════════════════════════════════
|
|
244
|
+
# SIGNAL HIERARCHY
|
|
245
|
+
# ═══════════════════════════════════════════════════════════════
|
|
246
|
+
|
|
247
|
+
@router.get("/hierarchy")
|
|
248
|
+
async def get_hierarchy():
|
|
249
|
+
"""Get the signal hierarchy."""
|
|
250
|
+
h = _get_hierarchy()
|
|
251
|
+
return {
|
|
252
|
+
"hierarchy": h.to_dict(),
|
|
253
|
+
"context_count": len(h._context_levels),
|
|
254
|
+
"link_count": len(h._links),
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
@router.post("/hierarchy/link")
|
|
258
|
+
async def add_hierarchy_link(body: dict = Body(...)):
|
|
259
|
+
"""Add a hierarchical link (upstream → downstream).
|
|
260
|
+
|
|
261
|
+
Body: {"upstream": "Global Macro", "downstream": "India Economy",
|
|
262
|
+
"relationship": "macro_affects", "weight": 0.8}
|
|
263
|
+
"""
|
|
264
|
+
from ..intelligence.signal_hierarchy import ContextLink
|
|
265
|
+
h = _get_hierarchy()
|
|
266
|
+
link = ContextLink.from_dict(body)
|
|
267
|
+
h.add_link(link)
|
|
268
|
+
|
|
269
|
+
store = _get_persistence()
|
|
270
|
+
store.save_hierarchy(h)
|
|
271
|
+
|
|
272
|
+
return {"status": "added", "link": link.to_dict()}
|
|
273
|
+
|
|
274
|
+
@router.post("/hierarchy/lateral")
|
|
275
|
+
async def add_lateral_link(body: dict = Body(...)):
|
|
276
|
+
"""Add a lateral link (peer ↔ peer).
|
|
277
|
+
|
|
278
|
+
Body: {"upstream": "TCS", "downstream": "Infosys",
|
|
279
|
+
"relationship": "competes_with", "weight": 0.7}
|
|
280
|
+
"""
|
|
281
|
+
from ..intelligence.signal_hierarchy import ContextLink
|
|
282
|
+
h = _get_hierarchy()
|
|
283
|
+
link = ContextLink.from_dict(body)
|
|
284
|
+
h.add_lateral_link(link)
|
|
285
|
+
|
|
286
|
+
store = _get_persistence()
|
|
287
|
+
store.save_hierarchy(h)
|
|
288
|
+
|
|
289
|
+
return {"status": "added", "link": link.to_dict()}
|
|
290
|
+
|
|
291
|
+
@router.post("/hierarchy/level")
|
|
292
|
+
async def set_level(body: dict = Body(...)):
|
|
293
|
+
"""Set hierarchy level for a context.
|
|
294
|
+
|
|
295
|
+
Body: {"context": "TCS", "level": "company"}
|
|
296
|
+
"""
|
|
297
|
+
h = _get_hierarchy()
|
|
298
|
+
h.set_level(body["context"], body["level"])
|
|
299
|
+
|
|
300
|
+
store = _get_persistence()
|
|
301
|
+
store.save_hierarchy(h)
|
|
302
|
+
|
|
303
|
+
return {"status": "set", "context": body["context"], "level": body["level"]}
|
|
304
|
+
|
|
305
|
+
@router.get("/hierarchy/chain/{context}")
|
|
306
|
+
async def get_chain(context: str):
|
|
307
|
+
"""Get upstream + downstream + lateral connections for a context."""
|
|
308
|
+
h = _get_hierarchy()
|
|
309
|
+
return h.get_all_connected(context)
|
|
310
|
+
|
|
311
|
+
# ═══════════════════════════════════════════════════════════════
|
|
312
|
+
# WATCHDOG
|
|
313
|
+
# ═══════════════════════════════════════════════════════════════
|
|
314
|
+
|
|
315
|
+
@router.get("/watchdog/status")
|
|
316
|
+
async def watchdog_status():
|
|
317
|
+
"""Get watchdog status and recent alerts."""
|
|
318
|
+
wd = _get_watchdog()
|
|
319
|
+
return {
|
|
320
|
+
"running": wd._running,
|
|
321
|
+
"sources": [{"url": s.url, "name": s.name} for s in wd._sources],
|
|
322
|
+
"recent_alerts": wd.get_alert_log(limit=20),
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
@router.post("/watchdog/start")
|
|
326
|
+
async def watchdog_start(interval: int = Query(60)):
|
|
327
|
+
"""Start the watchdog background loop."""
|
|
328
|
+
wd = _get_watchdog()
|
|
329
|
+
wd.start(interval_seconds=interval)
|
|
330
|
+
return {"status": "started", "interval_seconds": interval}
|
|
331
|
+
|
|
332
|
+
@router.post("/watchdog/stop")
|
|
333
|
+
async def watchdog_stop():
|
|
334
|
+
"""Stop the watchdog."""
|
|
335
|
+
wd = _get_watchdog()
|
|
336
|
+
wd.stop()
|
|
337
|
+
return {"status": "stopped"}
|
|
338
|
+
|
|
339
|
+
@router.post("/watchdog/check")
|
|
340
|
+
async def watchdog_check():
|
|
341
|
+
"""Run one watchdog check cycle immediately."""
|
|
342
|
+
wd = _get_watchdog()
|
|
343
|
+
result = wd.check()
|
|
344
|
+
return result.to_dict()
|
|
345
|
+
|
|
346
|
+
@router.post("/watchdog/source")
|
|
347
|
+
async def add_watchdog_source(body: dict = Body(...)):
|
|
348
|
+
"""Add a source to the watchdog.
|
|
349
|
+
|
|
350
|
+
Body: {"url": "https://feeds.reuters.com/...", "name": "Reuters"}
|
|
351
|
+
"""
|
|
352
|
+
wd = _get_watchdog()
|
|
353
|
+
wd.add_source(
|
|
354
|
+
url=body["url"],
|
|
355
|
+
name=body.get("name", ""),
|
|
356
|
+
source_type=body.get("source_type", "rss"),
|
|
357
|
+
)
|
|
358
|
+
store = _get_persistence()
|
|
359
|
+
store.save_watchdog_config(wd)
|
|
360
|
+
return {"status": "added", "source": body["url"]}
|
|
361
|
+
|
|
362
|
+
# ═══════════════════════════════════════════════════════════════
|
|
363
|
+
# SIGNALS + IMPACT
|
|
364
|
+
# ═══════════════════════════════════════════════════════════════
|
|
365
|
+
|
|
366
|
+
@router.get("/signals")
|
|
367
|
+
async def get_signals(limit: int = Query(50)):
|
|
368
|
+
"""Get recent propagated signals."""
|
|
369
|
+
h = _get_hierarchy()
|
|
370
|
+
reactive = _get_reactive()
|
|
371
|
+
return {
|
|
372
|
+
"propagation_log": h.get_propagation_log(limit),
|
|
373
|
+
"reaction_log": reactive.get_reaction_log(limit),
|
|
374
|
+
"active_escalations": reactive.get_escalations(),
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
@router.get("/impact")
|
|
378
|
+
async def get_impact():
|
|
379
|
+
"""Get impact tracker stats and training data."""
|
|
380
|
+
tracker = _get_impact_tracker()
|
|
381
|
+
return {
|
|
382
|
+
"link_stats": tracker.get_link_stats(),
|
|
383
|
+
"training_data_count": len(tracker.get_training_data()),
|
|
384
|
+
"training_sample": tracker.get_training_data()[:10],
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
@router.post("/impact/outcome")
|
|
388
|
+
async def record_outcome(body: dict = Body(...)):
|
|
389
|
+
"""Record an actual outcome for a predicted signal.
|
|
390
|
+
|
|
391
|
+
Body: {"signal_id": "...", "target_context": "TCS",
|
|
392
|
+
"actual_impact": 0.45, "metric": "stock_price_change_pct",
|
|
393
|
+
"observed_value": -2.3}
|
|
394
|
+
"""
|
|
395
|
+
tracker = _get_impact_tracker()
|
|
396
|
+
result = tracker.record_outcome(
|
|
397
|
+
signal_id=body["signal_id"],
|
|
398
|
+
target_context=body["target_context"],
|
|
399
|
+
actual_impact=body.get("actual_impact", 0.0),
|
|
400
|
+
metric=body.get("metric", ""),
|
|
401
|
+
observed_value=body.get("observed_value", 0.0),
|
|
402
|
+
)
|
|
403
|
+
if result is None:
|
|
404
|
+
raise HTTPException(404, "No matching prediction found")
|
|
405
|
+
return result
|
|
406
|
+
|
|
407
|
+
# ═══════════════════════════════════════════════════════════════
|
|
408
|
+
# TRACK — one-call entity tracking
|
|
409
|
+
# ═══════════════════════════════════════════════════════════════
|
|
410
|
+
|
|
411
|
+
_tracker = None
|
|
412
|
+
|
|
413
|
+
def _get_tracker():
|
|
414
|
+
nonlocal _tracker
|
|
415
|
+
if _tracker is None:
|
|
416
|
+
from ..intelligence.tracker import EntityTracker
|
|
417
|
+
_tracker = EntityTracker(
|
|
418
|
+
collector=_get_collector(),
|
|
419
|
+
hierarchy=_get_hierarchy(),
|
|
420
|
+
persistence=_get_persistence(),
|
|
421
|
+
)
|
|
422
|
+
return _tracker
|
|
423
|
+
|
|
424
|
+
@router.post("/track")
|
|
425
|
+
async def track_entity(body: dict = Body(...)):
|
|
426
|
+
"""Start tracking an entity with one call.
|
|
427
|
+
|
|
428
|
+
Body: {"entity": "TCS", "template": "indian_listed_company",
|
|
429
|
+
"extra": {"bse_code": "532540", "full_name": "Tata Consultancy Services"}}
|
|
430
|
+
"""
|
|
431
|
+
tracker = _get_tracker()
|
|
432
|
+
result = tracker.track(
|
|
433
|
+
entity_name=body["entity"],
|
|
434
|
+
template=body.get("template", "generic_topic"),
|
|
435
|
+
extra=body.get("extra"),
|
|
436
|
+
)
|
|
437
|
+
if "error" in result:
|
|
438
|
+
raise HTTPException(400, result["error"])
|
|
439
|
+
return result
|
|
440
|
+
|
|
441
|
+
@router.delete("/track/{entity}")
|
|
442
|
+
async def untrack_entity(entity: str):
|
|
443
|
+
"""Stop tracking an entity."""
|
|
444
|
+
tracker = _get_tracker()
|
|
445
|
+
return tracker.untrack(entity)
|
|
446
|
+
|
|
447
|
+
@router.get("/track")
|
|
448
|
+
async def list_tracked():
|
|
449
|
+
"""List all tracked entities."""
|
|
450
|
+
tracker = _get_tracker()
|
|
451
|
+
return {
|
|
452
|
+
"tracked": tracker.list_tracked(),
|
|
453
|
+
"templates": tracker.list_templates(),
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
# ═══════════════════════════════════════════════════════════════
|
|
457
|
+
# WEBHOOKS — push-based ingestion
|
|
458
|
+
# ═══════════════════════════════════════════════════════════════
|
|
459
|
+
|
|
460
|
+
_webhook_receiver = None
|
|
461
|
+
|
|
462
|
+
def _get_webhook_receiver():
|
|
463
|
+
nonlocal _webhook_receiver
|
|
464
|
+
if _webhook_receiver is None:
|
|
465
|
+
from ..intelligence.webhook_receiver import WebhookReceiver
|
|
466
|
+
_webhook_receiver = WebhookReceiver(db=None)
|
|
467
|
+
return _webhook_receiver
|
|
468
|
+
|
|
469
|
+
@router.post("/webhooks/register")
|
|
470
|
+
async def register_webhook(body: dict = Body(...)):
|
|
471
|
+
"""Register a webhook endpoint.
|
|
472
|
+
|
|
473
|
+
Body: {"webhook_id": "tcs_news", "context_name": "TCS", "strategy": "news_article"}
|
|
474
|
+
Returns: {"webhook_id": "tcs_news", "endpoint": "/intelligence/webhooks/tcs_news", "secret": "..."}
|
|
475
|
+
"""
|
|
476
|
+
receiver = _get_webhook_receiver()
|
|
477
|
+
config = receiver.register(
|
|
478
|
+
webhook_id=body["webhook_id"],
|
|
479
|
+
context_name=body["context_name"],
|
|
480
|
+
secret=body.get("secret", ""),
|
|
481
|
+
strategy=body.get("strategy", "news_article"),
|
|
482
|
+
)
|
|
483
|
+
return {
|
|
484
|
+
"webhook_id": config.webhook_id,
|
|
485
|
+
"endpoint": f"/intelligence/webhooks/{config.webhook_id}",
|
|
486
|
+
"secret": config.secret,
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
@router.get("/webhooks")
|
|
490
|
+
async def list_webhooks():
|
|
491
|
+
"""List all registered webhooks."""
|
|
492
|
+
receiver = _get_webhook_receiver()
|
|
493
|
+
return {"webhooks": receiver.list_webhooks()}
|
|
494
|
+
|
|
495
|
+
@router.post("/webhooks/{webhook_id}")
|
|
496
|
+
async def receive_webhook(webhook_id: str, body: dict = Body(...)):
|
|
497
|
+
"""Receive a webhook payload and ingest it.
|
|
498
|
+
|
|
499
|
+
Body: {"title": "...", "content": "...", "url": "...", "source": "..."}
|
|
500
|
+
"""
|
|
501
|
+
receiver = _get_webhook_receiver()
|
|
502
|
+
result = receiver.receive(webhook_id, body)
|
|
503
|
+
if result.status == "error":
|
|
504
|
+
raise HTTPException(400, result.error)
|
|
505
|
+
return result.to_dict()
|
|
506
|
+
|
|
507
|
+
# ═══════════════════════════════════════════════════════════════
|
|
508
|
+
# ALERTS — notification channels
|
|
509
|
+
# ═══════════════════════════════════════════════════════════════
|
|
510
|
+
|
|
511
|
+
_alert_dispatcher = None
|
|
512
|
+
|
|
513
|
+
def _get_alert_dispatcher():
|
|
514
|
+
nonlocal _alert_dispatcher
|
|
515
|
+
if _alert_dispatcher is None:
|
|
516
|
+
from ..intelligence.alerts import AlertDispatcher, AlertChannel
|
|
517
|
+
_alert_dispatcher = AlertDispatcher()
|
|
518
|
+
# Default: log channel always active
|
|
519
|
+
_alert_dispatcher.add_channel(AlertChannel(
|
|
520
|
+
name="system_log", channel_type="log", min_severity="warning",
|
|
521
|
+
))
|
|
522
|
+
return _alert_dispatcher
|
|
523
|
+
|
|
524
|
+
@router.post("/alerts/channel")
|
|
525
|
+
async def add_alert_channel(body: dict = Body(...)):
|
|
526
|
+
"""Add a notification channel.
|
|
527
|
+
|
|
528
|
+
Body: {"name": "slack", "channel_type": "webhook",
|
|
529
|
+
"config": {"url": "https://hooks.slack.com/..."}, "min_severity": "warning"}
|
|
530
|
+
"""
|
|
531
|
+
from ..intelligence.alerts import AlertChannel
|
|
532
|
+
dispatcher = _get_alert_dispatcher()
|
|
533
|
+
channel = AlertChannel.from_dict(body)
|
|
534
|
+
dispatcher.add_channel(channel)
|
|
535
|
+
return {"status": "added", "channel": channel.to_dict()}
|
|
536
|
+
|
|
537
|
+
@router.get("/alerts/channels")
|
|
538
|
+
async def list_alert_channels():
|
|
539
|
+
dispatcher = _get_alert_dispatcher()
|
|
540
|
+
return {"channels": dispatcher.list_channels()}
|
|
541
|
+
|
|
542
|
+
@router.get("/alerts/log")
|
|
543
|
+
async def get_alert_log(limit: int = Query(50)):
|
|
544
|
+
dispatcher = _get_alert_dispatcher()
|
|
545
|
+
return {"deliveries": dispatcher.get_delivery_log(limit)}
|
|
546
|
+
|
|
547
|
+
@router.post("/alerts/test")
|
|
548
|
+
async def test_alert(body: dict = Body(...)):
|
|
549
|
+
"""Send a test alert to verify channels work.
|
|
550
|
+
|
|
551
|
+
Body: {"severity": "warning", "message": "Test alert"}
|
|
552
|
+
"""
|
|
553
|
+
dispatcher = _get_alert_dispatcher()
|
|
554
|
+
results = dispatcher.dispatch(
|
|
555
|
+
signal_type="test",
|
|
556
|
+
entity_name="Test Entity",
|
|
557
|
+
context_name="Test Context",
|
|
558
|
+
severity=body.get("severity", "warning"),
|
|
559
|
+
details={"message": body.get("message", "Test alert from intelligence API")},
|
|
560
|
+
)
|
|
561
|
+
return {"results": [r.to_dict() for r in results]}
|
|
562
|
+
|
|
563
|
+
# ═══════════════════════════════════════════════════════════════
|
|
564
|
+
# BULK IMPORT — batch URL ingestion
|
|
565
|
+
# ═══════════════════════════════════════════════════════════════
|
|
566
|
+
|
|
567
|
+
@router.post("/bulk/ingest")
|
|
568
|
+
async def bulk_ingest(body: dict = Body(...)):
|
|
569
|
+
"""Ingest multiple URLs into a context in one call.
|
|
570
|
+
|
|
571
|
+
Body: {
|
|
572
|
+
"context_name": "TCS Intelligence",
|
|
573
|
+
"urls": ["https://...", "https://...", ...],
|
|
574
|
+
"strategy": "news_article", # optional
|
|
575
|
+
}
|
|
576
|
+
"""
|
|
577
|
+
context_name = body.get("context_name", "")
|
|
578
|
+
urls = body.get("urls", [])
|
|
579
|
+
strategy = body.get("strategy")
|
|
580
|
+
|
|
581
|
+
if not urls:
|
|
582
|
+
raise HTTPException(400, "No URLs provided")
|
|
583
|
+
|
|
584
|
+
from ..ingestion.smart_ingest import ingest_url
|
|
585
|
+
from ..ingestion.amplifier import build_context_purpose
|
|
586
|
+
|
|
587
|
+
# Get context purpose
|
|
588
|
+
collector = _get_collector()
|
|
589
|
+
plan = collector.get_plan(context_name)
|
|
590
|
+
context_purpose = ""
|
|
591
|
+
if plan:
|
|
592
|
+
context_purpose = build_context_purpose(
|
|
593
|
+
context_name=plan.context_name,
|
|
594
|
+
context_description=plan.context_description,
|
|
595
|
+
tags=plan.tags,
|
|
596
|
+
)
|
|
597
|
+
|
|
598
|
+
results = {
|
|
599
|
+
"total": len(urls),
|
|
600
|
+
"ingested": 0,
|
|
601
|
+
"deduped": 0,
|
|
602
|
+
"errors": 0,
|
|
603
|
+
"details": [],
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
for url in urls:
|
|
607
|
+
try:
|
|
608
|
+
# Get a db for the context namespace
|
|
609
|
+
if graph_registry:
|
|
610
|
+
ns = context_name.lower().replace(" ", "_") if context_name else "default"
|
|
611
|
+
db = graph_registry.get_graph(ns, load_if_missing=True)
|
|
612
|
+
if not db:
|
|
613
|
+
db = graph_registry.create_graph(ns)
|
|
614
|
+
else:
|
|
615
|
+
from ..core.registry import graph_registry as _gr
|
|
616
|
+
db = _gr.get_graph("default", load_if_missing=True)
|
|
617
|
+
|
|
618
|
+
r = ingest_url(
|
|
619
|
+
url, db,
|
|
620
|
+
strategy=strategy,
|
|
621
|
+
context_purpose=context_purpose,
|
|
622
|
+
)
|
|
623
|
+
|
|
624
|
+
if hasattr(r, 'errors') and r.errors:
|
|
625
|
+
if any("Duplicate" in str(e) for e in r.errors):
|
|
626
|
+
results["deduped"] += 1
|
|
627
|
+
results["details"].append({"url": url, "status": "deduped"})
|
|
628
|
+
else:
|
|
629
|
+
results["errors"] += 1
|
|
630
|
+
results["details"].append({"url": url, "status": "error", "error": str(r.errors)})
|
|
631
|
+
else:
|
|
632
|
+
results["ingested"] += 1
|
|
633
|
+
results["details"].append({
|
|
634
|
+
"url": url, "status": "ingested",
|
|
635
|
+
"entities": len(getattr(r, 'entity_ids', {})),
|
|
636
|
+
"facts": len(getattr(r, 'fact_ids', [])),
|
|
637
|
+
})
|
|
638
|
+
|
|
639
|
+
except Exception as exc:
|
|
640
|
+
results["errors"] += 1
|
|
641
|
+
results["details"].append({"url": url, "status": "error", "error": str(exc)})
|
|
642
|
+
|
|
643
|
+
return results
|
|
644
|
+
|
|
645
|
+
# ═══════════════════════════════════════════════════════════════
|
|
646
|
+
# TIME-SERIES — convenience temporal queries
|
|
647
|
+
# ═══════════════════════════════════════════════════════════════
|
|
648
|
+
|
|
649
|
+
@router.get("/timeseries/sentiment/{entity}")
|
|
650
|
+
async def sentiment_over_time(entity: str, days: int = Query(30), bucket: str = Query("day")):
|
|
651
|
+
"""Get sentiment distribution over time for an entity."""
|
|
652
|
+
from ..intelligence.timeseries import TimeSeriesQuery
|
|
653
|
+
db = graph_registry.get_graph("default", load_if_missing=True) if graph_registry else None
|
|
654
|
+
ts = TimeSeriesQuery(db)
|
|
655
|
+
return {"entity": entity, "days": days, "data": ts.sentiment_over_time(entity, days, bucket)}
|
|
656
|
+
|
|
657
|
+
@router.get("/timeseries/compare")
|
|
658
|
+
async def compare_sentiment(entities: str = Query(...), days: int = Query(7)):
|
|
659
|
+
"""Compare sentiment across entities. Comma-separated names."""
|
|
660
|
+
from ..intelligence.timeseries import TimeSeriesQuery
|
|
661
|
+
db = graph_registry.get_graph("default", load_if_missing=True) if graph_registry else None
|
|
662
|
+
ts = TimeSeriesQuery(db)
|
|
663
|
+
names = [e.strip() for e in entities.split(",")]
|
|
664
|
+
return ts.compare_sentiment(names, days)
|
|
665
|
+
|
|
666
|
+
@router.get("/timeseries/timeline/{entity}")
|
|
667
|
+
async def entity_timeline(entity: str, days: int = Query(30), limit: int = Query(50)):
|
|
668
|
+
"""Get chronological timeline of events for an entity."""
|
|
669
|
+
from ..intelligence.timeseries import TimeSeriesQuery
|
|
670
|
+
db = graph_registry.get_graph("default", load_if_missing=True) if graph_registry else None
|
|
671
|
+
ts = TimeSeriesQuery(db)
|
|
672
|
+
return {"entity": entity, "events": ts.entity_timeline(entity, days, limit)}
|
|
673
|
+
|
|
674
|
+
@router.get("/timeseries/indicator/{name}")
|
|
675
|
+
async def indicator_over_time(name: str, days: int = Query(90)):
|
|
676
|
+
"""Get indicator values over time."""
|
|
677
|
+
from ..intelligence.timeseries import TimeSeriesQuery
|
|
678
|
+
db = graph_registry.get_graph("default", load_if_missing=True) if graph_registry else None
|
|
679
|
+
ts = TimeSeriesQuery(db)
|
|
680
|
+
return {"indicator": name, "data": ts.indicator_over_time(name, days)}
|
|
681
|
+
|
|
682
|
+
# ═══════════════════════════════════════════════════════════════
|
|
683
|
+
# HEALTH — pipeline health monitoring
|
|
684
|
+
# ═══════════════════════════════════════════════════════════════
|
|
685
|
+
|
|
686
|
+
@router.get("/health")
|
|
687
|
+
async def pipeline_health(context: str = Query(""), pipeline: str = Query("")):
|
|
688
|
+
"""Get pipeline health status."""
|
|
689
|
+
monitor = _get_health_monitor()
|
|
690
|
+
return {"health": monitor.get_health(context, pipeline)}
|
|
691
|
+
|
|
692
|
+
@router.get("/health/failing")
|
|
693
|
+
async def failing_pipelines():
|
|
694
|
+
"""Get all failing pipelines."""
|
|
695
|
+
monitor = _get_health_monitor()
|
|
696
|
+
return {"failing": monitor.get_failing(), "degraded": monitor.get_degraded()}
|
|
697
|
+
|
|
698
|
+
# ═══════════════════════════════════════════════════════════════
|
|
699
|
+
# RATE LIMITING — per-source throttling
|
|
700
|
+
# ═══════════════════════════════════════════════════════════════
|
|
701
|
+
|
|
702
|
+
@router.get("/ratelimit/stats")
|
|
703
|
+
async def rate_limit_stats():
|
|
704
|
+
"""Get rate limiting stats per domain."""
|
|
705
|
+
from ..intelligence.rate_limiter import PipelineRateLimiter
|
|
706
|
+
limiter = PipelineRateLimiter()
|
|
707
|
+
return {"stats": limiter.get_stats()}
|
|
708
|
+
|
|
709
|
+
# ═══════════════════════════════════════════════════════════════
|
|
710
|
+
# RUNTIME CONTEXT — cross-context assembly
|
|
711
|
+
# ═══════════════════════════════════════════════════════════════
|
|
712
|
+
|
|
713
|
+
_assembler = None
|
|
714
|
+
|
|
715
|
+
def _get_assembler():
|
|
716
|
+
nonlocal _assembler
|
|
717
|
+
if _assembler is None:
|
|
718
|
+
from ..intelligence.runtime_context import RuntimeContextAssembler
|
|
719
|
+
_assembler = RuntimeContextAssembler(
|
|
720
|
+
graph_registry=graph_registry,
|
|
721
|
+
context_manager=context_manager,
|
|
722
|
+
)
|
|
723
|
+
return _assembler
|
|
724
|
+
|
|
725
|
+
@router.post("/runtime/assemble")
|
|
726
|
+
async def assemble_runtime_context(body: dict = Body(...)):
|
|
727
|
+
"""Assemble a runtime view across multiple atomic contexts.
|
|
728
|
+
|
|
729
|
+
Body: {
|
|
730
|
+
"contexts": ["TCS", "India Economy", "Banking Sector"],
|
|
731
|
+
"focus_entity": "TCS",
|
|
732
|
+
"days": 30
|
|
733
|
+
}
|
|
734
|
+
"""
|
|
735
|
+
contexts = body.get("contexts", [])
|
|
736
|
+
if not contexts:
|
|
737
|
+
raise HTTPException(400, "No contexts specified")
|
|
738
|
+
|
|
739
|
+
assembler = _get_assembler()
|
|
740
|
+
view = assembler.assemble(
|
|
741
|
+
contexts=contexts,
|
|
742
|
+
focus_entity=body.get("focus_entity", ""),
|
|
743
|
+
days=body.get("days", 30),
|
|
744
|
+
)
|
|
745
|
+
return view.to_dict()
|
|
746
|
+
|
|
747
|
+
@router.get("/runtime/compare")
|
|
748
|
+
async def compare_contexts(contexts: str = Query(...), days: int = Query(30)):
|
|
749
|
+
"""Quick comparison across contexts. Comma-separated names."""
|
|
750
|
+
assembler = _get_assembler()
|
|
751
|
+
names = [c.strip() for c in contexts.split(",")]
|
|
752
|
+
view = assembler.assemble(contexts=names, days=days)
|
|
753
|
+
return {
|
|
754
|
+
"contexts": names,
|
|
755
|
+
"sentiment_comparison": view.sentiment_comparison,
|
|
756
|
+
"cross_entity_pairs": view.cross_entity_pairs,
|
|
757
|
+
"stats": view.stats,
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
# ═══════════════════════════════════════════════════════════════
|
|
761
|
+
# SESSIONS — attach/detach contexts, insight write-back
|
|
762
|
+
# ═══════════════════════════════════════════════════════════════
|
|
763
|
+
|
|
764
|
+
_session_mgr = None
|
|
765
|
+
|
|
766
|
+
def _get_session_mgr():
|
|
767
|
+
nonlocal _session_mgr
|
|
768
|
+
if _session_mgr is None:
|
|
769
|
+
from ..intelligence.session import SessionManager
|
|
770
|
+
_session_mgr = SessionManager(
|
|
771
|
+
graph_registry=graph_registry,
|
|
772
|
+
context_manager=context_manager,
|
|
773
|
+
)
|
|
774
|
+
return _session_mgr
|
|
775
|
+
|
|
776
|
+
@router.post("/sessions")
|
|
777
|
+
async def create_session(body: dict = Body(...)):
|
|
778
|
+
"""Create a new session.
|
|
779
|
+
Body: {"name": "IT Sector Analysis"}
|
|
780
|
+
"""
|
|
781
|
+
mgr = _get_session_mgr()
|
|
782
|
+
session = mgr.create(body.get("name", "Untitled"))
|
|
783
|
+
|
|
784
|
+
# Auto-attach contexts if provided
|
|
785
|
+
for ctx in body.get("contexts", []):
|
|
786
|
+
session.attach(ctx)
|
|
787
|
+
|
|
788
|
+
return session.to_dict()
|
|
789
|
+
|
|
790
|
+
@router.get("/sessions")
|
|
791
|
+
async def list_sessions():
|
|
792
|
+
mgr = _get_session_mgr()
|
|
793
|
+
return {"sessions": mgr.list_sessions()}
|
|
794
|
+
|
|
795
|
+
@router.get("/sessions/{session_id}")
|
|
796
|
+
async def get_session(session_id: str):
|
|
797
|
+
mgr = _get_session_mgr()
|
|
798
|
+
session = mgr.get(session_id)
|
|
799
|
+
if not session:
|
|
800
|
+
raise HTTPException(404, "Session not found")
|
|
801
|
+
return session.to_dict()
|
|
802
|
+
|
|
803
|
+
@router.post("/sessions/{session_id}/attach")
|
|
804
|
+
async def attach_context(session_id: str, body: dict = Body(...)):
|
|
805
|
+
"""Attach a context to a session.
|
|
806
|
+
Body: {"context": "tcs"}
|
|
807
|
+
"""
|
|
808
|
+
mgr = _get_session_mgr()
|
|
809
|
+
session = mgr.get(session_id)
|
|
810
|
+
if not session:
|
|
811
|
+
raise HTTPException(404, "Session not found")
|
|
812
|
+
ctx = body.get("context", "")
|
|
813
|
+
if not ctx:
|
|
814
|
+
raise HTTPException(400, "No context specified")
|
|
815
|
+
session.attach(ctx)
|
|
816
|
+
return session.status()
|
|
817
|
+
|
|
818
|
+
@router.post("/sessions/{session_id}/detach")
|
|
819
|
+
async def detach_context(session_id: str, body: dict = Body(...)):
|
|
820
|
+
"""Detach a context from a session.
|
|
821
|
+
Body: {"context": "tcs"}
|
|
822
|
+
"""
|
|
823
|
+
mgr = _get_session_mgr()
|
|
824
|
+
session = mgr.get(session_id)
|
|
825
|
+
if not session:
|
|
826
|
+
raise HTTPException(404, "Session not found")
|
|
827
|
+
session.detach(body.get("context", ""))
|
|
828
|
+
return session.status()
|
|
829
|
+
|
|
830
|
+
@router.post("/sessions/{session_id}/assemble")
|
|
831
|
+
async def assemble_session(session_id: str, body: dict = Body({})):
|
|
832
|
+
"""Assemble the runtime view for a session."""
|
|
833
|
+
mgr = _get_session_mgr()
|
|
834
|
+
session = mgr.get(session_id)
|
|
835
|
+
if not session:
|
|
836
|
+
raise HTTPException(404, "Session not found")
|
|
837
|
+
view = session.assemble(
|
|
838
|
+
focus_entity=body.get("focus_entity", ""),
|
|
839
|
+
days=body.get("days", 30),
|
|
840
|
+
)
|
|
841
|
+
if not view:
|
|
842
|
+
return {"error": "No contexts attached"}
|
|
843
|
+
return view.to_dict()
|
|
844
|
+
|
|
845
|
+
@router.post("/sessions/{session_id}/insight")
|
|
846
|
+
async def add_insight(session_id: str, body: dict = Body(...)):
|
|
847
|
+
"""Write an insight back to a context (visible to all agents + dashboard).
|
|
848
|
+
|
|
849
|
+
Body: {"insight": "TCS outperforms when USD weakens",
|
|
850
|
+
"produced_by": "agent:claude", "confidence": 0.85,
|
|
851
|
+
"context": "tcs",
|
|
852
|
+
"evidence": ["TCS Q1 up 4.2%", "USD stable"]}
|
|
853
|
+
|
|
854
|
+
If context is specified, the insight is written INTO that context's
|
|
855
|
+
graph — visible in the dashboard, to other agents, and in runtime views.
|
|
856
|
+
If context is omitted, stored in session-only graph.
|
|
857
|
+
"""
|
|
858
|
+
mgr = _get_session_mgr()
|
|
859
|
+
session = mgr.get(session_id)
|
|
860
|
+
if not session:
|
|
861
|
+
raise HTTPException(404, "Session not found")
|
|
862
|
+
ins = session.add_insight(
|
|
863
|
+
insight=body.get("insight", ""),
|
|
864
|
+
produced_by=body.get("produced_by", ""),
|
|
865
|
+
confidence=body.get("confidence", 0.0),
|
|
866
|
+
evidence=body.get("evidence", []),
|
|
867
|
+
tags=body.get("tags", []),
|
|
868
|
+
context=body.get("context", ""),
|
|
869
|
+
)
|
|
870
|
+
return ins.to_dict()
|
|
871
|
+
|
|
872
|
+
@router.get("/sessions/{session_id}/insights")
|
|
873
|
+
async def get_insights(session_id: str, produced_by: str = Query("")):
|
|
874
|
+
"""Get insights from a session."""
|
|
875
|
+
mgr = _get_session_mgr()
|
|
876
|
+
session = mgr.get(session_id)
|
|
877
|
+
if not session:
|
|
878
|
+
raise HTTPException(404, "Session not found")
|
|
879
|
+
insights = session.get_insights(produced_by=produced_by)
|
|
880
|
+
return {"insights": [i.to_dict() for i in insights]}
|
|
881
|
+
|
|
882
|
+
@router.delete("/sessions/{session_id}")
|
|
883
|
+
async def delete_session(session_id: str):
|
|
884
|
+
mgr = _get_session_mgr()
|
|
885
|
+
if mgr.delete(session_id):
|
|
886
|
+
return {"status": "deleted"}
|
|
887
|
+
raise HTTPException(404, "Session not found")
|
|
888
|
+
|
|
889
|
+
# ── Prediction Tracker ──
|
|
890
|
+
|
|
891
|
+
def _get_predictor():
|
|
892
|
+
try:
|
|
893
|
+
from plugins.stock_analysis.predictor import PredictionTracker
|
|
894
|
+
return PredictionTracker(graph_registry=graph_registry)
|
|
895
|
+
except ImportError:
|
|
896
|
+
# Add project root to path and retry
|
|
897
|
+
import sys
|
|
898
|
+
from pathlib import Path
|
|
899
|
+
root = str(Path(__file__).parent.parent.parent)
|
|
900
|
+
if root not in sys.path:
|
|
901
|
+
sys.path.insert(0, root)
|
|
902
|
+
try:
|
|
903
|
+
from plugins.stock_analysis.predictor import PredictionTracker
|
|
904
|
+
return PredictionTracker(graph_registry=graph_registry)
|
|
905
|
+
except ImportError:
|
|
906
|
+
raise HTTPException(501, "Stock analysis plugin not installed")
|
|
907
|
+
|
|
908
|
+
@router.post("/predict/{entity}")
|
|
909
|
+
async def predict_entity(entity: str, horizon: str = Query("1d")):
|
|
910
|
+
"""Make a directional prediction for an entity."""
|
|
911
|
+
try:
|
|
912
|
+
tracker = _get_predictor()
|
|
913
|
+
pred = tracker.predict(entity, horizon=horizon)
|
|
914
|
+
return pred.to_dict()
|
|
915
|
+
except HTTPException:
|
|
916
|
+
raise
|
|
917
|
+
except Exception as e:
|
|
918
|
+
logger.exception("Prediction error for %s", entity)
|
|
919
|
+
raise HTTPException(500, str(e))
|
|
920
|
+
|
|
921
|
+
@router.post("/predict")
|
|
922
|
+
async def predict_all(companies: list = Body(None)):
|
|
923
|
+
"""Make predictions for all tracked companies."""
|
|
924
|
+
tracker = _get_predictor()
|
|
925
|
+
preds = tracker.predict_all(companies)
|
|
926
|
+
return {"predictions": [p.to_dict() for p in preds]}
|
|
927
|
+
|
|
928
|
+
@router.post("/predictions/verify")
|
|
929
|
+
async def verify_predictions():
|
|
930
|
+
"""Verify past predictions against actual outcomes."""
|
|
931
|
+
tracker = _get_predictor()
|
|
932
|
+
verified = tracker.verify_predictions()
|
|
933
|
+
return {"verified": verified, "count": len(verified)}
|
|
934
|
+
|
|
935
|
+
@router.get("/predictions/test")
|
|
936
|
+
async def test_predictor():
|
|
937
|
+
"""Test if predictor can be instantiated."""
|
|
938
|
+
try:
|
|
939
|
+
tracker = _get_predictor()
|
|
940
|
+
return {"status": "ok", "type": type(tracker).__name__}
|
|
941
|
+
except Exception as e:
|
|
942
|
+
return {"status": "error", "error": str(e), "type": type(e).__name__}
|
|
943
|
+
|
|
944
|
+
@router.get("/predictions/accuracy")
|
|
945
|
+
async def get_accuracy():
|
|
946
|
+
"""Get prediction accuracy stats."""
|
|
947
|
+
try:
|
|
948
|
+
tracker = _get_predictor()
|
|
949
|
+
return tracker.get_accuracy()
|
|
950
|
+
except HTTPException:
|
|
951
|
+
raise
|
|
952
|
+
except Exception as e:
|
|
953
|
+
logger.exception("Prediction accuracy error")
|
|
954
|
+
raise HTTPException(500, str(e))
|
|
955
|
+
|
|
956
|
+
@router.get("/predictions")
|
|
957
|
+
async def list_predictions(entity: str = Query(""), verified_only: bool = Query(False)):
|
|
958
|
+
"""List all predictions, optionally filtered."""
|
|
959
|
+
tracker = _get_predictor()
|
|
960
|
+
stats = tracker.get_accuracy()
|
|
961
|
+
preds = stats["predictions"]
|
|
962
|
+
if entity:
|
|
963
|
+
preds = [p for p in preds if p["entity"].lower() == entity.lower()]
|
|
964
|
+
if verified_only:
|
|
965
|
+
preds = [p for p in preds if p["verified"]]
|
|
966
|
+
return {"predictions": preds, "total": len(preds)}
|
|
967
|
+
|
|
968
|
+
# ── Signal Config ──
|
|
969
|
+
|
|
970
|
+
def _get_signal_config():
|
|
971
|
+
try:
|
|
972
|
+
import sys
|
|
973
|
+
from pathlib import Path
|
|
974
|
+
root = str(Path(__file__).parent.parent.parent)
|
|
975
|
+
if root not in sys.path:
|
|
976
|
+
sys.path.insert(0, root)
|
|
977
|
+
from plugins.stock_analysis.signal_config import SignalConfig
|
|
978
|
+
return SignalConfig()
|
|
979
|
+
except ImportError:
|
|
980
|
+
raise HTTPException(501, "Stock analysis plugin not installed")
|
|
981
|
+
|
|
982
|
+
@router.get("/signal-config/{entity}")
|
|
983
|
+
async def get_signal_config(entity: str):
|
|
984
|
+
"""Get full signal config for an entity (weights, thresholds, decay, RL)."""
|
|
985
|
+
cfg = _get_signal_config()
|
|
986
|
+
return cfg.get_full_config(entity)
|
|
987
|
+
|
|
988
|
+
@router.get("/signal-config")
|
|
989
|
+
async def get_all_signal_configs():
|
|
990
|
+
"""Get configs for all tracked stocks + defaults."""
|
|
991
|
+
cfg = _get_signal_config()
|
|
992
|
+
return {"configs": cfg.get_all_configs(), "defaults": cfg.get_defaults()}
|
|
993
|
+
|
|
994
|
+
@router.put("/signal-config/{entity}/weights")
|
|
995
|
+
async def set_signal_weights(entity: str, weights: dict = Body(...)):
|
|
996
|
+
"""Set custom weights for a stock. Auto-normalized to sum=1."""
|
|
997
|
+
cfg = _get_signal_config()
|
|
998
|
+
cfg.set_weights(entity, weights)
|
|
999
|
+
return {"status": "updated", "weights": cfg.get_weights(entity)}
|
|
1000
|
+
|
|
1001
|
+
@router.put("/signal-config/{entity}/thresholds")
|
|
1002
|
+
async def set_signal_thresholds(entity: str, thresholds: dict = Body(...)):
|
|
1003
|
+
"""Set custom thresholds for a stock."""
|
|
1004
|
+
cfg = _get_signal_config()
|
|
1005
|
+
cfg.set_thresholds(entity, thresholds)
|
|
1006
|
+
return {"status": "updated", "thresholds": cfg.get_thresholds(entity)}
|
|
1007
|
+
|
|
1008
|
+
@router.put("/signal-config/rl")
|
|
1009
|
+
async def set_rl_config(rl: dict = Body(...)):
|
|
1010
|
+
"""Set RL parameters (reward, penalty, enabled)."""
|
|
1011
|
+
cfg = _get_signal_config()
|
|
1012
|
+
cfg.set_rl(rl)
|
|
1013
|
+
return {"status": "updated", "rl": cfg.get_rl()}
|
|
1014
|
+
|
|
1015
|
+
@router.delete("/signal-config/{entity}")
|
|
1016
|
+
async def reset_signal_config(entity: str):
|
|
1017
|
+
"""Reset a stock's config to domain/global defaults."""
|
|
1018
|
+
cfg = _get_signal_config()
|
|
1019
|
+
cfg.reset(entity)
|
|
1020
|
+
return {"status": "reset", "config": cfg.get_full_config(entity)}
|
|
1021
|
+
|
|
1022
|
+
@router.get("/learned-weights")
|
|
1023
|
+
async def get_learned_weights():
|
|
1024
|
+
"""Get RL-learned weights for all stocks."""
|
|
1025
|
+
tracker = _get_predictor()
|
|
1026
|
+
return tracker.get_weights()
|
|
1027
|
+
|
|
1028
|
+
return router
|