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.
Files changed (776) hide show
  1. contextsynapse/__init__.py +107 -0
  2. contextsynapse/__main__.py +9 -0
  3. contextsynapse/a2a/__init__.py +28 -0
  4. contextsynapse/a2a/client.py +120 -0
  5. contextsynapse/a2a/discovery.py +128 -0
  6. contextsynapse/a2a/handlers.py +164 -0
  7. contextsynapse/a2a/models.py +246 -0
  8. contextsynapse/a2a/streaming.py +92 -0
  9. contextsynapse/a2a/task_manager.py +275 -0
  10. contextsynapse/adapters/__init__.py +98 -0
  11. contextsynapse/adapters/_base.py +298 -0
  12. contextsynapse/adapters/autogen/__init__.py +4 -0
  13. contextsynapse/adapters/autogen/tools.py +34 -0
  14. contextsynapse/adapters/crewai/__init__.py +4 -0
  15. contextsynapse/adapters/crewai/tools.py +63 -0
  16. contextsynapse/adapters/langchain/__init__.py +19 -0
  17. contextsynapse/adapters/langchain/message_history.py +17 -0
  18. contextsynapse/adapters/langchain/retriever.py +158 -0
  19. contextsynapse/adapters/langchain/tools.py +43 -0
  20. contextsynapse/adapters/langgraph/__init__.py +21 -0
  21. contextsynapse/adapters/langgraph/checkpoint.py +223 -0
  22. contextsynapse/adapters/langgraph/context_tools.py +341 -0
  23. contextsynapse/adapters/langgraph/message_history.py +138 -0
  24. contextsynapse/adapters/langgraph/tools.py +43 -0
  25. contextsynapse/adapters/llamaindex/__init__.py +4 -0
  26. contextsynapse/adapters/llamaindex/tools.py +64 -0
  27. contextsynapse/adapters/openai/__init__.py +24 -0
  28. contextsynapse/adapters/openai/tools.py +206 -0
  29. contextsynapse/adapters/pydantic_ai/__init__.py +4 -0
  30. contextsynapse/adapters/pydantic_ai/tools.py +36 -0
  31. contextsynapse/adapters/swarm/__init__.py +4 -0
  32. contextsynapse/adapters/swarm/tools.py +44 -0
  33. contextsynapse/agents/__init__.py +6 -0
  34. contextsynapse/agents/auto_capture.py +80 -0
  35. contextsynapse/agents/dispatcher.py +299 -0
  36. contextsynapse/agents/notifications.py +112 -0
  37. contextsynapse/aiql/__init__.py +71 -0
  38. contextsynapse/aiql/cache.py +341 -0
  39. contextsynapse/aiql/compiler/__init__.py +62 -0
  40. contextsynapse/aiql/compiler/optimizer.py +126 -0
  41. contextsynapse/aiql/compiler/planner.py +366 -0
  42. contextsynapse/aiql/compiler/statistics.py +235 -0
  43. contextsynapse/aiql/compiler/validator.py +425 -0
  44. contextsynapse/aiql/engine/__init__.py +11 -0
  45. contextsynapse/aiql/engine/chunk_storage.py +2584 -0
  46. contextsynapse/aiql/engine/executor.py +7737 -0
  47. contextsynapse/aiql/engine/extraction_integration.py +442 -0
  48. contextsynapse/aiql/engine/graph_builder.py +405 -0
  49. contextsynapse/aiql/engine/node_creation_from_normalized.py +691 -0
  50. contextsynapse/aiql/engine/performance_monitor.py +289 -0
  51. contextsynapse/aiql/engine/postprocessing.py +365 -0
  52. contextsynapse/aiql/engine/ray_stage_executors.py +918 -0
  53. contextsynapse/aiql/engine/redis_cache.py +106 -0
  54. contextsynapse/aiql/engine/streaming_json.py +366 -0
  55. contextsynapse/aiql/engine/streaming_parquet.py +568 -0
  56. contextsynapse/aiql/engine/streaming_ray_chunking.py +419 -0
  57. contextsynapse/aiql/grammar/__init__.py +14 -0
  58. contextsynapse/aiql/grammar/aiql_grammar.py +1822 -0
  59. contextsynapse/aiql/grammar/base.py +71 -0
  60. contextsynapse/aiql/parser/__init__.py +46 -0
  61. contextsynapse/aiql/parser/aiql_parser.py +11330 -0
  62. contextsynapse/aiql/parser/base_parser.py +47 -0
  63. contextsynapse/api/__init__.py +5 -0
  64. contextsynapse/api/a2a_router.py +233 -0
  65. contextsynapse/api/admin_router.py +140 -0
  66. contextsynapse/api/agent_worker_router.py +978 -0
  67. contextsynapse/api/algorithms_router.py +317 -0
  68. contextsynapse/api/api.py +4144 -0
  69. contextsynapse/api/audit.py +204 -0
  70. contextsynapse/api/auth.py +441 -0
  71. contextsynapse/api/auth_router.py +537 -0
  72. contextsynapse/api/billing.py +261 -0
  73. contextsynapse/api/billing_router.py +275 -0
  74. contextsynapse/api/boundary_router.py +452 -0
  75. contextsynapse/api/cognition_router.py +116 -0
  76. contextsynapse/api/connector_router.py +237 -0
  77. contextsynapse/api/context_router.py +1619 -0
  78. contextsynapse/api/dashboard_router.py +11367 -0
  79. contextsynapse/api/errors.py +183 -0
  80. contextsynapse/api/events.py +88 -0
  81. contextsynapse/api/experiment_bootstrap.py +321 -0
  82. contextsynapse/api/experiment_orchestrator.py +1078 -0
  83. contextsynapse/api/experiment_router.py +2089 -0
  84. contextsynapse/api/feature_gate.py +89 -0
  85. contextsynapse/api/federation_router.py +108 -0
  86. contextsynapse/api/integrations.py +567 -0
  87. contextsynapse/api/integrations_router.py +461 -0
  88. contextsynapse/api/intelligence_router.py +1028 -0
  89. contextsynapse/api/metering.py +155 -0
  90. contextsynapse/api/metrics.py +112 -0
  91. contextsynapse/api/models.py +38 -0
  92. contextsynapse/api/monitoring_router.py +174 -0
  93. contextsynapse/api/pipeline_router.py +1437 -0
  94. contextsynapse/api/playground_conn.py +116 -0
  95. contextsynapse/api/projects_router.py +131 -0
  96. contextsynapse/api/queries_router.py +282 -0
  97. contextsynapse/api/quota.py +95 -0
  98. contextsynapse/api/rate_limit.py +64 -0
  99. contextsynapse/api/search_router.py +261 -0
  100. contextsynapse/api/shield_router.py +447 -0
  101. contextsynapse/api/tenant_guard.py +103 -0
  102. contextsynapse/api/tenant_quotas.py +231 -0
  103. contextsynapse/api/tenants.py +305 -0
  104. contextsynapse/api/users.py +646 -0
  105. contextsynapse/api/v1.py +167 -0
  106. contextsynapse/api/vertical_builder_router.py +387 -0
  107. contextsynapse/benchmark/__init__.py +15 -0
  108. contextsynapse/benchmark/__main__.py +64 -0
  109. contextsynapse/benchmark/agents.py +338 -0
  110. contextsynapse/benchmark/live_ab.py +338 -0
  111. contextsynapse/benchmark/reporter.py +149 -0
  112. contextsynapse/benchmark/runner.py +264 -0
  113. contextsynapse/benchmark/savings.py +266 -0
  114. contextsynapse/benchmark/token_counter.py +212 -0
  115. contextsynapse/benchmark/workloads.py +289 -0
  116. contextsynapse/buffer/__init__.py +154 -0
  117. contextsynapse/buffer/base.py +257 -0
  118. contextsynapse/buffer/duckdb_buffer.py +246 -0
  119. contextsynapse/buffer/file_buffer.py +229 -0
  120. contextsynapse/buffer/lmdb_buffer.py +286 -0
  121. contextsynapse/buffer/local_buffer.py +154 -0
  122. contextsynapse/buffer/redis_buffer.py +211 -0
  123. contextsynapse/buffer/sqlite_buffer.py +266 -0
  124. contextsynapse/cache.py +2 -0
  125. contextsynapse/cli/__init__.py +40 -0
  126. contextsynapse/cli/cli.py +475 -0
  127. contextsynapse/cli/config_cli.py +177 -0
  128. contextsynapse/cli/scaffold.py +465 -0
  129. contextsynapse/cognition/__init__.py +31 -0
  130. contextsynapse/cognition/derivation.py +55 -0
  131. contextsynapse/cognition/events.py +135 -0
  132. contextsynapse/cognition/feedback.py +52 -0
  133. contextsynapse/cognition/hallucination.py +90 -0
  134. contextsynapse/cognition/invalidation.py +70 -0
  135. contextsynapse/cognition/read_tracker.py +50 -0
  136. contextsynapse/collaboration.py +2 -0
  137. contextsynapse/comparison.py +2 -0
  138. contextsynapse/config/__init__.py +10 -0
  139. contextsynapse/config/connectors/amfi_nav.yaml +18 -0
  140. contextsynapse/config/connectors/auto_sector.yaml +20 -0
  141. contextsynapse/config/connectors/banking_sector.yaml +20 -0
  142. contextsynapse/config/connectors/energy_sector.yaml +20 -0
  143. contextsynapse/config/connectors/fmcg_sector.yaml +20 -0
  144. contextsynapse/config/connectors/nsdl_fpi.yaml +9 -0
  145. contextsynapse/config/connectors/nse_price.yaml +61 -0
  146. contextsynapse/config/connectors/pharma_sector.yaml +20 -0
  147. contextsynapse/config/connectors/rbi_dbie.yaml +12 -0
  148. contextsynapse/config/context_presets/ai_research.json +25 -0
  149. contextsynapse/config/context_presets/chatgpt_conversations.json +19 -0
  150. contextsynapse/config/context_presets/claude_conversations.json +19 -0
  151. contextsynapse/config/context_presets/legal_compliance.json +21 -0
  152. contextsynapse/config/context_presets/meeting_notes.json +21 -0
  153. contextsynapse/config/context_presets/slack_engineering.json +21 -0
  154. contextsynapse/config/context_presets/tech_climate.json +24 -0
  155. contextsynapse/config/context_presets/toi_politics.json +25 -0
  156. contextsynapse/config/database_config.py +453 -0
  157. contextsynapse/config/deployment.py +221 -0
  158. contextsynapse/config/gpt_action_schema.json +164 -0
  159. contextsynapse/config/indicators/india_macro.yaml +213 -0
  160. contextsynapse/config/logging_config.py +88 -0
  161. contextsynapse/config/mode_config.py +223 -0
  162. contextsynapse/config/schemas/api_spec.yaml +48 -0
  163. contextsynapse/config/schemas/architecture_doc.yaml +51 -0
  164. contextsynapse/config/schemas/chat_history.yaml +90 -0
  165. contextsynapse/config/schemas/chatgpt_conversation.yaml +101 -0
  166. contextsynapse/config/schemas/claude_conversation.yaml +148 -0
  167. contextsynapse/config/schemas/contract.yaml +48 -0
  168. contextsynapse/config/schemas/conversation.yaml +167 -0
  169. contextsynapse/config/schemas/database.yaml +74 -0
  170. contextsynapse/config/schemas/decision.yaml +72 -0
  171. contextsynapse/config/schemas/ecommerce.yaml +56 -0
  172. contextsynapse/config/schemas/education.yaml +42 -0
  173. contextsynapse/config/schemas/healthcare.yaml +133 -0
  174. contextsynapse/config/schemas/hr_document.yaml +48 -0
  175. contextsynapse/config/schemas/invoice.yaml +40 -0
  176. contextsynapse/config/schemas/knowledge_base.yaml +75 -0
  177. contextsynapse/config/schemas/legal.yaml +42 -0
  178. contextsynapse/config/schemas/marketing_content.yaml +48 -0
  179. contextsynapse/config/schemas/meeting_notes.yaml +51 -0
  180. contextsynapse/config/schemas/news_article.yaml +61 -0
  181. contextsynapse/config/schemas/real_estate.yaml +49 -0
  182. contextsynapse/config/schemas/requirements_doc.yaml +48 -0
  183. contextsynapse/config/schemas/research_paper.yaml +56 -0
  184. contextsynapse/config/schemas/resume.yaml +49 -0
  185. contextsynapse/config/schemas/retail.yaml +42 -0
  186. contextsynapse/config/schemas/rule_meta.yaml +173 -0
  187. contextsynapse/config/schemas/rules.yaml +72 -0
  188. contextsynapse/config/schemas/screener_meta.yaml +236 -0
  189. contextsynapse/config/schemas/sdlc.yaml +103 -0
  190. contextsynapse/config/schemas/session_graph.yaml +172 -0
  191. contextsynapse/config/schemas/support_ticket.yaml +48 -0
  192. contextsynapse/config/schemas/web.yaml +64 -0
  193. contextsynapse/config/sensors/earnings_calendar.yaml +52 -0
  194. contextsynapse/config/sensors/global_risk.yaml +53 -0
  195. contextsynapse/config/sensors/government_policy.yaml +54 -0
  196. contextsynapse/config/sensors/supply_chain.yaml +50 -0
  197. contextsynapse/config/sensors/templates/behavioral.yaml +56 -0
  198. contextsynapse/config/sensors/templates/equity_sentiment.yaml +48 -0
  199. contextsynapse/config/sensors/templates/global_macro.yaml +47 -0
  200. contextsynapse/config/sensors/templates/market_data.yaml +118 -0
  201. contextsynapse/config/sensors/templates/sector_technical.yaml +47 -0
  202. contextsynapse/config/sensors/weather.yaml +52 -0
  203. contextsynapse/config/storage_strategy.py +202 -0
  204. contextsynapse/config/templates/competitive_intel.yaml +39 -0
  205. contextsynapse/config/templates/geopolitical_risk.yaml +46 -0
  206. contextsynapse/config/templates/market_analysis.yaml +58 -0
  207. contextsynapse/connectors/__init__.py +15 -0
  208. contextsynapse/connectors/api_router.py +121 -0
  209. contextsynapse/connectors/base.py +357 -0
  210. contextsynapse/connectors/financial/audio.py +170 -0
  211. contextsynapse/connectors/financial/youtube.py +272 -0
  212. contextsynapse/connectors/github.py +164 -0
  213. contextsynapse/connectors/jira.py +120 -0
  214. contextsynapse/connectors/kafka_connector.py +124 -0
  215. contextsynapse/connectors/pipeline_connector.py +747 -0
  216. contextsynapse/connectors/registry.py +116 -0
  217. contextsynapse/connectors/scheduler.py +87 -0
  218. contextsynapse/connectors/universal.py +360 -0
  219. contextsynapse/context/__init__.py +96 -0
  220. contextsynapse/context/__main__.py +4 -0
  221. contextsynapse/context/acl.py +125 -0
  222. contextsynapse/context/agent_card.py +201 -0
  223. contextsynapse/context/agent_memory.py +814 -0
  224. contextsynapse/context/agents.py +472 -0
  225. contextsynapse/context/agents_redis.py +391 -0
  226. contextsynapse/context/assembled.py +204 -0
  227. contextsynapse/context/attribution.py +113 -0
  228. contextsynapse/context/blob.py +184 -0
  229. contextsynapse/context/boundaries.py +187 -0
  230. contextsynapse/context/boundary.py +507 -0
  231. contextsynapse/context/bundle.py +155 -0
  232. contextsynapse/context/cli.py +519 -0
  233. contextsynapse/context/collaboration.py +165 -0
  234. contextsynapse/context/compiler.py +604 -0
  235. contextsynapse/context/composite.py +536 -0
  236. contextsynapse/context/compression.py +147 -0
  237. contextsynapse/context/context_manager.py +888 -0
  238. contextsynapse/context/context_schema.py +90 -0
  239. contextsynapse/context/context_schema.yaml +105 -0
  240. contextsynapse/context/context_units.py +891 -0
  241. contextsynapse/context/conversation.py +488 -0
  242. contextsynapse/context/dedup.py +207 -0
  243. contextsynapse/context/document_processor.py +698 -0
  244. contextsynapse/context/embedding_hooks.py +338 -0
  245. contextsynapse/context/execution_schema.py +274 -0
  246. contextsynapse/context/fan_out.py +138 -0
  247. contextsynapse/context/frozen.py +80 -0
  248. contextsynapse/context/gravity.py +227 -0
  249. contextsynapse/context/hub.py +1401 -0
  250. contextsynapse/context/ingest.py +391 -0
  251. contextsynapse/context/injection.py +226 -0
  252. contextsynapse/context/interaction_graphifier.py +562 -0
  253. contextsynapse/context/layers.py +574 -0
  254. contextsynapse/context/projection.py +1195 -0
  255. contextsynapse/context/promotion.py +634 -0
  256. contextsynapse/context/proof.py +131 -0
  257. contextsynapse/context/propagation.py +577 -0
  258. contextsynapse/context/pubsub.py +135 -0
  259. contextsynapse/context/quality.py +1054 -0
  260. contextsynapse/context/quality_tracker.py +270 -0
  261. contextsynapse/context/scoping.py +370 -0
  262. contextsynapse/context/seed.py +992 -0
  263. contextsynapse/context/session.py +1142 -0
  264. contextsynapse/context/session_graph.py +593 -0
  265. contextsynapse/context/session_memory.py +309 -0
  266. contextsynapse/context/session_resolver.py +111 -0
  267. contextsynapse/context/skill_learning.py +237 -0
  268. contextsynapse/context/smart_budget.py +129 -0
  269. contextsynapse/context/source_policy.py +242 -0
  270. contextsynapse/context/store_factory.py +53 -0
  271. contextsynapse/context/sync.py +648 -0
  272. contextsynapse/context/token.py +236 -0
  273. contextsynapse/context/vector_integration.py +297 -0
  274. contextsynapse/context/webhooks.py +194 -0
  275. contextsynapse/context/working_memory.py +224 -0
  276. contextsynapse/core/__init__.py +18 -0
  277. contextsynapse/core/archiver.py +237 -0
  278. contextsynapse/core/change_stream.py +282 -0
  279. contextsynapse/core/checkpoint.py +310 -0
  280. contextsynapse/core/cloud_storage.py +387 -0
  281. contextsynapse/core/context_state.py +651 -0
  282. contextsynapse/core/cron.py +324 -0
  283. contextsynapse/core/datasource.py +643 -0
  284. contextsynapse/core/db.py +289 -0
  285. contextsynapse/core/federation.py +283 -0
  286. contextsynapse/core/graph_coordinator.py +201 -0
  287. contextsynapse/core/graph_intelligence.py +388 -0
  288. contextsynapse/core/graph_structures.py +106 -0
  289. contextsynapse/core/graph_sync.py +157 -0
  290. contextsynapse/core/hybrid_graph_storage.py +2998 -0
  291. contextsynapse/core/lifecycle.py +691 -0
  292. contextsynapse/core/multiworker.py +378 -0
  293. contextsynapse/core/pruning.py +312 -0
  294. contextsynapse/core/redis_registry.py +524 -0
  295. contextsynapse/core/registry.py +1043 -0
  296. contextsynapse/core/registry_factory.py +51 -0
  297. contextsynapse/core/registry_metadata.py +294 -0
  298. contextsynapse/core/replay.py +354 -0
  299. contextsynapse/core/replication.py +339 -0
  300. contextsynapse/core/time_travel.py +260 -0
  301. contextsynapse/core/write_behind.py +152 -0
  302. contextsynapse/core/write_context.py +39 -0
  303. contextsynapse/db/__init__.py +1 -0
  304. contextsynapse/db/postgres.py +267 -0
  305. contextsynapse/db/profile.py +236 -0
  306. contextsynapse/db/rules.py +328 -0
  307. contextsynapse/demo/__init__.py +0 -0
  308. contextsynapse/demo/report.py +147 -0
  309. contextsynapse/demo/runner.py +520 -0
  310. contextsynapse/demo/scenario.py +165 -0
  311. contextsynapse/demo/session_demo.py +280 -0
  312. contextsynapse/demo/session_report.py +143 -0
  313. contextsynapse/demo/universal_runner.py +257 -0
  314. contextsynapse/engine/__init__.py +15 -0
  315. contextsynapse/engine/core.py +555 -0
  316. contextsynapse/extraction/__init__.py +69 -0
  317. contextsynapse/extraction/cli.py +274 -0
  318. contextsynapse/extraction/config.py +107 -0
  319. contextsynapse/extraction/examples/example_usage.py +118 -0
  320. contextsynapse/extraction/examples/sample_manifest.json +53 -0
  321. contextsynapse/extraction/examples/sample_normalized.json +185 -0
  322. contextsynapse/extraction/extract_engine.py +440 -0
  323. contextsynapse/extraction/extractors/__init__.py +37 -0
  324. contextsynapse/extraction/extractors/chat_export_extractor.py +189 -0
  325. contextsynapse/extraction/extractors/csv_extractor.py +83 -0
  326. contextsynapse/extraction/extractors/docx_extractor.py +129 -0
  327. contextsynapse/extraction/extractors/excel_extractor.py +152 -0
  328. contextsynapse/extraction/extractors/html_extractor.py +146 -0
  329. contextsynapse/extraction/extractors/pdf_extractor.py +635 -0
  330. contextsynapse/extraction/extractors/pdf_extractor_layoutparser.py +408 -0
  331. contextsynapse/extraction/extractors/register_extractors.py +63 -0
  332. contextsynapse/extraction/extractors/text_extractor.py +65 -0
  333. contextsynapse/extraction/extractors/txt_extractor.py +120 -0
  334. contextsynapse/extraction/extractors/website_extractor.py +246 -0
  335. contextsynapse/extraction/fact_extractor.py +236 -0
  336. contextsynapse/extraction/file_manager.py +353 -0
  337. contextsynapse/extraction/hierarchy.py +239 -0
  338. contextsynapse/extraction/id_generator.py +450 -0
  339. contextsynapse/extraction/layout_detector.py +409 -0
  340. contextsynapse/extraction/llm_entity_extractor.py +301 -0
  341. contextsynapse/extraction/normalized_store.py +480 -0
  342. contextsynapse/extraction/normalizer.py +594 -0
  343. contextsynapse/extraction/parquet_writer.py +598 -0
  344. contextsynapse/extraction/post_processors/__init__.py +40 -0
  345. contextsynapse/extraction/post_processors/column_reorganizer.py +507 -0
  346. contextsynapse/extraction/ray_normalization.py +233 -0
  347. contextsynapse/extraction/ray_runner.py +207 -0
  348. contextsynapse/extraction/redis_schema_registry.py +222 -0
  349. contextsynapse/extraction/registry.py +204 -0
  350. contextsynapse/extraction/schema_loader.py +663 -0
  351. contextsynapse/extraction/schema_registry.py +230 -0
  352. contextsynapse/extraction/semantic_normalizer.py +245 -0
  353. contextsynapse/extraction/version_manager.py +314 -0
  354. contextsynapse/gateway/__init__.py +24 -0
  355. contextsynapse/gateway/action_emitter.py +212 -0
  356. contextsynapse/gateway/cost_tracker.py +508 -0
  357. contextsynapse/gateway/gateway.py +377 -0
  358. contextsynapse/gateway/policy.py +303 -0
  359. contextsynapse/governance/__init__.py +13 -0
  360. contextsynapse/governance/layer.py +513 -0
  361. contextsynapse/ingestion/__init__.py +15 -0
  362. contextsynapse/ingestion/amplifier.py +288 -0
  363. contextsynapse/ingestion/async_ingest.py +463 -0
  364. contextsynapse/ingestion/chunker.py +303 -0
  365. contextsynapse/ingestion/chunking.py +58 -0
  366. contextsynapse/ingestion/cleaner.py +190 -0
  367. contextsynapse/ingestion/cleanse.py +252 -0
  368. contextsynapse/ingestion/connectors/__init__.py +38 -0
  369. contextsynapse/ingestion/connectors/base.py +300 -0
  370. contextsynapse/ingestion/connectors/chatgpt_memory.py +247 -0
  371. contextsynapse/ingestion/connectors/claude_memory.py +274 -0
  372. contextsynapse/ingestion/connectors/sap_ingestor.py +208 -0
  373. contextsynapse/ingestion/content_detector.py +80 -0
  374. contextsynapse/ingestion/content_monitor.py +502 -0
  375. contextsynapse/ingestion/dedup.py +106 -0
  376. contextsynapse/ingestion/feed.py +640 -0
  377. contextsynapse/ingestion/filters.py +461 -0
  378. contextsynapse/ingestion/graph_builder.py +705 -0
  379. contextsynapse/ingestion/input_classifier.py +307 -0
  380. contextsynapse/ingestion/job_manager.py +571 -0
  381. contextsynapse/ingestion/llm_extractor.py +610 -0
  382. contextsynapse/ingestion/orchestrator.py +116 -0
  383. contextsynapse/ingestion/parsers/__init__.py +19 -0
  384. contextsynapse/ingestion/parsers/graph_parser_registry.py +103 -0
  385. contextsynapse/ingestion/parsers/graphml_parser.py +141 -0
  386. contextsynapse/ingestion/parsers/jsonld_parser.py +171 -0
  387. contextsynapse/ingestion/parsers/rdf_parser.py +150 -0
  388. contextsynapse/ingestion/pipeline_context.py +228 -0
  389. contextsynapse/ingestion/pipeline_store.py +1195 -0
  390. contextsynapse/ingestion/queue.py +133 -0
  391. contextsynapse/ingestion/reprocess.py +179 -0
  392. contextsynapse/ingestion/scenario_router.py +224 -0
  393. contextsynapse/ingestion/schema_extractor.py +759 -0
  394. contextsynapse/ingestion/schema_validator.py +274 -0
  395. contextsynapse/ingestion/sdlc_ingest.py +87 -0
  396. contextsynapse/ingestion/smart_ingest.py +2451 -0
  397. contextsynapse/ingestion/source_connector.py +171 -0
  398. contextsynapse/ingestion/stage_executor.py +2095 -0
  399. contextsynapse/ingestion/stages/__init__.py +9 -0
  400. contextsynapse/ingestion/stages/tabular_to_graph.py +289 -0
  401. contextsynapse/ingestion/strategies.py +386 -0
  402. contextsynapse/ingestion/universal/__init__.py +4 -0
  403. contextsynapse/ingestion/universal/_operator_registry.py +59 -0
  404. contextsynapse/ingestion/universal/ingest_content.py +32 -0
  405. contextsynapse/ingestion/universal/operators/__init__.py +1 -0
  406. contextsynapse/ingestion/universal/operators/base.py +19 -0
  407. contextsynapse/ingestion/universal/operators/build_edges.py +75 -0
  408. contextsynapse/ingestion/universal/operators/cluster_topics.py +397 -0
  409. contextsynapse/ingestion/universal/operators/deduplicate.py +45 -0
  410. contextsynapse/ingestion/universal/operators/detect_signals.py +90 -0
  411. contextsynapse/ingestion/universal/operators/embed.py +80 -0
  412. contextsynapse/ingestion/universal/operators/extract_entities.py +253 -0
  413. contextsynapse/ingestion/universal/operators/extract_preferences.py +117 -0
  414. contextsynapse/ingestion/universal/operators/filter_content.py +69 -0
  415. contextsynapse/ingestion/universal/operators/index_bm25.py +52 -0
  416. contextsynapse/ingestion/universal/operators/infer_domains.py +69 -0
  417. contextsynapse/ingestion/universal/operators/link_cross_reference.py +210 -0
  418. contextsynapse/ingestion/universal/operators/parse_records.py +66 -0
  419. contextsynapse/ingestion/universal/operators/resolve_entities.py +261 -0
  420. contextsynapse/ingestion/universal/operators/scanners/__init__.py +16 -0
  421. contextsynapse/ingestion/universal/operators/scanners/edges.py +173 -0
  422. contextsynapse/ingestion/universal/operators/scanners/git.py +187 -0
  423. contextsynapse/ingestion/universal/operators/scanners/github.py +135 -0
  424. contextsynapse/ingestion/universal/operators/scanners/github_api.py +509 -0
  425. contextsynapse/ingestion/universal/operators/scanners/helpers.py +67 -0
  426. contextsynapse/ingestion/universal/operators/scanners/jira.py +65 -0
  427. contextsynapse/ingestion/universal/operators/scanners/llm_enrichment.py +82 -0
  428. contextsynapse/ingestion/universal/operators/scanners/repo.py +149 -0
  429. contextsynapse/ingestion/universal/operators/sdlc_scan.py +213 -0
  430. contextsynapse/ingestion/universal/operators/spec_scanner.py +149 -0
  431. contextsynapse/ingestion/universal/operators/store_documents.py +72 -0
  432. contextsynapse/ingestion/universal/operators/synthesize_cu.py +180 -0
  433. contextsynapse/ingestion/universal/operators/validate_gate.py +65 -0
  434. contextsynapse/ingestion/universal/parsers/__init__.py +5 -0
  435. contextsynapse/ingestion/universal/parsers/record_parser.py +203 -0
  436. contextsynapse/ingestion/universal/parsers/turn_parser.py +329 -0
  437. contextsynapse/ingestion/universal/shared_signals.py +161 -0
  438. contextsynapse/ingestion/universal/stage_executor.py +219 -0
  439. contextsynapse/ingestion/web_crawler.py +265 -0
  440. contextsynapse/intelligence/__init__.py +53 -0
  441. contextsynapse/intelligence/alerts.py +198 -0
  442. contextsynapse/intelligence/collector.py +655 -0
  443. contextsynapse/intelligence/comparison.py +198 -0
  444. contextsynapse/intelligence/config.py +56 -0
  445. contextsynapse/intelligence/conflict_detector.py +182 -0
  446. contextsynapse/intelligence/context_gaps.py +192 -0
  447. contextsynapse/intelligence/context_radar.py +135 -0
  448. contextsynapse/intelligence/correlation_engine.py +416 -0
  449. contextsynapse/intelligence/correlation_template.py +120 -0
  450. contextsynapse/intelligence/dedup.py +296 -0
  451. contextsynapse/intelligence/event_bus.py +91 -0
  452. contextsynapse/intelligence/feedback_loop.py +101 -0
  453. contextsynapse/intelligence/freshness.py +307 -0
  454. contextsynapse/intelligence/fundamental_feed.py +551 -0
  455. contextsynapse/intelligence/geo_data.py +170 -0
  456. contextsynapse/intelligence/geo_reference.py +112 -0
  457. contextsynapse/intelligence/graph_fusion.py +465 -0
  458. contextsynapse/intelligence/heuristics.py +176 -0
  459. contextsynapse/intelligence/impact_tracker.py +336 -0
  460. contextsynapse/intelligence/macro_indicators.py +319 -0
  461. contextsynapse/intelligence/ocr.py +104 -0
  462. contextsynapse/intelligence/persistence.py +267 -0
  463. contextsynapse/intelligence/pipeline_health.py +130 -0
  464. contextsynapse/intelligence/price_feed.py +221 -0
  465. contextsynapse/intelligence/rate_limiter.py +130 -0
  466. contextsynapse/intelligence/reactive.py +411 -0
  467. contextsynapse/intelligence/runtime_context.py +390 -0
  468. contextsynapse/intelligence/sentiment_decay.py +426 -0
  469. contextsynapse/intelligence/session.py +277 -0
  470. contextsynapse/intelligence/signal_hierarchy.py +438 -0
  471. contextsynapse/intelligence/signals.py +185 -0
  472. contextsynapse/intelligence/source_watcher.py +132 -0
  473. contextsynapse/intelligence/table_enricher.py +256 -0
  474. contextsynapse/intelligence/tagger.py +319 -0
  475. contextsynapse/intelligence/timeseries.py +209 -0
  476. contextsynapse/intelligence/tracker.py +318 -0
  477. contextsynapse/intelligence/watchdog.py +392 -0
  478. contextsynapse/intelligence/watchdog_manager.py +263 -0
  479. contextsynapse/intelligence/webhook_receiver.py +165 -0
  480. contextsynapse/intelligence/weight_learner.py +234 -0
  481. contextsynapse/llm/__init__.py +8 -0
  482. contextsynapse/llm/client.py +348 -0
  483. contextsynapse/llm/rate_limiter.py +166 -0
  484. contextsynapse/logging_config.py +2 -0
  485. contextsynapse/marketplace/__init__.py +7 -0
  486. contextsynapse/marketplace/registry.py +173 -0
  487. contextsynapse/mcp/__init__.py +11 -0
  488. contextsynapse/mcp/__main__.py +4 -0
  489. contextsynapse/mcp/auth_middleware.py +212 -0
  490. contextsynapse/mcp/connection_pool.py +128 -0
  491. contextsynapse/mcp/server.py +986 -0
  492. contextsynapse/mcp/session_context.py +67 -0
  493. contextsynapse/memory/__init__.py +34 -0
  494. contextsynapse/memory/engine.py +475 -0
  495. contextsynapse/memory/temporal.py +327 -0
  496. contextsynapse/metadata/__init__.py +12 -0
  497. contextsynapse/metadata/metadata_db.py +272 -0
  498. contextsynapse/metadata/metadata_query_tool.py +300 -0
  499. contextsynapse/metadata/metadata_tracker.py +425 -0
  500. contextsynapse/models/__init__.py +27 -0
  501. contextsynapse/models/embedding_service.py +360 -0
  502. contextsynapse/models/model_registry.py +228 -0
  503. contextsynapse/multiworker.py +2 -0
  504. contextsynapse/pipelines/__init__.py +1 -0
  505. contextsynapse/pipelines/executor.py +851 -0
  506. contextsynapse/pipelines/filters.py +189 -0
  507. contextsynapse/pipelines/models.py +176 -0
  508. contextsynapse/pipelines/scheduler.py +268 -0
  509. contextsynapse/playground/__init__.py +1 -0
  510. contextsynapse/playground/templates.py +85 -0
  511. contextsynapse/plugins/__init__.py +43 -0
  512. contextsynapse/plugins/api.py +66 -0
  513. contextsynapse/plugins/base.py +350 -0
  514. contextsynapse/plugins/domain_interface.py +508 -0
  515. contextsynapse/plugins/domain_loader.py +241 -0
  516. contextsynapse/plugins/loader.py +148 -0
  517. contextsynapse/plugins/registry.py +265 -0
  518. contextsynapse/project/__init__.py +11 -0
  519. contextsynapse/project/code_context.py +807 -0
  520. contextsynapse/project/connectors/__init__.py +4 -0
  521. contextsynapse/project/connectors/github_connector.py +113 -0
  522. contextsynapse/project/connectors/jira_connector.py +160 -0
  523. contextsynapse/project/graph.py +1023 -0
  524. contextsynapse/project/project_context.py +545 -0
  525. contextsynapse/project/scanner_registry.py +61 -0
  526. contextsynapse/project/schema_manager.py +322 -0
  527. contextsynapse/project/schemas/knowledge.yaml +59 -0
  528. contextsynapse/project/schemas/sdlc.yaml +369 -0
  529. contextsynapse/project/sdlc_schema.py +172 -0
  530. contextsynapse/project/spec_parser.py +85 -0
  531. contextsynapse/project/task_lock.py +181 -0
  532. contextsynapse/project/task_stream.py +203 -0
  533. contextsynapse/project/team_tools.py +537 -0
  534. contextsynapse/py.typed +0 -0
  535. contextsynapse/realtime/__init__.py +6 -0
  536. contextsynapse/realtime/collaboration.py +147 -0
  537. contextsynapse/rules/__init__.py +10 -0
  538. contextsynapse/rules/engine.py +116 -0
  539. contextsynapse/rules/evaluators/__init__.py +6 -0
  540. contextsynapse/rules/evaluators/aggregate.py +205 -0
  541. contextsynapse/rules/evaluators/custom.py +170 -0
  542. contextsynapse/rules/evaluators/pre_action.py +125 -0
  543. contextsynapse/rules/evaluators/temporal.py +129 -0
  544. contextsynapse/rules/loader.py +52 -0
  545. contextsynapse/rules/models.py +124 -0
  546. contextsynapse/rules/universal.py +393 -0
  547. contextsynapse/scheduler.py +229 -0
  548. contextsynapse/schema/__init__.py +42 -0
  549. contextsynapse/schema/compiler.py +252 -0
  550. contextsynapse/schema/composer.py +158 -0
  551. contextsynapse/schema/dedup.py +73 -0
  552. contextsynapse/schema/derivation.py +172 -0
  553. contextsynapse/schema/sdl.py +326 -0
  554. contextsynapse/schema/validation_gate.py +124 -0
  555. contextsynapse/sdk/__init__.py +63 -0
  556. contextsynapse/sdk/agent.py +214 -0
  557. contextsynapse/sdk/client.py +269 -0
  558. contextsynapse/sdk/exceptions.py +23 -0
  559. contextsynapse/sdk/models.py +78 -0
  560. contextsynapse/sdk/session.py +408 -0
  561. contextsynapse/sdk/tracked.py +218 -0
  562. contextsynapse/sdk/worker.py +266 -0
  563. contextsynapse/sdk/workspace_client.py +98 -0
  564. contextsynapse/sdk/ws.py +41 -0
  565. contextsynapse/search/__init__.py +20 -0
  566. contextsynapse/search/embedding_cache.py +140 -0
  567. contextsynapse/search/enhanced_search.py +152 -0
  568. contextsynapse/search/fulltext.py +130 -0
  569. contextsynapse/search/graph_search.py +634 -0
  570. contextsynapse/search/lmdb_index.py +1218 -0
  571. contextsynapse/search/nl_to_aiql.py +199 -0
  572. contextsynapse/search/rag.py +1346 -0
  573. contextsynapse/search/rag_cache.py +167 -0
  574. contextsynapse/search/reasoning_chain.py +231 -0
  575. contextsynapse/search/redis_search.py +207 -0
  576. contextsynapse/search/retrieval_quality.py +314 -0
  577. contextsynapse/search/semantic_query.py +455 -0
  578. contextsynapse/search/text_resolver.py +159 -0
  579. contextsynapse/search/whoosh_search.py +373 -0
  580. contextsynapse/security/__init__.py +17 -0
  581. contextsynapse/security/audit.py +297 -0
  582. contextsynapse/security/audit_logger.py +97 -0
  583. contextsynapse/security/audit_trail.py +270 -0
  584. contextsynapse/security/auth_unified.py +284 -0
  585. contextsynapse/security/auto_tagger.py +230 -0
  586. contextsynapse/security/data_security.py +218 -0
  587. contextsynapse/security/encryption.py +207 -0
  588. contextsynapse/security/identity.py +447 -0
  589. contextsynapse/security/jwt_identity.py +59 -0
  590. contextsynapse/security/middleware.py +336 -0
  591. contextsynapse/security/pii.py +233 -0
  592. contextsynapse/security/rbac.py +96 -0
  593. contextsynapse/security/rls.py +205 -0
  594. contextsynapse/security/sanitize.py +233 -0
  595. contextsynapse/security/scoped_encryption.py +131 -0
  596. contextsynapse/security/tenant.py +401 -0
  597. contextsynapse/shield/__init__.py +21 -0
  598. contextsynapse/shield/anomaly.py +56 -0
  599. contextsynapse/shield/permissions.py +99 -0
  600. contextsynapse/shield/profile.py +103 -0
  601. contextsynapse/shield/shield.py +108 -0
  602. contextsynapse/shield/trust_engine.py +69 -0
  603. contextsynapse/skills/__init__.py +14 -0
  604. contextsynapse/skills/engine.py +233 -0
  605. contextsynapse/skills/loader.py +59 -0
  606. contextsynapse/skills/models.py +139 -0
  607. contextsynapse/storage/__init__.py +7 -0
  608. contextsynapse/storage/cache.py +208 -0
  609. contextsynapse/storage/columnar_store_v2.py +178 -0
  610. contextsynapse/storage/csr_graph_storage.py +587 -0
  611. contextsynapse/storage/csr_store.py +388 -0
  612. contextsynapse/storage/document_store.py +491 -0
  613. contextsynapse/storage/hnsw_index.py +294 -0
  614. contextsynapse/storage/hybrid_store_v2.py +278 -0
  615. contextsynapse/storage/lmdb_graph_storage.py +366 -0
  616. contextsynapse/storage/migration.py +279 -0
  617. contextsynapse/storage/namespace_store.py +346 -0
  618. contextsynapse/storage/pure_graph_storage.py +185 -0
  619. contextsynapse/storage/redis_graph_adapter.py +186 -0
  620. contextsynapse/storage/redis_graph_storage.py +375 -0
  621. contextsynapse/storage/router/__init__.py +29 -0
  622. contextsynapse/storage/router/duckdb_store.py +398 -0
  623. contextsynapse/storage/router/factory.py +83 -0
  624. contextsynapse/storage/router/interface.py +125 -0
  625. contextsynapse/storage/router/postgres_store.py +339 -0
  626. contextsynapse/storage/router/resolver.py +192 -0
  627. contextsynapse/storage/router/router.py +300 -0
  628. contextsynapse/storage/single_file_storage.py +391 -0
  629. contextsynapse/storage/storage_manager.py +214 -0
  630. contextsynapse/storage/storage_query_optimizer.py +314 -0
  631. contextsynapse/storage/wal.py +359 -0
  632. contextsynapse/temporal/__init__.py +0 -0
  633. contextsynapse/temporal/storage.py +106 -0
  634. contextsynapse/tests/__init__.py +11 -0
  635. contextsynapse/tests/regression/__init__.py +1 -0
  636. contextsynapse/tests/regression/add_metadata.py +395 -0
  637. contextsynapse/tests/regression/add_skip_flags.py +85 -0
  638. contextsynapse/tests/regression/cases/test_basic_create_node.yaml +20 -0
  639. contextsynapse/tests/regression/cases/test_basic_select.yaml +21 -0
  640. contextsynapse/tests/regression/cases/test_blockchain_audit_trail.yaml +37 -0
  641. contextsynapse/tests/regression/cases/test_blockchain_audit_trail_filtered.yaml +39 -0
  642. contextsynapse/tests/regression/cases/test_blockchain_blocks_range.yaml +39 -0
  643. contextsynapse/tests/regression/cases/test_blockchain_get_block.yaml +35 -0
  644. contextsynapse/tests/regression/cases/test_blockchain_latest_block.yaml +35 -0
  645. contextsynapse/tests/regression/cases/test_blockchain_length.yaml +37 -0
  646. contextsynapse/tests/regression/cases/test_blockchain_merkle_root.yaml +37 -0
  647. contextsynapse/tests/regression/cases/test_blockchain_verify.yaml +35 -0
  648. contextsynapse/tests/regression/cases/test_blockchain_verify_block.yaml +35 -0
  649. contextsynapse/tests/regression/cases/test_create_edge.yaml +28 -0
  650. contextsynapse/tests/regression/cases/test_delete_edge_basic.yaml +29 -0
  651. contextsynapse/tests/regression/cases/test_delete_node_basic.yaml +29 -0
  652. contextsynapse/tests/regression/cases/test_evaluation_mrr.yaml +40 -0
  653. contextsynapse/tests/regression/cases/test_evaluation_ndcg.yaml +40 -0
  654. contextsynapse/tests/regression/cases/test_evaluation_precision.yaml +40 -0
  655. contextsynapse/tests/regression/cases/test_evaluation_recall.yaml +40 -0
  656. contextsynapse/tests/regression/cases/test_group_by_basic.yaml +56 -0
  657. contextsynapse/tests/regression/cases/test_match_node_basic.yaml +29 -0
  658. contextsynapse/tests/regression/cases/test_match_node_with_order_limit.yaml +31 -0
  659. contextsynapse/tests/regression/cases/test_namespace_switch.yaml +18 -0
  660. contextsynapse/tests/regression/cases/test_pipeline_cascaded.yaml +61 -0
  661. contextsynapse/tests/regression/cases/test_pipeline_fixed_size_chunking.yaml +48 -0
  662. contextsynapse/tests/regression/cases/test_pipeline_gpt4_chunking.yaml +54 -0
  663. contextsynapse/tests/regression/cases/test_pipeline_hybrid_indexing.yaml +57 -0
  664. contextsynapse/tests/regression/cases/test_pipeline_large_embedding.yaml +52 -0
  665. contextsynapse/tests/regression/cases/test_pipeline_paragraph_chunking.yaml +49 -0
  666. contextsynapse/tests/regression/cases/test_pipeline_recursive_chunking.yaml +47 -0
  667. contextsynapse/tests/regression/cases/test_pipeline_run_pipeline.yaml +56 -0
  668. contextsynapse/tests/regression/cases/test_pipeline_section_based_chunking.yaml +53 -0
  669. contextsynapse/tests/regression/cases/test_pipeline_semantic_chunking.yaml +51 -0
  670. contextsynapse/tests/regression/cases/test_pipeline_with_entity_extraction.yaml +57 -0
  671. contextsynapse/tests/regression/cases/test_pipeline_with_relationships.yaml +62 -0
  672. contextsynapse/tests/regression/cases/test_rag_evaluation.yaml +38 -0
  673. contextsynapse/tests/regression/cases/test_rag_graph_bfs_with_eval.yaml +48 -0
  674. contextsynapse/tests/regression/cases/test_rag_graph_centrality.yaml +40 -0
  675. contextsynapse/tests/regression/cases/test_rag_graph_centrality_eval.yaml +35 -0
  676. contextsynapse/tests/regression/cases/test_rag_graph_community.yaml +40 -0
  677. contextsynapse/tests/regression/cases/test_rag_graph_community_eval.yaml +35 -0
  678. contextsynapse/tests/regression/cases/test_rag_graph_dfs_with_eval.yaml +35 -0
  679. contextsynapse/tests/regression/cases/test_rag_graph_multi_hop_eval.yaml +35 -0
  680. contextsynapse/tests/regression/cases/test_rag_graph_path.yaml +40 -0
  681. contextsynapse/tests/regression/cases/test_rag_graph_search_basic.yaml +38 -0
  682. contextsynapse/tests/regression/cases/test_rag_graph_search_bfs.yaml +52 -0
  683. contextsynapse/tests/regression/cases/test_rag_graph_search_dfs.yaml +41 -0
  684. contextsynapse/tests/regression/cases/test_rag_graph_search_multi_hop.yaml +43 -0
  685. contextsynapse/tests/regression/cases/test_rag_graph_search_shortest_path.yaml +41 -0
  686. contextsynapse/tests/regression/cases/test_rag_graph_search_weighted.yaml +43 -0
  687. contextsynapse/tests/regression/cases/test_rag_graph_shortest_path_eval.yaml +35 -0
  688. contextsynapse/tests/regression/cases/test_rag_graph_traversal.yaml +30 -0
  689. contextsynapse/tests/regression/cases/test_rag_graph_weighted_eval.yaml +35 -0
  690. contextsynapse/tests/regression/cases/test_rag_hybrid_search.yaml +33 -0
  691. contextsynapse/tests/regression/cases/test_rag_hybrid_with_prompt_embedding.yaml +44 -0
  692. contextsynapse/tests/regression/cases/test_rag_keyword_search.yaml +32 -0
  693. contextsynapse/tests/regression/cases/test_rag_multi_hop_graph.yaml +32 -0
  694. contextsynapse/tests/regression/cases/test_rag_path_based.yaml +34 -0
  695. contextsynapse/tests/regression/cases/test_rag_profile_based.yaml +32 -0
  696. contextsynapse/tests/regression/cases/test_rag_vector_search.yaml +32 -0
  697. contextsynapse/tests/regression/cases/test_rag_weighted_graph.yaml +34 -0
  698. contextsynapse/tests/regression/cases/test_rag_weighted_hybrid.yaml +31 -0
  699. contextsynapse/tests/regression/cases/test_semantic_boundary_chunking.yaml +57 -0
  700. contextsynapse/tests/regression/cases/test_semantic_boundary_detection.yaml +33 -0
  701. contextsynapse/tests/regression/cases/test_semantic_hash_function.yaml +30 -0
  702. contextsynapse/tests/regression/cases/test_semantic_hash_with_model.yaml +31 -0
  703. contextsynapse/tests/regression/cases/test_tcs_ingestion_fixed_size.yaml +44 -0
  704. contextsynapse/tests/regression/cases/test_tcs_ingestion_recursive.yaml +46 -0
  705. contextsynapse/tests/regression/cases/test_tcs_ingestion_section.yaml +46 -0
  706. contextsynapse/tests/regression/cases/test_tcs_ingestion_semantic.yaml +44 -0
  707. contextsynapse/tests/regression/cases/test_time_travel_as_of.yaml +35 -0
  708. contextsynapse/tests/regression/cases/test_time_travel_at_commit.yaml +33 -0
  709. contextsynapse/tests/regression/cases/test_time_travel_at_timestamp.yaml +33 -0
  710. contextsynapse/tests/regression/cases/test_time_travel_for_system_time_all.yaml +37 -0
  711. contextsynapse/tests/regression/cases/test_time_travel_versions_between.yaml +39 -0
  712. contextsynapse/tests/regression/cases/test_traverse_basic.yaml +31 -0
  713. contextsynapse/tests/regression/cases/test_traverse_max_depth.yaml +34 -0
  714. contextsynapse/tests/regression/cases/test_update_edge_basic.yaml +29 -0
  715. contextsynapse/tests/regression/evaluation_metrics.py +227 -0
  716. contextsynapse/tests/regression/regression_runner.py +1081 -0
  717. contextsynapse/tests/regression/setup_regression_data.py +212 -0
  718. contextsynapse/tests/regression/test_basic_only.py +84 -0
  719. contextsynapse/tests/regression/test_namespace_creation.py +29 -0
  720. contextsynapse/tests/regression/test_single_fix.py +61 -0
  721. contextsynapse/tests/regression/test_single_query.py +20 -0
  722. contextsynapse/tests/regression/validate_test_syntax.py +179 -0
  723. contextsynapse/tests/test_advanced_features.py +347 -0
  724. contextsynapse/tests/test_aiql_summary.py +283 -0
  725. contextsynapse/tests/test_all_aiql_queries.py +948 -0
  726. contextsynapse/tests/test_all_create_queries.py +213 -0
  727. contextsynapse/tests/test_api_vs_native.py +280 -0
  728. contextsynapse/tests/test_chunking_dedup.py +199 -0
  729. contextsynapse/tests/test_connectors.py +171 -0
  730. contextsynapse/tests/test_edges_and_match.py +202 -0
  731. contextsynapse/tests/test_extraction_pipeline_integration.py +581 -0
  732. contextsynapse/tests/test_name_property.py +224 -0
  733. contextsynapse/tests/test_node_edge_metadata.py +250 -0
  734. contextsynapse/tests/test_playground_conn.py +114 -0
  735. contextsynapse/tests/test_query_cache.py +213 -0
  736. contextsynapse/tests/test_source_policy_auth.py +348 -0
  737. contextsynapse/tests/test_temporal_and_domain.py +224 -0
  738. contextsynapse/tests/test_time_travel_e2e.py +571 -0
  739. contextsynapse/tests/test_traversal_and_aggregation.py +275 -0
  740. contextsynapse/tools/__init__.py +36 -0
  741. contextsynapse/tools/a2a_tools.py +106 -0
  742. contextsynapse/tools/cognition_tools.py +118 -0
  743. contextsynapse/tools/context.py +905 -0
  744. contextsynapse/tools/formatting.py +92 -0
  745. contextsynapse/tools/gateway_tools.py +186 -0
  746. contextsynapse/tools/graph.py +1700 -0
  747. contextsynapse/tools/intelligence.py +235 -0
  748. contextsynapse/tools/memory.py +118 -0
  749. contextsynapse/tools/registry.py +373 -0
  750. contextsynapse/tools/shield_tools.py +159 -0
  751. contextsynapse/tools/tasks.py +341 -0
  752. contextsynapse/tools/workspace_tools.py +124 -0
  753. contextsynapse/utils/__init__.py +15 -0
  754. contextsynapse/utils/config_loader.py +198 -0
  755. contextsynapse/utils/env_loader.py +156 -0
  756. contextsynapse/utils/file_utils.py +288 -0
  757. contextsynapse/utils/logging_optimizer.py +119 -0
  758. contextsynapse/vector/__init__.py +30 -0
  759. contextsynapse/vector/chroma_vector_store.py +152 -0
  760. contextsynapse/vector/custom_vector_store.py +537 -0
  761. contextsynapse/vector/faiss_vector_store.py +296 -0
  762. contextsynapse/vector/qdrant_vector_store.py +163 -0
  763. contextsynapse/vector/vector_db.py +953 -0
  764. contextsynapse/vector/vector_db_manager.py +197 -0
  765. contextsynapse/workspace/__init__.py +27 -0
  766. contextsynapse/workspace/base.py +94 -0
  767. contextsynapse/workspace/git.py +228 -0
  768. contextsynapse/workspace/github.py +78 -0
  769. contextsynapse/workspace/local.py +73 -0
  770. contextsynapse/workspace/tools.py +222 -0
  771. contextsynapse-1.0.0.dist-info/METADATA +847 -0
  772. contextsynapse-1.0.0.dist-info/RECORD +776 -0
  773. contextsynapse-1.0.0.dist-info/WHEEL +5 -0
  774. contextsynapse-1.0.0.dist-info/entry_points.txt +4 -0
  775. contextsynapse-1.0.0.dist-info/licenses/LICENSE +200 -0
  776. contextsynapse-1.0.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,640 @@
1
+ """
2
+ Real-Time Feed Ingestion
3
+ =========================
4
+ Ingest news articles from RSS feeds, web scraping, APIs, and webhooks
5
+ into the graph with streaming enrichment (fast ingest → background deep enrichment).
6
+
7
+ Architecture:
8
+ Source → FeedManager.ingest_article() → Fast Ingest (2s, searchable)
9
+ → Background Enrichment Queue (30s, connected)
10
+
11
+ Usage::
12
+
13
+ feed = FeedManager(graph_registry, namespace="news_graph")
14
+
15
+ # From RSS
16
+ feed.add_rss_source("https://timesofindia.indiatimes.com/rssfeeds/...")
17
+ feed.poll_all() # fetches new articles
18
+
19
+ # From webhook
20
+ article = feed.ingest_article(url="https://...", title="...", content="...", source="toi")
21
+
22
+ # From API
23
+ feed.add_api_source("newsapi", api_key="...", query="India elections")
24
+ """
25
+
26
+ from __future__ import annotations
27
+
28
+ import hashlib
29
+ import json
30
+ import logging
31
+ import time
32
+ import threading
33
+ import uuid
34
+ from dataclasses import dataclass, field
35
+ from datetime import datetime, timezone
36
+ from typing import Any, Dict, List, Optional
37
+ from pathlib import Path
38
+
39
+ logger = logging.getLogger(__name__)
40
+
41
+
42
+ @dataclass
43
+ class FeedSource:
44
+ """A configured feed source."""
45
+ source_id: str
46
+ source_type: str # rss | scrape | api | webhook
47
+ name: str
48
+ url: str = ""
49
+ config: Dict[str, Any] = field(default_factory=dict)
50
+ poll_interval_minutes: int = 5
51
+ last_polled: Optional[str] = None
52
+ active: bool = True
53
+
54
+ def to_dict(self) -> Dict[str, Any]:
55
+ return {
56
+ "source_id": self.source_id,
57
+ "source_type": self.source_type,
58
+ "name": self.name,
59
+ "url": self.url,
60
+ "config": self.config,
61
+ "poll_interval_minutes": self.poll_interval_minutes,
62
+ "last_polled": self.last_polled,
63
+ "active": self.active,
64
+ }
65
+
66
+
67
+ @dataclass
68
+ class ArticleIngest:
69
+ """An article to be ingested into the graph."""
70
+ url: str = ""
71
+ title: str = ""
72
+ content: str = ""
73
+ source: str = "" # e.g. "toi", "reuters", "ndtv"
74
+ published_at: str = "" # ISO timestamp
75
+ author: str = ""
76
+ tags: List[str] = field(default_factory=list)
77
+ metadata: Dict[str, Any] = field(default_factory=dict)
78
+
79
+ @property
80
+ def content_hash(self) -> str:
81
+ """Deduplicate by content hash."""
82
+ return hashlib.sha256((self.url + self.title + self.content[:500]).encode()).hexdigest()[:16]
83
+
84
+
85
+ @dataclass
86
+ class IngestResult:
87
+ """Result of ingesting an article."""
88
+ article_id: str
89
+ status: str # "ingested" | "duplicate" | "error"
90
+ nodes_created: int = 0
91
+ enrichment_queued: bool = False
92
+ message: str = ""
93
+
94
+ def to_dict(self) -> Dict[str, Any]:
95
+ return {
96
+ "article_id": self.article_id,
97
+ "status": self.status,
98
+ "nodes_created": self.nodes_created,
99
+ "enrichment_queued": self.enrichment_queued,
100
+ "message": self.message,
101
+ }
102
+
103
+
104
+ class FeedManager:
105
+ """Manages feed sources and article ingestion with streaming enrichment.
106
+
107
+ Integrates with the Context + Session architecture:
108
+ - Each feed source maps to a Context (atomic data container)
109
+ - Context has a graph namespace where articles live
110
+ - Context attaches to Sessions (runtime boundaries)
111
+ - Agents in the Session see feed articles automatically
112
+
113
+ Flow: Feed Source → Context → Session → Agent sees articles
114
+ """
115
+
116
+ def __init__(self, graph_registry=None, namespace: str = "default",
117
+ context_manager=None, session_manager=None,
118
+ enrichment_enabled: bool = True):
119
+ self._registry = graph_registry
120
+ self._namespace = namespace
121
+ self._context_manager = context_manager
122
+ self._session_manager = session_manager
123
+ self._enrichment_enabled = enrichment_enabled
124
+ self._sources: Dict[str, FeedSource] = {}
125
+ self._ingested_hashes: set = set() # dedup cache
126
+ self._enrichment_queue: List[Dict] = []
127
+ self._enrichment_thread: Optional[threading.Thread] = None
128
+ self._running = False
129
+ # source_id → context_id mapping
130
+ self._source_contexts: Dict[str, str] = {}
131
+ # Smart pipeline: use new 7-stage ingestion (Clean→Chunk→Extract→Link→Embed→Index→Validate)
132
+ self._use_smart_pipeline = True
133
+
134
+ def _get_db(self):
135
+ if self._registry:
136
+ return self._registry.get_graph(self._namespace, load_if_missing=True)
137
+ return None
138
+
139
+ def _ensure_context_for_source(self, source: FeedSource) -> Optional[str]:
140
+ """Create or get the Context for a feed source.
141
+
142
+ Each feed source gets its own Context so articles are isolated
143
+ and can be attached/detached from Sessions independently.
144
+ """
145
+ if source.source_id in self._source_contexts:
146
+ return self._source_contexts[source.source_id]
147
+
148
+ if not self._context_manager:
149
+ return None
150
+
151
+ # Check if context already exists for this source
152
+ context_name = f"feed:{source.name}"
153
+ try:
154
+ existing = self._context_manager.get_context_by_name(context_name)
155
+ if existing:
156
+ self._source_contexts[source.source_id] = existing.context_id
157
+ self._namespace = existing.graph_namespace
158
+ return existing.context_id
159
+ except Exception:
160
+ pass
161
+
162
+ # Create new context for this feed source
163
+ try:
164
+ ctx = self._context_manager.create_context(
165
+ name=context_name,
166
+ context_type="web",
167
+ description=f"Real-time feed from {source.name} ({source.source_type})",
168
+ source=source.url or source.source_type,
169
+ sensitivity="public",
170
+ tags=["feed", source.source_type, source.name],
171
+ )
172
+ self._source_contexts[source.source_id] = ctx.context_id
173
+ self._namespace = ctx.graph_namespace
174
+ logger.info("[FEED] Created context '%s' for source '%s' (ns: %s)",
175
+ context_name, source.name, ctx.graph_namespace)
176
+ return ctx.context_id
177
+ except Exception as e:
178
+ logger.warning("[FEED] Could not create context for %s: %s", source.name, e)
179
+ return None
180
+
181
+ def attach_to_session(self, source_id: str, session_id: str) -> bool:
182
+ """Attach a feed source's context to a session.
183
+
184
+ After this, agents in the session see all articles from this feed.
185
+ New articles auto-appear as they're ingested.
186
+ """
187
+ if not self._session_manager:
188
+ logger.warning("[FEED] No session manager — cannot attach")
189
+ return False
190
+
191
+ context_id = self._source_contexts.get(source_id)
192
+ if not context_id:
193
+ source = self._sources.get(source_id)
194
+ if source:
195
+ context_id = self._ensure_context_for_source(source)
196
+ if not context_id:
197
+ return False
198
+
199
+ try:
200
+ ok = self._session_manager.attach_context(session_id, context_id, role="input")
201
+ if ok:
202
+ logger.info("[FEED] Attached feed '%s' to session '%s'", source_id, session_id[:12])
203
+ return ok
204
+ except Exception as e:
205
+ logger.warning("[FEED] Attach failed: %s", e)
206
+ return False
207
+
208
+ # ── Source Management ──────────────────────────────────
209
+
210
+ def add_rss_source(self, url: str, name: str = "", poll_minutes: int = 5) -> FeedSource:
211
+ """Add an RSS/Atom feed source."""
212
+ src = FeedSource(
213
+ source_id=str(uuid.uuid4())[:8],
214
+ source_type="rss",
215
+ name=name or url.split("/")[2],
216
+ url=url,
217
+ poll_interval_minutes=poll_minutes,
218
+ )
219
+ self._sources[src.source_id] = src
220
+ logger.info("[FEED] Added RSS source: %s (%s)", src.name, url)
221
+ return src
222
+
223
+ def add_scrape_source(self, url: str, name: str = "", selector: str = "article",
224
+ poll_minutes: int = 15) -> FeedSource:
225
+ """Add a web scraping source."""
226
+ src = FeedSource(
227
+ source_id=str(uuid.uuid4())[:8],
228
+ source_type="scrape",
229
+ name=name or url.split("/")[2],
230
+ url=url,
231
+ config={"selector": selector},
232
+ poll_interval_minutes=poll_minutes,
233
+ )
234
+ self._sources[src.source_id] = src
235
+ return src
236
+
237
+ def add_api_source(self, provider: str, api_key: str = "", query: str = "",
238
+ poll_minutes: int = 10) -> FeedSource:
239
+ """Add a news API source (NewsAPI, Google News, etc.)."""
240
+ src = FeedSource(
241
+ source_id=str(uuid.uuid4())[:8],
242
+ source_type="api",
243
+ name=f"{provider}:{query[:30]}",
244
+ url=provider,
245
+ config={"api_key": api_key, "query": query, "provider": provider},
246
+ poll_interval_minutes=poll_minutes,
247
+ )
248
+ self._sources[src.source_id] = src
249
+ return src
250
+
251
+ def remove_source(self, source_id: str) -> bool:
252
+ return self._sources.pop(source_id, None) is not None
253
+
254
+ def list_sources(self) -> List[FeedSource]:
255
+ return list(self._sources.values())
256
+
257
+ # ── Article Ingestion (Fast Path — 2s) ────────────────
258
+
259
+ def ingest_article(self, article: ArticleIngest, source_id: str = "") -> IngestResult:
260
+ """Ingest a single article into the graph (fast path).
261
+
262
+ If source_id is provided and has a Context, articles are written
263
+ to that Context's graph namespace. This means:
264
+ - Articles are isolated per feed source
265
+ - Sessions attached to the Context see articles automatically
266
+ - Detaching the Context removes all articles cleanly
267
+
268
+ Phase 1 (immediate, ~2s):
269
+ - Create Article node with metadata
270
+ - Chunk content into TextChunk nodes
271
+ - Index in BM25 for keyword search
272
+ - Deduplicate by content hash
273
+
274
+ Phase 2 (background, ~30s):
275
+ - Extract entities (people, places, orgs)
276
+ - Extract facts
277
+ - Generate vector embeddings
278
+ - Link to existing graph nodes
279
+ """
280
+ # Route to source's Context namespace if available
281
+ if source_id and source_id in self._sources:
282
+ self._ensure_context_for_source(self._sources[source_id])
283
+
284
+ # Dedup check
285
+ content_hash = article.content_hash
286
+ if content_hash in self._ingested_hashes:
287
+ return IngestResult(
288
+ article_id=content_hash,
289
+ status="duplicate",
290
+ message="Article already ingested (content hash match)",
291
+ )
292
+
293
+ db = self._get_db()
294
+ if not db:
295
+ return IngestResult(article_id="", status="error", message="No graph available")
296
+
297
+ # Smart pipeline: use new ingestion if available
298
+ if self._use_smart_pipeline:
299
+ try:
300
+ from .smart_ingest import ingest_text
301
+ result = ingest_text(
302
+ text=article.content,
303
+ db=db,
304
+ title=article.title,
305
+ source_url=article.url,
306
+ pipeline="smart_article",
307
+ )
308
+ self._ingested_hashes.add(content_hash)
309
+ return IngestResult(
310
+ article_id=result.document_id,
311
+ status="ingested" if not result.errors else "error",
312
+ message=f"Smart pipeline: {len(result.passage_ids)} passages, {len(result.entity_ids)} entities, {len(result.fact_ids)} facts, {result.edge_count} edges",
313
+ nodes_created=len(result.passage_ids) + len(result.entity_ids) + len(result.fact_ids) + 1,
314
+ edges_created=result.edge_count,
315
+ )
316
+ except Exception as e:
317
+ logger.warning("Smart pipeline failed, falling back to legacy: %s", e)
318
+
319
+ from ..core.graph_structures import GraphNode, GraphEdge
320
+
321
+ now = datetime.now(timezone.utc).isoformat()
322
+ article_id = f"article_{content_hash}"
323
+
324
+ # ── Phase 1: Fast Ingest ──
325
+
326
+ # 1. Create Article node
327
+ article_props = {
328
+ "name": article.title,
329
+ "title": article.title,
330
+ "url": article.url,
331
+ "source": article.source,
332
+ "author": article.author,
333
+ "published_at": article.published_at or now,
334
+ "content": article.content[:500], # preview in node
335
+ "content_hash": content_hash,
336
+ "tags": article.tags,
337
+ "ingested_at": now,
338
+ "_created_at": now,
339
+ "sensitivity": "public",
340
+ "status": "ingested", # becomes "enriched" after phase 2
341
+ }
342
+ article_props.update(article.metadata)
343
+
344
+ db.add_node(GraphNode(
345
+ id=article_id, label="Article", properties=article_props,
346
+ ), write_through=True)
347
+ nodes_created = 1
348
+
349
+ # 2. Chunk content into TextChunk nodes
350
+ chunks = self._chunk_text(article.content, max_chunk=500)
351
+ for i, chunk_text in enumerate(chunks):
352
+ chunk_id = f"{article_id}_chunk_{i}"
353
+ db.add_node(GraphNode(
354
+ id=chunk_id, label="TextChunk",
355
+ properties={
356
+ "name": f"{article.title[:50]} (chunk {i+1}/{len(chunks)})",
357
+ "content": chunk_text,
358
+ "chunk_index": i,
359
+ "article_id": article_id,
360
+ "source": article.source,
361
+ "_created_at": now,
362
+ },
363
+ ), write_through=True)
364
+ db.add_edge(GraphEdge(
365
+ id=str(uuid.uuid4()), source=article_id, target=chunk_id,
366
+ label="CONTAINS", properties={"chunk_index": i},
367
+ ))
368
+ nodes_created += 1
369
+
370
+ # 3. Extract quick facts (regex-based, no LLM needed)
371
+ quick_facts = self._extract_quick_facts(article.content, article.title)
372
+ for fact_text in quick_facts:
373
+ fact_id = f"fact_{hashlib.sha256(fact_text.encode()).hexdigest()[:12]}"
374
+ db.add_node(GraphNode(
375
+ id=fact_id, label="Fact",
376
+ properties={
377
+ "name": fact_text[:100],
378
+ "content": fact_text,
379
+ "source": article.source,
380
+ "article_id": article_id,
381
+ "_created_at": now,
382
+ },
383
+ ), write_through=True)
384
+ db.add_edge(GraphEdge(
385
+ id=str(uuid.uuid4()), source=article_id, target=fact_id,
386
+ label="STATES", properties={},
387
+ ))
388
+ nodes_created += 1
389
+
390
+ # 4a. Index into LMDB BM25 for keyword search
391
+ try:
392
+ from ..search.lmdb_index import get_lmdb_index
393
+ lmdb_idx = get_lmdb_index(self._namespace)
394
+ lmdb_idx.index_node(article_id, "Article", article_props)
395
+ for i, chunk_text in enumerate(chunks):
396
+ chunk_id = f"{article_id}_chunk_{i}"
397
+ lmdb_idx.index_node(chunk_id, "TextChunk", {
398
+ "name": f"{article.title[:50]} (chunk {i+1})",
399
+ "content": chunk_text,
400
+ })
401
+ except Exception:
402
+ pass
403
+
404
+ # 4b. Auto-embed for vector search (if available)
405
+ enrichment_queued = False
406
+ try:
407
+ from ..context.vector_integration import SessionVectorStore
408
+ svs = SessionVectorStore()
409
+ if svs.available:
410
+ # Embed the article summary + chunks
411
+ embed_text = f"{article.title}. {article.content[:300]}"
412
+ svs.add_text(self._namespace, embed_text, article_id,
413
+ metadata={"label": "Article", "name": article.title})
414
+ for i, chunk_text in enumerate(chunks):
415
+ svs.add_text(self._namespace, chunk_text, f"{article_id}_chunk_{i}",
416
+ metadata={"label": "TextChunk", "name": f"chunk {i+1}"})
417
+ except Exception:
418
+ pass
419
+
420
+ # 5. Queue for background enrichment
421
+ if self._enrichment_enabled:
422
+ self._enrichment_queue.append({
423
+ "article_id": article_id,
424
+ "title": article.title,
425
+ "content": article.content,
426
+ "source": article.source,
427
+ "namespace": self._namespace,
428
+ })
429
+ enrichment_queued = True
430
+ self._start_enrichment_worker()
431
+
432
+ self._ingested_hashes.add(content_hash)
433
+
434
+ logger.info("[FEED] Ingested article: %s (%d nodes, enrichment=%s)",
435
+ article.title[:50], nodes_created, enrichment_queued)
436
+
437
+ return IngestResult(
438
+ article_id=article_id,
439
+ status="ingested",
440
+ nodes_created=nodes_created,
441
+ enrichment_queued=enrichment_queued,
442
+ message=f"Article '{article.title[:50]}' ingested with {nodes_created} nodes",
443
+ )
444
+
445
+ # ── RSS Polling ───────────────────────────────────────
446
+
447
+ def poll_rss(self, source: FeedSource) -> List[IngestResult]:
448
+ """Poll an RSS feed and ingest new articles."""
449
+ results = []
450
+ try:
451
+ import feedparser
452
+ feed = feedparser.parse(source.url)
453
+ for entry in feed.entries[:20]: # max 20 per poll
454
+ article = ArticleIngest(
455
+ url=entry.get("link", ""),
456
+ title=entry.get("title", ""),
457
+ content=entry.get("summary", entry.get("description", "")),
458
+ source=source.name,
459
+ published_at=entry.get("published", ""),
460
+ author=entry.get("author", ""),
461
+ tags=[t.get("term", "") for t in entry.get("tags", [])],
462
+ )
463
+ result = self.ingest_article(article, source_id=source.source_id)
464
+ results.append(result)
465
+ except ImportError:
466
+ logger.warning("[FEED] feedparser not installed: pip install feedparser")
467
+ except Exception as e:
468
+ logger.error("[FEED] RSS poll failed for %s: %s", source.name, e)
469
+
470
+ source.last_polled = datetime.now(timezone.utc).isoformat()
471
+ return results
472
+
473
+ def poll_all(self) -> Dict[str, List[IngestResult]]:
474
+ """Poll all active feed sources."""
475
+ results = {}
476
+ for src in self._sources.values():
477
+ if not src.active:
478
+ continue
479
+ if src.source_type == "rss":
480
+ results[src.source_id] = self.poll_rss(src)
481
+ # scrape and api would go here
482
+ return results
483
+
484
+ # ── Background Enrichment (Deep Path — 30s) ──────────
485
+
486
+ def _start_enrichment_worker(self):
487
+ """Start background enrichment thread if not already running."""
488
+ if self._enrichment_thread and self._enrichment_thread.is_alive():
489
+ return
490
+ self._running = True
491
+ self._enrichment_thread = threading.Thread(
492
+ target=self._enrichment_loop, daemon=True, name="feed-enrichment",
493
+ )
494
+ self._enrichment_thread.start()
495
+
496
+ def _enrichment_loop(self):
497
+ """Process enrichment queue in background."""
498
+ while self._running and self._enrichment_queue:
499
+ item = self._enrichment_queue.pop(0)
500
+ try:
501
+ self._enrich_article(item)
502
+ except Exception as e:
503
+ logger.error("[FEED] Enrichment failed for %s: %s", item.get("article_id"), e)
504
+ time.sleep(0.5) # pace between articles
505
+ self._running = False
506
+
507
+ def _enrich_article(self, item: Dict):
508
+ """Deep enrichment: entities, facts, relationships, vectors."""
509
+ db = self._get_db()
510
+ if not db:
511
+ return
512
+
513
+ from ..core.graph_structures import GraphNode, GraphEdge
514
+
515
+ article_id = item["article_id"]
516
+ content = item["content"]
517
+ now = datetime.now(timezone.utc).isoformat()
518
+
519
+ # 1. Entity extraction (LLM-powered if available, regex fallback)
520
+ entities = self._extract_entities(content)
521
+ for ent_name, ent_type in entities:
522
+ ent_id = f"entity_{hashlib.sha256(ent_name.lower().encode()).hexdigest()[:12]}"
523
+ # Create or merge entity node
524
+ existing = db.get_node(ent_id)
525
+ if not existing:
526
+ db.add_node(GraphNode(
527
+ id=ent_id, label="Entity",
528
+ properties={
529
+ "name": ent_name,
530
+ "type": ent_type,
531
+ "_created_at": now,
532
+ "mention_count": 1,
533
+ },
534
+ ), write_through=True)
535
+ else:
536
+ # Increment mention count
537
+ props = getattr(existing, "properties", {}) or {}
538
+ props["mention_count"] = props.get("mention_count", 0) + 1
539
+ props["_updated_at"] = now
540
+
541
+ # Link article → entity
542
+ db.add_edge(GraphEdge(
543
+ id=str(uuid.uuid4()), source=article_id, target=ent_id,
544
+ label="MENTIONS", properties={"extracted_at": now},
545
+ ))
546
+
547
+ # 2. Update article status
548
+ article_node = db.get_node(article_id)
549
+ if article_node:
550
+ props = getattr(article_node, "properties", {}) or {}
551
+ props["status"] = "enriched"
552
+ props["enriched_at"] = now
553
+ props["entity_count"] = len(entities)
554
+
555
+ logger.info("[FEED] Enriched article %s: %d entities", article_id[:20], len(entities))
556
+
557
+ # ── Helpers ───────────────────────────────────────────
558
+
559
+ @staticmethod
560
+ def _chunk_text(text: str, max_chunk: int = 500) -> List[str]:
561
+ """Split text into chunks at sentence boundaries."""
562
+ if len(text) <= max_chunk:
563
+ return [text] if text.strip() else []
564
+
565
+ chunks = []
566
+ sentences = text.replace(". ", ".\n").split("\n")
567
+ current = ""
568
+ for sent in sentences:
569
+ if len(current) + len(sent) > max_chunk and current:
570
+ chunks.append(current.strip())
571
+ current = sent
572
+ else:
573
+ current += " " + sent if current else sent
574
+ if current.strip():
575
+ chunks.append(current.strip())
576
+ return chunks
577
+
578
+ @staticmethod
579
+ def _extract_quick_facts(content: str, title: str) -> List[str]:
580
+ """Extract obvious facts using patterns (no LLM needed).
581
+
582
+ Looks for: percentages, dates, names with titles, quoted statements.
583
+ """
584
+ import re
585
+ facts = []
586
+
587
+ # Sentences with percentages — split on ". " (not bare ".") to avoid breaking "92.5%"
588
+ sentences = re.split(r'\.\s+', content)
589
+ for sent in sentences:
590
+ sent = sent.strip()
591
+ if re.search(r'\d+\.?\d*\s*%', sent) and len(sent) > 20:
592
+ facts.append(sent[:200])
593
+
594
+ # Sentences with years (2024, 2025, 2026)
595
+ for sent in sentences:
596
+ sent = sent.strip()
597
+ if re.search(r'\b20[2-3]\d\b', sent) and len(sent) > 20 and sent not in facts:
598
+ facts.append(sent[:200])
599
+
600
+ # Quoted statements
601
+ quotes = re.findall(r'"([^"]{20,200})"', content)
602
+ for q in quotes[:3]:
603
+ facts.append(q)
604
+
605
+ return facts[:10] # max 10 quick facts
606
+
607
+ @staticmethod
608
+ def _extract_entities(content: str) -> List[tuple]:
609
+ """Extract named entities using regex patterns.
610
+
611
+ Returns list of (name, type) tuples.
612
+ """
613
+ import re
614
+ entities = set()
615
+
616
+ # Capitalized multi-word names (Person/Org)
617
+ for match in re.finditer(r'\b([A-Z][a-z]+(?:\s+[A-Z][a-z]+)+)\b', content):
618
+ name = match.group(1)
619
+ if len(name) > 4 and name not in ("The Times", "India News", "Breaking News"):
620
+ entities.add((name, "Person/Org"))
621
+
622
+ # Indian state/city names
623
+ known_places = {
624
+ "Mumbai", "Delhi", "Kolkata", "Chennai", "Bengaluru", "Hyderabad",
625
+ "Ahmedabad", "Pune", "Jaipur", "Lucknow", "Rajasthan", "Tamil Nadu",
626
+ "Maharashtra", "West Bengal", "Karnataka", "Kerala", "Gujarat",
627
+ }
628
+ for place in known_places:
629
+ if place in content:
630
+ entities.add((place, "Place"))
631
+
632
+ # Organization patterns
633
+ for match in re.finditer(r'\b((?:BJP|Congress|PMK|DMK|TMC|AAP|EC|RBSE|KMC)\b)', content):
634
+ entities.add((match.group(1), "Organization"))
635
+
636
+ return list(entities)[:20]
637
+
638
+ def stop(self):
639
+ """Stop background enrichment."""
640
+ self._running = False