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,165 @@
1
+ """Confluence connector — REST API sync + KG ingest."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import logging
6
+ from datetime import datetime
7
+ from urllib.parse import unquote_plus
8
+
9
+ import httpx
10
+
11
+ from knowledge_base.config import cfg
12
+ from knowledge_base.connectors.base import BaseConnector
13
+
14
+ logger = logging.getLogger(__name__)
15
+
16
+
17
+ class ConfluenceConnector(BaseConnector):
18
+ name = "confluence"
19
+ enabled = cfg.CONFLUENCE_ENABLED
20
+
21
+ def connect(self) -> bool:
22
+ if not self.enabled:
23
+ return False
24
+ if not cfg.CONFLUENCE_URL:
25
+ logger.warning("CONFLUENCE_URL not set")
26
+ return False
27
+ auth = self._auth()
28
+ if auth and auth[0] and auth[1]:
29
+ return True
30
+ logger.warning("Confluence credentials missing (set CONFLUENCE_* or JIRA_* auth vars)")
31
+ return False
32
+
33
+ def _auth(self):
34
+ """Return httpx auth tuple, preferring dedicated Confluence credentials."""
35
+ if getattr(cfg, "CONFLUENCE_API_TOKEN", ""):
36
+ return (cfg.CONFLUENCE_USERNAME, cfg.CONFLUENCE_API_TOKEN)
37
+ if cfg.JIRA_API_TOKEN:
38
+ return (cfg.JIRA_USERNAME, cfg.JIRA_API_TOKEN)
39
+ return None
40
+
41
+ def search(self, query: str, limit: int = 5, timeout: float | None = None) -> list[str]:
42
+ if not self.connect():
43
+ return []
44
+ client_timeout = min(15.0, timeout) if timeout and timeout > 0 else 15.0
45
+ try:
46
+ with httpx.Client(timeout=client_timeout) as client:
47
+ resp = client.get(
48
+ f"{cfg.CONFLUENCE_URL.rstrip('/')}/rest/api/content/search",
49
+ params={"cql": f'text ~ "{query.replace(chr(34), "")}"', "limit": limit},
50
+ auth=self._auth(),
51
+ )
52
+ resp.raise_for_status()
53
+ results = resp.json().get("results") or []
54
+ base = cfg.CONFLUENCE_URL.rstrip("/")
55
+ return [f"{base}{r.get('_links', {}).get('webui', '')}" for r in results]
56
+ except Exception as exc:
57
+ logger.warning("Confluence search failed: %s", exc)
58
+ return []
59
+
60
+ def fetch_page(self, url: str, timeout: float | None = None) -> dict | None:
61
+ """Fetch a single Confluence page by its webui URL and return a content dict."""
62
+ if not self.connect():
63
+ return None
64
+ import re
65
+ base = cfg.CONFLUENCE_URL.rstrip("/")
66
+ auth = self._auth()
67
+ client_timeout = min(15.0, timeout) if timeout and timeout > 0 else 15.0
68
+ page_id: str | None = None
69
+ # Cloud: /wiki/spaces/.../pages/123456/...
70
+ m = re.search(r"/pages/(\d+)", url)
71
+ if m:
72
+ page_id = m.group(1)
73
+ # Server/DC: ?pageId=123456
74
+ if not page_id:
75
+ m = re.search(r"pageId=(\d+)", url)
76
+ if m:
77
+ page_id = m.group(1)
78
+ try:
79
+ with httpx.Client(timeout=client_timeout) as client:
80
+ if not page_id:
81
+ # Fall back: search by title derived from URL tail
82
+ title = unquote_plus(url.rstrip("/").rsplit("/", 1)[-1])
83
+ r = client.get(
84
+ f"{base}/rest/api/content/search",
85
+ params={"cql": f'title = "{title.replace(chr(34), "")}"', "limit": 1, "expand": "body.storage"},
86
+ auth=auth,
87
+ )
88
+ r.raise_for_status()
89
+ results = r.json().get("results") or []
90
+ if not results:
91
+ return None
92
+ page_id = results[0]["id"]
93
+ resp = client.get(
94
+ f"{base}/rest/api/content/{page_id}",
95
+ params={"expand": "body.storage"},
96
+ auth=auth,
97
+ )
98
+ resp.raise_for_status()
99
+ page = resp.json()
100
+ body = (page.get("body") or {}).get("storage") or {}
101
+ return {
102
+ "title": page.get("title") or url,
103
+ "url": url,
104
+ "raw_text": (body.get("value") or "")[:8000],
105
+ "source_type": "confluence",
106
+ }
107
+ except Exception as exc:
108
+ logger.warning("Confluence fetch_page failed for %s: %s", url, exc)
109
+ return None
110
+
111
+ def sync(self, since: datetime | None = None) -> list[dict]:
112
+ if not self.connect():
113
+ return []
114
+ cql = "type=page ORDER BY lastModified DESC"
115
+ if since:
116
+ cql = f"type=page AND lastModified >= '{since.strftime('%Y-%m-%d')}' ORDER BY lastModified DESC"
117
+ items: list[dict] = []
118
+ try:
119
+ with httpx.Client(timeout=30.0) as client:
120
+ resp = client.get(
121
+ f"{cfg.CONFLUENCE_URL.rstrip('/')}/rest/api/content/search",
122
+ params={"cql": cql, "limit": 30, "expand": "body.storage"},
123
+ auth=self._auth(),
124
+ )
125
+ resp.raise_for_status()
126
+ base = cfg.CONFLUENCE_URL.rstrip("/")
127
+ for page in resp.json().get("results") or []:
128
+ body = (page.get("body") or {}).get("storage") or {}
129
+ items.append({
130
+ "title": page.get("title"),
131
+ "url": f"{base}{page.get('_links', {}).get('webui', '')}",
132
+ "raw_text": (body.get("value") or "")[:8000],
133
+ "source_type": "confluence",
134
+ })
135
+ except Exception as exc:
136
+ logger.warning("Confluence sync failed: %s", exc)
137
+ raise
138
+ return items
139
+
140
+ def ingest_to_kg(self, items: list[dict]) -> int:
141
+ if not items:
142
+ return 0
143
+ from knowledge_base.db.client import get_conn
144
+ import json
145
+ count = 0
146
+ with get_conn() as conn:
147
+ with conn.cursor() as cur:
148
+ for item in items:
149
+ title = item.get("title") or item.get("url")
150
+ if not title:
151
+ continue
152
+ cur.execute(
153
+ """
154
+ INSERT INTO knowledge_graph_nodes (node_type, value, normalized, metadata)
155
+ VALUES ('feature', %s, %s, %s::jsonb)
156
+ ON CONFLICT (node_type, normalized) DO UPDATE SET metadata = EXCLUDED.metadata
157
+ """,
158
+ (title[:4000], title.lower()[:2000], json.dumps(item)),
159
+ )
160
+ count += 1
161
+ return count
162
+
163
+ def ingest_to_rag(self, items: list[dict]) -> int:
164
+ from knowledge_base.ingestion.web_ingest import ingest_web_items
165
+ return ingest_web_items(items, source_label="confluence")
@@ -0,0 +1,174 @@
1
+ """JIRA connector — REST API sync + KG ingest."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import logging
6
+ from datetime import datetime
7
+
8
+ import httpx
9
+
10
+ from knowledge_base.config import cfg
11
+ from knowledge_base.connectors.base import BaseConnector
12
+
13
+ logger = logging.getLogger(__name__)
14
+
15
+
16
+ def _jira_text(value: object) -> str:
17
+ """Normalize Jira field payloads (plain text or ADF JSON) into readable text."""
18
+ if value is None:
19
+ return ""
20
+ if isinstance(value, str):
21
+ return value
22
+ if isinstance(value, (int, float, bool)):
23
+ return str(value)
24
+ if isinstance(value, list):
25
+ parts = [_jira_text(v) for v in value]
26
+ return "\n".join(p for p in parts if p)
27
+ if isinstance(value, dict):
28
+ txt = value.get("text")
29
+ if isinstance(txt, str) and txt.strip():
30
+ return txt
31
+ content = value.get("content")
32
+ if isinstance(content, list):
33
+ parts = [_jira_text(v) for v in content]
34
+ return "\n".join(p for p in parts if p)
35
+ val = value.get("value")
36
+ if isinstance(val, str):
37
+ return val
38
+ return ""
39
+
40
+
41
+ class JiraConnector(BaseConnector):
42
+ name = "jira"
43
+ enabled = cfg.JIRA_ENABLED
44
+
45
+ def connect(self) -> bool:
46
+ if not self.enabled:
47
+ return False
48
+ if not cfg.JIRA_URL or not cfg.JIRA_USERNAME or not cfg.JIRA_API_TOKEN:
49
+ logger.warning("JIRA credentials missing")
50
+ return False
51
+ return True
52
+
53
+ def search(self, query: str, limit: int = 5, timeout: float | None = None) -> list[str]:
54
+ if not self.connect():
55
+ return []
56
+ jql = f'text ~ "{query.replace(chr(34), "")}" ORDER BY updated DESC'
57
+ client_timeout = min(15.0, timeout) if timeout and timeout > 0 else 15.0
58
+ try:
59
+ with httpx.Client(timeout=client_timeout) as client:
60
+ resp = client.get(
61
+ f"{cfg.JIRA_URL.rstrip('/')}/rest/api/2/search",
62
+ params={"jql": jql, "maxResults": limit, "fields": "summary"},
63
+ auth=(cfg.JIRA_USERNAME, cfg.JIRA_API_TOKEN),
64
+ )
65
+ resp.raise_for_status()
66
+ issues = resp.json().get("issues") or []
67
+ return [
68
+ f"{cfg.JIRA_URL.rstrip('/')}/browse/{i['key']}"
69
+ for i in issues if i.get("key")
70
+ ]
71
+ except Exception as exc:
72
+ logger.warning("JIRA search failed: %s", exc)
73
+ return []
74
+
75
+ def sync(self, since: datetime | None = None) -> list[dict]:
76
+ if not self.connect():
77
+ return []
78
+ jql = "ORDER BY updated DESC"
79
+ if since:
80
+ jql = f'updated >= "{since.strftime("%Y-%m-%d %H:%M")}" ORDER BY updated DESC'
81
+ items: list[dict] = []
82
+ try:
83
+ with httpx.Client(timeout=30.0) as client:
84
+ resp = client.get(
85
+ f"{cfg.JIRA_URL.rstrip('/')}/rest/api/2/search",
86
+ params={"jql": jql, "maxResults": 50, "fields": "summary,description,status,components,fixVersions,issuetype"},
87
+ auth=(cfg.JIRA_USERNAME, cfg.JIRA_API_TOKEN),
88
+ )
89
+ resp.raise_for_status()
90
+ for issue in resp.json().get("issues") or []:
91
+ fields = issue.get("fields") or {}
92
+ items.append({
93
+ "key": issue.get("key"),
94
+ "title": fields.get("summary") or issue.get("key"),
95
+ "url": f"{cfg.JIRA_URL.rstrip('/')}/browse/{issue.get('key')}",
96
+ "content": _jira_text(fields.get("description"))[:8000],
97
+ "status": (fields.get("status") or {}).get("name"),
98
+ "issue_type": (fields.get("issuetype") or {}).get("name"),
99
+ "source_type": "jira",
100
+ })
101
+ except Exception as exc:
102
+ logger.warning("JIRA sync failed: %s", exc)
103
+ raise
104
+ return items
105
+
106
+ def ingest_to_kg(self, items: list[dict]) -> int:
107
+ if not items:
108
+ return 0
109
+ from knowledge_base.db.client import get_conn
110
+ count = 0
111
+ with get_conn() as conn:
112
+ with conn.cursor() as cur:
113
+ for item in items:
114
+ key = item.get("key") or item.get("title")
115
+ if not key:
116
+ continue
117
+ cur.execute(
118
+ """
119
+ INSERT INTO knowledge_graph_nodes (node_type, value, normalized, metadata)
120
+ VALUES ('defect', %s, %s, %s::jsonb)
121
+ ON CONFLICT (node_type, normalized) DO UPDATE SET metadata = EXCLUDED.metadata
122
+ """,
123
+ (str(key), str(key).lower(), __import__("json").dumps(item)),
124
+ )
125
+ count += 1
126
+ return count
127
+
128
+ def fetch_page(self, url: str, timeout: float | None = None) -> dict | None:
129
+ """Fetch a single Jira issue by its browse URL and return a content dict."""
130
+ if not self.connect():
131
+ return None
132
+ # Extract issue key from URL tail (e.g. .../browse/PROJ-123)
133
+ key = url.rstrip("/").rsplit("/", 1)[-1]
134
+ if not key:
135
+ return None
136
+ client_timeout = min(15.0, timeout) if timeout and timeout > 0 else 15.0
137
+ try:
138
+ with httpx.Client(timeout=client_timeout) as client:
139
+ resp = client.get(
140
+ f"{cfg.JIRA_URL.rstrip('/')}/rest/api/2/issue/{key}",
141
+ params={"fields": "summary,description,status,issuetype,comment"},
142
+ auth=(cfg.JIRA_USERNAME, cfg.JIRA_API_TOKEN),
143
+ )
144
+ resp.raise_for_status()
145
+ data = resp.json()
146
+ fields = data.get("fields") or {}
147
+ description = _jira_text(fields.get("description"))
148
+ comments = []
149
+ for c in ((fields.get("comment") or {}).get("comments") or [])[:3]:
150
+ body = _jira_text(c.get("body"))
151
+ if body.strip():
152
+ comments.append(body)
153
+ comments_text = "\n".join(comments)
154
+ return {
155
+ "title": fields.get("summary") or key,
156
+ "url": url,
157
+ "raw_text": f"{description}\n\n{comments_text}".strip()[:8000],
158
+ "source_type": "jira",
159
+ }
160
+ except Exception as exc:
161
+ logger.warning("JIRA fetch_page failed for %s: %s", url, exc)
162
+ return None
163
+
164
+ def ingest_to_rag(self, items: list[dict]) -> int:
165
+ from knowledge_base.ingestion.web_ingest import ingest_web_items
166
+ web_items = []
167
+ for item in items:
168
+ web_items.append({
169
+ "url": item.get("url", ""),
170
+ "title": item.get("title", ""),
171
+ "raw_text": item.get("content", ""),
172
+ "source_type": "jira",
173
+ })
174
+ return ingest_web_items(web_items, source_label="jira")
@@ -0,0 +1,145 @@
1
+ """IGEL Knowledge Base (https://kb.igel.com) connector — crawl + RAG ingest."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import logging
6
+ from datetime import datetime
7
+ from typing import Any
8
+ from urllib.parse import urlparse
9
+
10
+ from knowledge_base.config import cfg
11
+ from knowledge_base.connectors.base import BaseConnector
12
+ from knowledge_base.connectors.web_fetcher import fetch_url, search_confluence_site
13
+ from knowledge_base.parsers.html_parser import parse_html_page
14
+
15
+ logger = logging.getLogger(__name__)
16
+
17
+ # Map common IGEL abbreviations / topics → kb.igel.com section paths
18
+ _KB_TOPIC_PATHS: dict[str, str] = {
19
+ "ums": "/en/universal-management-suite/current",
20
+ "universal management suite": "/en/universal-management-suite/current",
21
+ "sso": "/en/how-to-configure-single-sign-on-sso-on-igel-os-12/current",
22
+ "single sign-on": "/en/how-to-configure-single-sign-on-sso-on-igel-os-12/current",
23
+ "entra": "/en/how-to-configure-single-sign-on-sso-on-igel-os-12/current",
24
+ "azure ad": "/en/how-to-configure-single-sign-on-sso-on-igel-os-12/current",
25
+ "okta": "/en/how-to-configure-single-sign-on-sso-on-igel-os-12/current",
26
+ "igel os": "/en/igel-os-base-system/current",
27
+ "icg": "/en/igel-cloud-gateway/current",
28
+ "cloud gateway": "/en/igel-cloud-gateway/current",
29
+ "pxe": "/en/how-to-deploy-igel-os-12-with-pxe/current",
30
+ "active directory": "/en/active-directory-kerberos-configuration-in-igel-os-12/current",
31
+ "kerberos": "/en/active-directory-kerberos-configuration-in-igel-os-12/current",
32
+ "profile": "/en/universal-management-suite/current",
33
+ "factory reset": "/en/igel-os-base-system/current",
34
+ }
35
+
36
+
37
+ class KbIgelConnector(BaseConnector):
38
+ """Fetch and ingest pages from the official IGEL Knowledge Base."""
39
+
40
+ name = "kb_igel"
41
+
42
+ def __init__(self) -> None:
43
+ self.base_url = getattr(cfg, "KB_IGEL_BASE_URL", "https://kb.igel.com").rstrip("/")
44
+ self.enabled = getattr(cfg, "KB_IGEL_ENABLED", True)
45
+
46
+ def connect(self) -> bool:
47
+ result = fetch_url(self.base_url, cache_ttl=300)
48
+ return result.status_code == 200 and len(result.html) > 100
49
+
50
+ def search(self, query: str, *, limit: int | None = None, timeout: float | None = None) -> list[str]:
51
+ max_pages = limit or getattr(cfg, "WEB_MAX_PAGES", 3)
52
+ q_lower = query.lower()
53
+ urls: list[str] = []
54
+
55
+ for topic, path in _KB_TOPIC_PATHS.items():
56
+ if topic in q_lower:
57
+ full = f"{self.base_url}{path}"
58
+ if full not in urls:
59
+ urls.append(full)
60
+ if len(urls) >= max_pages:
61
+ return urls[:max_pages]
62
+
63
+ rest = search_confluence_site(self.base_url, query, limit=max_pages)
64
+ for u in rest:
65
+ if u not in urls and "/search?" not in u:
66
+ urls.append(u)
67
+ if len(urls) >= max_pages:
68
+ break
69
+ return urls[:max_pages]
70
+
71
+ def fetch_page(self, url: str, timeout: float | None = None) -> dict[str, Any] | None:
72
+ if not url.startswith("http"):
73
+ url = f"{self.base_url}/{url.lstrip('/')}"
74
+ host = urlparse(url).netloc
75
+ host_l = host.lower()
76
+ if host_l != "igel.com" and not host_l.endswith(".igel.com"):
77
+ logger.warning("kb_igel: refusing non-IGEL URL %s", url)
78
+ return None
79
+
80
+ fetch_timeout = min(20.0, timeout) if timeout and timeout > 0 else 20.0
81
+ result = fetch_url(
82
+ url,
83
+ cache_ttl=getattr(cfg, "WEB_CACHE_TTL_SECONDS", 3600),
84
+ timeout=fetch_timeout,
85
+ )
86
+ if result.status_code != 200 or not result.html:
87
+ return None
88
+
89
+ parsed = parse_html_page(result.html, url, source_type="kb_web")
90
+ return {
91
+ "url": url,
92
+ "title": parsed.title,
93
+ "sections": parsed.sections,
94
+ "raw_text": parsed.raw_text,
95
+ "source_type": "kb_web",
96
+ "product": "IGEL",
97
+ }
98
+
99
+ def sync(self, since: datetime | None = None) -> list[dict]:
100
+ """Sync seed pages from IGEL KB navigation (batch ingest)."""
101
+ seed_paths = [
102
+ "/en/igel-os-base-system",
103
+ "/en/universal-management-suite",
104
+ "/en/igel-cloud-gateway",
105
+ "/en/security-safety",
106
+ "/en/how-to-start-with-igel",
107
+ "/en/igel-apps",
108
+ ]
109
+ items: list[dict] = []
110
+ for path in seed_paths:
111
+ page = self.fetch_page(f"{self.base_url}{path}")
112
+ if page:
113
+ items.append(page)
114
+ logger.info("kb_igel sync: fetched %d seed pages", len(items))
115
+ return items
116
+
117
+ def ingest_to_kg(self, items: list[dict]) -> int:
118
+ if not items:
119
+ return 0
120
+ from knowledge_base.db.client import get_conn
121
+ import json
122
+ count = 0
123
+ with get_conn() as conn:
124
+ with conn.cursor() as cur:
125
+ for item in items:
126
+ title = item.get("title") or item.get("url", "")
127
+ product = item.get("product") or "IGEL"
128
+ for entity in (title, product):
129
+ if not entity:
130
+ continue
131
+ cur.execute(
132
+ """
133
+ INSERT INTO knowledge_graph_nodes (node_type, value, normalized, metadata)
134
+ VALUES ('feature', %s, %s, %s::jsonb)
135
+ ON CONFLICT (node_type, normalized) DO NOTHING
136
+ """,
137
+ (entity[:4000], entity.lower()[:2000], json.dumps({"source": "kb_igel"})),
138
+ )
139
+ count += 1
140
+ return count
141
+
142
+ def ingest_to_rag(self, items: list[dict]) -> int:
143
+ from knowledge_base.ingestion.web_ingest import ingest_web_items
144
+
145
+ return ingest_web_items(items, source_label="kb.igel.com")
@@ -0,0 +1,50 @@
1
+ """Playwright-based page fetcher — Crawl4AI-compatible fallback for Python 3.8."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import logging
6
+ import re
7
+ from html import unescape
8
+
9
+ logger = logging.getLogger(__name__)
10
+
11
+
12
+ def _html_to_text(html: str) -> str:
13
+ text = re.sub(r"(?is)<(script|style|nav|footer|header|aside|ins)[^>]*>.*?</\1>", " ", html)
14
+ text = re.sub(r"(?is)<br\s*/?>", "\n", text)
15
+ text = re.sub(r"(?is)</?(p|div|li|h[1-6]|tr|td|th|article|section)[^>]*>", "\n", text)
16
+ text = re.sub(r"<[^>]+>", " ", text)
17
+ text = unescape(text)
18
+ text = re.sub(r"[ \t]+\n", "\n", text)
19
+ text = re.sub(r"\n{3,}", "\n\n", text)
20
+ return text.strip()
21
+
22
+
23
+ def fetch_page_playwright(url: str, *, timeout_ms: int = 30000) -> tuple[str, str]:
24
+ """
25
+ Fetch URL with headless Chromium via Playwright.
26
+ Returns (title, markdown-ish plain text).
27
+ """
28
+ from playwright.sync_api import sync_playwright
29
+
30
+ with sync_playwright() as p:
31
+ browser = p.chromium.launch(headless=True)
32
+ try:
33
+ page = browser.new_page()
34
+ page.goto(url, wait_until="domcontentloaded", timeout=timeout_ms)
35
+ page.wait_for_timeout(500)
36
+ title = page.title() or url
37
+ html = page.content()
38
+ finally:
39
+ browser.close()
40
+
41
+ return title[:300], _html_to_text(html)
42
+
43
+
44
+ def playwright_available() -> bool:
45
+ try:
46
+ import playwright # noqa: F401
47
+
48
+ return True
49
+ except ImportError:
50
+ return False