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,649 @@
1
+ """
2
+ Creates and manages the PostgreSQL schema for the IGEL Knowledge Base.
3
+ Tables:
4
+ kb_documents - one row per source file
5
+ kb_parent_chunks - large sections (800-1200 tokens), returned as context
6
+ kb_chunks - small indexed units (150-300 tokens) with vector embeddings
7
+ feedback_events - user feedback for continuous learning (Phase 2)
8
+ eval_runs - evaluation run summaries (Phase 0)
9
+ eval_results - per-query evaluation results (Phase 0)
10
+ """
11
+ from __future__ import annotations
12
+ from knowledge_base.db.client import get_conn
13
+ from knowledge_base.config import cfg
14
+
15
+ _SCHEMA_SQL = f"""
16
+ -- Enable required extensions
17
+ CREATE EXTENSION IF NOT EXISTS vector;
18
+ CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
19
+
20
+ -- Source documents registry
21
+ CREATE TABLE IF NOT EXISTS kb_documents (
22
+ id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
23
+ file_name TEXT NOT NULL,
24
+ file_path TEXT NOT NULL,
25
+ file_type TEXT NOT NULL,
26
+ file_hash TEXT UNIQUE NOT NULL,
27
+ product TEXT,
28
+ total_parent_chunks INT DEFAULT 0,
29
+ total_chunks INT DEFAULT 0,
30
+ ingested_at TIMESTAMP DEFAULT NOW(),
31
+ metadata JSONB DEFAULT '{{}}'::jsonb
32
+ );
33
+
34
+ -- Parent chunks: full sections returned during retrieval for full context
35
+ CREATE TABLE IF NOT EXISTS kb_parent_chunks (
36
+ id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
37
+ document_id UUID NOT NULL REFERENCES kb_documents(id) ON DELETE CASCADE,
38
+ chunk_index INT NOT NULL,
39
+ content TEXT NOT NULL,
40
+ section_title TEXT,
41
+ chunk_type TEXT,
42
+ page_number INT,
43
+ metadata JSONB DEFAULT '{{}}'::jsonb,
44
+ created_at TIMESTAMP DEFAULT NOW()
45
+ );
46
+
47
+ -- Child chunks: indexed for vector + full-text search
48
+ CREATE TABLE IF NOT EXISTS kb_chunks (
49
+ id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
50
+ parent_chunk_id UUID NOT NULL REFERENCES kb_parent_chunks(id) ON DELETE CASCADE,
51
+ document_id UUID NOT NULL REFERENCES kb_documents(id) ON DELETE CASCADE,
52
+ chunk_index INT NOT NULL,
53
+ content TEXT NOT NULL,
54
+ embedding VECTOR({cfg.EMBEDDING_DIM}),
55
+ token_count INT,
56
+ contains_steps BOOLEAN DEFAULT FALSE,
57
+ has_table BOOLEAN DEFAULT FALSE,
58
+ chunk_type TEXT,
59
+ metadata JSONB DEFAULT '{{}}'::jsonb,
60
+ created_at TIMESTAMP DEFAULT NOW()
61
+ );
62
+
63
+ -- Note: no ANN index on `embedding` — text-embedding-3-large is 3072-dim,
64
+ -- which exceeds pgvector's 2000-dim cap for IVFFlat/HNSW with vector_cosine_ops.
65
+ -- For this corpus size (~100 docs, a few thousand chunks) sequential scan
66
+ -- via vector_cosine_ops operator class is fast enough. If we ever exceed
67
+ -- ~50k chunks, switch to halfvec(3072) + HNSW (4000-dim cap).
68
+
69
+ -- Full-text search index (BM25-style fallback)
70
+ CREATE INDEX IF NOT EXISTS kb_chunks_fts_idx
71
+ ON kb_chunks USING gin(to_tsvector('english', content));
72
+
73
+ -- Lookup indexes
74
+ CREATE INDEX IF NOT EXISTS kb_chunks_doc_idx ON kb_chunks(document_id);
75
+ CREATE INDEX IF NOT EXISTS kb_chunks_parent_idx ON kb_chunks(parent_chunk_id);
76
+ CREATE INDEX IF NOT EXISTS kb_chunks_type_idx ON kb_chunks(chunk_type);
77
+ CREATE INDEX IF NOT EXISTS kb_parent_doc_idx ON kb_parent_chunks(document_id);
78
+ CREATE INDEX IF NOT EXISTS kb_docs_hash_idx ON kb_documents(file_hash);
79
+ CREATE INDEX IF NOT EXISTS kb_docs_product_idx ON kb_documents(product);
80
+
81
+ -- ─────────────────────────────────────────────────────────────────────────────
82
+ -- Images table: every image extracted from any source document.
83
+ -- Hard FK to kb_documents (cascades on doc delete) and soft FK to nearest section.
84
+ -- Stores binary + multiple text representations (OCR-grade verbatim + semantic).
85
+ -- ─────────────────────────────────────────────────────────────────────────────
86
+ CREATE TABLE IF NOT EXISTS kb_images (
87
+ id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
88
+ document_id UUID NOT NULL REFERENCES kb_documents(id) ON DELETE CASCADE,
89
+ parent_chunk_id UUID REFERENCES kb_parent_chunks(id) ON DELETE SET NULL,
90
+
91
+ -- Position / identity
92
+ page_number INT,
93
+ sequence_index INT NOT NULL, -- order across the whole document
94
+ bbox JSONB, -- {{x0,y0,x1,y1}} on page (PDFs)
95
+ image_hash TEXT NOT NULL, -- sha256 of bytes (per-document, not globally unique)
96
+ image_format TEXT NOT NULL, -- png, jpeg, etc.
97
+ width INT,
98
+ height INT,
99
+ image_bytes BYTEA NOT NULL, -- raw image preserved
100
+
101
+ -- Captured text representations (no context loss)
102
+ image_type TEXT, -- ui_screenshot, diagram, photo, icon, ...
103
+ verbatim_text TEXT, -- every visible label/button/value
104
+ ui_elements JSONB, -- structured controls + state
105
+ semantic_description TEXT, -- 2-4 sentence narrative
106
+ test_relevance TEXT, -- what test cases this illustrates
107
+ surrounding_text TEXT, -- paragraph above/below in source doc
108
+ caption TEXT, -- explicit caption if any
109
+
110
+ -- Vector embedding of the combined description (for similarity search)
111
+ description_embedding VECTOR({cfg.EMBEDDING_DIM}),
112
+
113
+ metadata JSONB DEFAULT '{{}}'::jsonb,
114
+ created_at TIMESTAMP DEFAULT NOW(),
115
+
116
+ UNIQUE (document_id, sequence_index) -- per-doc dedup; same image in different docs ok
117
+ );
118
+
119
+ CREATE INDEX IF NOT EXISTS kb_images_doc_idx ON kb_images(document_id);
120
+ CREATE INDEX IF NOT EXISTS kb_images_parent_idx ON kb_images(parent_chunk_id);
121
+ CREATE INDEX IF NOT EXISTS kb_images_page_idx ON kb_images(document_id, page_number);
122
+ CREATE INDEX IF NOT EXISTS kb_images_hash_idx ON kb_images(image_hash);
123
+ CREATE INDEX IF NOT EXISTS kb_images_fts_idx
124
+ ON kb_images USING gin(to_tsvector('english',
125
+ coalesce(verbatim_text,'') || ' ' ||
126
+ coalesce(semantic_description,'') || ' ' ||
127
+ coalesce(surrounding_text,'')));
128
+ """
129
+
130
+ _KG_TABLES_SQL = """
131
+ CREATE TABLE IF NOT EXISTS knowledge_graph_nodes (
132
+ id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
133
+ node_type TEXT NOT NULL,
134
+ value TEXT NOT NULL,
135
+ normalized TEXT NOT NULL DEFAULT '',
136
+ metadata JSONB DEFAULT '{}'::jsonb,
137
+ parent_chunk_id UUID REFERENCES kb_parent_chunks(id) ON DELETE SET NULL,
138
+ created_at TIMESTAMP DEFAULT NOW(),
139
+ UNIQUE (node_type, normalized)
140
+ );
141
+
142
+ CREATE TABLE IF NOT EXISTS knowledge_graph_edges (
143
+ id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
144
+ source_node_id UUID NOT NULL REFERENCES knowledge_graph_nodes(id) ON DELETE CASCADE,
145
+ target_node_id UUID NOT NULL REFERENCES knowledge_graph_nodes(id) ON DELETE CASCADE,
146
+ relation TEXT NOT NULL DEFAULT 'related_to',
147
+ weight DOUBLE PRECISION DEFAULT 1.0,
148
+ metadata JSONB DEFAULT '{}'::jsonb,
149
+ created_at TIMESTAMP DEFAULT NOW(),
150
+ UNIQUE (source_node_id, target_node_id, relation)
151
+ );
152
+ """
153
+
154
+ _KG_INDEX_SQL = """
155
+ CREATE INDEX IF NOT EXISTS ix_kgn_chunk ON knowledge_graph_nodes(parent_chunk_id);
156
+ CREATE INDEX IF NOT EXISTS ix_kgn_type ON knowledge_graph_nodes(node_type);
157
+ CREATE INDEX IF NOT EXISTS ix_kgn_norm ON knowledge_graph_nodes(normalized);
158
+ CREATE INDEX IF NOT EXISTS ix_kge_src ON knowledge_graph_edges(source_node_id);
159
+ CREATE INDEX IF NOT EXISTS ix_kge_tgt ON knowledge_graph_edges(target_node_id);
160
+ """
161
+
162
+ # Backward compat alias
163
+ _KG_GRAPH_SQL = _KG_TABLES_SQL + _KG_INDEX_SQL
164
+
165
+
166
+ def ensure_knowledge_graph_tables() -> None:
167
+ """Create KG tables if absent; migrate legacy tables missing columns."""
168
+ with get_conn() as conn:
169
+ with conn.cursor() as cur:
170
+ cur.execute(_KG_TABLES_SQL)
171
+ cur.execute(
172
+ """
173
+ ALTER TABLE knowledge_graph_nodes
174
+ ADD COLUMN IF NOT EXISTS parent_chunk_id UUID
175
+ REFERENCES kb_parent_chunks(id) ON DELETE SET NULL
176
+ """
177
+ )
178
+ cur.execute(
179
+ """
180
+ ALTER TABLE knowledge_graph_nodes
181
+ ADD COLUMN IF NOT EXISTS normalized TEXT NOT NULL DEFAULT ''
182
+ """
183
+ )
184
+ cur.execute(
185
+ """
186
+ ALTER TABLE knowledge_graph_nodes
187
+ ADD COLUMN IF NOT EXISTS created_at TIMESTAMP DEFAULT NOW()
188
+ """
189
+ )
190
+ cur.execute(_KG_INDEX_SQL)
191
+ try:
192
+ from knowledge_base.db.kg_introspect import clear_kg_layout_cache
193
+ clear_kg_layout_cache()
194
+ except Exception:
195
+ pass
196
+
197
+
198
+
199
+
200
+ def create_schema() -> None:
201
+ """Ensure schema via Alembic migrations, with legacy DDL fallback."""
202
+ try:
203
+ from knowledge_base.db.migrations import run_migrations
204
+ run_migrations("head")
205
+ except Exception as exc:
206
+ import logging
207
+ logging.getLogger(__name__).warning(
208
+ "Alembic migration failed (%s) — applying legacy DDL fallback", exc,
209
+ )
210
+ with get_conn() as conn:
211
+ with conn.cursor() as cur:
212
+ cur.execute(_SCHEMA_SQL)
213
+ ensure_knowledge_graph_tables()
214
+ from knowledge_base.db.feedback_schema import ensure_feedback_tables
215
+ ensure_feedback_tables()
216
+ print(" Schema ready (Alembic + kb_documents, kb_parent_chunks, kb_chunks, knowledge_graph_*)")
217
+
218
+
219
+ def drop_schema() -> None:
220
+ """Full reset — drops all KB tables. Use only for clean re-ingestion."""
221
+ sql = """
222
+ DROP TABLE IF EXISTS knowledge_graph_edges CASCADE;
223
+ DROP TABLE IF EXISTS knowledge_graph_nodes CASCADE;
224
+ DROP TABLE IF EXISTS kb_images CASCADE;
225
+ DROP TABLE IF EXISTS kb_chunks CASCADE;
226
+ DROP TABLE IF EXISTS kb_parent_chunks CASCADE;
227
+ DROP TABLE IF EXISTS kb_documents CASCADE;
228
+ """
229
+ with get_conn() as conn:
230
+ with conn.cursor() as cur:
231
+ cur.execute(sql)
232
+ print(" Schema dropped.")
233
+
234
+
235
+ def document_exists(file_hash: str) -> str | None:
236
+ """Return document id if already ingested, else None."""
237
+ with get_conn() as conn:
238
+ with conn.cursor() as cur:
239
+ cur.execute(
240
+ "SELECT id FROM kb_documents WHERE file_hash = %s",
241
+ (file_hash,)
242
+ )
243
+ row = cur.fetchone()
244
+ return str(row[0]) if row else None
245
+
246
+
247
+ def document_has_chunks(document_id: str) -> bool:
248
+ """True if document has at least one indexed child chunk."""
249
+ with get_conn() as conn:
250
+ with conn.cursor() as cur:
251
+ cur.execute(
252
+ "SELECT COALESCE(total_chunks, 0) FROM kb_documents WHERE id = %s",
253
+ (document_id,),
254
+ )
255
+ row = cur.fetchone()
256
+ return bool(row and row[0] and row[0] > 0)
257
+
258
+
259
+ def insert_document(
260
+ file_name: str,
261
+ file_path: str,
262
+ file_type: str,
263
+ file_hash: str,
264
+ product: str,
265
+ metadata: dict,
266
+ *,
267
+ conn=None,
268
+ ) -> str:
269
+ sql = """
270
+ INSERT INTO kb_documents (file_name, file_path, file_type, file_hash, product, metadata)
271
+ VALUES (%s, %s, %s, %s, %s, %s::jsonb)
272
+ RETURNING id
273
+ """
274
+ import json
275
+
276
+ params = (file_name, file_path, file_type, file_hash, product, json.dumps(metadata))
277
+
278
+ def _exec(cur):
279
+ cur.execute(sql, params)
280
+ return str(cur.fetchone()[0])
281
+
282
+ if conn is not None:
283
+ with conn.cursor() as cur:
284
+ return _exec(cur)
285
+ with get_conn() as c:
286
+ with c.cursor() as cur:
287
+ return _exec(cur)
288
+
289
+
290
+ def insert_parent_chunk(
291
+ document_id: str,
292
+ chunk_index: int,
293
+ content: str,
294
+ section_title: str,
295
+ chunk_type: str,
296
+ page_number: int,
297
+ metadata: dict,
298
+ *,
299
+ conn=None,
300
+ ) -> str:
301
+ sql = """
302
+ INSERT INTO kb_parent_chunks (document_id, chunk_index, content, section_title, chunk_type, page_number, metadata)
303
+ VALUES (%s, %s, %s, %s, %s, %s, %s::jsonb)
304
+ RETURNING id
305
+ """
306
+ import json
307
+
308
+ params = (
309
+ document_id, chunk_index, content, section_title,
310
+ chunk_type, page_number, json.dumps(metadata),
311
+ )
312
+
313
+ def _exec(cur):
314
+ cur.execute(sql, params)
315
+ return str(cur.fetchone()[0])
316
+
317
+ if conn is not None:
318
+ with conn.cursor() as cur:
319
+ return _exec(cur)
320
+ with get_conn() as c:
321
+ with c.cursor() as cur:
322
+ return _exec(cur)
323
+
324
+
325
+ def insert_chunks_batch(rows: list[dict], *, conn=None) -> None:
326
+ """Batch insert child chunks with embeddings."""
327
+ if not rows:
328
+ return
329
+ import json
330
+ from psycopg2.extras import execute_values
331
+
332
+ sql = """
333
+ INSERT INTO kb_chunks
334
+ (parent_chunk_id, document_id, chunk_index, content, embedding,
335
+ token_count, contains_steps, has_table, chunk_type, metadata)
336
+ VALUES %s
337
+ """
338
+ values = [
339
+ (
340
+ r["parent_chunk_id"],
341
+ r["document_id"],
342
+ r["chunk_index"],
343
+ r["content"],
344
+ r["embedding"],
345
+ r["token_count"],
346
+ r["contains_steps"],
347
+ r["has_table"],
348
+ r["chunk_type"],
349
+ json.dumps(r["metadata"]),
350
+ )
351
+ for r in rows
352
+ ]
353
+
354
+ def _exec(cur):
355
+ execute_values(cur, sql, values, template=None, page_size=50)
356
+
357
+ if conn is not None:
358
+ with conn.cursor() as cur:
359
+ _exec(cur)
360
+ else:
361
+ with get_conn() as c:
362
+ with c.cursor() as cur:
363
+ _exec(cur)
364
+
365
+
366
+ def update_document_counts(document_id: str, parent_count: int, chunk_count: int, *, conn=None) -> None:
367
+ sql = "UPDATE kb_documents SET total_parent_chunks=%s, total_chunks=%s WHERE id=%s"
368
+ params = (parent_count, chunk_count, document_id)
369
+
370
+ def _exec(cur):
371
+ cur.execute(sql, params)
372
+
373
+ if conn is not None:
374
+ with conn.cursor() as cur:
375
+ _exec(cur)
376
+ else:
377
+ with get_conn() as c:
378
+ with c.cursor() as cur:
379
+ _exec(cur)
380
+
381
+
382
+ def clear_document_chunks(document_id: str, *, conn=None) -> None:
383
+ """Remove all chunks for a document so it can be re-ingested safely."""
384
+ def _exec(cur):
385
+ cur.execute("DELETE FROM kb_chunks WHERE document_id = %s", (document_id,))
386
+ cur.execute("DELETE FROM kb_parent_chunks WHERE document_id = %s", (document_id,))
387
+ cur.execute(
388
+ "UPDATE kb_documents SET total_parent_chunks=0, total_chunks=0 WHERE id=%s",
389
+ (document_id,),
390
+ )
391
+
392
+ if conn is not None:
393
+ with conn.cursor() as cur:
394
+ _exec(cur)
395
+ else:
396
+ with get_conn() as c:
397
+ with c.cursor() as cur:
398
+ _exec(cur)
399
+
400
+
401
+ def ingest_document_atomic(
402
+ *,
403
+ file_name: str,
404
+ file_path: str,
405
+ file_type: str,
406
+ file_hash: str,
407
+ product: str,
408
+ doc_metadata: dict,
409
+ parents: list,
410
+ existing_id: str | None,
411
+ embed_fn,
412
+ format_fn,
413
+ batch_size: int,
414
+ ) -> tuple[str, int, int, int]:
415
+ """
416
+ Atomically insert document + parent + child chunks in one transaction.
417
+ Returns (doc_id, parent_count, chunk_count, validation_skipped).
418
+ """
419
+ from knowledge_base.db.client import get_transaction
420
+
421
+ validation_skipped = 0
422
+ with get_transaction() as conn:
423
+ if existing_id:
424
+ doc_id = existing_id
425
+ clear_document_chunks(doc_id, conn=conn)
426
+ else:
427
+ doc_id = insert_document(
428
+ file_name=file_name,
429
+ file_path=file_path,
430
+ file_type=file_type,
431
+ file_hash=file_hash,
432
+ product=product,
433
+ metadata=doc_metadata,
434
+ conn=conn,
435
+ )
436
+
437
+ total_children = 0
438
+ for p_idx, parent in enumerate(parents):
439
+ parent_id = insert_parent_chunk(
440
+ document_id=doc_id,
441
+ chunk_index=p_idx,
442
+ content=parent.content,
443
+ section_title=parent.section_title,
444
+ chunk_type=parent.chunk_type,
445
+ page_number=parent.page_number,
446
+ metadata=parent.metadata,
447
+ conn=conn,
448
+ )
449
+ if not parent.children:
450
+ continue
451
+
452
+ valid_children = []
453
+ for child in parent.children:
454
+ if getattr(child, "validation", None) and not child.validation.passed:
455
+ validation_skipped += 1
456
+ continue
457
+ valid_children.append(child)
458
+ if not valid_children:
459
+ continue
460
+
461
+ child_texts = [c.content for c in valid_children]
462
+ embeddings = embed_fn(child_texts, batch_size=batch_size)
463
+ rows = []
464
+ for c_idx, (child, embedding) in enumerate(zip(valid_children, embeddings)):
465
+ rows.append({
466
+ "parent_chunk_id": parent_id,
467
+ "document_id": doc_id,
468
+ "chunk_index": total_children + c_idx,
469
+ "content": child.content,
470
+ "embedding": format_fn(embedding),
471
+ "token_count": child.token_count,
472
+ "contains_steps": child.contains_steps,
473
+ "has_table": child.has_table,
474
+ "chunk_type": child.chunk_type,
475
+ "metadata": {**child.metadata, "parent_chunk_index": p_idx},
476
+ })
477
+ insert_chunks_batch(rows, conn=conn)
478
+ total_children += len(rows)
479
+
480
+ update_document_counts(doc_id, len(parents), total_children, conn=conn)
481
+ return doc_id, len(parents), total_children, validation_skipped
482
+
483
+
484
+ def get_stats() -> dict:
485
+ with get_conn() as conn:
486
+ with conn.cursor() as cur:
487
+ cur.execute("SELECT COUNT(*) FROM kb_documents")
488
+ docs = cur.fetchone()[0]
489
+ cur.execute("SELECT COUNT(*) FROM kb_parent_chunks")
490
+ parents = cur.fetchone()[0]
491
+ cur.execute("SELECT COUNT(*) FROM kb_chunks")
492
+ chunks = cur.fetchone()[0]
493
+ cur.execute("SELECT COUNT(*) FROM kb_chunks WHERE embedding IS NULL")
494
+ missing_emb = cur.fetchone()[0]
495
+ try:
496
+ cur.execute("SELECT COUNT(*) FROM kb_images")
497
+ images = cur.fetchone()[0]
498
+ cur.execute("SELECT COUNT(*) FROM kb_images WHERE description_embedding IS NULL")
499
+ images_missing_emb = cur.fetchone()[0]
500
+ except Exception:
501
+ images, images_missing_emb = 0, 0
502
+
503
+ # Knowledge Graph stats
504
+ kg_nodes = kg_edges = kg_orphan_parents = 0
505
+ try:
506
+ cur.execute("SELECT COUNT(*) FROM knowledge_graph_nodes")
507
+ kg_nodes = cur.fetchone()[0]
508
+ cur.execute("SELECT COUNT(*) FROM knowledge_graph_edges")
509
+ kg_edges = cur.fetchone()[0]
510
+ cur.execute("""
511
+ SELECT COUNT(*) FROM kb_parent_chunks p
512
+ WHERE NOT EXISTS (
513
+ SELECT 1 FROM knowledge_graph_nodes n
514
+ WHERE n.parent_chunk_id = p.id
515
+ )
516
+ """)
517
+ kg_orphan_parents = cur.fetchone()[0]
518
+ except Exception:
519
+ pass
520
+
521
+ return {
522
+ "documents": docs,
523
+ "parent_chunks": parents,
524
+ "chunks": chunks,
525
+ "missing_embeddings": missing_emb,
526
+ "images": images,
527
+ "images_missing_embeddings": images_missing_emb,
528
+ "kg_nodes": kg_nodes,
529
+ "kg_edges": kg_edges,
530
+ "kg_orphan_parents (no KG entity)": kg_orphan_parents,
531
+ }
532
+
533
+
534
+ # ── Image helpers ─────────────────────────────────────────────────────────────
535
+
536
+ def insert_image(
537
+ document_id: str,
538
+ parent_chunk_id: str | None,
539
+ sequence_index: int,
540
+ page_number: int | None,
541
+ bbox: dict | None,
542
+ image_hash: str,
543
+ image_format: str,
544
+ width: int | None,
545
+ height: int | None,
546
+ image_bytes: bytes,
547
+ image_type: str,
548
+ verbatim_text: str,
549
+ ui_elements: list | dict | None,
550
+ semantic_description: str,
551
+ test_relevance: str,
552
+ surrounding_text: str,
553
+ caption: str | None,
554
+ description_embedding: str,
555
+ metadata: dict,
556
+ ) -> str:
557
+ import json
558
+ sql = """
559
+ INSERT INTO kb_images (
560
+ document_id, parent_chunk_id, sequence_index, page_number, bbox,
561
+ image_hash, image_format, width, height, image_bytes,
562
+ image_type, verbatim_text, ui_elements, semantic_description,
563
+ test_relevance, surrounding_text, caption,
564
+ description_embedding, metadata
565
+ ) VALUES (
566
+ %s, %s, %s, %s, %s::jsonb,
567
+ %s, %s, %s, %s, %s,
568
+ %s, %s, %s::jsonb, %s,
569
+ %s, %s, %s,
570
+ %s, %s::jsonb
571
+ )
572
+ ON CONFLICT (document_id, sequence_index) DO UPDATE SET
573
+ parent_chunk_id = EXCLUDED.parent_chunk_id,
574
+ page_number = EXCLUDED.page_number,
575
+ bbox = EXCLUDED.bbox,
576
+ image_hash = EXCLUDED.image_hash,
577
+ image_format = EXCLUDED.image_format,
578
+ width = EXCLUDED.width,
579
+ height = EXCLUDED.height,
580
+ image_bytes = EXCLUDED.image_bytes,
581
+ image_type = EXCLUDED.image_type,
582
+ verbatim_text = EXCLUDED.verbatim_text,
583
+ ui_elements = EXCLUDED.ui_elements,
584
+ semantic_description = EXCLUDED.semantic_description,
585
+ test_relevance = EXCLUDED.test_relevance,
586
+ surrounding_text = EXCLUDED.surrounding_text,
587
+ caption = EXCLUDED.caption,
588
+ description_embedding = EXCLUDED.description_embedding,
589
+ metadata = EXCLUDED.metadata
590
+ RETURNING id
591
+ """
592
+ with get_conn() as conn:
593
+ with conn.cursor() as cur:
594
+ cur.execute(sql, (
595
+ document_id, parent_chunk_id, sequence_index, page_number,
596
+ json.dumps(bbox) if bbox else None,
597
+ image_hash, image_format, width, height, image_bytes,
598
+ image_type, verbatim_text,
599
+ json.dumps(ui_elements) if ui_elements is not None else None,
600
+ semantic_description, test_relevance, surrounding_text, caption,
601
+ description_embedding,
602
+ json.dumps(metadata),
603
+ ))
604
+ return str(cur.fetchone()[0])
605
+
606
+
607
+ def find_nearest_parent_chunk(document_id: str, page_number: int | None) -> str | None:
608
+ """Return the kb_parent_chunks.id whose page_number is closest to the given page,
609
+ so an image can be linked to the most relevant section."""
610
+ if page_number is None:
611
+ # Just pick the first parent chunk for this document
612
+ sql = "SELECT id FROM kb_parent_chunks WHERE document_id = %s ORDER BY chunk_index ASC LIMIT 1"
613
+ params = (document_id,)
614
+ else:
615
+ sql = """
616
+ SELECT id FROM kb_parent_chunks
617
+ WHERE document_id = %s
618
+ ORDER BY ABS(COALESCE(page_number, 0) - %s) ASC, chunk_index ASC
619
+ LIMIT 1
620
+ """
621
+ params = (document_id, page_number)
622
+ with get_conn() as conn:
623
+ with conn.cursor() as cur:
624
+ cur.execute(sql, params)
625
+ row = cur.fetchone()
626
+ return str(row[0]) if row else None
627
+
628
+
629
+ def list_documents_for_image_pass() -> list[dict]:
630
+ """Documents already ingested as text — for the image pass to iterate over."""
631
+ sql = """
632
+ SELECT id, file_name, file_path, file_type
633
+ FROM kb_documents
634
+ ORDER BY ingested_at ASC
635
+ """
636
+ with get_conn() as conn:
637
+ with conn.cursor() as cur:
638
+ cur.execute(sql)
639
+ return [
640
+ {"id": str(r[0]), "file_name": r[1], "file_path": r[2], "file_type": r[3]}
641
+ for r in cur.fetchall()
642
+ ]
643
+
644
+
645
+ def document_has_images(document_id: str) -> bool:
646
+ with get_conn() as conn:
647
+ with conn.cursor() as cur:
648
+ cur.execute("SELECT 1 FROM kb_images WHERE document_id = %s LIMIT 1", (document_id,))
649
+ return cur.fetchone() is not None
@@ -0,0 +1,23 @@
1
+ FROM python:3.10-slim
2
+
3
+ WORKDIR /app
4
+
5
+ RUN apt-get update && apt-get install -y --no-install-recommends curl \
6
+ && rm -rf /var/lib/apt/lists/*
7
+
8
+ COPY knowledge_base/ knowledge_base/
9
+ COPY alembic.ini alembic.ini
10
+
11
+ COPY knowledge_base/deploy/requirements.txt requirements.txt
12
+ RUN pip install --no-cache-dir -r requirements.txt
13
+
14
+ ENV PYTHONPATH=/app
15
+ ENV TESTGEN_HOST=0.0.0.0
16
+ ENV TESTGEN_PORT=8000
17
+
18
+ EXPOSE 8000
19
+
20
+ HEALTHCHECK --interval=30s --timeout=10s --start-period=120s --retries=3 \
21
+ CMD curl -f http://localhost:8000/health || exit 1
22
+
23
+ CMD ["uvicorn", "knowledge_base.api.server:app", "--host", "0.0.0.0", "--port", "8000", "--workers", "2"]
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env bash
2
+ # Apply pending feedback promotions and ingest to operational memory.
3
+ set -euo pipefail
4
+ cd "$(dirname "$0")/../.."
5
+ export PYTHONPATH="${PWD}:${PYTHONPATH:-}"
6
+ /usr/bin/python3.8 - <<'PY'
7
+ from knowledge_base.db.client import init_pool, close_pool
8
+ from knowledge_base.feedback.applier import apply_pending_promotions
9
+ from knowledge_base.memory.ingest_memory import ingest_curated_feedback
10
+
11
+ init_pool()
12
+ results = apply_pending_promotions(limit=50)
13
+ print(f"Applied {len(results)} feedback promotions")
14
+ if results:
15
+ stats = ingest_curated_feedback(limit=len(results))
16
+ print(f"Memory ingest: {stats}")
17
+ close_pool()
18
+ PY