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,759 @@
1
+ """IngestionSchema model and loader for the extraction pipeline.
2
+
3
+ Provides a typed schema that governs what node/edge types the extraction
4
+ pipeline is allowed to produce, what fields are required vs optional, and
5
+ how to fall back to a permissive default when no schema is configured.
6
+ """
7
+ from __future__ import annotations
8
+
9
+ import json
10
+ import logging
11
+ import re
12
+ from dataclasses import dataclass, field
13
+ from typing import Any, Dict, List, Optional
14
+
15
+ logger = logging.getLogger(__name__)
16
+
17
+ # ---------------------------------------------------------------------------
18
+ # Default node types — permissive schema covering common categories
19
+ # ---------------------------------------------------------------------------
20
+
21
+ DEFAULT_NODE_TYPES: Dict[str, Dict[str, Any]] = {
22
+ "Knowledge": {
23
+ "required": ["name"],
24
+ "optional": ["description", "source", "confidence"],
25
+ },
26
+ "Fact": {
27
+ "required": ["name"],
28
+ "optional": ["description", "source", "confidence", "evidence"],
29
+ },
30
+ "Finding": {
31
+ "required": ["name"],
32
+ "optional": ["description", "source", "confidence", "methodology"],
33
+ },
34
+ "Insight": {
35
+ "required": ["name"],
36
+ "optional": ["description", "source", "confidence", "reasoning"],
37
+ },
38
+ "Decision": {
39
+ "required": ["name"],
40
+ "optional": ["description", "rationale", "alternatives", "decided_by"],
41
+ },
42
+ "Entity": {
43
+ "required": ["name"],
44
+ "optional": ["description", "entity_type", "aliases"],
45
+ },
46
+ "Person": {
47
+ "required": ["name"],
48
+ "optional": ["role", "organization", "email"],
49
+ },
50
+ "Organization": {
51
+ "required": ["name"],
52
+ "optional": ["description", "industry", "url"],
53
+ },
54
+ "Event": {
55
+ "required": ["name"],
56
+ "optional": ["date", "location", "description", "participants"],
57
+ },
58
+ "Document": {
59
+ "required": ["name"],
60
+ "optional": ["url", "author", "date", "content_type", "summary"],
61
+ },
62
+ "TextChunk": {
63
+ "required": ["name"],
64
+ "optional": ["text", "source", "chunk_index", "token_count"],
65
+ },
66
+ "Requirement": {
67
+ "required": ["name"],
68
+ "optional": ["description", "priority", "status", "assignee"],
69
+ },
70
+ "CodeFile": {
71
+ "required": ["name"],
72
+ "optional": ["path", "language", "description", "functions"],
73
+ },
74
+ "Topic": {
75
+ "required": ["name"],
76
+ "optional": ["description", "keywords", "relevance"],
77
+ },
78
+ }
79
+
80
+ # ---------------------------------------------------------------------------
81
+ # Default edge types
82
+ # ---------------------------------------------------------------------------
83
+
84
+ DEFAULT_EDGE_TYPES: Dict[str, Dict[str, Any]] = {
85
+ "RELATES_TO": {"description": "General relationship"},
86
+ "MENTIONS": {"description": "Source mentions target"},
87
+ "PART_OF": {"description": "Target contains source"},
88
+ "DEPENDS_ON": {"description": "Source depends on target"},
89
+ "DERIVED_FROM": {"description": "Source was derived from target"},
90
+ "AUTHORED_BY": {"description": "Document/artifact authored by person"},
91
+ "BELONGS_TO": {"description": "Entity belongs to organization/group"},
92
+ "OCCURRED_AT": {"description": "Event occurred at location/time"},
93
+ "SUPPORTS": {"description": "Source evidence supports target claim"},
94
+ "CONTRADICTS": {"description": "Source contradicts target"},
95
+ }
96
+
97
+ # ---------------------------------------------------------------------------
98
+ # IngestionSchema dataclass
99
+ # ---------------------------------------------------------------------------
100
+
101
+
102
+ @dataclass
103
+ class IngestionSchema:
104
+ """Schema governing what an extraction pipeline may produce."""
105
+
106
+ name: str
107
+ node_types: Dict[str, Dict[str, Any]]
108
+ edge_types: Dict[str, Dict[str, Any]]
109
+ content_type: Optional[str] = None
110
+ field_hints: Dict[str, Any] = field(default_factory=dict)
111
+
112
+ # ------------------------------------------------------------------
113
+ @classmethod
114
+ def default(cls) -> IngestionSchema:
115
+ """Return a permissive default schema with standard types."""
116
+ return cls(
117
+ name="default",
118
+ node_types=dict(DEFAULT_NODE_TYPES),
119
+ edge_types=dict(DEFAULT_EDGE_TYPES),
120
+ )
121
+
122
+
123
+ # ---------------------------------------------------------------------------
124
+ # load_schema — resolve schema from graph metadata or fall back to default
125
+ # ---------------------------------------------------------------------------
126
+
127
+
128
+ def _parse_yaml_schema(yaml_str: str, name: str = "custom") -> Optional[IngestionSchema]:
129
+ """Parse a YAML schema string into IngestionSchema."""
130
+ try:
131
+ import yaml
132
+ data = yaml.safe_load(yaml_str)
133
+ if not data or not isinstance(data, dict):
134
+ return None
135
+ node_types = {}
136
+ for nt_name, nt_def in data.get("node_types", {}).items():
137
+ if isinstance(nt_def, dict):
138
+ node_types[nt_name] = {
139
+ "required": nt_def.get("required", nt_def.get("fields", ["name"])),
140
+ "optional": nt_def.get("optional", []),
141
+ "description": nt_def.get("description", ""),
142
+ }
143
+ edge_types = {}
144
+ for et_name, et_def in data.get("edge_types", {}).items():
145
+ if isinstance(et_def, dict):
146
+ edge_types[et_name] = {
147
+ "from": et_def.get("source", et_def.get("from", "*")),
148
+ "to": et_def.get("target", et_def.get("to", "*")),
149
+ }
150
+ if node_types:
151
+ hints = data.get("field_hints", {})
152
+ if data.get("strategy_hints"):
153
+ hints["_strategy_hints"] = data["strategy_hints"]
154
+ return IngestionSchema(
155
+ name=data.get("name", name),
156
+ node_types=node_types,
157
+ edge_types=edge_types,
158
+ content_type=data.get("content_type"),
159
+ field_hints=hints,
160
+ )
161
+ except Exception:
162
+ pass
163
+ return None
164
+
165
+
166
+ def load_schema(
167
+ db,
168
+ content_type: Optional[str] = None,
169
+ context_id: Optional[str] = None,
170
+ ) -> IngestionSchema:
171
+ """Load the ingestion schema for *db*.
172
+
173
+ Resolution order:
174
+ 1. Context's extraction_schema_yaml (from context manager config)
175
+ 2. _context_meta node's schema property (JSON or dict)
176
+ 3. Builtin schema matching context type (from schema registry)
177
+ 4. Permissive default schema
178
+ """
179
+ # 1. Try context manager's extraction schema (YAML)
180
+ if context_id:
181
+ try:
182
+ from ..context.context_manager import ContextManager
183
+ cm = ContextManager()
184
+ ctx = cm.get_context(context_id)
185
+ if ctx and ctx.config.get("extraction_schema_yaml"):
186
+ schema = _parse_yaml_schema(ctx.config["extraction_schema_yaml"], name=f"context:{context_id}")
187
+ if schema:
188
+ schema.content_type = content_type or ctx.context_type
189
+ logger.debug("Loaded schema from context manager: %s", context_id)
190
+ return schema
191
+ except Exception:
192
+ pass
193
+
194
+ # 2. Try _context_meta node's schema property
195
+ try:
196
+ meta = db.get_node("_context_meta")
197
+ if meta:
198
+ # Check for YAML schema in config
199
+ config = meta.properties.get("config", {})
200
+ if isinstance(config, dict) and config.get("extraction_schema_yaml"):
201
+ schema = _parse_yaml_schema(config["extraction_schema_yaml"])
202
+ if schema:
203
+ schema.content_type = content_type
204
+ return schema
205
+
206
+ # Check for JSON schema
207
+ if meta.properties.get("schema"):
208
+ raw = meta.properties["schema"]
209
+ data = json.loads(raw) if isinstance(raw, str) else raw
210
+ schema = IngestionSchema(
211
+ name=data.get("name", "custom"),
212
+ node_types=data.get("node_types", {}),
213
+ edge_types=data.get("edge_types", {}),
214
+ content_type=content_type or data.get("content_type"),
215
+ field_hints=data.get("field_hints", {}),
216
+ )
217
+ logger.debug("Loaded custom ingestion schema '%s'", schema.name)
218
+ return schema
219
+ except Exception as exc:
220
+ logger.warning("Failed to load schema from _context_meta: %s", exc)
221
+
222
+ # 3. Try builtin schema from registry matching content type
223
+ if content_type:
224
+ try:
225
+ from ..extraction.schema_registry import get_schema_registry
226
+ reg = get_schema_registry()
227
+ yaml_str = reg.get_schema(content_type)
228
+ if yaml_str:
229
+ schema = _parse_yaml_schema(yaml_str, name=content_type)
230
+ if schema:
231
+ schema.content_type = content_type
232
+ return schema
233
+ except Exception:
234
+ pass
235
+
236
+ # 4. Fallback — permissive default
237
+ schema = IngestionSchema.default()
238
+ schema.content_type = content_type
239
+ return schema
240
+
241
+
242
+ # ---------------------------------------------------------------------------
243
+ # A2: SchemaPatternGenerator — generate regex patterns from schema
244
+ # ---------------------------------------------------------------------------
245
+
246
+ # Field name → regex pattern mapping
247
+ FIELD_PATTERNS = {
248
+ "email": r'\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b',
249
+ "phone": r'\b\+?[\d\s\-\(\)]{7,15}\b',
250
+ "url": r'https?://[^\s<>"{}|\\^`\[\]]+',
251
+ "date": r'\b\d{4}[-/]\d{1,2}[-/]\d{1,2}\b|\b(?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\w*\s+\d{1,2},?\s*\d{4}\b',
252
+ "percentage": r'\b\d+(?:\.\d+)?%\b',
253
+ "amount": r'\$[\d,]+(?:\.\d{2})?|\b\d+(?:,\d{3})+(?:\.\d{2})?\b',
254
+ }
255
+
256
+ NAMED_ENTITY_PATTERN = r'\b[A-Z][a-z]+(?:\s+[A-Z][a-z]+)+\b'
257
+
258
+
259
+ @dataclass
260
+ class ExtractionPattern:
261
+ """A compiled regex pattern targeting a specific node type and field."""
262
+ pattern: re.Pattern
263
+ target_type: str
264
+ target_field: str
265
+ description: str = ""
266
+
267
+
268
+ class SchemaPatternGenerator:
269
+ """Generate regex extraction patterns from schema definition.
270
+
271
+ Named entity patterns (capitalized multi-word) only apply to entity-like
272
+ types: Person, Organization, Entity. Other types (Fact, Decision, Event)
273
+ are only created when their distinguishing fields (statement, date) match.
274
+ """
275
+
276
+ # Types where a capitalized name alone is enough to create a candidate
277
+ ENTITY_TYPES = {"Person", "Organization", "Entity"}
278
+
279
+ def __init__(self, schema: IngestionSchema):
280
+ self.schema = schema
281
+
282
+ def generate(self) -> List[ExtractionPattern]:
283
+ patterns = []
284
+ structural = {"Document", "TextChunk", "ContextMeta"}
285
+ for type_name, type_def in self.schema.node_types.items():
286
+ if type_name in structural:
287
+ continue
288
+ all_fields = type_def.get("required", []) + type_def.get("optional", [])
289
+
290
+ # Named entity pattern — only for entity-like types
291
+ if "name" in all_fields and type_name in self.ENTITY_TYPES:
292
+ patterns.append(ExtractionPattern(
293
+ pattern=re.compile(NAMED_ENTITY_PATTERN),
294
+ target_type=type_name,
295
+ target_field="name",
296
+ description=f"Named entity for {type_name}",
297
+ ))
298
+
299
+ # Field-specific patterns
300
+ for field_name in all_fields:
301
+ if field_name in FIELD_PATTERNS:
302
+ patterns.append(ExtractionPattern(
303
+ pattern=re.compile(FIELD_PATTERNS[field_name]),
304
+ target_type=type_name,
305
+ target_field=field_name,
306
+ description=f"{field_name} pattern for {type_name}",
307
+ ))
308
+
309
+ # Statement/fact patterns — these CREATE the node (not just fill a field)
310
+ if "statement" in all_fields:
311
+ patterns.append(ExtractionPattern(
312
+ pattern=re.compile(r'[A-Z][^.!?]*(?:\d+%|(?:19|20)\d{2}|"[^"]+")[^.!?]*[.!?]'),
313
+ target_type=type_name,
314
+ target_field="_statement_creates_node",
315
+ description=f"Statement with data for {type_name}",
316
+ ))
317
+
318
+ return patterns
319
+
320
+
321
+ # ---------------------------------------------------------------------------
322
+ # B1: regex_extract — fast schema-driven regex extraction
323
+ # ---------------------------------------------------------------------------
324
+
325
+
326
+ # Words that indicate a name is a location, not a person
327
+ _LOCATION_HINTS = {
328
+ "states", "kingdom", "republic", "union", "islands", "coast", "gulf",
329
+ "sea", "ocean", "river", "mountain", "valley", "peninsula", "strait",
330
+ "america", "africa", "europe", "asia", "arabia", "emirates",
331
+ }
332
+
333
+ # Words that indicate organization, not person
334
+ _ORG_HINTS = {
335
+ "commission", "council", "committee", "agency", "department", "ministry",
336
+ "corporation", "company", "group", "institute", "university", "bank",
337
+ "pentagon", "congress", "parliament", "senate", "nato", "opec",
338
+ }
339
+
340
+
341
+ _CONCEPT_HINTS = frozenset({
342
+ "insurance", "learning", "network", "model", "system", "algorithm",
343
+ "framework", "architecture", "protocol", "pattern", "method", "analysis",
344
+ "processing", "detection", "classification", "regression", "optimization",
345
+ "validation", "testing", "deployment", "integration", "management",
346
+ "security", "authentication", "authorization", "encryption",
347
+ "database", "storage", "index", "cache", "queue", "stream",
348
+ "variable", "function", "module", "package", "library", "dependencies",
349
+ "life", "term", "whole", "universal", "premium", "coverage",
350
+ "portable", "retail", "frozen", "updated", "install", "description",
351
+ })
352
+
353
+
354
+ def _guess_entity_type(name: str, schema_types: set) -> str:
355
+ """Guess the best entity type for a regex-extracted name."""
356
+ name_lower = name.lower()
357
+ words = set(name_lower.split())
358
+
359
+ if words & _LOCATION_HINTS:
360
+ if "Location" in schema_types:
361
+ return "Location"
362
+ return "Organization"
363
+ if words & _ORG_HINTS:
364
+ return "Organization"
365
+ if words & _CONCEPT_HINTS:
366
+ if "Concept" in schema_types:
367
+ return "Concept"
368
+ if "Topic" in schema_types:
369
+ return "Topic"
370
+ return "Concept"
371
+ # Default: Concept (safer than Person — regex can't distinguish real names from concepts)
372
+ if "Concept" in schema_types:
373
+ return "Concept"
374
+ return "Entity"
375
+
376
+
377
+ def regex_extract(text: str, schema: IngestionSchema) -> List[Dict[str, Any]]:
378
+ """Phase 1: Fast regex extraction driven by schema patterns.
379
+
380
+ Returns list of {"label": str, "properties": dict}.
381
+ Each name only creates ONE node (best-guess type), not duplicates across types.
382
+ """
383
+ gen = SchemaPatternGenerator(schema)
384
+ patterns = gen.generate()
385
+ schema_types = set(schema.node_types.keys())
386
+
387
+ candidates: Dict[str, Dict[str, Any]] = {} # "name_lower" -> node dict
388
+ # Track names already seen (by lowercase) — only one type per name
389
+ seen_names: Dict[str, str] = {} # name_lower -> assigned type
390
+
391
+ for ep in patterns:
392
+ matches = ep.pattern.findall(text)
393
+ for match in matches:
394
+ if not match or len(match.strip()) < 2:
395
+ continue
396
+ match = match.strip()
397
+
398
+ if ep.target_field == "name":
399
+ name_lower = match.lower()
400
+ # Only create ONE node per name — pick best type
401
+ if name_lower in seen_names:
402
+ continue # already assigned a type
403
+ best_type = _guess_entity_type(match, schema_types)
404
+ seen_names[name_lower] = best_type
405
+ key = f"{best_type}:{name_lower}"
406
+ if key not in candidates:
407
+ candidates[key] = {
408
+ "label": best_type,
409
+ "properties": {"name": match, "confidence": 0.7, "_extraction_method": "regex"},
410
+ }
411
+ elif ep.target_field == "_statement_creates_node":
412
+ # Statement pattern — creates a Fact/Decision node from the statement text
413
+ name = match[:80].strip()
414
+ key = f"{ep.target_type}:{name.lower()}"
415
+ if key not in candidates:
416
+ candidates[key] = {
417
+ "label": ep.target_type,
418
+ "properties": {"name": name, "statement": match, "confidence": 0.7, "_extraction_method": "regex"},
419
+ }
420
+ else:
421
+ # Field pattern — attaches to existing candidate of same type
422
+ for ckey, cnode in candidates.items():
423
+ if cnode["label"] == ep.target_type and ep.target_field not in cnode["properties"]:
424
+ cnode["properties"][ep.target_field] = match
425
+ break
426
+
427
+ return list(candidates.values())
428
+
429
+
430
+ # ---------------------------------------------------------------------------
431
+ # B2: build_llm_prompt + llm_extract — schema-aware LLM extraction
432
+ # ---------------------------------------------------------------------------
433
+
434
+
435
+ def build_llm_prompt(text: str, schema: IngestionSchema) -> str:
436
+ """Build a schema-constrained LLM extraction prompt."""
437
+ structural = {"Document", "TextChunk", "ContextMeta"}
438
+ lines = [
439
+ "Extract entities and relationships from the following text.",
440
+ "Return ONLY valid JSON with two arrays: 'entities' and 'relationships'.",
441
+ "",
442
+ "Node types to extract:"
443
+ ]
444
+
445
+ for type_name, type_def in schema.node_types.items():
446
+ if type_name in structural:
447
+ continue
448
+ required = type_def.get("required", [])
449
+ optional = type_def.get("optional", [])
450
+ req_str = ", ".join(f"{f} (required)" for f in required)
451
+ opt_str = ", ".join(optional)
452
+ fields = req_str + (", " + opt_str if opt_str else "")
453
+ desc = type_def.get("description", "")
454
+ lines.append(f"- {type_name}: {fields}" + (f" — {desc}" if desc else ""))
455
+
456
+ if schema.edge_types:
457
+ lines.append("")
458
+ lines.append("Relationships to extract:")
459
+ for edge_name, edge_def in schema.edge_types.items():
460
+ src = edge_def.get("from", "*")
461
+ tgt = edge_def.get("to", "*")
462
+ lines.append(f"- {edge_name}: {src} -> {tgt}")
463
+
464
+ # Strategy hints (if provided)
465
+ strategy_hints = schema.field_hints.get("_strategy_hints", "")
466
+ if strategy_hints:
467
+ lines.extend(["", "Content-specific instructions:", strategy_hints])
468
+
469
+ lines.extend([
470
+ "",
471
+ "Rules:",
472
+ "- Only extract types listed above.",
473
+ "- If unsure about a field, omit it rather than guess.",
474
+ "- Each entity needs at minimum its required fields.",
475
+ "- Extract facts as separate items with 'statement' field.",
476
+ "",
477
+ "Text:",
478
+ "---",
479
+ text[:3000],
480
+ "---",
481
+ "",
482
+ 'Return JSON: {"entities": [{"type": "...", "name": "...", ...}], "relationships": [{"type": "...", "source": "...", "target": "..."}]}'
483
+ ])
484
+
485
+ return "\n".join(lines)
486
+
487
+
488
+ def llm_extract(text: str, schema: IngestionSchema, llm_fn=None):
489
+ """Phase 2: LLM extraction constrained by schema.
490
+
491
+ Args:
492
+ text: Content to extract from.
493
+ schema: IngestionSchema defining allowed types.
494
+ llm_fn: Optional callable(prompt) -> str. If None, returns empty.
495
+
496
+ Returns tuple: (nodes: List[dict], edges: List[dict])
497
+ nodes: [{"label": str, "properties": dict}, ...]
498
+ edges: [{"label": str, "source_name": str, "target_name": str, "properties": dict}, ...]
499
+ """
500
+ if not llm_fn:
501
+ return [], []
502
+
503
+ prompt = build_llm_prompt(text, schema)
504
+ response = None
505
+ try:
506
+ response = llm_fn(prompt)
507
+ # Strip markdown code fences if present
508
+ if isinstance(response, str):
509
+ response = response.strip()
510
+ if response.startswith("```"):
511
+ response = response.split("\n", 1)[-1] # remove first line
512
+ if response.endswith("```"):
513
+ response = response[:-3]
514
+ response = response.strip()
515
+ data = json.loads(response)
516
+ entities = data.get("entities", [])
517
+ relationships = data.get("relationships", [])
518
+ except (json.JSONDecodeError, Exception) as e:
519
+ logger.warning("LLM extraction JSON parse failed: %s (response: %.100s...)", e, str(response or "")[:100])
520
+ return [], []
521
+
522
+ nodes = []
523
+ for ent in entities:
524
+ ent_type = ent.pop("type", "Entity")
525
+ name = ent.get("name", "")
526
+ if not name:
527
+ continue
528
+ nodes.append({
529
+ "label": ent_type,
530
+ "properties": {**ent, "confidence": 0.85, "_extraction_method": "llm"},
531
+ })
532
+
533
+ edges = []
534
+ for rel in relationships:
535
+ rel_type = rel.get("type", "RELATED_TO")
536
+ source = rel.get("source", "")
537
+ target = rel.get("target", "")
538
+ if source and target:
539
+ edges.append({
540
+ "label": rel_type,
541
+ "source_name": source,
542
+ "target_name": target,
543
+ "properties": {"confidence": 0.8, "_extraction_method": "llm"},
544
+ })
545
+
546
+ return nodes, edges
547
+
548
+
549
+ # ---------------------------------------------------------------------------
550
+ # B3: merge_extractions — combine Phase 1 + Phase 2
551
+ # ---------------------------------------------------------------------------
552
+
553
+
554
+ def merge_extractions(
555
+ phase1: List[Dict[str, Any]],
556
+ phase2: List[Dict[str, Any]],
557
+ ) -> List[Dict[str, Any]]:
558
+ """Merge regex + LLM extractions. LLM wins on type conflicts.
559
+
560
+ Strategy:
561
+ 1. Start with LLM results (better types, cleaner names)
562
+ 2. Add regex-only entities that LLM missed (no name overlap)
563
+ 3. LLM type wins when regex and LLM extracted same entity with different types
564
+ """
565
+ # Start with LLM results (they have better types)
566
+ merged: Dict[str, Dict[str, Any]] = {}
567
+ llm_names = set() # lowercase names from LLM
568
+
569
+ for node in phase2:
570
+ name = node['properties'].get('name', '').strip()
571
+ if not name:
572
+ continue
573
+ key = f"{node['label']}:{name.lower()}"
574
+ merged[key] = {"label": node["label"], "properties": dict(node["properties"])}
575
+ llm_names.add(name.lower())
576
+
577
+ # Add regex entities that LLM missed
578
+ for node in phase1:
579
+ name = node['properties'].get('name', '').strip()
580
+ if not name:
581
+ continue
582
+ name_lower = name.lower()
583
+
584
+ # Skip if LLM already has this name (any type) — LLM type is better
585
+ if name_lower in llm_names:
586
+ continue
587
+
588
+ # Skip if LLM has a shorter version of this name
589
+ # e.g., regex: "Defense Secretary Lloyd Austin", LLM: "Lloyd Austin"
590
+ llm_match = False
591
+ for ln in llm_names:
592
+ if ln in name_lower or name_lower in ln:
593
+ llm_match = True
594
+ break
595
+ if llm_match:
596
+ continue
597
+
598
+ key = f"{node['label']}:{name_lower}"
599
+ if key not in merged:
600
+ merged[key] = {"label": node["label"], "properties": dict(node["properties"])}
601
+
602
+ # Merge properties: for matched entities, fill gaps from regex
603
+ for node in phase1:
604
+ name = node['properties'].get('name', '').strip().lower()
605
+ for key, m in merged.items():
606
+ m_name = m['properties'].get('name', '').lower()
607
+ if name and (name in m_name or m_name in name):
608
+ # Fill missing fields from regex
609
+ for k, v in node['properties'].items():
610
+ if k.startswith('_') or k in ('name', 'confidence'):
611
+ continue
612
+ if v and not m['properties'].get(k):
613
+ m['properties'][k] = v
614
+ m['properties']['_extraction_method'] = 'merged'
615
+ break
616
+
617
+ # Deduplicate: if same name appears with multiple types, keep best one
618
+ by_name: Dict[str, List] = {}
619
+ for node in merged.values():
620
+ name = node['properties'].get('name', '').lower()
621
+ by_name.setdefault(name, []).append(node)
622
+
623
+ final = []
624
+ for name, nodes in by_name.items():
625
+ if len(nodes) == 1:
626
+ final.append(nodes[0])
627
+ else:
628
+ # Prefer LLM (merged/llm) over regex, prefer specific type over "Entity"
629
+ best = nodes[0]
630
+ for n in nodes[1:]:
631
+ method = n['properties'].get('_extraction_method', '')
632
+ best_method = best['properties'].get('_extraction_method', '')
633
+ # LLM/merged wins over regex
634
+ if method in ('llm', 'merged') and best_method == 'regex':
635
+ best = n
636
+ # Specific type wins over "Entity"
637
+ elif n['label'] != 'Entity' and best['label'] == 'Entity':
638
+ best = n
639
+ final.append(best)
640
+
641
+ return final
642
+
643
+
644
+ # ---------------------------------------------------------------------------
645
+ # Edge inference — create edges from co-occurring entities in text
646
+ # ---------------------------------------------------------------------------
647
+
648
+ # Patterns that suggest relationships between entity types
649
+ RELATIONSHIP_HINTS = {
650
+ ("Person", "Organization"): "WORKS_AT",
651
+ ("Organization", "Organization"): "RELATED_TO",
652
+ ("Person", "Event"): "PARTICIPATED_IN",
653
+ ("Person", "Decision"): "DECIDED_BY",
654
+ }
655
+
656
+
657
+ def _find_edge_type(a_type, b_type, a_name, b_name, schema):
658
+ """Find the best schema edge type for a pair of node types."""
659
+ edge_label = None
660
+ source_name, target_name = a_name, b_name
661
+
662
+ for etype, edef in schema.edge_types.items():
663
+ efrom = edef.get("from", edef.get("source", "*"))
664
+ eto = edef.get("to", edef.get("target", "*"))
665
+ if efrom == a_type and eto == b_type:
666
+ edge_label = etype
667
+ break
668
+ if efrom == b_type and eto == a_type:
669
+ edge_label = etype
670
+ source_name, target_name = b_name, a_name
671
+ break
672
+
673
+ # Fallback to hints for different-type pairs
674
+ if not edge_label and a_type != b_type:
675
+ pair = (a_type, b_type)
676
+ reverse_pair = (b_type, a_type)
677
+ edge_label = RELATIONSHIP_HINTS.get(pair) or RELATIONSHIP_HINTS.get(reverse_pair)
678
+ if RELATIONSHIP_HINTS.get(reverse_pair) and not RELATIONSHIP_HINTS.get(pair):
679
+ source_name, target_name = b_name, a_name
680
+
681
+ return edge_label, source_name, target_name
682
+
683
+
684
+ def infer_edges(
685
+ nodes: List[Dict[str, Any]],
686
+ text: str,
687
+ schema: IngestionSchema,
688
+ ) -> List[Dict[str, Any]]:
689
+ """Infer edges between extracted nodes based on co-occurrence in text.
690
+
691
+ Two passes:
692
+ 1. Paragraph-level: entities in the same paragraph get edges (confidence 0.7)
693
+ 2. Document-level: all entity pairs checked for schema-defined edges (confidence 0.5)
694
+ This catches connections across paragraphs (critical for conversations).
695
+
696
+ Returns list of {"label": str, "source_name": str, "target_name": str, "properties": dict}.
697
+ """
698
+ edges = []
699
+ seen = set()
700
+
701
+ def _add_edge(label, src_name, tgt_name, confidence, method):
702
+ edge_key = f"{label}:{src_name.lower()}:{tgt_name.lower()}"
703
+ if edge_key not in seen:
704
+ seen.add(edge_key)
705
+ edges.append({
706
+ "label": label,
707
+ "source_name": src_name,
708
+ "target_name": tgt_name,
709
+ "properties": {"confidence": confidence, "_extraction_method": method},
710
+ })
711
+
712
+ # Pass 1: Paragraph-level co-occurrence (high confidence)
713
+ paragraphs = [p.strip() for p in text.split("\n\n") if p.strip()]
714
+ if not paragraphs:
715
+ paragraphs = [text]
716
+
717
+ for para in paragraphs:
718
+ para_lower = para.lower()
719
+ present = [n for n in nodes
720
+ if n["properties"].get("name", "") and
721
+ n["properties"]["name"].lower() in para_lower]
722
+
723
+ for i, a in enumerate(present):
724
+ for b in present[i + 1:]:
725
+ a_name = a["properties"]["name"]
726
+ b_name = b["properties"]["name"]
727
+ if a_name.lower() == b_name.lower():
728
+ continue
729
+
730
+ edge_label, src, tgt = _find_edge_type(
731
+ a["label"], b["label"], a_name, b_name, schema)
732
+ if edge_label:
733
+ _add_edge(edge_label, src, tgt, 0.7, "co_occurrence_paragraph")
734
+
735
+ # Pass 2: Adjacent-paragraph window — cross-type, proximity-based edges
736
+ # Only allow "safe" edge types at document level. IMPLEMENTS, DECIDED, SOLVED_BY
737
+ # require explicit LLM extraction — too strong for co-occurrence inference.
738
+ _PROXIMITY_ONLY = {"USED_WITH", "RELATED_TO", "DISCUSSES", "INVOLVES",
739
+ "PART_OF", "ABOUT", "ADDRESSES", "RECOMMENDS"}
740
+ if len(paragraphs) > 1:
741
+ for pi in range(len(paragraphs)):
742
+ window = " ".join(paragraphs[pi:pi + 2]).lower()
743
+ present = [n for n in nodes
744
+ if n["properties"].get("name", "") and
745
+ n["properties"]["name"].lower() in window]
746
+
747
+ for i, a in enumerate(present):
748
+ for b in present[i + 1:]:
749
+ a_name = a["properties"]["name"]
750
+ b_name = b["properties"]["name"]
751
+ if a_name.lower() == b_name.lower() or a["label"] == b["label"]:
752
+ continue
753
+
754
+ edge_label, src, tgt = _find_edge_type(
755
+ a["label"], b["label"], a_name, b_name, schema)
756
+ if edge_label and edge_label in _PROXIMITY_ONLY:
757
+ _add_edge(edge_label, src, tgt, 0.5, "co_occurrence_adjacent")
758
+
759
+ return edges