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,116 @@
|
|
|
1
|
+
"""
|
|
2
|
+
PlaygroundConn — Shared connection wrapper for playground simulation endpoints.
|
|
3
|
+
|
|
4
|
+
Bridges the gap between the ToolContext interface (used by MCP tools) and the
|
|
5
|
+
graph registry. Resolves tenant-scoped namespaces to bare graph names, loads
|
|
6
|
+
graphs from disk, and proxies query/build_context/get_edges/get_node calls.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import logging
|
|
12
|
+
from typing import Any, Optional
|
|
13
|
+
|
|
14
|
+
logger = logging.getLogger(__name__)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class PlaygroundConn:
|
|
18
|
+
"""Connection wrapper that MCP tools can use via ToolContext.conn."""
|
|
19
|
+
|
|
20
|
+
def __init__(self, namespace: str, registry):
|
|
21
|
+
self.namespace = namespace
|
|
22
|
+
self._namespace = namespace # context tools use _namespace
|
|
23
|
+
self.graph_registry = registry
|
|
24
|
+
self._graph_registry = registry # context tools use _graph_registry
|
|
25
|
+
|
|
26
|
+
# Resolve graph: try scoped name, then bare name after ":"
|
|
27
|
+
self.db = None
|
|
28
|
+
self._resolved_ns = namespace
|
|
29
|
+
if namespace and registry:
|
|
30
|
+
self.db = registry.get_graph(namespace, load_if_missing=True)
|
|
31
|
+
if self.db is None and ":" in namespace:
|
|
32
|
+
bare = namespace.split(":", 1)[1]
|
|
33
|
+
self.db = registry.get_graph(bare, load_if_missing=True)
|
|
34
|
+
if self.db:
|
|
35
|
+
self._resolved_ns = bare
|
|
36
|
+
if self.db is None:
|
|
37
|
+
self.db = registry.get_graph_for_request(namespace)
|
|
38
|
+
|
|
39
|
+
self.executor = None
|
|
40
|
+
try:
|
|
41
|
+
from ..aiql import AIQLExecutor
|
|
42
|
+
self.executor = AIQLExecutor(contextcore=self.db, graph_registry=registry)
|
|
43
|
+
if self._resolved_ns:
|
|
44
|
+
self.executor.active_namespace = self._resolved_ns
|
|
45
|
+
except Exception:
|
|
46
|
+
pass
|
|
47
|
+
|
|
48
|
+
def get_graph(self):
|
|
49
|
+
return self.db
|
|
50
|
+
|
|
51
|
+
def query(self, q: str):
|
|
52
|
+
if self.executor:
|
|
53
|
+
return self.executor.execute(q)
|
|
54
|
+
raise RuntimeError("No executor available — select a graph first")
|
|
55
|
+
|
|
56
|
+
def build_context(self, **kwargs):
|
|
57
|
+
if self.executor:
|
|
58
|
+
return self.executor.build_context(**kwargs)
|
|
59
|
+
raise RuntimeError("No executor available")
|
|
60
|
+
|
|
61
|
+
def get_edges(self, label: Optional[str] = None):
|
|
62
|
+
if self.db and hasattr(self.db, "get_all_edges"):
|
|
63
|
+
edges = self.db.get_all_edges()
|
|
64
|
+
if label:
|
|
65
|
+
edges = [e for e in edges if getattr(e, "label", "") == label]
|
|
66
|
+
return edges
|
|
67
|
+
return []
|
|
68
|
+
|
|
69
|
+
def get_node(self, node_id: str):
|
|
70
|
+
if self.db and hasattr(self.db, "get_node"):
|
|
71
|
+
return self.db.get_node(node_id)
|
|
72
|
+
return None
|
|
73
|
+
|
|
74
|
+
def get_nodes(self, label: str = None, where: dict = None):
|
|
75
|
+
"""Return nodes from the graph, optionally filtered by label and properties.
|
|
76
|
+
|
|
77
|
+
Required by ProjectGraph.__init__ to find/create the Project node.
|
|
78
|
+
"""
|
|
79
|
+
if not self.db or not hasattr(self.db, "get_all_nodes"):
|
|
80
|
+
return []
|
|
81
|
+
nodes = self.db.get_all_nodes()
|
|
82
|
+
if label:
|
|
83
|
+
nodes = [n for n in nodes if getattr(n, "label", "") == label]
|
|
84
|
+
if where:
|
|
85
|
+
filtered = []
|
|
86
|
+
for n in nodes:
|
|
87
|
+
props = getattr(n, "properties", {}) or {}
|
|
88
|
+
if all(props.get(k) == v for k, v in where.items()):
|
|
89
|
+
filtered.append(n)
|
|
90
|
+
nodes = filtered
|
|
91
|
+
return nodes
|
|
92
|
+
|
|
93
|
+
def add_edge(self, source_id: str, target_id: str, label: str, properties: dict = None):
|
|
94
|
+
"""Add an edge between two nodes. Required by ProjectGraph."""
|
|
95
|
+
if self.executor:
|
|
96
|
+
if properties:
|
|
97
|
+
props_str = ", ".join(f'{k}: "{v}"' for k, v in properties.items())
|
|
98
|
+
self.executor.execute(
|
|
99
|
+
f'CREATE EDGE {label} FROM "{source_id}" TO "{target_id}" {{{props_str}}}'
|
|
100
|
+
)
|
|
101
|
+
else:
|
|
102
|
+
self.executor.execute(
|
|
103
|
+
f'CREATE EDGE {label} FROM "{source_id}" TO "{target_id}"'
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def create_tool_context(conn: PlaygroundConn, agent_id: str, agent_name: str):
|
|
108
|
+
"""Create a ToolContext with all required attributes for playground tools."""
|
|
109
|
+
from ..tools.registry import ToolContext
|
|
110
|
+
ctx = ToolContext(
|
|
111
|
+
conn=conn,
|
|
112
|
+
agent_id=agent_id,
|
|
113
|
+
agent_name=agent_name,
|
|
114
|
+
)
|
|
115
|
+
ctx.db = conn.db # intelligence tools use ctx.db
|
|
116
|
+
return ctx
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Projects Router
|
|
3
|
+
=================
|
|
4
|
+
REST API for SDLC context projects. Each project is one namespace with
|
|
5
|
+
typed SDLC nodes. Delegates to ProjectContext.
|
|
6
|
+
"""
|
|
7
|
+
import logging
|
|
8
|
+
from typing import Any, Dict, List
|
|
9
|
+
|
|
10
|
+
from fastapi import APIRouter, HTTPException, Query
|
|
11
|
+
from pydantic import BaseModel
|
|
12
|
+
|
|
13
|
+
logger = logging.getLogger(__name__)
|
|
14
|
+
|
|
15
|
+
try:
|
|
16
|
+
from ..project.code_context import CodeContext
|
|
17
|
+
except Exception:
|
|
18
|
+
CodeContext = None # type: ignore
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class CreateProjectRequest(BaseModel):
|
|
22
|
+
name: str
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def create_projects_router(user_auth=None, base_path: str = "contextcore_data") -> APIRouter:
|
|
26
|
+
"""Create the /dashboard/projects router."""
|
|
27
|
+
from ..project.project_context import ProjectContext
|
|
28
|
+
|
|
29
|
+
router = APIRouter(prefix="/dashboard/projects", tags=["Projects"])
|
|
30
|
+
|
|
31
|
+
def _load(name: str) -> ProjectContext:
|
|
32
|
+
try:
|
|
33
|
+
return ProjectContext.load(name, base_path=base_path)
|
|
34
|
+
except KeyError as exc:
|
|
35
|
+
raise HTTPException(status_code=404, detail=str(exc))
|
|
36
|
+
|
|
37
|
+
def _list_project_names() -> List[str]:
|
|
38
|
+
return ProjectContext.list_names(base_path=base_path)
|
|
39
|
+
|
|
40
|
+
# ── CRUD ─────────────────────────────────────────────────────────────────
|
|
41
|
+
|
|
42
|
+
@router.post("", status_code=201)
|
|
43
|
+
async def create_project(req: CreateProjectRequest) -> Dict[str, Any]:
|
|
44
|
+
"""Create (or open) a project namespace."""
|
|
45
|
+
pc = ProjectContext.create(req.name, base_path=base_path)
|
|
46
|
+
return {"name": pc.name, "status": "created"}
|
|
47
|
+
|
|
48
|
+
@router.get("")
|
|
49
|
+
async def list_projects() -> List[Dict[str, str]]:
|
|
50
|
+
"""List all project namespaces."""
|
|
51
|
+
return [{"name": name} for name in sorted(_list_project_names())]
|
|
52
|
+
|
|
53
|
+
@router.get("/{name}")
|
|
54
|
+
async def get_project(name: str) -> Dict[str, Any]:
|
|
55
|
+
"""Project overview including coverage scores."""
|
|
56
|
+
pc = _load(name)
|
|
57
|
+
return {"name": pc.name, "coverage": pc.coverage_score()}
|
|
58
|
+
|
|
59
|
+
# ── SDLC endpoints ───────────────────────────────────────────────────────
|
|
60
|
+
|
|
61
|
+
@router.get("/{name}/coverage")
|
|
62
|
+
async def get_coverage(name: str) -> Dict[str, Any]:
|
|
63
|
+
"""Per-layer SDLC coverage scores."""
|
|
64
|
+
return _load(name).coverage_score()
|
|
65
|
+
|
|
66
|
+
@router.get("/{name}/stale")
|
|
67
|
+
async def get_stale(name: str) -> List[Dict[str, Any]]:
|
|
68
|
+
"""All stale SDLC nodes in the project namespace."""
|
|
69
|
+
return _load(name).stale_report()
|
|
70
|
+
|
|
71
|
+
@router.get("/{name}/brief")
|
|
72
|
+
async def get_brief(
|
|
73
|
+
name: str,
|
|
74
|
+
topic: str = Query(default=""),
|
|
75
|
+
phase: str = Query(default=""),
|
|
76
|
+
) -> Dict[str, Any]:
|
|
77
|
+
"""Scoped context package for an agent about to work."""
|
|
78
|
+
return _load(name).agent_brief(topic=topic, phase=phase)
|
|
79
|
+
|
|
80
|
+
@router.get("/{name}/timeline")
|
|
81
|
+
async def get_timeline(
|
|
82
|
+
name: str,
|
|
83
|
+
limit: int = Query(default=50, ge=1, le=500),
|
|
84
|
+
) -> List[Dict[str, Any]]:
|
|
85
|
+
"""Recent graph changes in the project namespace."""
|
|
86
|
+
pc = _load(name)
|
|
87
|
+
try:
|
|
88
|
+
from ..core.time_travel import GraphTimeTraveler
|
|
89
|
+
tt = GraphTimeTraveler(pc.db)
|
|
90
|
+
events = tt.get_graph_timeline(limit=limit)
|
|
91
|
+
return events if isinstance(events, list) else []
|
|
92
|
+
except Exception:
|
|
93
|
+
return []
|
|
94
|
+
|
|
95
|
+
# ── Spec / Requirements ──────────────────────────────────
|
|
96
|
+
|
|
97
|
+
def _get_cc(name: str):
|
|
98
|
+
"""Load a CodeContext by name."""
|
|
99
|
+
return CodeContext.load(name)
|
|
100
|
+
|
|
101
|
+
@router.post("/{name}/spec/ingest")
|
|
102
|
+
async def ingest_spec(name: str):
|
|
103
|
+
"""Parse the project spec into Requirement/Feature/Constraint graph nodes."""
|
|
104
|
+
cc = _get_cc(name)
|
|
105
|
+
spec_text = cc.ns.get_metadata("spec") or ""
|
|
106
|
+
try:
|
|
107
|
+
ids = cc.ingest_spec(spec_text)
|
|
108
|
+
return {"ingested": ids, "count": len(ids)}
|
|
109
|
+
except Exception as e:
|
|
110
|
+
raise HTTPException(500, f"Spec ingestion failed: {e}")
|
|
111
|
+
|
|
112
|
+
@router.get("/{name}/spec/requirements")
|
|
113
|
+
async def get_requirements(name: str):
|
|
114
|
+
"""Return all Requirement/Feature/Constraint nodes for the project."""
|
|
115
|
+
cc = _get_cc(name)
|
|
116
|
+
try:
|
|
117
|
+
nodes = cc.get_requirements()
|
|
118
|
+
return {"nodes": nodes, "total": len(nodes)}
|
|
119
|
+
except Exception as e:
|
|
120
|
+
raise HTTPException(500, f"Failed to load requirements: {e}")
|
|
121
|
+
|
|
122
|
+
@router.get("/{name}/spec/coverage")
|
|
123
|
+
async def get_coverage_spec(name: str):
|
|
124
|
+
"""Return traceability coverage for the project's requirements."""
|
|
125
|
+
cc = _get_cc(name)
|
|
126
|
+
try:
|
|
127
|
+
return cc.check_coverage()
|
|
128
|
+
except Exception as e:
|
|
129
|
+
raise HTTPException(500, f"Coverage check failed: {e}")
|
|
130
|
+
|
|
131
|
+
return router
|
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Queries Router
|
|
3
|
+
==============
|
|
4
|
+
AIQL query execution, saved queries, and query history.
|
|
5
|
+
|
|
6
|
+
POST /dashboard/query/execute — Execute AIQL query
|
|
7
|
+
GET /dashboard/queries/saved — List saved queries
|
|
8
|
+
POST /dashboard/queries/saved — Save a query
|
|
9
|
+
DELETE /dashboard/queries/saved/{id} — Delete saved query
|
|
10
|
+
GET /dashboard/queries/history — Query execution history
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
import json
|
|
16
|
+
import logging
|
|
17
|
+
import secrets
|
|
18
|
+
import time
|
|
19
|
+
from datetime import datetime, timezone
|
|
20
|
+
from typing import Any, Dict, List, Optional
|
|
21
|
+
|
|
22
|
+
from fastapi import APIRouter, Depends, HTTPException, Query
|
|
23
|
+
from .models import paginate
|
|
24
|
+
from pydantic import BaseModel, Field
|
|
25
|
+
|
|
26
|
+
logger = logging.getLogger(__name__)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
# ---------------------------------------------------------------------------
|
|
30
|
+
# Request models
|
|
31
|
+
# ---------------------------------------------------------------------------
|
|
32
|
+
|
|
33
|
+
class ExecuteQueryRequest(BaseModel):
|
|
34
|
+
query: str = Field(..., min_length=1, description="AIQL query")
|
|
35
|
+
graph: Optional[str] = Field(None, description="Target graph name")
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class SaveQueryRequest(BaseModel):
|
|
39
|
+
name: str = Field(..., min_length=1, max_length=100)
|
|
40
|
+
query: str = Field(..., min_length=1)
|
|
41
|
+
description: Optional[str] = None
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
# ---------------------------------------------------------------------------
|
|
45
|
+
# Router factory
|
|
46
|
+
# ---------------------------------------------------------------------------
|
|
47
|
+
|
|
48
|
+
def create_queries_router(
|
|
49
|
+
user_registry,
|
|
50
|
+
tenant_registry,
|
|
51
|
+
graph_registry,
|
|
52
|
+
usage_meter=None,
|
|
53
|
+
) -> APIRouter:
|
|
54
|
+
"""Create the /dashboard/query and /dashboard/queries routers."""
|
|
55
|
+
|
|
56
|
+
from .auth import UserAuth
|
|
57
|
+
from ..aiql.engine import AIQLExecutor
|
|
58
|
+
|
|
59
|
+
router = APIRouter(tags=["queries"])
|
|
60
|
+
user_auth = UserAuth(user_registry)
|
|
61
|
+
|
|
62
|
+
def _get_tenant(user):
|
|
63
|
+
tenant_id = user_registry.get_primary_tenant_id(user.user_id)
|
|
64
|
+
if not tenant_id:
|
|
65
|
+
raise HTTPException(status_code=404, detail="No workspace found")
|
|
66
|
+
tenant = tenant_registry.get(tenant_id)
|
|
67
|
+
if not tenant:
|
|
68
|
+
raise HTTPException(status_code=404, detail="Workspace not found")
|
|
69
|
+
return tenant
|
|
70
|
+
|
|
71
|
+
# ------------------------------------------------------------------
|
|
72
|
+
# POST /dashboard/query/execute
|
|
73
|
+
# ------------------------------------------------------------------
|
|
74
|
+
@router.post("/dashboard/query/execute")
|
|
75
|
+
async def execute_query(req: ExecuteQueryRequest, user=Depends(user_auth)):
|
|
76
|
+
tenant = _get_tenant(user)
|
|
77
|
+
|
|
78
|
+
try:
|
|
79
|
+
executor = AIQLExecutor(graph_registry=graph_registry)
|
|
80
|
+
|
|
81
|
+
# Scope to tenant's graph if specified
|
|
82
|
+
if req.graph:
|
|
83
|
+
scoped_name = f"{tenant.tenant_id}:{req.graph}"
|
|
84
|
+
try:
|
|
85
|
+
executor.execute(f"USE GRAPH {scoped_name}")
|
|
86
|
+
except Exception:
|
|
87
|
+
# Try without namespace prefix
|
|
88
|
+
executor.execute(f"USE GRAPH {req.graph}")
|
|
89
|
+
|
|
90
|
+
start = time.time()
|
|
91
|
+
result = executor.execute(req.query)
|
|
92
|
+
duration_ms = int((time.time() - start) * 1000)
|
|
93
|
+
|
|
94
|
+
# Record in metering
|
|
95
|
+
if usage_meter:
|
|
96
|
+
try:
|
|
97
|
+
usage_meter.record(tenant.tenant_id, "query")
|
|
98
|
+
except Exception:
|
|
99
|
+
pass
|
|
100
|
+
|
|
101
|
+
# Record in query history
|
|
102
|
+
_record_history(tenant.tenant_id, user.user_id, req.query, duration_ms, "success")
|
|
103
|
+
|
|
104
|
+
# Normalize result for frontend
|
|
105
|
+
if isinstance(result, list):
|
|
106
|
+
data = result
|
|
107
|
+
elif isinstance(result, dict):
|
|
108
|
+
data = result
|
|
109
|
+
else:
|
|
110
|
+
data = str(result)
|
|
111
|
+
|
|
112
|
+
# Also return graph nodes/edges for visualization
|
|
113
|
+
nodes_out = []
|
|
114
|
+
edges_out = []
|
|
115
|
+
try:
|
|
116
|
+
scoped_name = f"{tenant.tenant_id}:{req.graph}" if req.graph else None
|
|
117
|
+
graph = graph_registry.get_graph(scoped_name) if scoped_name else None
|
|
118
|
+
if not graph and req.graph:
|
|
119
|
+
graph = graph_registry.get_graph(req.graph)
|
|
120
|
+
if graph:
|
|
121
|
+
for n in graph.get_all_nodes():
|
|
122
|
+
nodes_out.append({
|
|
123
|
+
"id": n.id, "label": n.label,
|
|
124
|
+
"name": n.name or (n.properties or {}).get("name", ""),
|
|
125
|
+
"properties": n.properties or {},
|
|
126
|
+
})
|
|
127
|
+
for e in graph.get_all_edges():
|
|
128
|
+
edges_out.append({
|
|
129
|
+
"id": e.id, "source": e.source, "target": e.target,
|
|
130
|
+
"label": e.label, "properties": e.properties or {},
|
|
131
|
+
})
|
|
132
|
+
except Exception:
|
|
133
|
+
pass
|
|
134
|
+
|
|
135
|
+
return {
|
|
136
|
+
"data": data,
|
|
137
|
+
"query": req.query,
|
|
138
|
+
"duration_ms": duration_ms,
|
|
139
|
+
"row_count": len(data) if isinstance(data, list) else None,
|
|
140
|
+
"nodes": nodes_out,
|
|
141
|
+
"edges": edges_out,
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
except Exception as e:
|
|
145
|
+
_record_history(tenant.tenant_id, user.user_id, req.query, 0, "error", str(e))
|
|
146
|
+
raise HTTPException(status_code=400, detail=str(e))
|
|
147
|
+
|
|
148
|
+
# ------------------------------------------------------------------
|
|
149
|
+
# Saved queries — stored in user_registry's DB
|
|
150
|
+
# ------------------------------------------------------------------
|
|
151
|
+
|
|
152
|
+
@router.get("/dashboard/queries/saved")
|
|
153
|
+
async def list_saved(
|
|
154
|
+
limit: int = Query(50, ge=1, le=500),
|
|
155
|
+
offset: int = Query(0, ge=0),
|
|
156
|
+
user=Depends(user_auth),
|
|
157
|
+
):
|
|
158
|
+
tenant = _get_tenant(user)
|
|
159
|
+
queries = _get_saved_queries(tenant.tenant_id, user.user_id)
|
|
160
|
+
page = paginate(queries, limit, offset)
|
|
161
|
+
return {"queries": page["items"], "total": page["total"], "limit": page["limit"], "offset": page["offset"]}
|
|
162
|
+
|
|
163
|
+
@router.post("/dashboard/queries/saved")
|
|
164
|
+
async def save_query(req: SaveQueryRequest, user=Depends(user_auth)):
|
|
165
|
+
tenant = _get_tenant(user)
|
|
166
|
+
query_id = secrets.token_hex(12)
|
|
167
|
+
now = datetime.now(timezone.utc).isoformat()
|
|
168
|
+
_insert_saved_query(query_id, tenant.tenant_id, user.user_id, req.name, req.query, req.description, now)
|
|
169
|
+
return {"query_id": query_id, "message": "Query saved"}
|
|
170
|
+
|
|
171
|
+
@router.delete("/dashboard/queries/saved/{query_id}")
|
|
172
|
+
async def delete_saved(query_id: str, user=Depends(user_auth)):
|
|
173
|
+
tenant = _get_tenant(user)
|
|
174
|
+
_delete_saved_query(query_id, tenant.tenant_id)
|
|
175
|
+
return {"message": "Query deleted"}
|
|
176
|
+
|
|
177
|
+
# ------------------------------------------------------------------
|
|
178
|
+
# Query history
|
|
179
|
+
# ------------------------------------------------------------------
|
|
180
|
+
|
|
181
|
+
@router.get("/dashboard/queries/history")
|
|
182
|
+
async def get_history(
|
|
183
|
+
limit: int = Query(50, ge=1, le=500),
|
|
184
|
+
offset: int = Query(0, ge=0),
|
|
185
|
+
user=Depends(user_auth),
|
|
186
|
+
):
|
|
187
|
+
tenant = _get_tenant(user)
|
|
188
|
+
history = _get_query_history(tenant.tenant_id, user.user_id)
|
|
189
|
+
page = paginate(history, limit, offset)
|
|
190
|
+
return {"history": page["items"], "total": page["total"], "limit": page["limit"], "offset": page["offset"]}
|
|
191
|
+
|
|
192
|
+
# ------------------------------------------------------------------
|
|
193
|
+
# SQLite helpers (reuse user_registry's DB connection pattern)
|
|
194
|
+
# ------------------------------------------------------------------
|
|
195
|
+
import sqlite3
|
|
196
|
+
from pathlib import Path
|
|
197
|
+
|
|
198
|
+
_db_path = Path(__file__).resolve().parents[2] / "contextcore_data" / "queries.db"
|
|
199
|
+
_db_path.parent.mkdir(parents=True, exist_ok=True)
|
|
200
|
+
|
|
201
|
+
def _get_conn():
|
|
202
|
+
conn = sqlite3.connect(str(_db_path))
|
|
203
|
+
conn.row_factory = sqlite3.Row
|
|
204
|
+
conn.execute("PRAGMA journal_mode=WAL")
|
|
205
|
+
return conn
|
|
206
|
+
|
|
207
|
+
# Init tables
|
|
208
|
+
with _get_conn() as conn:
|
|
209
|
+
conn.executescript("""
|
|
210
|
+
CREATE TABLE IF NOT EXISTS saved_queries (
|
|
211
|
+
query_id TEXT PRIMARY KEY,
|
|
212
|
+
tenant_id TEXT NOT NULL,
|
|
213
|
+
user_id TEXT NOT NULL,
|
|
214
|
+
name TEXT NOT NULL,
|
|
215
|
+
query TEXT NOT NULL,
|
|
216
|
+
description TEXT,
|
|
217
|
+
created_at TEXT NOT NULL
|
|
218
|
+
);
|
|
219
|
+
CREATE INDEX IF NOT EXISTS idx_saved_queries_tenant
|
|
220
|
+
ON saved_queries(tenant_id, user_id);
|
|
221
|
+
|
|
222
|
+
CREATE TABLE IF NOT EXISTS query_history (
|
|
223
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
224
|
+
tenant_id TEXT NOT NULL,
|
|
225
|
+
user_id TEXT NOT NULL,
|
|
226
|
+
query TEXT NOT NULL,
|
|
227
|
+
duration_ms INTEGER DEFAULT 0,
|
|
228
|
+
status TEXT DEFAULT 'success',
|
|
229
|
+
error TEXT,
|
|
230
|
+
timestamp TEXT NOT NULL
|
|
231
|
+
);
|
|
232
|
+
CREATE INDEX IF NOT EXISTS idx_query_history_tenant
|
|
233
|
+
ON query_history(tenant_id, user_id);
|
|
234
|
+
""")
|
|
235
|
+
|
|
236
|
+
def _get_saved_queries(tenant_id, user_id):
|
|
237
|
+
with _get_conn() as conn:
|
|
238
|
+
rows = conn.execute(
|
|
239
|
+
"SELECT * FROM saved_queries WHERE tenant_id = ? AND user_id = ? ORDER BY created_at DESC",
|
|
240
|
+
(tenant_id, user_id),
|
|
241
|
+
).fetchall()
|
|
242
|
+
return [dict(r) for r in rows]
|
|
243
|
+
|
|
244
|
+
def _insert_saved_query(query_id, tenant_id, user_id, name, query, description, created_at):
|
|
245
|
+
with _get_conn() as conn:
|
|
246
|
+
conn.execute(
|
|
247
|
+
"INSERT INTO saved_queries (query_id, tenant_id, user_id, name, query, description, created_at) VALUES (?, ?, ?, ?, ?, ?, ?)",
|
|
248
|
+
(query_id, tenant_id, user_id, name, query, description, created_at),
|
|
249
|
+
)
|
|
250
|
+
|
|
251
|
+
def _delete_saved_query(query_id, tenant_id):
|
|
252
|
+
with _get_conn() as conn:
|
|
253
|
+
conn.execute("DELETE FROM saved_queries WHERE query_id = ? AND tenant_id = ?", (query_id, tenant_id))
|
|
254
|
+
|
|
255
|
+
def _record_history(tenant_id, user_id, query, duration_ms, status, error=None):
|
|
256
|
+
try:
|
|
257
|
+
now = datetime.now(timezone.utc).isoformat()
|
|
258
|
+
with _get_conn() as conn:
|
|
259
|
+
conn.execute(
|
|
260
|
+
"INSERT INTO query_history (tenant_id, user_id, query, duration_ms, status, error, timestamp) VALUES (?, ?, ?, ?, ?, ?, ?)",
|
|
261
|
+
(tenant_id, user_id, query, duration_ms, status, error, now),
|
|
262
|
+
)
|
|
263
|
+
# Keep only last 100 entries per user
|
|
264
|
+
conn.execute(
|
|
265
|
+
"""DELETE FROM query_history WHERE id NOT IN (
|
|
266
|
+
SELECT id FROM query_history WHERE tenant_id = ? AND user_id = ?
|
|
267
|
+
ORDER BY timestamp DESC LIMIT 100
|
|
268
|
+
) AND tenant_id = ? AND user_id = ?""",
|
|
269
|
+
(tenant_id, user_id, tenant_id, user_id),
|
|
270
|
+
)
|
|
271
|
+
except Exception:
|
|
272
|
+
pass
|
|
273
|
+
|
|
274
|
+
def _get_query_history(tenant_id, user_id):
|
|
275
|
+
with _get_conn() as conn:
|
|
276
|
+
rows = conn.execute(
|
|
277
|
+
"SELECT query, duration_ms, status, error, timestamp FROM query_history WHERE tenant_id = ? AND user_id = ? ORDER BY timestamp DESC LIMIT 20",
|
|
278
|
+
(tenant_id, user_id),
|
|
279
|
+
).fetchall()
|
|
280
|
+
return [dict(r) for r in rows]
|
|
281
|
+
|
|
282
|
+
return router
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Quota Enforcer
|
|
3
|
+
==============
|
|
4
|
+
FastAPI dependency that checks tenant usage against plan limits before
|
|
5
|
+
allowing resource creation.
|
|
6
|
+
|
|
7
|
+
Usage::
|
|
8
|
+
|
|
9
|
+
quota = QuotaEnforcer(billing_manager, usage_meter)
|
|
10
|
+
|
|
11
|
+
@app.post("/graphs")
|
|
12
|
+
async def create_graph(..., _=Depends(quota.check_graphs(tenant))):
|
|
13
|
+
...
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
import logging
|
|
19
|
+
from typing import Optional
|
|
20
|
+
|
|
21
|
+
from fastapi import HTTPException
|
|
22
|
+
|
|
23
|
+
logger = logging.getLogger(__name__)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class QuotaEnforcer:
|
|
27
|
+
"""Checks current usage against plan limits and raises 429 if over quota."""
|
|
28
|
+
|
|
29
|
+
def __init__(self, billing_manager, usage_meter):
|
|
30
|
+
self._billing = billing_manager
|
|
31
|
+
self._meter = usage_meter
|
|
32
|
+
|
|
33
|
+
def check(self, tenant_id: str, resource: str, current_count: int = 0) -> None:
|
|
34
|
+
"""
|
|
35
|
+
Check if tenant is within quota for a given resource.
|
|
36
|
+
|
|
37
|
+
Args:
|
|
38
|
+
tenant_id: The tenant to check.
|
|
39
|
+
resource: One of 'graphs', 'api_calls', 'agents', 'nodes'.
|
|
40
|
+
current_count: Current count of the resource (for graphs/agents/nodes).
|
|
41
|
+
|
|
42
|
+
Raises:
|
|
43
|
+
HTTPException 429 if over quota.
|
|
44
|
+
"""
|
|
45
|
+
limits = self._billing.get_limits(tenant_id)
|
|
46
|
+
|
|
47
|
+
limit_map = {
|
|
48
|
+
"graphs": "max_graphs",
|
|
49
|
+
"api_calls": "max_api_calls",
|
|
50
|
+
"agents": "max_agents",
|
|
51
|
+
"nodes": "max_nodes",
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
limit_key = limit_map.get(resource)
|
|
55
|
+
if not limit_key:
|
|
56
|
+
return # unknown resource, allow
|
|
57
|
+
|
|
58
|
+
max_val = limits.get(limit_key, -1)
|
|
59
|
+
if max_val == -1:
|
|
60
|
+
return # unlimited
|
|
61
|
+
|
|
62
|
+
# For api_calls, check monthly usage
|
|
63
|
+
if resource == "api_calls":
|
|
64
|
+
usage = self._meter.get_summary(tenant_id)
|
|
65
|
+
current_count = usage.get("api_call", 0)
|
|
66
|
+
|
|
67
|
+
if current_count >= max_val:
|
|
68
|
+
plan = limits.get("plan", "free")
|
|
69
|
+
raise HTTPException(
|
|
70
|
+
status_code=429,
|
|
71
|
+
detail={
|
|
72
|
+
"error": "quota_exceeded",
|
|
73
|
+
"resource": resource,
|
|
74
|
+
"current": current_count,
|
|
75
|
+
"limit": max_val,
|
|
76
|
+
"plan": plan,
|
|
77
|
+
"message": f"You've reached the {resource} limit ({max_val}) on the {plan} plan. "
|
|
78
|
+
f"Upgrade your plan to increase limits.",
|
|
79
|
+
},
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
def check_feature(self, tenant_id: str, feature: str) -> None:
|
|
83
|
+
"""Check if tenant's plan includes a feature. Raises 403 if not."""
|
|
84
|
+
if not self._billing.has_feature(tenant_id, feature):
|
|
85
|
+
plan = self._billing.get_or_create(tenant_id).plan
|
|
86
|
+
raise HTTPException(
|
|
87
|
+
status_code=403,
|
|
88
|
+
detail={
|
|
89
|
+
"error": "feature_not_available",
|
|
90
|
+
"feature": feature,
|
|
91
|
+
"plan": plan,
|
|
92
|
+
"message": f"The '{feature}' feature is not available on the {plan} plan. "
|
|
93
|
+
f"Upgrade to access this feature.",
|
|
94
|
+
},
|
|
95
|
+
)
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Rate Limiting
|
|
3
|
+
=============
|
|
4
|
+
Per-tenant rate limiting using slowapi. Reads ``rate_limit_rpm`` from the
|
|
5
|
+
tenant's config dict (default: 60 req/min).
|
|
6
|
+
|
|
7
|
+
Usage::
|
|
8
|
+
|
|
9
|
+
from .rate_limit import install_rate_limiter
|
|
10
|
+
|
|
11
|
+
install_rate_limiter(app)
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
import logging
|
|
15
|
+
import os
|
|
16
|
+
|
|
17
|
+
from fastapi import FastAPI, Request
|
|
18
|
+
from fastapi.responses import JSONResponse
|
|
19
|
+
|
|
20
|
+
logger = logging.getLogger(__name__)
|
|
21
|
+
|
|
22
|
+
# Default rates (requests per minute)
|
|
23
|
+
_DEFAULT_RPM = int(os.environ.get("CONTEXTSYNAPSE_RATE_LIMIT_RPM") or os.environ.get("AICONTEXTDB_RATE_LIMIT_RPM", "300"))
|
|
24
|
+
_PRO_RPM = 600
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def _key_func(request: Request) -> str:
|
|
28
|
+
"""Rate-limit key: tenant_id if authenticated, else client IP."""
|
|
29
|
+
tenant = getattr(request.state, "tenant", None)
|
|
30
|
+
if tenant:
|
|
31
|
+
return f"tenant:{tenant.tenant_id}"
|
|
32
|
+
user = getattr(request.state, "user", None)
|
|
33
|
+
if user:
|
|
34
|
+
return f"user:{user.user_id}"
|
|
35
|
+
return f"ip:{request.client.host if request.client else 'unknown'}"
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def install_rate_limiter(app: FastAPI):
|
|
39
|
+
"""Install rate limiting middleware on the FastAPI app."""
|
|
40
|
+
try:
|
|
41
|
+
from slowapi import Limiter, _rate_limit_exceeded_handler
|
|
42
|
+
from slowapi.errors import RateLimitExceeded
|
|
43
|
+
except ImportError:
|
|
44
|
+
logger.warning("slowapi not installed — rate limiting is disabled. Run: pip install slowapi")
|
|
45
|
+
return
|
|
46
|
+
|
|
47
|
+
limiter = Limiter(key_func=_key_func, default_limits=[f"{_DEFAULT_RPM}/minute"])
|
|
48
|
+
app.state.limiter = limiter
|
|
49
|
+
|
|
50
|
+
@app.exception_handler(RateLimitExceeded)
|
|
51
|
+
async def rate_limit_handler(request: Request, exc: RateLimitExceeded):
|
|
52
|
+
return JSONResponse(
|
|
53
|
+
status_code=429,
|
|
54
|
+
content={
|
|
55
|
+
"error_code": "RATE_LIMITED",
|
|
56
|
+
"message": f"Rate limit exceeded. Try again later.",
|
|
57
|
+
"detail": str(exc.detail) if hasattr(exc, "detail") else None,
|
|
58
|
+
},
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
# Apply limiter as middleware
|
|
62
|
+
from slowapi.middleware import SlowAPIMiddleware
|
|
63
|
+
app.add_middleware(SlowAPIMiddleware)
|
|
64
|
+
logger.info(f"Rate limiting enabled: {_DEFAULT_RPM} req/min default")
|