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,354 @@
1
+ """
2
+ Source Precedence Rules for IGEL KB System
3
+ ===========================================
4
+ Enforces trust hierarchy when conflicting information appears in multiple sources.
5
+
6
+ Precedence Order (Highest → Lowest):
7
+ 1. Gold TC (manual, reviewed, canonical test cases)
8
+ 2. Jira TC export (test cases from Jira, may be outdated)
9
+ 3. Confluence (team documentation, wiki pages)
10
+ 4. Official IGEL docs (reference manuals, admin guides)
11
+ 5. Screenshots (UI images with OCR)
12
+ 6. Execution history (past test runs, logs)
13
+
14
+ Rules:
15
+ - Higher precedence sources override lower ones for conflicting info
16
+ - When merging: use highest-precedence source for each fact
17
+ - Surface source attribution in output (which doc provided which info)
18
+ - Flag conflicts for human review if precedence gap is small
19
+ """
20
+ from __future__ import annotations
21
+
22
+ from dataclasses import dataclass
23
+ from typing import Any
24
+
25
+
26
+ @dataclass
27
+ class SourceType:
28
+ """Source type with precedence rank."""
29
+ name: str
30
+ rank: int # Lower number = higher precedence
31
+ description: str
32
+
33
+ def __lt__(self, other: SourceType) -> bool:
34
+ return self.rank < other.rank
35
+
36
+ def __le__(self, other: SourceType) -> bool:
37
+ return self.rank <= other.rank
38
+
39
+
40
+ # Define source types (precedence order)
41
+ SOURCE_TYPES = {
42
+ "gold_tc": SourceType(
43
+ name="gold_tc",
44
+ rank=1,
45
+ description="Gold Test Case (manual, reviewed, canonical)",
46
+ ),
47
+ "jira_tc": SourceType(
48
+ name="jira_tc",
49
+ rank=2,
50
+ description="Jira Test Case Export",
51
+ ),
52
+ "confluence": SourceType(
53
+ name="confluence",
54
+ rank=3,
55
+ description="Confluence Documentation",
56
+ ),
57
+ "igel_docs": SourceType(
58
+ name="igel_docs",
59
+ rank=4,
60
+ description="Official IGEL Documentation",
61
+ ),
62
+ "kb_web": SourceType(
63
+ name="kb_web",
64
+ rank=4,
65
+ description="IGEL Knowledge Base (kb.igel.com)",
66
+ ),
67
+ "community": SourceType(
68
+ name="community",
69
+ rank=5,
70
+ description="IGEL Community / Forum Posts",
71
+ ),
72
+ "screenshot": SourceType(
73
+ name="screenshot",
74
+ rank=6,
75
+ description="Screenshot with OCR",
76
+ ),
77
+ "execution_history": SourceType(
78
+ name="execution_history",
79
+ rank=7,
80
+ description="Test Execution History",
81
+ ),
82
+ }
83
+
84
+
85
+ def infer_source_type(chunk_metadata: dict) -> SourceType:
86
+ """
87
+ Infer source type from chunk metadata.
88
+
89
+ Looks at:
90
+ - metadata.product (e.g., "IGEL-GOLD-TC", "IGEL-JIRA", "IGEL-CONFLUENCE")
91
+ - metadata.file_path
92
+ - metadata.source_type (if explicitly tagged)
93
+ """
94
+ # Check explicit source_type tag
95
+ if "source_type" in chunk_metadata:
96
+ source_name = chunk_metadata["source_type"].lower()
97
+ if source_name in SOURCE_TYPES:
98
+ return SOURCE_TYPES[source_name]
99
+ product = chunk_metadata.get("product", "").lower()
100
+ if "gold" in product or "canonical" in product:
101
+ return SOURCE_TYPES["gold_tc"]
102
+ elif "jira" in product:
103
+ return SOURCE_TYPES["jira_tc"]
104
+ elif "confluence" in product or "wiki" in product:
105
+ return SOURCE_TYPES["confluence"]
106
+ elif "igel" in product or "docs" in product or "manual" in product:
107
+ return SOURCE_TYPES["igel_docs"]
108
+ elif "kb_web" in product or "kb.igel" in product:
109
+ return SOURCE_TYPES["kb_web"]
110
+ elif "community" in product or "forum" in product:
111
+ return SOURCE_TYPES["community"]
112
+ elif "screenshot" in product or "image" in product:
113
+ return SOURCE_TYPES["screenshot"]
114
+ elif "execution" in product or "history" in product or "log" in product:
115
+ return SOURCE_TYPES["execution_history"]
116
+
117
+ # Check file path
118
+ file_path = chunk_metadata.get("file_path", "").lower()
119
+ if "gold" in file_path:
120
+ return SOURCE_TYPES["gold_tc"]
121
+ elif "jira" in file_path:
122
+ return SOURCE_TYPES["jira_tc"]
123
+ elif "confluence" in file_path or "wiki" in file_path:
124
+ return SOURCE_TYPES["confluence"]
125
+ elif "manual" in file_path or "guide" in file_path or "reference" in file_path:
126
+ return SOURCE_TYPES["igel_docs"]
127
+ elif "kb.igel" in file_path or file_path.startswith("web:kb"):
128
+ return SOURCE_TYPES["kb_web"]
129
+ elif "community" in file_path or file_path.startswith("web:community"):
130
+ return SOURCE_TYPES["community"]
131
+ elif "screenshot" in file_path or "image" in file_path:
132
+ return SOURCE_TYPES["screenshot"]
133
+ elif "execution" in file_path or "history" in file_path:
134
+ return SOURCE_TYPES["execution_history"]
135
+
136
+ # Default to IGEL docs (safest middle ground)
137
+ return SOURCE_TYPES["igel_docs"]
138
+
139
+
140
+ @dataclass
141
+ class RankedChunk:
142
+ """Chunk with source precedence metadata."""
143
+ chunk: Any # Original chunk object
144
+ source_type: SourceType
145
+ retrieval_score: float # RRF or rerank score
146
+ content: str
147
+ metadata: dict
148
+
149
+ @property
150
+ def composite_rank(self) -> tuple[int, float]:
151
+ """
152
+ Composite ranking key: (source_rank, -retrieval_score).
153
+ Sorts by source precedence first, then retrieval score (higher better).
154
+ """
155
+ return (self.source_type.rank, -self.retrieval_score)
156
+
157
+
158
+ def rank_chunks_by_precedence(
159
+ chunks: list,
160
+ retrieval_scores: dict | None = None,
161
+ ) -> list[RankedChunk]:
162
+ """
163
+ Rank chunks by source precedence, then retrieval score.
164
+
165
+ Args:
166
+ chunks: List of chunk objects (must have .metadata attribute)
167
+ retrieval_scores: Optional dict mapping chunk ID to retrieval score
168
+
169
+ Returns:
170
+ List of RankedChunk objects, sorted by precedence
171
+ """
172
+ ranked = []
173
+
174
+ for chunk in chunks:
175
+ # Extract metadata
176
+ metadata = getattr(chunk, 'metadata', {})
177
+ if isinstance(metadata, str):
178
+ import json
179
+ try:
180
+ metadata = json.loads(metadata)
181
+ except:
182
+ metadata = {}
183
+
184
+ # Infer source type
185
+ source_type = infer_source_type(metadata)
186
+
187
+ # Get retrieval score
188
+ chunk_id = getattr(chunk, 'id', None)
189
+ retrieval_score = 0.0
190
+ if retrieval_scores and chunk_id:
191
+ retrieval_score = retrieval_scores.get(str(chunk_id), 0.0)
192
+ elif hasattr(chunk, 'rrf_score'):
193
+ retrieval_score = chunk.rrf_score
194
+ elif hasattr(chunk, 'rerank_score'):
195
+ retrieval_score = chunk.rerank_score
196
+
197
+ # Get content
198
+ content = getattr(chunk, 'content', '') or getattr(chunk, 'parent_content', '') or getattr(chunk, 'child_content', '')
199
+
200
+ ranked.append(RankedChunk(
201
+ chunk=chunk,
202
+ source_type=source_type,
203
+ retrieval_score=retrieval_score,
204
+ content=content,
205
+ metadata=metadata,
206
+ ))
207
+
208
+ # Sort by composite rank (source precedence, then retrieval score)
209
+ ranked.sort(key=lambda rc: rc.composite_rank)
210
+
211
+ return ranked
212
+
213
+
214
+ def apply_precedence_filter(
215
+ chunks: list,
216
+ top_k: int = 10,
217
+ retrieval_scores: dict | None = None,
218
+ ) -> tuple[list, dict]:
219
+ """
220
+ Apply source precedence to filter and rank chunks.
221
+
222
+ Strategy:
223
+ 1. Rank all chunks by precedence
224
+ 2. Select top K chunks
225
+ 3. Within each precedence tier, prefer higher retrieval scores
226
+ 4. Return filtered list + attribution metadata
227
+
228
+ Returns:
229
+ (filtered_chunks, attribution_metadata)
230
+
231
+ attribution_metadata: dict with source breakdown
232
+ """
233
+ # Rank chunks
234
+ ranked = rank_chunks_by_precedence(chunks, retrieval_scores)
235
+
236
+ # Take top K
237
+ top_chunks = ranked[:top_k]
238
+
239
+ # Build attribution metadata
240
+ source_counts = {}
241
+ for rc in top_chunks:
242
+ source_name = rc.source_type.name
243
+ source_counts[source_name] = source_counts.get(source_name, 0) + 1
244
+
245
+ attribution = {
246
+ "total_chunks": len(top_chunks),
247
+ "source_breakdown": source_counts,
248
+ "highest_precedence": top_chunks[0].source_type.name if top_chunks else None,
249
+ "precedence_order": [rc.source_type.name for rc in top_chunks],
250
+ }
251
+
252
+ # Extract original chunks
253
+ filtered = [rc.chunk for rc in top_chunks]
254
+
255
+ return filtered, attribution
256
+
257
+
258
+ def resolve_conflict(
259
+ sources: list[tuple[SourceType, Any]],
260
+ ) -> tuple[SourceType, Any] | None:
261
+ """
262
+ Resolve conflicting information by source precedence (lowest rank wins).
263
+
264
+ Args:
265
+ sources: List of (SourceType, value) tuples from different sources.
266
+
267
+ Returns:
268
+ Winning (SourceType, value) or None if empty.
269
+ """
270
+ if not sources:
271
+ return None
272
+ return min(sources, key=lambda pair: pair[0].rank)
273
+
274
+
275
+ def detect_precedence_conflicts(
276
+ chunks: list,
277
+ conflict_threshold: int = 2,
278
+ ) -> list[dict]:
279
+ """
280
+ Detect conflicts where similar information appears in sources
281
+ with different precedence levels.
282
+
283
+ Args:
284
+ chunks: List of chunks
285
+ conflict_threshold: Max precedence rank gap to consider conflict
286
+
287
+ Returns:
288
+ List of conflict reports (for human review)
289
+ """
290
+ ranked = rank_chunks_by_precedence(chunks)
291
+ conflicts = []
292
+
293
+ # Group by similar content (simplified: just check for registry keys/UI paths)
294
+ import re
295
+
296
+ # Extract entities from each chunk
297
+ for i, rc1 in enumerate(ranked):
298
+ entities_1 = set(re.findall(r'app\.[a-z0-9_.]+', rc1.content.lower()))
299
+ entities_1.update(re.findall(r'TC Setup[^>\n]*(?:>[^>\n]*)*', rc1.content, re.I))
300
+
301
+ for rc2 in ranked[i+1:]:
302
+ # Check precedence gap
303
+ rank_gap = abs(rc1.source_type.rank - rc2.source_type.rank)
304
+ if rank_gap > conflict_threshold:
305
+ continue # Too far apart in precedence
306
+
307
+ # Check for overlapping entities
308
+ entities_2 = set(re.findall(r'app\.[a-z0-9_.]+', rc2.content.lower()))
309
+ entities_2.update(re.findall(r'TC Setup[^>\n]*(?:>[^>\n]*)*', rc2.content, re.I))
310
+
311
+ overlap = entities_1 & entities_2
312
+ if overlap:
313
+ conflicts.append({
314
+ "entity": list(overlap)[0] if overlap else "unknown",
315
+ "source_1": rc1.source_type.name,
316
+ "source_2": rc2.source_type.name,
317
+ "rank_gap": rank_gap,
318
+ "chunk_1_id": getattr(rc1.chunk, 'id', None),
319
+ "chunk_2_id": getattr(rc2.chunk, 'id', None),
320
+ "recommendation": f"Prefer {rc1.source_type.name} over {rc2.source_type.name}",
321
+ })
322
+
323
+ return conflicts
324
+
325
+
326
+ def format_source_attribution(attribution: dict) -> str:
327
+ """
328
+ Format source attribution as human-readable text.
329
+
330
+ Example:
331
+ Sources (10 chunks):
332
+ - Gold TC: 4 chunks
333
+ - IGEL Docs: 3 chunks
334
+ - Confluence: 2 chunks
335
+ - Jira TC: 1 chunk
336
+ """
337
+ lines = [f"Sources ({attribution['total_chunks']} chunks):"]
338
+
339
+ # Sort by precedence
340
+ breakdown = attribution['source_breakdown']
341
+ sorted_sources = sorted(
342
+ breakdown.items(),
343
+ key=lambda x: SOURCE_TYPES[x[0]].rank
344
+ )
345
+
346
+ for source_name, count in sorted_sources:
347
+ source_desc = SOURCE_TYPES[source_name].description
348
+ lines.append(f" - {source_desc}: {count} chunk{'s' if count > 1 else ''}")
349
+
350
+ if attribution.get('highest_precedence'):
351
+ highest = SOURCE_TYPES[attribution['highest_precedence']].description
352
+ lines.append(f"\nPrimary source: {highest}")
353
+
354
+ return "\n".join(lines)
@@ -0,0 +1,54 @@
1
+ """Request context and API security helpers."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import hashlib
6
+ import uuid
7
+ from contextvars import ContextVar
8
+
9
+ request_id_var: ContextVar[str] = ContextVar("request_id", default="")
10
+ api_key_scope_var: ContextVar[str] = ContextVar("api_key_scope", default="read")
11
+
12
+
13
+ def new_request_id() -> str:
14
+ rid = str(uuid.uuid4())
15
+ request_id_var.set(rid)
16
+ return rid
17
+
18
+
19
+ def get_request_id() -> str:
20
+ return request_id_var.get() or ""
21
+
22
+
23
+ def hash_api_key(key: str | None) -> str:
24
+ if not key:
25
+ return "anonymous"
26
+ return hashlib.sha256(key.encode()).hexdigest()[:16]
27
+
28
+
29
+ def parse_api_keys(raw: str) -> dict[str, str]:
30
+ """
31
+ Parse API_KEYS env: key:scope,key2:scope2
32
+ scope in read|write|admin
33
+ """
34
+ out: dict[str, str] = {}
35
+ for part in (raw or "").split(","):
36
+ part = part.strip()
37
+ if not part:
38
+ continue
39
+ if ":" in part:
40
+ k, scope = part.split(":", 1)
41
+ out[k.strip()] = scope.strip().lower()
42
+ else:
43
+ out[part] = "admin"
44
+ return out
45
+
46
+
47
+ def resolve_scope(api_key: str | None, keys: dict[str, str], legacy_key: str) -> str:
48
+ if not api_key:
49
+ return "anonymous"
50
+ if api_key in keys:
51
+ return keys[api_key]
52
+ if legacy_key and api_key == legacy_key:
53
+ return "admin"
54
+ return "invalid"
File without changes
@@ -0,0 +1,100 @@
1
+ from __future__ import annotations
2
+
3
+ import logging
4
+ import os
5
+
6
+ import psycopg2
7
+ import psycopg2.pool
8
+ from contextlib import contextmanager
9
+
10
+ from knowledge_base.config import cfg
11
+
12
+ logger = logging.getLogger(__name__)
13
+
14
+ _pool: psycopg2.pool.ThreadedConnectionPool | None = None
15
+
16
+
17
+ def _pool_kwargs() -> dict:
18
+ sslmode = os.getenv("DB_SSLMODE", "prefer")
19
+ return {
20
+ "host": cfg.DB_HOST,
21
+ "port": cfg.DB_PORT,
22
+ "dbname": cfg.DB_NAME,
23
+ "user": cfg.DB_USER,
24
+ "password": cfg.DB_PASSWORD,
25
+ "connect_timeout": 10,
26
+ "keepalives": 1,
27
+ "keepalives_idle": 30,
28
+ "keepalives_interval": 10,
29
+ "keepalives_count": 5,
30
+ "sslmode": sslmode,
31
+ }
32
+
33
+
34
+ def init_pool(minconn: int = 2, maxconn: int = 10) -> None:
35
+ global _pool
36
+ if _pool is not None:
37
+ return
38
+ _pool = psycopg2.pool.ThreadedConnectionPool(
39
+ minconn,
40
+ maxconn,
41
+ **_pool_kwargs(),
42
+ )
43
+
44
+
45
+ def _ping(conn) -> None:
46
+ with conn.cursor() as cur:
47
+ cur.execute("SELECT 1")
48
+
49
+
50
+ @contextmanager
51
+ def get_conn(*, autocommit: bool = True):
52
+ """Get a pooled connection. Commits on success when autocommit=True."""
53
+ if _pool is None:
54
+ init_pool()
55
+ conn = _pool.getconn()
56
+ try:
57
+ try:
58
+ _ping(conn)
59
+ except Exception:
60
+ conn.close()
61
+ conn = _pool.getconn()
62
+ _ping(conn)
63
+ yield conn
64
+ if autocommit:
65
+ conn.commit()
66
+ except Exception:
67
+ try:
68
+ conn.rollback()
69
+ except Exception as rollback_exc:
70
+ logger.debug("Rollback failed while handling DB error: %s", rollback_exc)
71
+ raise
72
+ finally:
73
+ _pool.putconn(conn)
74
+
75
+
76
+ @contextmanager
77
+ def get_transaction():
78
+ """Yield a connection inside a single transaction (commit on success)."""
79
+ with get_conn(autocommit=False) as conn:
80
+ yield conn
81
+
82
+
83
+ def test_connection() -> bool:
84
+ try:
85
+ with get_conn() as conn:
86
+ with conn.cursor() as cur:
87
+ cur.execute("SELECT version();")
88
+ version = cur.fetchone()[0]
89
+ print(f" Connected: {version[:60]}")
90
+ return True
91
+ except Exception as e:
92
+ print(f" DB connection failed: {e}")
93
+ return False
94
+
95
+
96
+ def close_pool() -> None:
97
+ global _pool
98
+ if _pool:
99
+ _pool.closeall()
100
+ _pool = None
@@ -0,0 +1,43 @@
1
+ """Connector incremental sync state persistence."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from datetime import datetime, timezone
6
+
7
+ from knowledge_base.db.client import get_conn
8
+
9
+
10
+ def get_last_sync(connector_name: str) -> datetime | None:
11
+ with get_conn() as conn:
12
+ with conn.cursor() as cur:
13
+ cur.execute(
14
+ "SELECT last_sync_timestamp FROM connector_sync_state WHERE connector_name=%s",
15
+ (connector_name,),
16
+ )
17
+ row = cur.fetchone()
18
+ return row[0] if row and row[0] else None
19
+
20
+
21
+ def update_sync_state(
22
+ connector_name: str,
23
+ *,
24
+ status: str = "synced",
25
+ cursor: str | None = None,
26
+ advance_timestamp: bool = True,
27
+ ) -> None:
28
+ now = datetime.now(timezone.utc)
29
+ last_sync_timestamp = now if advance_timestamp else None
30
+ with get_conn() as conn:
31
+ with conn.cursor() as cur:
32
+ cur.execute(
33
+ """
34
+ INSERT INTO connector_sync_state (connector_name, last_sync_timestamp, cursor, status, updated_at)
35
+ VALUES (%s, %s, %s, %s, %s)
36
+ ON CONFLICT (connector_name) DO UPDATE SET
37
+ last_sync_timestamp = COALESCE(EXCLUDED.last_sync_timestamp, connector_sync_state.last_sync_timestamp),
38
+ cursor = EXCLUDED.cursor,
39
+ status = EXCLUDED.status,
40
+ updated_at = EXCLUDED.updated_at
41
+ """,
42
+ (connector_name, last_sync_timestamp, cursor, status, now),
43
+ )