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,905 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Context Tools — briefing, ask, session context.
|
|
3
|
+
|
|
4
|
+
These are the high-level tools agents use to understand the project
|
|
5
|
+
without needing to know graph structure or query syntax.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import logging
|
|
11
|
+
|
|
12
|
+
from .registry import ToolContext, ToolParam, tool
|
|
13
|
+
|
|
14
|
+
logger = logging.getLogger(__name__)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def _record_shadow(ctx: ToolContext, tool_name: str, actual_result: str):
|
|
18
|
+
"""Record shadow token count: actual result vs full graph dump baseline.
|
|
19
|
+
|
|
20
|
+
Controlled by AICONTEXTDB_SHADOW_COUNTING env var (default: on).
|
|
21
|
+
Set to 'off' or '0' to disable.
|
|
22
|
+
"""
|
|
23
|
+
import os
|
|
24
|
+
if os.environ.get("CONTEXTSYNAPSE_SHADOW_COUNTING") or os.environ.get("AICONTEXTDB_SHADOW_COUNTING", "on").lower() in ("off", "0", "false", "no"):
|
|
25
|
+
return
|
|
26
|
+
try:
|
|
27
|
+
ns = ctx.conn._namespace if ctx.conn else ""
|
|
28
|
+
if not ns:
|
|
29
|
+
return
|
|
30
|
+
# Actual: what we're sending to the agent
|
|
31
|
+
actual_chars = len(actual_result)
|
|
32
|
+
|
|
33
|
+
# Baseline: what a full graph dump would be (without AIContextDB)
|
|
34
|
+
registry = getattr(ctx.conn, "_graph_registry", None)
|
|
35
|
+
if not registry:
|
|
36
|
+
return
|
|
37
|
+
db = registry.get_graph(ns)
|
|
38
|
+
if not db:
|
|
39
|
+
return
|
|
40
|
+
total_chars = 0
|
|
41
|
+
for node in db.get_all_nodes():
|
|
42
|
+
props = node.properties if hasattr(node, "properties") else {}
|
|
43
|
+
for v in props.values():
|
|
44
|
+
if isinstance(v, str):
|
|
45
|
+
total_chars += len(v)
|
|
46
|
+
if total_chars == 0:
|
|
47
|
+
return
|
|
48
|
+
|
|
49
|
+
from contextsynapse.gateway.cost_tracker import get_cost_tracker
|
|
50
|
+
get_cost_tracker().record_shadow(
|
|
51
|
+
namespace=ns,
|
|
52
|
+
agent_id=ctx.agent_id or "unknown",
|
|
53
|
+
tool_name=tool_name,
|
|
54
|
+
actual_chars=actual_chars,
|
|
55
|
+
baseline_chars=total_chars,
|
|
56
|
+
)
|
|
57
|
+
except Exception:
|
|
58
|
+
pass # shadow counting should never break the tool
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
@tool(
|
|
62
|
+
"briefing", "context",
|
|
63
|
+
"Get your full project briefing — requirements, tasks, decisions, "
|
|
64
|
+
"code files, attached context data, and what other agents have done. "
|
|
65
|
+
"Call this to understand the project before starting work.",
|
|
66
|
+
requires_project=True,
|
|
67
|
+
)
|
|
68
|
+
def _briefing(ctx: ToolContext) -> str:
|
|
69
|
+
# Inject manifest summary if available
|
|
70
|
+
manifest_text = ""
|
|
71
|
+
try:
|
|
72
|
+
from ..context.quality import get_manifest, ManifestBuilder
|
|
73
|
+
ns = getattr(ctx.conn, 'namespace', '') or getattr(ctx.conn, '_namespace', '')
|
|
74
|
+
redis_client = getattr(ctx.conn.db, '_redis', None) if hasattr(ctx.conn, 'db') else None
|
|
75
|
+
manifest = get_manifest(ns, redis_client)
|
|
76
|
+
if manifest:
|
|
77
|
+
manifest_text = ManifestBuilder.to_text(manifest)
|
|
78
|
+
except Exception:
|
|
79
|
+
pass
|
|
80
|
+
|
|
81
|
+
if not ctx.project:
|
|
82
|
+
# Fallback: build a basic briefing from the graph directly
|
|
83
|
+
try:
|
|
84
|
+
result = ctx.conn.query("SELECT *")
|
|
85
|
+
nodes = result.get("nodes", [])
|
|
86
|
+
if not nodes:
|
|
87
|
+
return "No data in the current graph. Ingest some content first."
|
|
88
|
+
type_counts = {}
|
|
89
|
+
for n in nodes:
|
|
90
|
+
label = n.get("label", "Unknown") if isinstance(n, dict) else getattr(n, "label", "Unknown")
|
|
91
|
+
type_counts[label] = type_counts.get(label, 0) + 1
|
|
92
|
+
lines = [f"Graph briefing ({len(nodes)} nodes):"]
|
|
93
|
+
for label, count in sorted(type_counts.items(), key=lambda x: -x[1]):
|
|
94
|
+
lines.append(f" - {label}: {count}")
|
|
95
|
+
edges = ctx.conn.get_edges()
|
|
96
|
+
if edges:
|
|
97
|
+
lines.append(f"\n{len(edges)} relationships in the graph.")
|
|
98
|
+
return "\n".join(lines)
|
|
99
|
+
except Exception:
|
|
100
|
+
return "No project loaded and could not read graph directly."
|
|
101
|
+
briefing = ctx.project.build_agent_context(
|
|
102
|
+
agent_id=ctx.agent_id, agent_name=ctx.agent_name,
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
# Apply Context Relevance Gravity — re-rank based on agent behavior
|
|
106
|
+
try:
|
|
107
|
+
from contextsynapse.context.gravity import get_gravity
|
|
108
|
+
intent = get_gravity().get_intent(ctx.agent_id or "")
|
|
109
|
+
if intent:
|
|
110
|
+
top_keywords = sorted(intent.items(), key=lambda x: x[1], reverse=True)[:5]
|
|
111
|
+
focus_terms = ", ".join(k for k, _ in top_keywords)
|
|
112
|
+
briefing += f"\n\n### Your Current Focus\nBased on your recent activity, you seem focused on: {focus_terms}"
|
|
113
|
+
except Exception:
|
|
114
|
+
pass
|
|
115
|
+
|
|
116
|
+
# Cross-Agent Propagation — inject updates from other agents
|
|
117
|
+
try:
|
|
118
|
+
from contextsynapse.context.propagation import get_propagator
|
|
119
|
+
ns = ctx.conn._namespace if ctx.conn else ""
|
|
120
|
+
updates = get_propagator().get_pending(ctx.agent_id or "", ns)
|
|
121
|
+
if updates:
|
|
122
|
+
briefing += "\n\n### Updates from Other Agents\n" + "\n".join(f"- {u}" for u in updates)
|
|
123
|
+
except Exception:
|
|
124
|
+
pass
|
|
125
|
+
|
|
126
|
+
# Append summary of attached contexts
|
|
127
|
+
try:
|
|
128
|
+
if ctx.conn and ctx.conn._graph_registry:
|
|
129
|
+
registry = ctx.conn._graph_registry
|
|
130
|
+
ns = ctx.conn._namespace
|
|
131
|
+
graph = registry.get_graph(ns)
|
|
132
|
+
if graph:
|
|
133
|
+
context_summaries = []
|
|
134
|
+
for node in graph.get_all_nodes():
|
|
135
|
+
label = node.label if hasattr(node, "label") else ""
|
|
136
|
+
if label == "ContextRef":
|
|
137
|
+
props = node.properties if hasattr(node, "properties") else {}
|
|
138
|
+
ctx_name = props.get("name", node.id[:12] if hasattr(node, "id") else "?")
|
|
139
|
+
ctx_type = props.get("context_type", "")
|
|
140
|
+
item_count = props.get("item_count", 0)
|
|
141
|
+
context_summaries.append(f"- {ctx_name} ({ctx_type}, {item_count} items)")
|
|
142
|
+
if context_summaries:
|
|
143
|
+
briefing += "\n\n### Attached Contexts\n" + "\n".join(context_summaries)
|
|
144
|
+
briefing += "\n\nUse ask(question) to search across these contexts."
|
|
145
|
+
except Exception:
|
|
146
|
+
pass
|
|
147
|
+
|
|
148
|
+
# Runtime Context Assembly — pull in linked upstream/lateral contexts
|
|
149
|
+
try:
|
|
150
|
+
from ..intelligence.runtime_context import RuntimeContextAssembler
|
|
151
|
+
from ..intelligence.signal_hierarchy import SignalHierarchy
|
|
152
|
+
|
|
153
|
+
ns = getattr(ctx.conn, '_namespace', '') or getattr(ctx.conn, 'namespace', '') or ''
|
|
154
|
+
if ns and hasattr(ctx.conn, '_graph_registry') and ctx.conn._graph_registry:
|
|
155
|
+
assembler = RuntimeContextAssembler(
|
|
156
|
+
graph_registry=ctx.conn._graph_registry,
|
|
157
|
+
)
|
|
158
|
+
# Auto-discover linked contexts from hierarchy
|
|
159
|
+
linked_contexts = [ns]
|
|
160
|
+
try:
|
|
161
|
+
from ..intelligence.persistence import IntelligencePersistence
|
|
162
|
+
# Try to load hierarchy and find upstream/lateral contexts
|
|
163
|
+
hierarchy = None
|
|
164
|
+
if hasattr(ctx.conn, '_context_manager'):
|
|
165
|
+
store = IntelligencePersistence(ctx.conn._context_manager)
|
|
166
|
+
hierarchy = store.load_hierarchy()
|
|
167
|
+
if hierarchy:
|
|
168
|
+
upstream = hierarchy.get_upstream(ns)
|
|
169
|
+
lateral = hierarchy.get_lateral(ns)
|
|
170
|
+
linked_contexts.extend(upstream[:3]) # top 3 upstream
|
|
171
|
+
linked_contexts.extend(lateral[:3]) # top 3 peers
|
|
172
|
+
except Exception:
|
|
173
|
+
pass
|
|
174
|
+
|
|
175
|
+
if len(linked_contexts) > 1:
|
|
176
|
+
view = assembler.assemble(contexts=linked_contexts, focus_entity=ns, days=7)
|
|
177
|
+
if view.facts or view.indicators:
|
|
178
|
+
runtime_lines = ["\n\n### Upstream & Peer Context"]
|
|
179
|
+
# Cross-context facts
|
|
180
|
+
external_facts = [f for f in view.facts if f.source_context != ns]
|
|
181
|
+
if external_facts:
|
|
182
|
+
runtime_lines.append(f"\n{len(external_facts)} recent signals from linked contexts:")
|
|
183
|
+
for fact in external_facts[:5]:
|
|
184
|
+
sent_tag = f" [{fact.sentiment}]" if fact.sentiment else ""
|
|
185
|
+
runtime_lines.append(f" [{fact.source_context}] {fact.statement[:100]}{sent_tag}")
|
|
186
|
+
# Cross-context indicators
|
|
187
|
+
external_indicators = [i for i in view.indicators if i.source_context != ns]
|
|
188
|
+
if external_indicators:
|
|
189
|
+
runtime_lines.append(f"\n{len(external_indicators)} indicators from linked contexts:")
|
|
190
|
+
for ind in external_indicators[:5]:
|
|
191
|
+
runtime_lines.append(f" [{ind.source_context}] {ind.name}: {ind.value} {ind.unit}")
|
|
192
|
+
# Sentiment comparison
|
|
193
|
+
if view.sentiment_comparison:
|
|
194
|
+
runtime_lines.append("\nSentiment across contexts:")
|
|
195
|
+
for entity, counts in list(view.sentiment_comparison.items())[:5]:
|
|
196
|
+
net = counts.get("net", 0)
|
|
197
|
+
arrow = "+" if net > 0 else "" if net < 0 else "~"
|
|
198
|
+
runtime_lines.append(f" {entity}: {arrow}{net} (pos={counts.get('positive',0)}, neg={counts.get('negative',0)})")
|
|
199
|
+
briefing += "\n".join(runtime_lines)
|
|
200
|
+
except Exception:
|
|
201
|
+
pass
|
|
202
|
+
|
|
203
|
+
ctx.record_provenance("read", "briefing", "")
|
|
204
|
+
if manifest_text:
|
|
205
|
+
return manifest_text + "\n\n---\n\n" + briefing
|
|
206
|
+
return briefing
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
@tool(
|
|
210
|
+
"ask", "context",
|
|
211
|
+
"Ask about the project in plain English. Searches the execution graph "
|
|
212
|
+
"AND all attached contexts (ingested documents, requirements, etc.). "
|
|
213
|
+
"Examples: 'What are the requirements?', 'What has codex done?', "
|
|
214
|
+
"'Show me the decisions', 'What tasks are open?'",
|
|
215
|
+
params=[ToolParam("question", "string", "Your question in plain English")],
|
|
216
|
+
)
|
|
217
|
+
def _ask(ctx: ToolContext, question: str) -> str:
|
|
218
|
+
from contextsynapse.search.semantic_query import semantic_search
|
|
219
|
+
# Track re-queries: if agent asks right after orient(), projection didn't surface this
|
|
220
|
+
try:
|
|
221
|
+
from contextsynapse.context.projection import record_requery
|
|
222
|
+
_ns = getattr(ctx.conn, 'namespace', '') or getattr(ctx.conn, '_namespace', '')
|
|
223
|
+
if _ns and ctx.agent_id:
|
|
224
|
+
record_requery(_ns, ctx.agent_id)
|
|
225
|
+
except Exception as _e:
|
|
226
|
+
import logging as _logging
|
|
227
|
+
_logging.getLogger(__name__).debug("record_requery call error: %s", _e)
|
|
228
|
+
|
|
229
|
+
# 1. Search the boundary's own graph
|
|
230
|
+
result = semantic_search(question, ctx.conn, ctx.project)
|
|
231
|
+
summary = result.get("summary", "")
|
|
232
|
+
all_nodes = result.get("nodes", [])
|
|
233
|
+
|
|
234
|
+
# 2. Also search all attached context graphs
|
|
235
|
+
try:
|
|
236
|
+
if ctx.conn and ctx.conn._graph_registry:
|
|
237
|
+
registry = ctx.conn._graph_registry
|
|
238
|
+
ns = ctx.conn._namespace
|
|
239
|
+
|
|
240
|
+
# Find attached contexts via ContextRef nodes or session_contexts table
|
|
241
|
+
context_namespaces = []
|
|
242
|
+
graph = registry.get_graph(ns)
|
|
243
|
+
if graph:
|
|
244
|
+
for node in graph.get_all_nodes():
|
|
245
|
+
label = node.label if hasattr(node, "label") else ""
|
|
246
|
+
if label == "ContextRef":
|
|
247
|
+
props = node.properties if hasattr(node, "properties") else {}
|
|
248
|
+
ctx_id = props.get("context_id") or node.id
|
|
249
|
+
# Try to find the context's graph namespace
|
|
250
|
+
try:
|
|
251
|
+
from contextsynapse.context.context_manager import ContextManager
|
|
252
|
+
cm = ContextManager(graph_registry=registry)
|
|
253
|
+
ctx_obj = cm.get_context(ctx_id)
|
|
254
|
+
if ctx_obj:
|
|
255
|
+
context_namespaces.append((ctx_obj.name, ctx_obj.graph_namespace))
|
|
256
|
+
except Exception:
|
|
257
|
+
pass
|
|
258
|
+
|
|
259
|
+
# Search each attached context
|
|
260
|
+
from contextsynapse.adapters._base import AIContextDBConnection
|
|
261
|
+
for ctx_name, ctx_ns in context_namespaces:
|
|
262
|
+
try:
|
|
263
|
+
ctx_conn = AIContextDBConnection(namespace=ctx_ns, graph_registry=registry)
|
|
264
|
+
ctx_result = semantic_search(question, ctx_conn, None)
|
|
265
|
+
ctx_summary = ctx_result.get("summary", "")
|
|
266
|
+
ctx_nodes = ctx_result.get("nodes", [])
|
|
267
|
+
if ctx_nodes and "No " not in ctx_summary and "not " not in ctx_summary.lower():
|
|
268
|
+
summary += f"\n\n--- From context '{ctx_name}' ---\n{ctx_summary}"
|
|
269
|
+
all_nodes.extend(ctx_nodes)
|
|
270
|
+
except Exception:
|
|
271
|
+
pass
|
|
272
|
+
except Exception as e:
|
|
273
|
+
logger.debug("Cross-context search failed: %s", e)
|
|
274
|
+
|
|
275
|
+
if not summary or summary.strip().startswith("No "):
|
|
276
|
+
# Fallback: keyword search across all graphs
|
|
277
|
+
try:
|
|
278
|
+
q_terms = question.lower().split()
|
|
279
|
+
found = []
|
|
280
|
+
if ctx.conn and ctx.conn._graph_registry:
|
|
281
|
+
for ctx_name, ctx_ns in context_namespaces:
|
|
282
|
+
try:
|
|
283
|
+
ctx_graph = registry.get_graph(ctx_ns)
|
|
284
|
+
if not ctx_graph:
|
|
285
|
+
continue
|
|
286
|
+
for node in ctx_graph.get_all_nodes():
|
|
287
|
+
props = node.properties if hasattr(node, "properties") else {}
|
|
288
|
+
text = " ".join(str(v) for v in props.values()).lower()
|
|
289
|
+
if any(t in text for t in q_terms):
|
|
290
|
+
name = props.get("name") or props.get("title") or ""
|
|
291
|
+
content = props.get("content") or props.get("description") or props.get("statement") or ""
|
|
292
|
+
label = node.label if hasattr(node, "label") else ""
|
|
293
|
+
if name or content:
|
|
294
|
+
found.append(f"[{label}] {name}: {content[:150]}")
|
|
295
|
+
except Exception:
|
|
296
|
+
pass
|
|
297
|
+
if found:
|
|
298
|
+
summary = f"Found {len(found)} results across attached contexts:\n" + "\n".join(f"- {f}" for f in found[:10])
|
|
299
|
+
except Exception:
|
|
300
|
+
pass
|
|
301
|
+
|
|
302
|
+
result = summary if summary.strip() else "No results found across any context."
|
|
303
|
+
_record_shadow(ctx, "ask", result)
|
|
304
|
+
return result
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
@tool(
|
|
308
|
+
"task_context", "context",
|
|
309
|
+
"Get everything about a specific task — its requirements, "
|
|
310
|
+
"related decisions, dependencies, and code files.",
|
|
311
|
+
params=[ToolParam("task_id", "string", "UUID or partial ID of the task")],
|
|
312
|
+
requires_project=True,
|
|
313
|
+
)
|
|
314
|
+
def _task_context(ctx: ToolContext, task_id: str) -> str:
|
|
315
|
+
if not ctx.project:
|
|
316
|
+
return "Error: No project loaded."
|
|
317
|
+
|
|
318
|
+
node = ctx.project._resolve_task_id(task_id)
|
|
319
|
+
if not node:
|
|
320
|
+
return f"Task {task_id} not found."
|
|
321
|
+
|
|
322
|
+
props = node.properties if hasattr(node, "properties") else {}
|
|
323
|
+
tid = node.id if hasattr(node, "id") else task_id
|
|
324
|
+
|
|
325
|
+
lines = [
|
|
326
|
+
f"Task: {props.get('title', '?')}",
|
|
327
|
+
f"Status: {props.get('status', '?')}",
|
|
328
|
+
f"Priority: {props.get('priority', '?')}",
|
|
329
|
+
f"Assigned to: {props.get('assigned_to', 'unassigned')}",
|
|
330
|
+
]
|
|
331
|
+
if props.get("description"):
|
|
332
|
+
lines.append(f"Description: {props['description']}")
|
|
333
|
+
|
|
334
|
+
# Find related nodes via edges
|
|
335
|
+
edges = ctx.conn.get_edges()
|
|
336
|
+
for e in edges:
|
|
337
|
+
src = e.source if hasattr(e, "source") else ""
|
|
338
|
+
tgt = e.target if hasattr(e, "target") else ""
|
|
339
|
+
lbl = e.label if hasattr(e, "label") else ""
|
|
340
|
+
if src == tid or tgt == tid:
|
|
341
|
+
other_id = tgt if src == tid else src
|
|
342
|
+
other = ctx.conn.get_node(other_id)
|
|
343
|
+
if other:
|
|
344
|
+
other_label = other.label if hasattr(other, "label") else "?"
|
|
345
|
+
other_props = other.properties if hasattr(other, "properties") else {}
|
|
346
|
+
name = other_props.get("title", other_props.get("path", other_props.get("name", other_id[:8])))
|
|
347
|
+
lines.append(f" {lbl} -> [{other_label}] {name}")
|
|
348
|
+
|
|
349
|
+
return "\n".join(lines)
|
|
350
|
+
|
|
351
|
+
|
|
352
|
+
@tool(
|
|
353
|
+
"rag_query", "context",
|
|
354
|
+
"Ask a question and get an answer grounded in your graph data (RAG). "
|
|
355
|
+
"Uses vector similarity + keyword search to find relevant nodes, "
|
|
356
|
+
"then generates a cited answer via LLM. Best for questions about "
|
|
357
|
+
"ingested documents, knowledge bases, and detailed content.",
|
|
358
|
+
params=[
|
|
359
|
+
ToolParam("question", "string", "Your question in natural language"),
|
|
360
|
+
ToolParam("graph", "string", "Graph name to search (optional, defaults to current)", required=False, default=""),
|
|
361
|
+
],
|
|
362
|
+
)
|
|
363
|
+
def _rag_query(ctx: ToolContext, question: str, graph: str = "") -> str:
|
|
364
|
+
import time as _t
|
|
365
|
+
steps = []
|
|
366
|
+
|
|
367
|
+
db, graph_name = _get_graph_db(ctx, graph)
|
|
368
|
+
if not db:
|
|
369
|
+
return "No graph data available. Ingest documents or add knowledge first."
|
|
370
|
+
|
|
371
|
+
try:
|
|
372
|
+
from contextsynapse.search.rag import graph_retrieve, generate_answer
|
|
373
|
+
except ImportError:
|
|
374
|
+
return "RAG retrieval not available."
|
|
375
|
+
|
|
376
|
+
# Step 1: Vector-first + BM25-first retrieval with graph expansion
|
|
377
|
+
t0 = _t.time()
|
|
378
|
+
top, sources = graph_retrieve(db, question, graph_name)
|
|
379
|
+
retrieve_ms = int((_t.time() - t0) * 1000)
|
|
380
|
+
|
|
381
|
+
signal_names = set()
|
|
382
|
+
all_entities = []
|
|
383
|
+
for r in top:
|
|
384
|
+
for s in r.get("signals_used", []):
|
|
385
|
+
signal_names.add(s)
|
|
386
|
+
for e in r.get("entities", []):
|
|
387
|
+
if e.get("name") and e["name"] not in [x["name"] for x in all_entities]:
|
|
388
|
+
all_entities.append(e)
|
|
389
|
+
|
|
390
|
+
steps.append(f"[Retrieve] {len(top)} content nodes via {', '.join(signal_names) or 'search'} ({retrieve_ms}ms)")
|
|
391
|
+
if all_entities:
|
|
392
|
+
entity_names = [f"{e['name']} ({e['label']})" for e in all_entities[:8]]
|
|
393
|
+
steps.append(f"[Expand] {len(all_entities)} entities: {', '.join(entity_names)}")
|
|
394
|
+
|
|
395
|
+
if not top:
|
|
396
|
+
return f"No relevant information found for: {question}"
|
|
397
|
+
|
|
398
|
+
# Step 2: Check answer cache
|
|
399
|
+
from contextsynapse.search.rag_cache import get_rag_cache, make_cache_key
|
|
400
|
+
cache = get_rag_cache()
|
|
401
|
+
node_ids = [r.get("node_id", "") for r in top]
|
|
402
|
+
cache_key = make_cache_key(graph_name, question, node_ids)
|
|
403
|
+
|
|
404
|
+
cached = cache.get(cache_key)
|
|
405
|
+
if cached:
|
|
406
|
+
answer = cached.get("answer", "")
|
|
407
|
+
steps.append(f"[Cache HIT] Answer served from cache")
|
|
408
|
+
else:
|
|
409
|
+
# Step 3: Generate answer with LLM
|
|
410
|
+
t0 = _t.time()
|
|
411
|
+
result = generate_answer(question, top, sources)
|
|
412
|
+
gen_ms = int((_t.time() - t0) * 1000)
|
|
413
|
+
answer = result.get("answer", "")
|
|
414
|
+
steps.append(f"[Generate] LLM answer ({gen_ms}ms)")
|
|
415
|
+
|
|
416
|
+
if result.get("llm_errors"):
|
|
417
|
+
steps.append(f"[Warning] {'; '.join(result['llm_errors'][:2])}")
|
|
418
|
+
|
|
419
|
+
# Cache the answer
|
|
420
|
+
if answer and not result.get("llm_errors"):
|
|
421
|
+
cache.put(cache_key, {"answer": answer, "sources": [s.get("label", "") for s in sources[:5]]})
|
|
422
|
+
|
|
423
|
+
# Build response
|
|
424
|
+
output = "— RAG Pipeline —\n" + "\n".join(steps) + "\n\n"
|
|
425
|
+
output += "— Answer —\n" + answer
|
|
426
|
+
|
|
427
|
+
if sources:
|
|
428
|
+
output += "\n\n— Sources —"
|
|
429
|
+
for s in sources[:5]:
|
|
430
|
+
sigs = s.get("signals", 1)
|
|
431
|
+
output += f"\n • [{s.get('node_type', '')}] {s.get('label', '')[:80]} ({s.get('score', 0):.0%}, {sigs} signal{'s' if sigs != 1 else ''})"
|
|
432
|
+
|
|
433
|
+
ctx.record_provenance("read", "rag_query", question[:100])
|
|
434
|
+
_record_shadow(ctx, "rag_query", answer)
|
|
435
|
+
return output
|
|
436
|
+
|
|
437
|
+
|
|
438
|
+
def _get_graph_db(ctx: ToolContext, graph: str = ""):
|
|
439
|
+
"""Helper: resolve graph DB from tool context."""
|
|
440
|
+
graph_name = graph or (ctx.conn._namespace if ctx.conn else "")
|
|
441
|
+
|
|
442
|
+
# First: use the already-loaded db from conn (PlaygroundConn resolves scoped names)
|
|
443
|
+
db = getattr(ctx.conn, "db", None) if ctx.conn else None
|
|
444
|
+
if db:
|
|
445
|
+
return db, graph_name
|
|
446
|
+
|
|
447
|
+
# Fallback: try registry with scoped name, then bare name
|
|
448
|
+
if ctx.conn:
|
|
449
|
+
registry = getattr(ctx.conn, "_graph_registry", None)
|
|
450
|
+
if registry:
|
|
451
|
+
db = registry.get_graph(graph_name, load_if_missing=True)
|
|
452
|
+
if db is None and ":" in graph_name:
|
|
453
|
+
bare = graph_name.split(":", 1)[1]
|
|
454
|
+
db = registry.get_graph(bare, load_if_missing=True)
|
|
455
|
+
if db:
|
|
456
|
+
graph_name = bare
|
|
457
|
+
return db, graph_name
|
|
458
|
+
|
|
459
|
+
|
|
460
|
+
@tool(
|
|
461
|
+
"rag_graph", "context",
|
|
462
|
+
"Deep graph-aware RAG — searches using BM25 full-text, vector similarity, "
|
|
463
|
+
"AND entity extraction simultaneously. Nodes found by multiple signals "
|
|
464
|
+
"get boosted ranking (dedup scoring). Best for complex questions that "
|
|
465
|
+
"span multiple documents or topics.",
|
|
466
|
+
params=[
|
|
467
|
+
ToolParam("question", "string", "Your question in natural language"),
|
|
468
|
+
ToolParam("graph", "string", "Graph name to search (optional)", required=False, default=""),
|
|
469
|
+
],
|
|
470
|
+
)
|
|
471
|
+
def _rag_graph(ctx: ToolContext, question: str, graph: str = "") -> str:
|
|
472
|
+
db, graph_name = _get_graph_db(ctx, graph)
|
|
473
|
+
if not db:
|
|
474
|
+
return "No graph data available."
|
|
475
|
+
|
|
476
|
+
from contextsynapse.search.rag import graph_retrieve, generate_answer
|
|
477
|
+
|
|
478
|
+
top, sources = graph_retrieve(db, question, graph_name)
|
|
479
|
+
if not top:
|
|
480
|
+
return f"No relevant information found for: {question}"
|
|
481
|
+
|
|
482
|
+
result = generate_answer(question, top, sources)
|
|
483
|
+
answer = result.get("answer", "")
|
|
484
|
+
|
|
485
|
+
# Append source citations with signal count
|
|
486
|
+
if sources:
|
|
487
|
+
answer += "\n\nSources:"
|
|
488
|
+
for s in sources[:8]:
|
|
489
|
+
sig = s.get("signals", 1)
|
|
490
|
+
sig_label = f", {sig} signals" if sig > 1 else ""
|
|
491
|
+
answer += f"\n - [{s.get('node_type', '')}] {s.get('label', '')} (relevance: {s.get('score', 0):.0%}{sig_label})"
|
|
492
|
+
|
|
493
|
+
ctx.record_provenance("read", "rag_graph", question[:100])
|
|
494
|
+
_record_shadow(ctx, "rag_graph", answer)
|
|
495
|
+
return answer
|
|
496
|
+
|
|
497
|
+
|
|
498
|
+
@tool(
|
|
499
|
+
"search", "context",
|
|
500
|
+
"Plain keyword search across graph nodes. No LLM, no vectors — just fast "
|
|
501
|
+
"text matching. Returns matching nodes with snippets. Use for quick lookups "
|
|
502
|
+
"when you know what you're looking for.",
|
|
503
|
+
params=[
|
|
504
|
+
ToolParam("query", "string", "Search keywords"),
|
|
505
|
+
ToolParam("graph", "string", "Graph name (optional)", required=False, default=""),
|
|
506
|
+
ToolParam("limit", "string", "Max results (default 10)", required=False, default="10"),
|
|
507
|
+
],
|
|
508
|
+
)
|
|
509
|
+
def _plain_search(ctx: ToolContext, query: str = "", graph: str = "", limit: str = "10", keywords: str = "", **kwargs) -> str:
|
|
510
|
+
# Accept 'keywords' as alias for 'query' (LLMs sometimes use wrong param name)
|
|
511
|
+
query = query or keywords or kwargs.get("q", "")
|
|
512
|
+
if not query:
|
|
513
|
+
return "Please provide a query. Usage: search(query=\"your search terms\")"
|
|
514
|
+
db, _ = _get_graph_db(ctx, graph)
|
|
515
|
+
if not db:
|
|
516
|
+
return "No graph data available."
|
|
517
|
+
|
|
518
|
+
# Track previous searches to detect repeated queries
|
|
519
|
+
if not hasattr(ctx, '_search_history'):
|
|
520
|
+
ctx._search_history = []
|
|
521
|
+
query_lower = query.lower().strip()
|
|
522
|
+
similar_count = sum(1 for q in ctx._search_history if q in query_lower or query_lower in q)
|
|
523
|
+
ctx._search_history.append(query_lower)
|
|
524
|
+
|
|
525
|
+
from contextsynapse.search.rag import plain_search, hybrid_retrieve, parallel_retrieve
|
|
526
|
+
|
|
527
|
+
# Infrastructure labels — always hidden from search results
|
|
528
|
+
INFRA_LABELS = {
|
|
529
|
+
"AgentThought", "AgentAction", "AgentMessage", "AgentPresence",
|
|
530
|
+
"ExperimentRun", "ExperimentScore", "PipelineRun",
|
|
531
|
+
"VectorIndex", "BM25Index", "Session", "ContextRef",
|
|
532
|
+
"Context", "Project", "KnowledgeBase", "CodeBase",
|
|
533
|
+
"SystemStore", "UserStore", "WebStore", "GeneratedStore",
|
|
534
|
+
"MemoryStore", "ArtifactStore", "ToolStore",
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
k = min(int(limit) if limit.isdigit() else 15, 50)
|
|
538
|
+
ns = getattr(db, "name", "") or graph
|
|
539
|
+
|
|
540
|
+
# Fast mode: skip vector search (3-5s) when in experiment context or repeated search
|
|
541
|
+
# Use plain_search (keyword + BM25, ~200ms) as primary, parallel_retrieve as fallback
|
|
542
|
+
results = []
|
|
543
|
+
try:
|
|
544
|
+
raw = plain_search(db, query, k=k + 20)
|
|
545
|
+
results = [r for r in raw if r.get("label", "") not in INFRA_LABELS][:k]
|
|
546
|
+
except Exception:
|
|
547
|
+
pass
|
|
548
|
+
|
|
549
|
+
# If plain_search found nothing, try parallel (includes vector — slower but broader)
|
|
550
|
+
if not results:
|
|
551
|
+
try:
|
|
552
|
+
raw = parallel_retrieve(db, query, graph_name=ns, k=k)
|
|
553
|
+
results = [r for r in raw if r.get("label", "") not in INFRA_LABELS]
|
|
554
|
+
except Exception:
|
|
555
|
+
pass
|
|
556
|
+
|
|
557
|
+
if not results:
|
|
558
|
+
return f"No results for: {query}"
|
|
559
|
+
|
|
560
|
+
lines = [f"Found {len(results)} result(s):"]
|
|
561
|
+
for r in results:
|
|
562
|
+
label = r.get("label", "")
|
|
563
|
+
name = r.get("name", "")
|
|
564
|
+
content = r.get("content", "") or r.get("snippet", "")
|
|
565
|
+
|
|
566
|
+
# Show full content if available, otherwise name
|
|
567
|
+
if content and content != name:
|
|
568
|
+
text = str(content)[:250]
|
|
569
|
+
else:
|
|
570
|
+
text = name
|
|
571
|
+
|
|
572
|
+
signals = r.get("signals", 0)
|
|
573
|
+
sig_tag = f" [{signals}sig]" if signals and signals > 1 else ""
|
|
574
|
+
line = f"- [{label}]{sig_tag} {text}"
|
|
575
|
+
lines.append(line)
|
|
576
|
+
|
|
577
|
+
# 1-hop entity expansion: surface Person/Location/Event connected to result nodes
|
|
578
|
+
try:
|
|
579
|
+
ENTITY_TYPES = {"Person", "Organization", "Location", "Event"}
|
|
580
|
+
seen_entities = set()
|
|
581
|
+
entity_lines = []
|
|
582
|
+
adapter = getattr(db, 'csr_adapter', None) or db
|
|
583
|
+
for r in results[:5]: # expand top 5 results only
|
|
584
|
+
nid = r.get("node_id", "")
|
|
585
|
+
if not nid:
|
|
586
|
+
continue
|
|
587
|
+
try:
|
|
588
|
+
edges = adapter.get_edges_for_node(nid) if hasattr(adapter, 'get_edges_for_node') else []
|
|
589
|
+
for edge in edges[:20]:
|
|
590
|
+
target_id = edge.target if hasattr(edge, 'target') else edge.get('target', '')
|
|
591
|
+
if target_id == nid:
|
|
592
|
+
target_id = edge.source if hasattr(edge, 'source') else edge.get('source', '')
|
|
593
|
+
if target_id in seen_entities:
|
|
594
|
+
continue
|
|
595
|
+
try:
|
|
596
|
+
target = adapter.get_node(target_id)
|
|
597
|
+
if target:
|
|
598
|
+
t_label = getattr(target, 'label', getattr(target, 'node_type', ''))
|
|
599
|
+
if t_label in ENTITY_TYPES:
|
|
600
|
+
t_name = (target.properties or {}).get('name', '')
|
|
601
|
+
if t_name and t_name not in seen_entities:
|
|
602
|
+
seen_entities.add(t_name)
|
|
603
|
+
entity_lines.append(f"- [{t_label}] {t_name}")
|
|
604
|
+
except Exception:
|
|
605
|
+
pass
|
|
606
|
+
except Exception:
|
|
607
|
+
pass
|
|
608
|
+
if entity_lines:
|
|
609
|
+
lines.append("\nConnected entities:")
|
|
610
|
+
lines.extend(entity_lines[:8])
|
|
611
|
+
except Exception:
|
|
612
|
+
pass
|
|
613
|
+
|
|
614
|
+
ctx.record_provenance("read", "search", query[:100])
|
|
615
|
+
result = "\n".join(lines)
|
|
616
|
+
|
|
617
|
+
# Redirect if searching same thing repeatedly — suggest alternatives from manifest
|
|
618
|
+
if similar_count >= 1:
|
|
619
|
+
alt_lines = []
|
|
620
|
+
try:
|
|
621
|
+
from ..context.quality import get_manifest
|
|
622
|
+
import os as _os
|
|
623
|
+
_redis = None
|
|
624
|
+
try:
|
|
625
|
+
import redis as _redis_mod
|
|
626
|
+
_redis = _redis_mod.from_url(_os.environ.get("CONTEXTSYNAPSE_REDIS_URL") or os.environ.get("AICONTEXTDB_REDIS_URL", "redis://localhost:6379"))
|
|
627
|
+
except Exception:
|
|
628
|
+
pass
|
|
629
|
+
manifest = get_manifest(ns, _redis) if _redis else None
|
|
630
|
+
if manifest:
|
|
631
|
+
hints = manifest.get("tool_hints", [])
|
|
632
|
+
top_ents = manifest.get("top_entities", {})
|
|
633
|
+
# Suggest entity-type searches the agent hasn't tried
|
|
634
|
+
for label, entities in list(top_ents.items())[:3]:
|
|
635
|
+
top_name = entities[0]["name"] if entities else ""
|
|
636
|
+
alt_lines.append(f' - search_nodes(label="{label}") -> {top_name}...')
|
|
637
|
+
if not alt_lines:
|
|
638
|
+
for h in hints[:3]:
|
|
639
|
+
alt_lines.append(f' - {h["tool"]}({h["example"]})')
|
|
640
|
+
except Exception:
|
|
641
|
+
pass
|
|
642
|
+
|
|
643
|
+
if similar_count >= 2:
|
|
644
|
+
redirect = "\n\n>>> You searched this before. Try a DIFFERENT approach:"
|
|
645
|
+
if alt_lines:
|
|
646
|
+
redirect += "\n" + "\n".join(alt_lines)
|
|
647
|
+
redirect += "\n Or say done with what you have."
|
|
648
|
+
result += redirect
|
|
649
|
+
else:
|
|
650
|
+
redirect = "\n\n> Similar search done. Try different queries:"
|
|
651
|
+
if alt_lines:
|
|
652
|
+
redirect += "\n" + "\n".join(alt_lines)
|
|
653
|
+
result += redirect
|
|
654
|
+
|
|
655
|
+
_record_shadow(ctx, "search", result)
|
|
656
|
+
return result
|
|
657
|
+
|
|
658
|
+
|
|
659
|
+
@tool(
|
|
660
|
+
"web_fetch", "context",
|
|
661
|
+
"Fetch a web page and return its text content. Use this to get live, "
|
|
662
|
+
"fresh data from a URL. The content is NOT auto-saved to the graph — "
|
|
663
|
+
"use add_knowledge to save what's relevant.",
|
|
664
|
+
params=[
|
|
665
|
+
ToolParam("url", "string", "URL to fetch"),
|
|
666
|
+
],
|
|
667
|
+
)
|
|
668
|
+
def _web_fetch(ctx: ToolContext, url: str) -> str:
|
|
669
|
+
from contextsynapse.ingestion.web_crawler import fetch_single_page
|
|
670
|
+
|
|
671
|
+
if not url.startswith("http"):
|
|
672
|
+
url = "https://" + url
|
|
673
|
+
|
|
674
|
+
page = fetch_single_page(url)
|
|
675
|
+
if not page:
|
|
676
|
+
return f"Failed to fetch content from {url}"
|
|
677
|
+
|
|
678
|
+
title = page.get("title", "")
|
|
679
|
+
content = page.get("content", "")
|
|
680
|
+
char_count = page.get("char_count", 0)
|
|
681
|
+
|
|
682
|
+
# Truncate for agent context window
|
|
683
|
+
if len(content) > 8000:
|
|
684
|
+
content = content[:8000] + f"\n\n... (truncated, {char_count} total chars)"
|
|
685
|
+
|
|
686
|
+
ctx.record_provenance("read", "web_fetch", url[:100])
|
|
687
|
+
return f"# {title}\nSource: {url}\n\n{content}"
|
|
688
|
+
|
|
689
|
+
|
|
690
|
+
@tool(
|
|
691
|
+
"ingest_url", "context",
|
|
692
|
+
"Ingest a web page into the knowledge graph. Runs the full smart pipeline: "
|
|
693
|
+
"clean HTML (Trafilatura) -> chunk into passages -> extract entities/facts/relationships "
|
|
694
|
+
"(LLM + regex) -> create graph edges -> embed for vector search -> BM25 index. "
|
|
695
|
+
"Produces Document, Passage, Entity, Fact, and Link nodes with full relationship edges.",
|
|
696
|
+
params=[
|
|
697
|
+
ToolParam("url", "string", "URL of the web page to ingest"),
|
|
698
|
+
ToolParam("pipeline", "string",
|
|
699
|
+
"Pipeline to use: 'smart_article' (default, full LLM extraction), "
|
|
700
|
+
"'fast_ingest' (regex only, no LLM, faster)",
|
|
701
|
+
required=False, default="smart_article"),
|
|
702
|
+
ToolParam("schema_id", "string",
|
|
703
|
+
"Schema to use for extraction (e.g. 'healthcare', 'finance', 'news_article'). "
|
|
704
|
+
"Controls entity types, edge types, and domain-aware context unit generation. "
|
|
705
|
+
"Use list_schemas to see available schemas.",
|
|
706
|
+
required=False, default=""),
|
|
707
|
+
],
|
|
708
|
+
)
|
|
709
|
+
def _ingest_url(ctx: ToolContext, url: str, pipeline: str = "smart_article", schema_id: str = "") -> str:
|
|
710
|
+
if not url.startswith("http"):
|
|
711
|
+
url = "https://" + url
|
|
712
|
+
|
|
713
|
+
try:
|
|
714
|
+
from ..ingestion.smart_ingest import ingest_url
|
|
715
|
+
|
|
716
|
+
# Resolve schema if specified
|
|
717
|
+
schema = None
|
|
718
|
+
if schema_id:
|
|
719
|
+
from ..ingestion.schema_extractor import IngestionSchema
|
|
720
|
+
from ..extraction.schema_loader import load_schema as _load_ext_schema
|
|
721
|
+
from pathlib import Path as _Path
|
|
722
|
+
_schema_path = _Path(__file__).parent.parent / "config" / "schemas" / f"{schema_id}.yaml"
|
|
723
|
+
if _schema_path.exists():
|
|
724
|
+
ext_schema = _load_ext_schema(str(_schema_path))
|
|
725
|
+
schema = IngestionSchema.from_extraction_schema(ext_schema) if hasattr(IngestionSchema, 'from_extraction_schema') else None
|
|
726
|
+
if schema is None:
|
|
727
|
+
# Build IngestionSchema manually from ExtractionSchema
|
|
728
|
+
schema = IngestionSchema(
|
|
729
|
+
name=ext_schema.name,
|
|
730
|
+
node_types={k: {"fields": v.fields, "required": v.required, "description": v.description}
|
|
731
|
+
for k, v in ext_schema.node_types.items()},
|
|
732
|
+
edge_types={k: {"source": v.source, "target": v.target}
|
|
733
|
+
for k, v in ext_schema.edge_types.items()},
|
|
734
|
+
)
|
|
735
|
+
|
|
736
|
+
result = ingest_url(url, ctx.conn.contextcore, schema=schema, pipeline=pipeline)
|
|
737
|
+
|
|
738
|
+
if result.errors:
|
|
739
|
+
return f"Ingestion failed: {'; '.join(result.errors)}"
|
|
740
|
+
|
|
741
|
+
lines = [
|
|
742
|
+
f"Ingested: {url}",
|
|
743
|
+
f" Document: {result.document_id}",
|
|
744
|
+
f" Passages: {len(result.passage_ids)}",
|
|
745
|
+
f" Entities: {len(result.entity_ids)}",
|
|
746
|
+
f" Facts: {len(result.fact_ids)}",
|
|
747
|
+
f" Links: {len(result.link_ids)}",
|
|
748
|
+
f" Edges: {result.edge_count}",
|
|
749
|
+
f" Pipeline: {pipeline}",
|
|
750
|
+
f" Schema: {schema_id or 'default'}",
|
|
751
|
+
]
|
|
752
|
+
|
|
753
|
+
# List extracted entities
|
|
754
|
+
if result.entity_ids:
|
|
755
|
+
lines.append("\n Entities found:")
|
|
756
|
+
for key, eid in list(result.entity_ids.items())[:10]:
|
|
757
|
+
node = ctx.conn.contextsynapse.csr_adapter.get_node(eid)
|
|
758
|
+
if node:
|
|
759
|
+
label = getattr(node, 'node_type', getattr(node, 'label', ''))
|
|
760
|
+
name = node.properties.get('name', '?')
|
|
761
|
+
lines.append(f" [{label}] {name}")
|
|
762
|
+
|
|
763
|
+
# Persist graph to disk and update metadata so it appears in graph listings
|
|
764
|
+
try:
|
|
765
|
+
ns = ctx.conn.namespace
|
|
766
|
+
reg = ctx.conn.graph_registry
|
|
767
|
+
if reg and ns:
|
|
768
|
+
reg.save_graph(ns, create_checkpoint=False)
|
|
769
|
+
except Exception:
|
|
770
|
+
pass # best-effort save
|
|
771
|
+
|
|
772
|
+
ctx.record_provenance("write", "ingest_url", url[:100])
|
|
773
|
+
return "\n".join(lines)
|
|
774
|
+
except Exception as e:
|
|
775
|
+
return f"Ingestion error: {e}"
|
|
776
|
+
|
|
777
|
+
|
|
778
|
+
@tool(
|
|
779
|
+
"list_pipelines", "context",
|
|
780
|
+
"List available ingestion pipelines with descriptions. "
|
|
781
|
+
"Use this to understand which pipeline to choose for different content types.",
|
|
782
|
+
params=[],
|
|
783
|
+
)
|
|
784
|
+
def _list_pipelines(ctx: ToolContext) -> str:
|
|
785
|
+
from ..ingestion.smart_ingest import list_pipelines
|
|
786
|
+
pipelines = list_pipelines()
|
|
787
|
+
lines = ["Available ingestion pipelines:\n"]
|
|
788
|
+
for name, info in pipelines.items():
|
|
789
|
+
lines.append(f"**{name}**: {info['name']}")
|
|
790
|
+
lines.append(f" {info['description']}")
|
|
791
|
+
lines.append(f" Input: {info['input_types']}")
|
|
792
|
+
lines.append(f" Produces: {', '.join(info['produces'])}")
|
|
793
|
+
lines.append(f" LLM required: {info['llm_required']}")
|
|
794
|
+
lines.append("")
|
|
795
|
+
return "\n".join(lines)
|
|
796
|
+
|
|
797
|
+
|
|
798
|
+
@tool(
|
|
799
|
+
"reason", "context",
|
|
800
|
+
"Answer complex questions by traversing the knowledge graph. Follows edges "
|
|
801
|
+
"between nodes to find multi-hop connections. Best for 'who/what/how' questions "
|
|
802
|
+
"that require understanding relationships. Example: 'Who is responsible for auth security?'",
|
|
803
|
+
params=[ToolParam("question", "string", "Your question")],
|
|
804
|
+
)
|
|
805
|
+
def _reason(ctx: ToolContext, question: str) -> str:
|
|
806
|
+
db, _ = _get_graph_db(ctx)
|
|
807
|
+
if not db:
|
|
808
|
+
return "No graph data available."
|
|
809
|
+
from contextsynapse.search.reasoning_chain import reason_over_graph
|
|
810
|
+
result = reason_over_graph(db, question)
|
|
811
|
+
answer = result.get("answer", "No path found.")
|
|
812
|
+
hops = result.get("hops", 0)
|
|
813
|
+
visited = result.get("nodes_visited", 0)
|
|
814
|
+
seeds = result.get("seeds", [])
|
|
815
|
+
footer = f"\n\n[Traversed {visited} nodes in {hops} hops from: {', '.join(seeds[:3])}]"
|
|
816
|
+
_record_shadow(ctx, "reason", answer)
|
|
817
|
+
return answer + footer
|
|
818
|
+
|
|
819
|
+
|
|
820
|
+
@tool(
|
|
821
|
+
"graph_timeline", "context",
|
|
822
|
+
"See the activity timeline for the current graph — what was created when, "
|
|
823
|
+
"by which agent. Use to understand project history and recent changes.",
|
|
824
|
+
params=[
|
|
825
|
+
ToolParam("hours", "string", "How many hours of history (default 24)", required=False, default="24"),
|
|
826
|
+
],
|
|
827
|
+
)
|
|
828
|
+
def _graph_timeline(ctx: ToolContext, hours: str = "24") -> str:
|
|
829
|
+
db, graph_name = _get_graph_db(ctx)
|
|
830
|
+
if not db:
|
|
831
|
+
return "No graph data available."
|
|
832
|
+
from contextsynapse.core.time_travel import GraphTimeTraveler
|
|
833
|
+
h = int(hours) if hours.isdigit() else 24
|
|
834
|
+
timeline = GraphTimeTraveler(db).timeline(hours=h)
|
|
835
|
+
if not timeline:
|
|
836
|
+
return f"No activity in the last {h} hours."
|
|
837
|
+
lines = [f"Graph activity (last {h}h):"]
|
|
838
|
+
for bucket in timeline:
|
|
839
|
+
agents = ", ".join(a[:12] for a in bucket.get("agents", []))
|
|
840
|
+
labels = ", ".join(f"{k}:{v}" for k, v in bucket.get("labels", {}).items())
|
|
841
|
+
lines.append(f" {bucket['timestamp'][:16]} — {bucket['count']} nodes ({labels}) by {agents or 'system'}")
|
|
842
|
+
return "\n".join(lines)
|
|
843
|
+
|
|
844
|
+
|
|
845
|
+
@tool(
|
|
846
|
+
"graph_diff", "context",
|
|
847
|
+
"Compare the graph between two timestamps — what was added, removed, or modified. "
|
|
848
|
+
"Use to understand what changed between agent runs.",
|
|
849
|
+
params=[
|
|
850
|
+
ToolParam("from_time", "string", "Start timestamp (ISO format, e.g. 2026-03-24T10:00:00)"),
|
|
851
|
+
ToolParam("to_time", "string", "End timestamp (ISO format, e.g. 2026-03-25T10:00:00)"),
|
|
852
|
+
],
|
|
853
|
+
)
|
|
854
|
+
def _graph_diff(ctx: ToolContext, from_time: str, to_time: str) -> str:
|
|
855
|
+
db, _ = _get_graph_db(ctx)
|
|
856
|
+
if not db:
|
|
857
|
+
return "No graph data available."
|
|
858
|
+
from contextsynapse.core.time_travel import GraphTimeTraveler
|
|
859
|
+
diff = GraphTimeTraveler(db).diff(from_time, to_time)
|
|
860
|
+
lines = [diff["summary"]]
|
|
861
|
+
if diff["added_nodes"]:
|
|
862
|
+
lines.append(f"\nAdded ({diff['added']}):")
|
|
863
|
+
for n in diff["added_nodes"][:10]:
|
|
864
|
+
lines.append(f" + [{n['label']}] {n['name']} (by {n.get('agent','')[:12]})")
|
|
865
|
+
if diff["modified_nodes"]:
|
|
866
|
+
lines.append(f"\nModified ({diff['modified']}):")
|
|
867
|
+
for n in diff["modified_nodes"][:5]:
|
|
868
|
+
lines.append(f" ~ [{n['label']}] {n['name']} (by {n.get('updated_by','')[:12]})")
|
|
869
|
+
return "\n".join(lines)
|
|
870
|
+
|
|
871
|
+
|
|
872
|
+
@tool(
|
|
873
|
+
"explain_context", "context",
|
|
874
|
+
"Show why context items were selected — scoring breakdowns and rankings.",
|
|
875
|
+
params=[],
|
|
876
|
+
)
|
|
877
|
+
def _explain_context(ctx: ToolContext) -> str:
|
|
878
|
+
from ..context.hub import ContextHub, ContextRole, ContextItem
|
|
879
|
+
from ..context.scoping import ContextScoper, ScopingConfig
|
|
880
|
+
|
|
881
|
+
hub = ContextHub(system_prompt="Context explanation", max_tokens=4000)
|
|
882
|
+
hub._db = ctx.conn.contextcore
|
|
883
|
+
nodes = ctx.conn.contextsynapse.csr_adapter.get_all_nodes()
|
|
884
|
+
content_nodes = [n for n in nodes if getattr(n, 'node_type', getattr(n, 'label', '')) not in
|
|
885
|
+
("ContextMeta", "VectorIndex", "BM25Index", "Session", "ContextRef", "Agent")]
|
|
886
|
+
if not content_nodes:
|
|
887
|
+
return "No context available — graph is empty."
|
|
888
|
+
hub.add_nodes(content_nodes[:20])
|
|
889
|
+
hub.set_scoping(ScopingConfig(max_tokens=4000))
|
|
890
|
+
hub.to_prompt()
|
|
891
|
+
scored = hub.get_last_scored_items()
|
|
892
|
+
if not scored:
|
|
893
|
+
return "No scored items."
|
|
894
|
+
|
|
895
|
+
lines = [f"**Context Explanation** ({len(scored)} items)\n"]
|
|
896
|
+
for i, si in enumerate(scored[:15], 1):
|
|
897
|
+
preview = si.item.content[:60].replace('\n', ' ')
|
|
898
|
+
role = si.item.role.value if hasattr(si.item.role, 'value') else str(si.item.role)
|
|
899
|
+
bd = ", ".join(f"{k}: {v:.2f}" for k, v in si.breakdown.items() if v > 0) if si.breakdown else "n/a"
|
|
900
|
+
lines.append(f"{i}. **{role}** (score: {si.score:.3f}) — {preview}...")
|
|
901
|
+
lines.append(f" Breakdown: {bd}")
|
|
902
|
+
if si.reason:
|
|
903
|
+
lines.append(f" Reason: {si.reason}")
|
|
904
|
+
ctx.record_provenance("read", "explain_context", "")
|
|
905
|
+
return "\n".join(lines)
|