igel-qe-core 1.0.0

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 (394) hide show
  1. package/dist/cli/index.js +38 -0
  2. package/dist/mcp/server.js +104 -0
  3. package/knowledge_base/__init__.py +3 -0
  4. package/knowledge_base/__pycache__/__init__.cpython-314.pyc +0 -0
  5. package/knowledge_base/__pycache__/__init__.cpython-37.pyc +0 -0
  6. package/knowledge_base/__pycache__/__init__.cpython-38.pyc +0 -0
  7. package/knowledge_base/__pycache__/confidence.cpython-37.pyc +0 -0
  8. package/knowledge_base/__pycache__/config.cpython-314.pyc +0 -0
  9. package/knowledge_base/__pycache__/config.cpython-37.pyc +0 -0
  10. package/knowledge_base/__pycache__/config.cpython-38.pyc +0 -0
  11. package/knowledge_base/__pycache__/context_fusion.cpython-314.pyc +0 -0
  12. package/knowledge_base/__pycache__/context_fusion.cpython-37.pyc +0 -0
  13. package/knowledge_base/__pycache__/feedback_analytics.cpython-37.pyc +0 -0
  14. package/knowledge_base/__pycache__/feedback_applier.cpython-37.pyc +0 -0
  15. package/knowledge_base/__pycache__/feedback_cli.cpython-37.pyc +0 -0
  16. package/knowledge_base/__pycache__/feedback_curator.cpython-37.pyc +0 -0
  17. package/knowledge_base/__pycache__/feedback_handler.cpython-37.pyc +0 -0
  18. package/knowledge_base/__pycache__/generate.cpython-37.pyc +0 -0
  19. package/knowledge_base/__pycache__/generate_qcapps134.cpython-37.pyc +0 -0
  20. package/knowledge_base/__pycache__/input_classifier.cpython-314.pyc +0 -0
  21. package/knowledge_base/__pycache__/input_classifier.cpython-37.pyc +0 -0
  22. package/knowledge_base/__pycache__/kg_extractor.cpython-37.pyc +0 -0
  23. package/knowledge_base/__pycache__/knowledge_graph.cpython-314.pyc +0 -0
  24. package/knowledge_base/__pycache__/knowledge_graph.cpython-37.pyc +0 -0
  25. package/knowledge_base/__pycache__/manual_tc_parser.cpython-314.pyc +0 -0
  26. package/knowledge_base/__pycache__/manual_tc_parser.cpython-37.pyc +0 -0
  27. package/knowledge_base/__pycache__/precedence.cpython-37.pyc +0 -0
  28. package/knowledge_base/__pycache__/prompts.cpython-314.pyc +0 -0
  29. package/knowledge_base/__pycache__/prompts.cpython-37.pyc +0 -0
  30. package/knowledge_base/__pycache__/reranker.cpython-314.pyc +0 -0
  31. package/knowledge_base/__pycache__/reranker.cpython-37.pyc +0 -0
  32. package/knowledge_base/__pycache__/retrieval_debug.cpython-314.pyc +0 -0
  33. package/knowledge_base/__pycache__/retrieval_debug.cpython-37.pyc +0 -0
  34. package/knowledge_base/__pycache__/retriever.cpython-314.pyc +0 -0
  35. package/knowledge_base/__pycache__/retriever.cpython-37.pyc +0 -0
  36. package/knowledge_base/__pycache__/server.cpython-37.pyc +0 -0
  37. package/knowledge_base/__pycache__/tc_transformer.cpython-314.pyc +0 -0
  38. package/knowledge_base/__pycache__/tc_transformer.cpython-37.pyc +0 -0
  39. package/knowledge_base/__pycache__/test_generator.cpython-314.pyc +0 -0
  40. package/knowledge_base/__pycache__/test_generator.cpython-37.pyc +0 -0
  41. package/knowledge_base/agents/__init__.py +6 -0
  42. package/knowledge_base/agents/__pycache__/__init__.cpython-37.pyc +0 -0
  43. package/knowledge_base/agents/__pycache__/__init__.cpython-38.pyc +0 -0
  44. package/knowledge_base/agents/__pycache__/fusion_agent.cpython-37.pyc +0 -0
  45. package/knowledge_base/agents/__pycache__/fusion_agent.cpython-38.pyc +0 -0
  46. package/knowledge_base/agents/__pycache__/graph.cpython-37.pyc +0 -0
  47. package/knowledge_base/agents/__pycache__/graph.cpython-38.pyc +0 -0
  48. package/knowledge_base/agents/__pycache__/healing_agent.cpython-37.pyc +0 -0
  49. package/knowledge_base/agents/__pycache__/intent_agent.cpython-37.pyc +0 -0
  50. package/knowledge_base/agents/__pycache__/intent_agent.cpython-38.pyc +0 -0
  51. package/knowledge_base/agents/__pycache__/kg_agent.cpython-37.pyc +0 -0
  52. package/knowledge_base/agents/__pycache__/kg_agent.cpython-38.pyc +0 -0
  53. package/knowledge_base/agents/__pycache__/memory_agent.cpython-37.pyc +0 -0
  54. package/knowledge_base/agents/__pycache__/memory_agent.cpython-38.pyc +0 -0
  55. package/knowledge_base/agents/__pycache__/rca_agent.cpython-37.pyc +0 -0
  56. package/knowledge_base/agents/__pycache__/reflection_agent.cpython-37.pyc +0 -0
  57. package/knowledge_base/agents/__pycache__/reflection_agent.cpython-38.pyc +0 -0
  58. package/knowledge_base/agents/__pycache__/regression_agent.cpython-37.pyc +0 -0
  59. package/knowledge_base/agents/__pycache__/retrieval_agent.cpython-37.pyc +0 -0
  60. package/knowledge_base/agents/__pycache__/retrieval_agent.cpython-38.pyc +0 -0
  61. package/knowledge_base/agents/__pycache__/script_agent.cpython-37.pyc +0 -0
  62. package/knowledge_base/agents/__pycache__/script_agent.cpython-38.pyc +0 -0
  63. package/knowledge_base/agents/__pycache__/state.cpython-37.pyc +0 -0
  64. package/knowledge_base/agents/__pycache__/state.cpython-38.pyc +0 -0
  65. package/knowledge_base/agents/__pycache__/temporal_agent.cpython-37.pyc +0 -0
  66. package/knowledge_base/agents/__pycache__/temporal_agent.cpython-38.pyc +0 -0
  67. package/knowledge_base/agents/__pycache__/validation_agent.cpython-37.pyc +0 -0
  68. package/knowledge_base/agents/__pycache__/validation_agent.cpython-38.pyc +0 -0
  69. package/knowledge_base/agents/automation_design_agent.py +123 -0
  70. package/knowledge_base/agents/code_intelligence_agent.py +147 -0
  71. package/knowledge_base/agents/confidence_agent.py +94 -0
  72. package/knowledge_base/agents/execution_validation_agent.py +129 -0
  73. package/knowledge_base/agents/feedback_agent.py +56 -0
  74. package/knowledge_base/agents/fusion_agent.py +113 -0
  75. package/knowledge_base/agents/graph.py +152 -0
  76. package/knowledge_base/agents/healing_agent.py +62 -0
  77. package/knowledge_base/agents/intent_agent.py +76 -0
  78. package/knowledge_base/agents/kg_agent.py +91 -0
  79. package/knowledge_base/agents/memory_agent.py +62 -0
  80. package/knowledge_base/agents/rca_agent.py +53 -0
  81. package/knowledge_base/agents/reflection_agent.py +62 -0
  82. package/knowledge_base/agents/regression_agent.py +81 -0
  83. package/knowledge_base/agents/requirement_agent.py +71 -0
  84. package/knowledge_base/agents/retrieval_agent.py +66 -0
  85. package/knowledge_base/agents/scenario_agent.py +81 -0
  86. package/knowledge_base/agents/script_agent.py +121 -0
  87. package/knowledge_base/agents/script_generator_agent.py +77 -0
  88. package/knowledge_base/agents/state.py +53 -0
  89. package/knowledge_base/agents/temporal_agent.py +48 -0
  90. package/knowledge_base/agents/testcase_agent.py +111 -0
  91. package/knowledge_base/agents/validation_agent.py +49 -0
  92. package/knowledge_base/agents/workflow_engine.py +87 -0
  93. package/knowledge_base/alembic/README.md +104 -0
  94. package/knowledge_base/alembic/__pycache__/env.cpython-37.pyc +0 -0
  95. package/knowledge_base/alembic/__pycache__/env.cpython-38.pyc +0 -0
  96. package/knowledge_base/alembic/env.py +76 -0
  97. package/knowledge_base/alembic/script.py.mako +5 -0
  98. package/knowledge_base/alembic/versions/.gitkeep +26 -0
  99. package/knowledge_base/alembic/versions/001_phase0_feedback.py +122 -0
  100. package/knowledge_base/alembic/versions/002_feedback_promotions.py +60 -0
  101. package/knowledge_base/alembic/versions/003_enterprise_hardening.py +74 -0
  102. package/knowledge_base/alembic/versions/__pycache__/001_phase0_feedback.cpython-37.pyc +0 -0
  103. package/knowledge_base/alembic/versions/__pycache__/001_phase0_feedback.cpython-38.pyc +0 -0
  104. package/knowledge_base/alembic/versions/__pycache__/002_feedback_promotions.cpython-37.pyc +0 -0
  105. package/knowledge_base/alembic/versions/__pycache__/002_feedback_promotions.cpython-38.pyc +0 -0
  106. package/knowledge_base/api/__init__.py +1 -0
  107. package/knowledge_base/api/__pycache__/__init__.cpython-37.pyc +0 -0
  108. package/knowledge_base/api/__pycache__/__init__.cpython-38.pyc +0 -0
  109. package/knowledge_base/api/__pycache__/middleware.cpython-38.pyc +0 -0
  110. package/knowledge_base/api/__pycache__/server.cpython-37.pyc +0 -0
  111. package/knowledge_base/api/__pycache__/server.cpython-38.pyc +0 -0
  112. package/knowledge_base/api/middleware.py +78 -0
  113. package/knowledge_base/api/server.py +778 -0
  114. package/knowledge_base/chunking/__init__.py +0 -0
  115. package/knowledge_base/chunking/__pycache__/__init__.cpython-37.pyc +0 -0
  116. package/knowledge_base/chunking/__pycache__/__init__.cpython-38.pyc +0 -0
  117. package/knowledge_base/chunking/__pycache__/chunker.cpython-37.pyc +0 -0
  118. package/knowledge_base/chunking/__pycache__/chunker.cpython-38.pyc +0 -0
  119. package/knowledge_base/chunking/__pycache__/validators.cpython-37.pyc +0 -0
  120. package/knowledge_base/chunking/__pycache__/validators.cpython-38.pyc +0 -0
  121. package/knowledge_base/chunking/chunker.py +402 -0
  122. package/knowledge_base/chunking/validators.py +144 -0
  123. package/knowledge_base/cli/__init__.py +1 -0
  124. package/knowledge_base/cli/__pycache__/__init__.cpython-37.pyc +0 -0
  125. package/knowledge_base/cli/__pycache__/__init__.cpython-38.pyc +0 -0
  126. package/knowledge_base/cli/__pycache__/feedback_cli.cpython-37.pyc +0 -0
  127. package/knowledge_base/cli/__pycache__/feedback_cli.cpython-38.pyc +0 -0
  128. package/knowledge_base/cli/__pycache__/generate.cpython-37.pyc +0 -0
  129. package/knowledge_base/cli/__pycache__/generate.cpython-38.pyc +0 -0
  130. package/knowledge_base/cli/__pycache__/generate_qcapps134.cpython-37.pyc +0 -0
  131. package/knowledge_base/cli/feedback_cli.py +121 -0
  132. package/knowledge_base/cli/generate.py +308 -0
  133. package/knowledge_base/cli/generate_qcapps134.py +391 -0
  134. package/knowledge_base/config/.env +100 -0
  135. package/knowledge_base/config/.env.example +75 -0
  136. package/knowledge_base/config/__init__.py +5 -0
  137. package/knowledge_base/config/__pycache__/__init__.cpython-37.pyc +0 -0
  138. package/knowledge_base/config/__pycache__/__init__.cpython-38.pyc +0 -0
  139. package/knowledge_base/config/__pycache__/settings.cpython-37.pyc +0 -0
  140. package/knowledge_base/config/__pycache__/settings.cpython-38.pyc +0 -0
  141. package/knowledge_base/config/settings.py +280 -0
  142. package/knowledge_base/connectors/__init__.py +5 -0
  143. package/knowledge_base/connectors/__pycache__/__init__.cpython-37.pyc +0 -0
  144. package/knowledge_base/connectors/__pycache__/__init__.cpython-38.pyc +0 -0
  145. package/knowledge_base/connectors/__pycache__/base.cpython-37.pyc +0 -0
  146. package/knowledge_base/connectors/__pycache__/base.cpython-38.pyc +0 -0
  147. package/knowledge_base/connectors/__pycache__/bitbucket_connector.cpython-37.pyc +0 -0
  148. package/knowledge_base/connectors/__pycache__/bitbucket_connector.cpython-38.pyc +0 -0
  149. package/knowledge_base/connectors/__pycache__/community_connector.cpython-38.pyc +0 -0
  150. package/knowledge_base/connectors/__pycache__/confluence_connector.cpython-37.pyc +0 -0
  151. package/knowledge_base/connectors/__pycache__/confluence_connector.cpython-38.pyc +0 -0
  152. package/knowledge_base/connectors/__pycache__/jira_connector.cpython-37.pyc +0 -0
  153. package/knowledge_base/connectors/__pycache__/jira_connector.cpython-38.pyc +0 -0
  154. package/knowledge_base/connectors/__pycache__/kb_igel_connector.cpython-38.pyc +0 -0
  155. package/knowledge_base/connectors/__pycache__/playwright_fetcher.cpython-38.pyc +0 -0
  156. package/knowledge_base/connectors/__pycache__/query_bridge.cpython-38.pyc +0 -0
  157. package/knowledge_base/connectors/__pycache__/search_backends.cpython-38.pyc +0 -0
  158. package/knowledge_base/connectors/__pycache__/serviceNow_connector.cpython-37.pyc +0 -0
  159. package/knowledge_base/connectors/__pycache__/serviceNow_connector.cpython-38.pyc +0 -0
  160. package/knowledge_base/connectors/__pycache__/sync_scheduler.cpython-37.pyc +0 -0
  161. package/knowledge_base/connectors/__pycache__/sync_scheduler.cpython-38.pyc +0 -0
  162. package/knowledge_base/connectors/__pycache__/teamcity_connector.cpython-37.pyc +0 -0
  163. package/knowledge_base/connectors/__pycache__/teamcity_connector.cpython-38.pyc +0 -0
  164. package/knowledge_base/connectors/__pycache__/testRail_connector.cpython-37.pyc +0 -0
  165. package/knowledge_base/connectors/__pycache__/testRail_connector.cpython-38.pyc +0 -0
  166. package/knowledge_base/connectors/__pycache__/web_fetcher.cpython-38.pyc +0 -0
  167. package/knowledge_base/connectors/base.py +44 -0
  168. package/knowledge_base/connectors/bitbucket_connector.py +25 -0
  169. package/knowledge_base/connectors/community_connector.py +112 -0
  170. package/knowledge_base/connectors/confluence_connector.py +165 -0
  171. package/knowledge_base/connectors/jira_connector.py +174 -0
  172. package/knowledge_base/connectors/kb_igel_connector.py +145 -0
  173. package/knowledge_base/connectors/playwright_fetcher.py +50 -0
  174. package/knowledge_base/connectors/query_bridge.py +170 -0
  175. package/knowledge_base/connectors/search_backends.py +368 -0
  176. package/knowledge_base/connectors/serviceNow_connector.py +25 -0
  177. package/knowledge_base/connectors/sync_scheduler.py +111 -0
  178. package/knowledge_base/connectors/teamcity_connector.py +25 -0
  179. package/knowledge_base/connectors/testRail_connector.py +25 -0
  180. package/knowledge_base/connectors/web_fetcher.py +280 -0
  181. package/knowledge_base/core/__init__.py +8 -0
  182. package/knowledge_base/core/__pycache__/__init__.cpython-37.pyc +0 -0
  183. package/knowledge_base/core/__pycache__/__init__.cpython-38.pyc +0 -0
  184. package/knowledge_base/core/__pycache__/cache.cpython-38.pyc +0 -0
  185. package/knowledge_base/core/__pycache__/circuit_breaker.cpython-38.pyc +0 -0
  186. package/knowledge_base/core/__pycache__/confidence.cpython-37.pyc +0 -0
  187. package/knowledge_base/core/__pycache__/confidence.cpython-38.pyc +0 -0
  188. package/knowledge_base/core/__pycache__/logging_setup.cpython-38.pyc +0 -0
  189. package/knowledge_base/core/__pycache__/metrics.cpython-38.pyc +0 -0
  190. package/knowledge_base/core/__pycache__/precedence.cpython-37.pyc +0 -0
  191. package/knowledge_base/core/__pycache__/precedence.cpython-38.pyc +0 -0
  192. package/knowledge_base/core/__pycache__/request_context.cpython-38.pyc +0 -0
  193. package/knowledge_base/core/cache.py +98 -0
  194. package/knowledge_base/core/circuit_breaker.py +33 -0
  195. package/knowledge_base/core/confidence.py +254 -0
  196. package/knowledge_base/core/logging_setup.py +39 -0
  197. package/knowledge_base/core/metrics.py +63 -0
  198. package/knowledge_base/core/precedence.py +354 -0
  199. package/knowledge_base/core/request_context.py +54 -0
  200. package/knowledge_base/db/__init__.py +0 -0
  201. package/knowledge_base/db/__pycache__/__init__.cpython-314.pyc +0 -0
  202. package/knowledge_base/db/__pycache__/__init__.cpython-37.pyc +0 -0
  203. package/knowledge_base/db/__pycache__/__init__.cpython-38.pyc +0 -0
  204. package/knowledge_base/db/__pycache__/client.cpython-314.pyc +0 -0
  205. package/knowledge_base/db/__pycache__/client.cpython-37.pyc +0 -0
  206. package/knowledge_base/db/__pycache__/client.cpython-38.pyc +0 -0
  207. package/knowledge_base/db/__pycache__/connector_sync.cpython-38.pyc +0 -0
  208. package/knowledge_base/db/__pycache__/feedback_schema.cpython-37.pyc +0 -0
  209. package/knowledge_base/db/__pycache__/feedback_schema.cpython-38.pyc +0 -0
  210. package/knowledge_base/db/__pycache__/kg_introspect.cpython-314.pyc +0 -0
  211. package/knowledge_base/db/__pycache__/kg_introspect.cpython-37.pyc +0 -0
  212. package/knowledge_base/db/__pycache__/kg_introspect.cpython-38.pyc +0 -0
  213. package/knowledge_base/db/__pycache__/schema.cpython-37.pyc +0 -0
  214. package/knowledge_base/db/__pycache__/schema.cpython-38.pyc +0 -0
  215. package/knowledge_base/db/client.py +100 -0
  216. package/knowledge_base/db/connector_sync.py +43 -0
  217. package/knowledge_base/db/feedback_schema.py +548 -0
  218. package/knowledge_base/db/igel_schema.py +150 -0
  219. package/knowledge_base/db/kg_introspect.py +116 -0
  220. package/knowledge_base/db/migrations.py +22 -0
  221. package/knowledge_base/db/schema.py +649 -0
  222. package/knowledge_base/deploy/Dockerfile +23 -0
  223. package/knowledge_base/deploy/feedback_automation.sh +18 -0
  224. package/knowledge_base/deploy/firecrawl-docker.sh +50 -0
  225. package/knowledge_base/deploy/igel-testgen.service +26 -0
  226. package/knowledge_base/deploy/install_reranker.sh +92 -0
  227. package/knowledge_base/deploy/requirements.txt +68 -0
  228. package/knowledge_base/deploy/run_server.sh +35 -0
  229. package/knowledge_base/docs/TEST_GENERATION_GUIDE.md +174 -0
  230. package/knowledge_base/embeddings/__init__.py +0 -0
  231. package/knowledge_base/embeddings/__pycache__/__init__.cpython-314.pyc +0 -0
  232. package/knowledge_base/embeddings/__pycache__/__init__.cpython-37.pyc +0 -0
  233. package/knowledge_base/embeddings/__pycache__/__init__.cpython-38.pyc +0 -0
  234. package/knowledge_base/embeddings/__pycache__/azure_embedder.cpython-314.pyc +0 -0
  235. package/knowledge_base/embeddings/__pycache__/azure_embedder.cpython-37.pyc +0 -0
  236. package/knowledge_base/embeddings/__pycache__/azure_embedder.cpython-38.pyc +0 -0
  237. package/knowledge_base/embeddings/azure_embedder.py +99 -0
  238. package/knowledge_base/eval/__init__.py +33 -0
  239. package/knowledge_base/eval/__main__.py +172 -0
  240. package/knowledge_base/eval/__pycache__/__init__.cpython-37.pyc +0 -0
  241. package/knowledge_base/eval/__pycache__/__init__.cpython-38.pyc +0 -0
  242. package/knowledge_base/eval/__pycache__/__main__.cpython-37.pyc +0 -0
  243. package/knowledge_base/eval/__pycache__/baseline.cpython-37.pyc +0 -0
  244. package/knowledge_base/eval/__pycache__/baseline.cpython-38.pyc +0 -0
  245. package/knowledge_base/eval/__pycache__/harness.cpython-37.pyc +0 -0
  246. package/knowledge_base/eval/__pycache__/harness.cpython-38.pyc +0 -0
  247. package/knowledge_base/eval/__pycache__/scorers.cpython-37.pyc +0 -0
  248. package/knowledge_base/eval/__pycache__/scorers.cpython-38.pyc +0 -0
  249. package/knowledge_base/eval/baseline.py +213 -0
  250. package/knowledge_base/eval/harness.py +400 -0
  251. package/knowledge_base/eval/queries_50.json +302 -0
  252. package/knowledge_base/eval/scorers.py +494 -0
  253. package/knowledge_base/exemplars/README.md +7 -0
  254. package/knowledge_base/exemplars/__pycache__/pytest_reference_snippet.cpython-37.pyc +0 -0
  255. package/knowledge_base/exemplars/gold_manual_concise_snippet.md +8 -0
  256. package/knowledge_base/exemplars/gold_manual_detailed_snippet.md +7 -0
  257. package/knowledge_base/exemplars/pytest_reference_snippet.py +37 -0
  258. package/knowledge_base/feedback/__init__.py +10 -0
  259. package/knowledge_base/feedback/__pycache__/__init__.cpython-37.pyc +0 -0
  260. package/knowledge_base/feedback/__pycache__/__init__.cpython-38.pyc +0 -0
  261. package/knowledge_base/feedback/__pycache__/analytics.cpython-37.pyc +0 -0
  262. package/knowledge_base/feedback/__pycache__/analytics.cpython-38.pyc +0 -0
  263. package/knowledge_base/feedback/__pycache__/applier.cpython-37.pyc +0 -0
  264. package/knowledge_base/feedback/__pycache__/applier.cpython-38.pyc +0 -0
  265. package/knowledge_base/feedback/__pycache__/curator.cpython-37.pyc +0 -0
  266. package/knowledge_base/feedback/__pycache__/curator.cpython-38.pyc +0 -0
  267. package/knowledge_base/feedback/__pycache__/handler.cpython-37.pyc +0 -0
  268. package/knowledge_base/feedback/__pycache__/handler.cpython-38.pyc +0 -0
  269. package/knowledge_base/feedback/analytics.py +72 -0
  270. package/knowledge_base/feedback/applier.py +93 -0
  271. package/knowledge_base/feedback/curator.py +49 -0
  272. package/knowledge_base/feedback/handler.py +151 -0
  273. package/knowledge_base/generation/__init__.py +5 -0
  274. package/knowledge_base/generation/__pycache__/__init__.cpython-37.pyc +0 -0
  275. package/knowledge_base/generation/__pycache__/__init__.cpython-38.pyc +0 -0
  276. package/knowledge_base/generation/__pycache__/input_classifier.cpython-37.pyc +0 -0
  277. package/knowledge_base/generation/__pycache__/input_classifier.cpython-38.pyc +0 -0
  278. package/knowledge_base/generation/__pycache__/manual_tc_parser.cpython-37.pyc +0 -0
  279. package/knowledge_base/generation/__pycache__/manual_tc_parser.cpython-38.pyc +0 -0
  280. package/knowledge_base/generation/__pycache__/prompts.cpython-37.pyc +0 -0
  281. package/knowledge_base/generation/__pycache__/prompts.cpython-38.pyc +0 -0
  282. package/knowledge_base/generation/__pycache__/tc_transformer.cpython-37.pyc +0 -0
  283. package/knowledge_base/generation/__pycache__/test_generator.cpython-37.pyc +0 -0
  284. package/knowledge_base/generation/__pycache__/test_generator.cpython-38.pyc +0 -0
  285. package/knowledge_base/generation/input_classifier.py +148 -0
  286. package/knowledge_base/generation/manual_tc_parser.py +180 -0
  287. package/knowledge_base/generation/prompts.py +755 -0
  288. package/knowledge_base/generation/tc_transformer.py +368 -0
  289. package/knowledge_base/generation/test_generator.py +553 -0
  290. package/knowledge_base/graph_db/__init__.py +5 -0
  291. package/knowledge_base/graph_db/__pycache__/__init__.cpython-37.pyc +0 -0
  292. package/knowledge_base/graph_db/__pycache__/__init__.cpython-38.pyc +0 -0
  293. package/knowledge_base/graph_db/__pycache__/graphiti_client.cpython-37.pyc +0 -0
  294. package/knowledge_base/graph_db/__pycache__/kg_traversal.cpython-37.pyc +0 -0
  295. package/knowledge_base/graph_db/__pycache__/migrate_kg.cpython-37.pyc +0 -0
  296. package/knowledge_base/graph_db/__pycache__/neo4j_client.cpython-37.pyc +0 -0
  297. package/knowledge_base/graph_db/__pycache__/neo4j_client.cpython-38.pyc +0 -0
  298. package/knowledge_base/graph_db/__pycache__/schema.cpython-37.pyc +0 -0
  299. package/knowledge_base/graph_db/__pycache__/temporal_ingest.cpython-37.pyc +0 -0
  300. package/knowledge_base/graph_db/graphiti_client.py +127 -0
  301. package/knowledge_base/graph_db/kg_traversal.py +192 -0
  302. package/knowledge_base/graph_db/migrate_kg.py +93 -0
  303. package/knowledge_base/graph_db/neo4j_client.py +116 -0
  304. package/knowledge_base/graph_db/schema.py +22 -0
  305. package/knowledge_base/graph_db/temporal_ingest.py +59 -0
  306. package/knowledge_base/ingestion/__init__.py +1 -0
  307. package/knowledge_base/ingestion/__pycache__/__init__.cpython-37.pyc +0 -0
  308. package/knowledge_base/ingestion/__pycache__/__init__.cpython-38.pyc +0 -0
  309. package/knowledge_base/ingestion/__pycache__/ingest.cpython-37.pyc +0 -0
  310. package/knowledge_base/ingestion/__pycache__/ingest.cpython-38.pyc +0 -0
  311. package/knowledge_base/ingestion/__pycache__/ingest_images.cpython-37.pyc +0 -0
  312. package/knowledge_base/ingestion/__pycache__/ingest_images.cpython-38.pyc +0 -0
  313. package/knowledge_base/ingestion/__pycache__/kg_extractor.cpython-37.pyc +0 -0
  314. package/knowledge_base/ingestion/__pycache__/kg_extractor.cpython-38.pyc +0 -0
  315. package/knowledge_base/ingestion/__pycache__/web_ingest.cpython-38.pyc +0 -0
  316. package/knowledge_base/ingestion/ingest.py +346 -0
  317. package/knowledge_base/ingestion/ingest_images.py +330 -0
  318. package/knowledge_base/ingestion/kg_extractor.py +358 -0
  319. package/knowledge_base/ingestion/web_ingest.py +83 -0
  320. package/knowledge_base/intelligence/__init__.py +1 -0
  321. package/knowledge_base/intelligence/__pycache__/__init__.cpython-37.pyc +0 -0
  322. package/knowledge_base/intelligence/__pycache__/execution_ingest.cpython-37.pyc +0 -0
  323. package/knowledge_base/intelligence/__pycache__/predictive_engine.cpython-37.pyc +0 -0
  324. package/knowledge_base/intelligence/__pycache__/risk_scorer.cpython-37.pyc +0 -0
  325. package/knowledge_base/intelligence/execution_ingest.py +34 -0
  326. package/knowledge_base/intelligence/predictive_engine.py +7 -0
  327. package/knowledge_base/intelligence/risk_scorer.py +8 -0
  328. package/knowledge_base/memory/__init__.py +6 -0
  329. package/knowledge_base/memory/__pycache__/__init__.cpython-37.pyc +0 -0
  330. package/knowledge_base/memory/__pycache__/__init__.cpython-38.pyc +0 -0
  331. package/knowledge_base/memory/__pycache__/cognee_client.cpython-37.pyc +0 -0
  332. package/knowledge_base/memory/__pycache__/cognee_client.cpython-38.pyc +0 -0
  333. package/knowledge_base/memory/__pycache__/ingest_memory.cpython-37.pyc +0 -0
  334. package/knowledge_base/memory/__pycache__/memory_types.cpython-37.pyc +0 -0
  335. package/knowledge_base/memory/__pycache__/memory_types.cpython-38.pyc +0 -0
  336. package/knowledge_base/memory/cognee_client.py +164 -0
  337. package/knowledge_base/memory/ingest_memory.py +60 -0
  338. package/knowledge_base/memory/memory_types.py +23 -0
  339. package/knowledge_base/models/test_requirement.py +84 -0
  340. package/knowledge_base/parsers/__init__.py +0 -0
  341. package/knowledge_base/parsers/__pycache__/__init__.cpython-37.pyc +0 -0
  342. package/knowledge_base/parsers/__pycache__/__init__.cpython-38.pyc +0 -0
  343. package/knowledge_base/parsers/__pycache__/csv_parser.cpython-37.pyc +0 -0
  344. package/knowledge_base/parsers/__pycache__/csv_parser.cpython-38.pyc +0 -0
  345. package/knowledge_base/parsers/__pycache__/doc_parser.cpython-37.pyc +0 -0
  346. package/knowledge_base/parsers/__pycache__/doc_parser.cpython-38.pyc +0 -0
  347. package/knowledge_base/parsers/__pycache__/html_parser.cpython-38.pyc +0 -0
  348. package/knowledge_base/parsers/__pycache__/image_extractor.cpython-37.pyc +0 -0
  349. package/knowledge_base/parsers/__pycache__/image_extractor.cpython-38.pyc +0 -0
  350. package/knowledge_base/parsers/__pycache__/markdown_parser.cpython-37.pyc +0 -0
  351. package/knowledge_base/parsers/__pycache__/markdown_parser.cpython-38.pyc +0 -0
  352. package/knowledge_base/parsers/__pycache__/pdf_parser.cpython-37.pyc +0 -0
  353. package/knowledge_base/parsers/__pycache__/pdf_parser.cpython-38.pyc +0 -0
  354. package/knowledge_base/parsers/csv_parser.py +126 -0
  355. package/knowledge_base/parsers/doc_parser.py +176 -0
  356. package/knowledge_base/parsers/html_parser.py +151 -0
  357. package/knowledge_base/parsers/image_extractor.py +370 -0
  358. package/knowledge_base/parsers/markdown_parser.py +116 -0
  359. package/knowledge_base/parsers/pdf_parser.py +178 -0
  360. package/knowledge_base/retrieval/__init__.py +1 -0
  361. package/knowledge_base/retrieval/__pycache__/__init__.cpython-37.pyc +0 -0
  362. package/knowledge_base/retrieval/__pycache__/__init__.cpython-38.pyc +0 -0
  363. package/knowledge_base/retrieval/__pycache__/context_fusion.cpython-37.pyc +0 -0
  364. package/knowledge_base/retrieval/__pycache__/context_fusion.cpython-38.pyc +0 -0
  365. package/knowledge_base/retrieval/__pycache__/knowledge_graph.cpython-37.pyc +0 -0
  366. package/knowledge_base/retrieval/__pycache__/knowledge_graph.cpython-38.pyc +0 -0
  367. package/knowledge_base/retrieval/__pycache__/reranker.cpython-37.pyc +0 -0
  368. package/knowledge_base/retrieval/__pycache__/reranker.cpython-38.pyc +0 -0
  369. package/knowledge_base/retrieval/__pycache__/retrieval_debug.cpython-37.pyc +0 -0
  370. package/knowledge_base/retrieval/__pycache__/retrieval_debug.cpython-38.pyc +0 -0
  371. package/knowledge_base/retrieval/__pycache__/retriever.cpython-37.pyc +0 -0
  372. package/knowledge_base/retrieval/__pycache__/retriever.cpython-38.pyc +0 -0
  373. package/knowledge_base/retrieval/__pycache__/unified_query.cpython-38.pyc +0 -0
  374. package/knowledge_base/retrieval/__pycache__/web_retriever.cpython-38.pyc +0 -0
  375. package/knowledge_base/retrieval/context_fusion.py +264 -0
  376. package/knowledge_base/retrieval/knowledge_graph.py +267 -0
  377. package/knowledge_base/retrieval/reranker.py +308 -0
  378. package/knowledge_base/retrieval/retrieval_debug.py +69 -0
  379. package/knowledge_base/retrieval/retriever.py +885 -0
  380. package/knowledge_base/retrieval/unified_query.py +189 -0
  381. package/knowledge_base/retrieval/web_retriever.py +337 -0
  382. package/knowledge_base/validators/__init__.py +1 -0
  383. package/knowledge_base/validators/__pycache__/__init__.cpython-314.pyc +0 -0
  384. package/knowledge_base/validators/__pycache__/__init__.cpython-37.pyc +0 -0
  385. package/knowledge_base/validators/__pycache__/step_fidelity.cpython-314.pyc +0 -0
  386. package/knowledge_base/validators/__pycache__/step_fidelity.cpython-37.pyc +0 -0
  387. package/knowledge_base/validators/step_fidelity.py +88 -0
  388. package/knowledge_base/vision/__init__.py +0 -0
  389. package/knowledge_base/vision/__pycache__/__init__.cpython-37.pyc +0 -0
  390. package/knowledge_base/vision/__pycache__/__init__.cpython-38.pyc +0 -0
  391. package/knowledge_base/vision/__pycache__/image_describer.cpython-37.pyc +0 -0
  392. package/knowledge_base/vision/__pycache__/image_describer.cpython-38.pyc +0 -0
  393. package/knowledge_base/vision/image_describer.py +202 -0
  394. package/package.json +31 -0
@@ -0,0 +1,25 @@
1
+ """TestRail connector — Phase 8 stub."""
2
+ from __future__ import annotations
3
+
4
+ from datetime import datetime
5
+
6
+ from knowledge_base.config import cfg
7
+ from knowledge_base.connectors.base import BaseConnector
8
+
9
+
10
+ class TestRailConnector(BaseConnector):
11
+ name = "testrail"
12
+ enabled = cfg.TESTRAIL_ENABLED
13
+ supports_sync = False
14
+
15
+ def connect(self) -> bool:
16
+ return self.enabled
17
+
18
+ def sync(self, since: datetime | None = None) -> list[dict]:
19
+ return []
20
+
21
+ def ingest_to_kg(self, items: list[dict]) -> int:
22
+ return 0
23
+
24
+ def ingest_to_rag(self, items: list[dict]) -> int:
25
+ return 0
@@ -0,0 +1,280 @@
1
+ """Generic HTTP fetcher with in-memory cache for web retrieval."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import logging
6
+ import socket
7
+ import time
8
+ from ipaddress import ip_address
9
+ from dataclasses import dataclass
10
+ from threading import Lock
11
+ from typing import Optional
12
+ from urllib.parse import quote_plus, urljoin, urlparse
13
+
14
+ from knowledge_base.config import cfg
15
+
16
+ logger = logging.getLogger(__name__)
17
+
18
+ _CACHE: dict[str, tuple[float, str]] = {}
19
+ _CACHE_LOCK = Lock()
20
+ _DEFAULT_HEADERS = {
21
+ "User-Agent": "IGEL-KB-Agent/1.0 (+https://kb.igel.com; internal-test-automation)",
22
+ "Accept": "text/html,application/xhtml+xml;q=0.9,*/*;q=0.8",
23
+ "Accept-Language": "en-US,en;q=0.9,de;q=0.8",
24
+ }
25
+
26
+
27
+ @dataclass
28
+ class FetchResult:
29
+ url: str
30
+ html: str
31
+ status_code: int
32
+ from_cache: bool = False
33
+
34
+
35
+ def is_safe_fetch_target(url: str) -> bool:
36
+ """Block unsafe SSRF targets unless explicitly enabled by config."""
37
+ parsed = urlparse(url)
38
+ if parsed.scheme not in ("http", "https"):
39
+ return False
40
+
41
+ host = (parsed.hostname or "").strip().lower()
42
+ if not host:
43
+ return False
44
+
45
+ allow_private = bool(getattr(cfg, "WEB_ALLOW_PRIVATE_HOSTS", False))
46
+ if host in {"localhost"} or host.endswith(".localhost") or host.endswith(".local"):
47
+ return allow_private
48
+
49
+ try:
50
+ ip = ip_address(host)
51
+ except ValueError:
52
+ if allow_private:
53
+ return True
54
+ return _host_resolves_public_ips(host)
55
+
56
+ if allow_private:
57
+ return True
58
+ return _is_public_ip(ip)
59
+
60
+
61
+ def _is_public_ip(ip) -> bool:
62
+ return not (
63
+ ip.is_private
64
+ or ip.is_loopback
65
+ or ip.is_link_local
66
+ or ip.is_multicast
67
+ or ip.is_reserved
68
+ or ip.is_unspecified
69
+ )
70
+
71
+
72
+ def _host_resolves_public_ips(host: str) -> bool:
73
+ """Return True only when DNS answers resolve exclusively to public IPs."""
74
+ try:
75
+ infos = socket.getaddrinfo(host, None)
76
+ except Exception:
77
+ return False
78
+ if not infos:
79
+ return False
80
+ for info in infos:
81
+ addr = info[4][0]
82
+ try:
83
+ ip = ip_address(addr)
84
+ except ValueError:
85
+ return False
86
+ if not _is_public_ip(ip):
87
+ return False
88
+ return True
89
+
90
+
91
+ def _cache_get(url: str, ttl: int) -> Optional[str]:
92
+ with _CACHE_LOCK:
93
+ entry = _CACHE.get(url)
94
+ if not entry:
95
+ return None
96
+ ts, html = entry
97
+ if time.time() - ts > ttl:
98
+ _CACHE.pop(url, None)
99
+ return None
100
+ return html
101
+
102
+
103
+ def _cache_set(url: str, html: str) -> None:
104
+ with _CACHE_LOCK:
105
+ _CACHE[url] = (time.time(), html)
106
+ if len(_CACHE) > 256:
107
+ oldest = sorted(_CACHE.items(), key=lambda kv: kv[1][0])[:32]
108
+ for key, _ in oldest:
109
+ _CACHE.pop(key, None)
110
+
111
+
112
+ def strip_promoted_content(html: str, patterns: list[str] | tuple[str, ...] | None = None) -> str:
113
+ """Remove ads, sponsored blocks, and promotional elements from HTML."""
114
+ if not html:
115
+ return html
116
+ try:
117
+ from bs4 import BeautifulSoup
118
+ except ImportError:
119
+ return html
120
+
121
+ try:
122
+ soup = BeautifulSoup(html, "lxml")
123
+ except Exception:
124
+ soup = BeautifulSoup(html, "html.parser")
125
+ for tag in soup.find_all(["aside", "ins"]):
126
+ tag.decompose()
127
+
128
+ promo_patterns = list(patterns or getattr(cfg, "STRIP_PROMOTED_PATTERNS", ()))
129
+ for pattern in promo_patterns:
130
+ p = (pattern or "").strip().lower()
131
+ if not p:
132
+ continue
133
+ for el in soup.find_all(True):
134
+ classes = el.get("class") or []
135
+ class_str = " ".join(str(c) for c in classes).lower()
136
+ el_id = (el.get("id") or "").lower()
137
+ if p in class_str or p in el_id:
138
+ el.decompose()
139
+ return str(soup)
140
+
141
+
142
+ def fetch_url(
143
+ url: str,
144
+ *,
145
+ timeout: float = 20.0,
146
+ cache_ttl: int = 3600,
147
+ strip_promoted: bool = False,
148
+ ) -> FetchResult:
149
+ """Fetch a URL and return HTML using httpx with explicit safe-redirect handling."""
150
+ if not is_safe_fetch_target(url):
151
+ logger.warning("fetch_url blocked unsafe target: %s", url)
152
+ return FetchResult(url=url, html="", status_code=0)
153
+
154
+ cached = _cache_get(url, cache_ttl)
155
+ if cached is not None:
156
+ html = cached
157
+ if strip_promoted:
158
+ html = strip_promoted_content(html)
159
+ return FetchResult(url=url, html=html, status_code=200, from_cache=True)
160
+
161
+ html = ""
162
+ status = 0
163
+ try:
164
+ import httpx
165
+
166
+ with httpx.Client(follow_redirects=False, timeout=timeout, headers=_DEFAULT_HEADERS) as client:
167
+ current = url
168
+ max_redirects = 6
169
+ for _ in range(max_redirects + 1):
170
+ if not is_safe_fetch_target(current):
171
+ logger.warning("fetch_url blocked unsafe target: %s", current)
172
+ return FetchResult(url=url, html="", status_code=0)
173
+ resp = client.get(current)
174
+ if resp.is_redirect:
175
+ location = resp.headers.get("location")
176
+ if not location:
177
+ status = resp.status_code
178
+ html = resp.text
179
+ break
180
+ nxt = urljoin(current, location)
181
+ if not is_safe_fetch_target(nxt):
182
+ logger.warning("fetch_url blocked unsafe redirect target: %s -> %s", current, nxt)
183
+ return FetchResult(url=url, html="", status_code=0)
184
+ current = nxt
185
+ continue
186
+ status = resp.status_code
187
+ html = resp.text
188
+ break
189
+ else:
190
+ logger.warning("fetch_url exceeded redirect limit for %s", url)
191
+ return FetchResult(url=url, html="", status_code=0)
192
+ except ImportError:
193
+ logger.warning("httpx is required for fetch_url but is not installed")
194
+ return FetchResult(url=url, html="", status_code=0)
195
+ except Exception as e:
196
+ logger.warning("fetch_url failed for %s: %s", url, e)
197
+ return FetchResult(url=url, html="", status_code=status or 0)
198
+
199
+ if status == 200 and html:
200
+ if strip_promoted:
201
+ html = strip_promoted_content(html)
202
+ _cache_set(url, html)
203
+ return FetchResult(url=url, html=html, status_code=status)
204
+
205
+
206
+ def search_confluence_site(base_url: str, query: str, *, limit: int = 5) -> list[str]:
207
+ """
208
+ Search a Confluence/Scroll Sites KB (e.g. kb.igel.com) via REST API.
209
+ Returns list of page URLs.
210
+ """
211
+ base = base_url.rstrip("/")
212
+ cql = f'siteSearch ~ "{query.replace(chr(34), " ")}" AND type = page'
213
+ api = f"{base}/rest/api/content/search?cql={quote_plus(cql)}&limit={limit}"
214
+
215
+ try:
216
+ import httpx
217
+
218
+ with httpx.Client(follow_redirects=False, timeout=20.0, headers=_DEFAULT_HEADERS) as client:
219
+ current = api
220
+ data = None
221
+ max_redirects = 6
222
+ for _ in range(max_redirects + 1):
223
+ if not is_safe_fetch_target(current):
224
+ logger.warning("Confluence search blocked unsafe target: %s", current)
225
+ return _fallback_search_urls(base, query, limit=limit)
226
+ resp = client.get(current)
227
+ if resp.is_redirect:
228
+ location = resp.headers.get("location")
229
+ if not location:
230
+ break
231
+ nxt = urljoin(current, location)
232
+ if not is_safe_fetch_target(nxt):
233
+ logger.warning("Confluence search blocked unsafe redirect target: %s -> %s", current, nxt)
234
+ return _fallback_search_urls(base, query, limit=limit)
235
+ current = nxt
236
+ continue
237
+
238
+ if resp.status_code != 200:
239
+ logger.debug("Confluence search HTTP %s for %s", resp.status_code, current)
240
+ return _fallback_search_urls(base, query, limit=limit)
241
+ data = resp.json()
242
+ break
243
+
244
+ if data is None:
245
+ logger.debug("Confluence search redirect resolution failed for %s", api)
246
+ return _fallback_search_urls(base, query, limit=limit)
247
+ except Exception as e:
248
+ logger.debug("Confluence search failed: %s", e)
249
+ return _fallback_search_urls(base, query, limit=limit)
250
+
251
+ urls: list[str] = []
252
+ for item in data.get("results", []):
253
+ links = item.get("_links", {})
254
+ webui = links.get("webui") or links.get("tinyui")
255
+ if webui:
256
+ urls.append(urljoin(base + "/", webui.lstrip("/")))
257
+ elif item.get("id"):
258
+ urls.append(f"{base}/pages/viewpage.action?pageId={item['id']}")
259
+ return urls[:limit]
260
+
261
+
262
+ def _fallback_search_urls(base_url: str, query: str, *, limit: int) -> list[str]:
263
+ """Construct likely KB URLs from query keywords when REST search is unavailable."""
264
+ base = base_url.rstrip("/")
265
+ keywords = [w.lower() for w in query.split() if len(w) > 3][:4]
266
+ paths = [
267
+ "/en/igel-os-base-system",
268
+ "/en/universal-management-suite",
269
+ "/en/igel-cloud-gateway",
270
+ "/en/security-safety",
271
+ "/en/how-to-start-with-igel",
272
+ ]
273
+ urls: list[str] = []
274
+ for kw in keywords:
275
+ for path in paths:
276
+ if kw in path.replace("-", " "):
277
+ urls.append(f"{base}{path}")
278
+ if not urls:
279
+ urls.append(f"{base}/search?query={quote_plus(query)}")
280
+ return urls[:limit]
@@ -0,0 +1,8 @@
1
+ """Core scoring and precedence rules."""
2
+
3
+ from knowledge_base.core.confidence import flatten_confidence_for_api, score_generation
4
+
5
+ __all__ = [
6
+ "flatten_confidence_for_api",
7
+ "score_generation",
8
+ ]
@@ -0,0 +1,98 @@
1
+ """Query/embedding cache with optional Redis backend."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import hashlib
6
+ import json
7
+ import logging
8
+ import threading
9
+ import time
10
+ from functools import lru_cache
11
+
12
+ logger = logging.getLogger(__name__)
13
+
14
+ _redis_client = None
15
+ _redis_lock = threading.Lock()
16
+
17
+
18
+ def _redis():
19
+ global _redis_client
20
+ if _redis_client is not None:
21
+ return _redis_client
22
+ with _redis_lock:
23
+ if _redis_client is not None:
24
+ return _redis_client
25
+ try:
26
+ from knowledge_base.config import cfg
27
+ if not getattr(cfg, "REDIS_URL", ""):
28
+ return None
29
+ import redis
30
+ _redis_client = redis.from_url(cfg.REDIS_URL, decode_responses=True)
31
+ _redis_client.ping()
32
+ return _redis_client
33
+ except Exception as exc:
34
+ logger.debug("Redis unavailable: %s", exc)
35
+ return None
36
+
37
+
38
+ def _key(prefix: str, payload: str) -> str:
39
+ h = hashlib.sha256(payload.encode()).hexdigest()[:24]
40
+ return f"igel:kb:{prefix}:{h}"
41
+
42
+
43
+ def cache_get(prefix: str, payload: str) -> str | None:
44
+ r = _redis()
45
+ if not r:
46
+ return None
47
+ try:
48
+ return r.get(_key(prefix, payload))
49
+ except Exception:
50
+ return None
51
+
52
+
53
+ def cache_set(prefix: str, payload: str, value: str, ttl: int) -> None:
54
+ r = _redis()
55
+ if not r:
56
+ return
57
+ try:
58
+ r.setex(_key(prefix, payload), ttl, value)
59
+ except Exception:
60
+ pass
61
+
62
+
63
+ @lru_cache(maxsize=512)
64
+ def embed_query_cached(query: str) -> tuple[float, ...] | None:
65
+ """In-process LRU for embedding vectors (serialized as tuple)."""
66
+ return None
67
+
68
+
69
+ def store_embedding_cache(query: str, vector: list[float]) -> None:
70
+ embed_query_cached.cache_clear()
71
+ # LRU handled by caller storing in Redis for cross-process
72
+ payload = json.dumps(vector)
73
+ from knowledge_base.config import cfg
74
+ cache_set("embed", query.strip().lower(), payload, getattr(cfg, "EMBED_CACHE_TTL", 3600))
75
+
76
+
77
+ def get_embedding_cache(query: str) -> list[float] | None:
78
+ raw = cache_get("embed", query.strip().lower())
79
+ if not raw:
80
+ return None
81
+ try:
82
+ return json.loads(raw)
83
+ except Exception:
84
+ return None
85
+
86
+
87
+ def get_retrieval_cache(key: str) -> dict | None:
88
+ raw = cache_get("retrieve", key)
89
+ if not raw:
90
+ return None
91
+ try:
92
+ return json.loads(raw)
93
+ except Exception:
94
+ return None
95
+
96
+
97
+ def set_retrieval_cache(key: str, data: dict, ttl: int = 300) -> None:
98
+ cache_set("retrieve", key, json.dumps(data, default=str), ttl)
@@ -0,0 +1,33 @@
1
+ """MCP circuit breaker for external connectors."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import time
6
+ import threading
7
+
8
+ _lock = threading.Lock()
9
+ _failures: dict[str, int] = {}
10
+ _disabled_until: dict[str, float] = {}
11
+
12
+
13
+ def record_failure(source: str, *, threshold: int = 3, cooldown_sec: int = 300) -> None:
14
+ with _lock:
15
+ _failures[source] = _failures.get(source, 0) + 1
16
+ if _failures[source] >= threshold:
17
+ _disabled_until[source] = time.time() + cooldown_sec
18
+
19
+
20
+ def record_success(source: str) -> None:
21
+ with _lock:
22
+ _failures[source] = 0
23
+ _disabled_until.pop(source, None)
24
+
25
+
26
+ def is_open(source: str) -> bool:
27
+ with _lock:
28
+ until = _disabled_until.get(source)
29
+ if until and time.time() < until:
30
+ return True
31
+ if until:
32
+ _disabled_until.pop(source, None)
33
+ return False