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,178 @@
1
+ """
2
+ PDF Parser — three-tier strategy:
3
+ Tier 1: If a pre-converted markdown file exists in markdown_output/, use it.
4
+ (Higher quality — processed by marker-pdf + Azure OpenAI LLM)
5
+ Tier 2: Direct pdfplumber extraction.
6
+ Tier 3: PyMuPDF (fitz) fallback — handles PDFs pdfplumber can't (encrypted
7
+ XRefs, exotic fonts, malformed cross-reference tables).
8
+ """
9
+ from __future__ import annotations
10
+ import logging
11
+ from pathlib import Path
12
+ from knowledge_base.parsers.markdown_parser import Section, parse_markdown_file, find_markdown_for_pdf
13
+
14
+ logger = logging.getLogger(__name__)
15
+
16
+
17
+ def parse_pdf(pdf_path: Path, markdown_output_dir: Path) -> tuple[list[Section], str]:
18
+ """
19
+ Returns (sections, parse_method) where parse_method is one of:
20
+ 'markdown', 'pdfplumber', 'pymupdf'.
21
+ """
22
+ # Tier 1: use pre-converted markdown
23
+ md_path = find_markdown_for_pdf(pdf_path.name, markdown_output_dir)
24
+ if md_path:
25
+ sections = parse_markdown_file(md_path)
26
+ if sections:
27
+ return sections, "markdown"
28
+
29
+ # Tier 2: pdfplumber
30
+ try:
31
+ sections = _parse_with_pdfplumber(pdf_path)
32
+ if sections and any(s.content.strip() for s in sections):
33
+ return sections, "pdfplumber"
34
+ logger.info("pdfplumber returned empty sections for %s — trying PyMuPDF", pdf_path.name)
35
+ except Exception as e:
36
+ logger.warning("pdfplumber failed on %s: %s — falling back to PyMuPDF", pdf_path.name, e)
37
+
38
+ # Tier 3: PyMuPDF
39
+ sections = _parse_with_pymupdf(pdf_path)
40
+ return sections, "pymupdf"
41
+
42
+
43
+ def _parse_with_pymupdf(pdf_path: Path) -> list[Section]:
44
+ import fitz
45
+ sections: list[Section] = []
46
+ try:
47
+ doc = fitz.open(str(pdf_path))
48
+ except Exception as e:
49
+ logger.error("PyMuPDF could not open %s: %s", pdf_path.name, e)
50
+ return [Section(title=pdf_path.stem, level=0, content="", page_hint=1)]
51
+
52
+ try:
53
+ for page_idx in range(len(doc)):
54
+ page = doc[page_idx]
55
+ text = page.get_text("text") or ""
56
+ text = text.strip()
57
+ if not text:
58
+ continue
59
+ sections.append(Section(
60
+ title=pdf_path.stem,
61
+ level=0,
62
+ content=text,
63
+ page_hint=page_idx + 1,
64
+ ))
65
+ finally:
66
+ doc.close()
67
+
68
+ return sections if sections else [Section(title=pdf_path.stem, level=0, content="", page_hint=1)]
69
+
70
+
71
+ def _parse_with_pdfplumber(pdf_path: Path) -> list[Section]:
72
+ import pdfplumber
73
+ import re
74
+
75
+ all_sections: list[Section] = []
76
+ current_title = pdf_path.stem
77
+ current_level = 0
78
+ current_lines: list[str] = []
79
+ current_page = 1
80
+ full_text_lines: list[str] = []
81
+
82
+ with pdfplumber.open(str(pdf_path)) as pdf:
83
+ for page_num, page in enumerate(pdf.pages, start=1):
84
+ # Extract tables first — preserve as markdown
85
+ tables = page.extract_tables()
86
+ table_md_blocks = [_table_to_markdown(t) for t in tables if t]
87
+
88
+ text = page.extract_text(x_tolerance=3, y_tolerance=3) or ""
89
+ lines = text.split("\n")
90
+
91
+ # Strip header/footer (first and last line are usually page number / product name)
92
+ if len(lines) > 3:
93
+ lines = lines[1:-1]
94
+
95
+ for line in lines:
96
+ line = line.strip()
97
+ if not line:
98
+ full_text_lines.append("")
99
+ continue
100
+
101
+ # Heading detection by ALL_CAPS short lines or common IGEL heading patterns
102
+ if _is_heading(line):
103
+ # Flush current section
104
+ content = "\n".join(current_lines).strip()
105
+ if content:
106
+ all_sections.append(Section(
107
+ title=current_title,
108
+ level=current_level,
109
+ content=content,
110
+ page_hint=current_page,
111
+ ))
112
+ current_title = line.rstrip(":")
113
+ current_level = _estimate_heading_level(line)
114
+ current_lines = []
115
+ current_page = page_num
116
+ else:
117
+ current_lines.append(line)
118
+ full_text_lines.append(line)
119
+
120
+ # Append tables as a block at end of page content
121
+ for table_md in table_md_blocks:
122
+ current_lines.append(table_md)
123
+
124
+ # Flush final section
125
+ content = "\n".join(current_lines).strip()
126
+ if content:
127
+ all_sections.append(Section(
128
+ title=current_title,
129
+ level=current_level,
130
+ content=content,
131
+ page_hint=current_page,
132
+ ))
133
+
134
+ # If no structure detected, treat whole doc as one section
135
+ if len(all_sections) <= 1:
136
+ all_text = "\n".join(full_text_lines).strip()
137
+ return [Section(title=pdf_path.stem, level=0, content=all_text, page_hint=1)]
138
+
139
+ return all_sections
140
+
141
+
142
+ def _is_heading(line: str) -> bool:
143
+ import re
144
+ if len(line) > 120:
145
+ return False
146
+ # Short ALL CAPS lines
147
+ if line.isupper() and 3 < len(line) < 80:
148
+ return True
149
+ # Numbered heading: "1.2 Title" or "3. Title"
150
+ if re.match(r"^\d+(\.\d+)*\.?\s+[A-Z]", line):
151
+ return True
152
+ # Lines ending with colon that look like section titles
153
+ if line.endswith(":") and len(line) < 60 and line[0].isupper():
154
+ return True
155
+ return False
156
+
157
+
158
+ def _estimate_heading_level(line: str) -> int:
159
+ import re
160
+ m = re.match(r"^(\d+)(\.\d+)*", line)
161
+ if m:
162
+ depth = line[:line.index(" ")].count(".")
163
+ return min(depth + 1, 3)
164
+ return 2
165
+
166
+
167
+ def _table_to_markdown(table: list) -> str:
168
+ if not table or not table[0]:
169
+ return ""
170
+ rows = [[str(cell or "").strip() for cell in row] for row in table]
171
+ # Find max columns
172
+ cols = max(len(r) for r in rows)
173
+ rows = [r + [""] * (cols - len(r)) for r in rows]
174
+
175
+ header = "| " + " | ".join(rows[0]) + " |"
176
+ divider = "| " + " | ".join(["---"] * cols) + " |"
177
+ body = ["| " + " | ".join(r) + " |" for r in rows[1:]]
178
+ return "\n".join([header, divider] + body)
@@ -0,0 +1 @@
1
+ """Retrieval, reranking, and context fusion."""
@@ -0,0 +1,264 @@
1
+ """Fuse RAG hits, KG text, and image snippets under a dynamic token budget."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import logging
6
+ from typing import TYPE_CHECKING
7
+
8
+ from knowledge_base.config import cfg
9
+ from knowledge_base.core.precedence import (
10
+ apply_precedence_filter,
11
+ detect_precedence_conflicts,
12
+ infer_source_type,
13
+ )
14
+
15
+ if TYPE_CHECKING:
16
+ from knowledge_base.retrieval.retriever import SearchResult
17
+
18
+ logger = logging.getLogger(__name__)
19
+
20
+ _CHARS_PER_TOKEN = 3.8
21
+
22
+ _EXCLUDE_SOURCE_MAP = {
23
+ "jira": "jira_tc",
24
+ "defects": "execution_history",
25
+ "defect": "execution_history",
26
+ "confluence": "confluence",
27
+ "screenshots": "screenshot",
28
+ "execution": "execution_history",
29
+ }
30
+
31
+
32
+ def _chunk_content(chunk) -> str:
33
+ """Extract searchable text from a chunk object."""
34
+ for attr in ("parent_content", "content", "child_content"):
35
+ val = getattr(chunk, attr, None)
36
+ if val:
37
+ return str(val)
38
+ return ""
39
+
40
+
41
+ def _chunk_metadata(chunk) -> dict:
42
+ meta = getattr(chunk, "metadata", None)
43
+ if isinstance(meta, dict) and meta:
44
+ return meta
45
+ return {
46
+ "file_path": getattr(chunk, "file_name", "") or "",
47
+ "product": getattr(chunk, "product", "") or "",
48
+ "section_title": getattr(chunk, "section_title", "") or "",
49
+ }
50
+
51
+
52
+ def _attach_chunk_helpers(chunk):
53
+ """Ensure precedence helpers can read content/metadata from SearchResult."""
54
+ if not getattr(chunk, "metadata", None):
55
+ chunk.metadata = _chunk_metadata(chunk)
56
+ if not getattr(chunk, "content", None):
57
+ chunk.content = _chunk_content(chunk)
58
+ return chunk
59
+
60
+
61
+ def filter_excluded_sources(results: list) -> list:
62
+ """Remove chunks from excluded source types (RETRIEVAL_EXCLUDE_SOURCES)."""
63
+ excluded = cfg.RETRIEVAL_EXCLUDE_SOURCES
64
+ if not excluded or not results:
65
+ return results
66
+
67
+ excluded_types = set()
68
+ for label in excluded:
69
+ mapped = _EXCLUDE_SOURCE_MAP.get(label.lower(), label.lower())
70
+ excluded_types.add(mapped)
71
+ if "defect" in label.lower():
72
+ excluded_types.add("execution_history")
73
+
74
+ kept: list = []
75
+ removed = 0
76
+ for chunk in results:
77
+ chunk = _attach_chunk_helpers(chunk)
78
+ source = infer_source_type(_chunk_metadata(chunk))
79
+ file_path = (_chunk_metadata(chunk).get("file_path") or "").lower()
80
+ if source.name in excluded_types:
81
+ removed += 1
82
+ continue
83
+ if "defect" in excluded and "defect" in file_path:
84
+ removed += 1
85
+ continue
86
+ kept.append(chunk)
87
+
88
+ if removed:
89
+ logger.info("context_fusion: excluded %d chunks by source filter %s", removed, excluded)
90
+ return kept
91
+
92
+
93
+ def apply_precedence_ranking(
94
+ results: list,
95
+ *,
96
+ max_chunks: int | None = None,
97
+ ) -> tuple[list, dict, list[dict]]:
98
+ """
99
+ Rank chunks by source precedence, detect conflicts.
100
+
101
+ Returns:
102
+ (filtered_chunks, attribution_metadata, conflicts)
103
+ """
104
+ if not results:
105
+ return [], {"total_chunks": 0, "source_breakdown": {}}, []
106
+
107
+ prepared = [_attach_chunk_helpers(r) for r in results]
108
+ top_k = max_chunks or len(prepared)
109
+ filtered, attribution = apply_precedence_filter(prepared, top_k=top_k)
110
+ conflicts = detect_precedence_conflicts(filtered)
111
+ if conflicts:
112
+ logger.info("context_fusion: detected %d precedence conflicts", len(conflicts))
113
+ return filtered, attribution, conflicts
114
+
115
+
116
+ def fuse_retrieval_context(
117
+ results: list,
118
+ kg_text: str,
119
+ image_hits: list | None,
120
+ *,
121
+ token_budget: int,
122
+ reserve_prompt: int,
123
+ kg_ratio: float,
124
+ image_ratio: float,
125
+ min_score: float | None,
126
+ max_chunks: int,
127
+ web_block: str = "",
128
+ web_ratio: float | None = None,
129
+ ) -> tuple[list, str, str, str, int, dict, list[dict]]:
130
+ """
131
+ Full fusion pipeline: source filter → precedence → dynamic budget.
132
+
133
+ Returns:
134
+ (selected_results, kg_trimmed, image_block, web_trimmed, rag_tokens, attribution, conflicts)
135
+ """
136
+ filtered = filter_excluded_sources(results)
137
+ ranked, attribution, conflicts = apply_precedence_ranking(
138
+ filtered,
139
+ max_chunks=max(max_chunks, len(filtered)),
140
+ )
141
+ selected, kg_out, img_block, web_out, rag_used = apply_dynamic_budget(
142
+ ranked,
143
+ kg_text,
144
+ image_hits,
145
+ token_budget=token_budget,
146
+ reserve_prompt=reserve_prompt,
147
+ kg_ratio=kg_ratio,
148
+ image_ratio=image_ratio,
149
+ min_score=min_score,
150
+ max_chunks=max_chunks,
151
+ web_block=web_block,
152
+ web_ratio=web_ratio,
153
+ )
154
+ return selected, kg_out, img_block, web_out, rag_used, attribution, conflicts
155
+
156
+
157
+ def _truncate_words(text: str, max_chars: int) -> str:
158
+ if len(text) <= max_chars:
159
+ return text
160
+ truncated = text[:max_chars]
161
+ last_space = truncated.rfind(" ")
162
+ return truncated[:last_space] if last_space > 0 else truncated
163
+
164
+
165
+ def _est_tokens(text: str) -> int:
166
+ return max(1, int(len(text) / _CHARS_PER_TOKEN))
167
+
168
+
169
+ def _format_image_snippets(image_hits: list, max_images: int = 4) -> str:
170
+ lines: list[str] = []
171
+ for h in image_hits[:max_images]:
172
+ lines.append(
173
+ f"[{h.image_type}] ({h.file_name} p.{h.page_number or '?'}) "
174
+ f"{getattr(h, 'semantic_description', '') or ''}\n"
175
+ f"Visible text: {(getattr(h, 'verbatim_text', '') or '')[:800]}\n"
176
+ f"Test hints: {(getattr(h, 'test_relevance', '') or '')[:800]}"
177
+ )
178
+ if not lines:
179
+ return ""
180
+ return "—— Related documentation images ——\n" + "\n\n".join(lines)
181
+
182
+
183
+ def apply_dynamic_budget(
184
+ results: list,
185
+ kg_text: str,
186
+ image_hits: list | None,
187
+ *,
188
+ token_budget: int,
189
+ reserve_prompt: int,
190
+ kg_ratio: float,
191
+ image_ratio: float,
192
+ min_score: float | None,
193
+ max_chunks: int,
194
+ web_block: str = "",
195
+ web_ratio: float | None = None,
196
+ ) -> tuple[list, str, str, str, int]:
197
+ """
198
+ Select RAG parent sections + trim KG + images + web to fit token_budget.
199
+
200
+ Returns:
201
+ (selected_SearchResult_list, kg_trimmed, image_block, web_trimmed, rag_tokens_used_approx)
202
+ """
203
+ available = max(200, token_budget - reserve_prompt)
204
+ web_ratio_val = web_ratio if web_ratio is not None else float(getattr(cfg, "WEB_BUDGET_RATIO", 0.12))
205
+ web_cap = int(available * web_ratio_val) if web_block else 0
206
+ kg_cap = int(available * kg_ratio)
207
+ img_cap = int(available * image_ratio)
208
+ rag_cap = max(200, available - kg_cap - img_cap - web_cap)
209
+
210
+ filtered: list = []
211
+ for r in results:
212
+ score = getattr(r, "rerank_score", None)
213
+ if score is None:
214
+ score = float(getattr(r, "rrf_score", 0.0))
215
+ if min_score is not None and score < min_score:
216
+ continue
217
+ filtered.append(r)
218
+
219
+ filtered = filtered[: max(1, max_chunks)]
220
+
221
+ selected: list = []
222
+ rag_used = 0
223
+ for r in filtered:
224
+ body = getattr(r, "parent_content", "") or ""
225
+ need = _est_tokens(body)
226
+ if rag_used + need <= rag_cap:
227
+ selected.append(r)
228
+ rag_used += need
229
+ elif rag_cap - rag_used > 150:
230
+ remain = int((rag_cap - rag_used) * _CHARS_PER_TOKEN)
231
+ shortened = _truncate_words(body, max(0, remain))
232
+ import copy as _copy
233
+
234
+ cp = _copy.copy(r)
235
+ cp.parent_content = shortened + "\n[...truncated by context budget]"
236
+ selected.append(cp)
237
+ rag_used += _est_tokens(shortened)
238
+ break
239
+ else:
240
+ break
241
+
242
+ kg_out = (kg_text or "").strip()
243
+ if kg_out and _est_tokens(kg_out) > kg_cap:
244
+ kg_out = _truncate_words(kg_out, int(kg_cap * _CHARS_PER_TOKEN)) + "\n[...kg truncated]"
245
+
246
+ img_block = _format_image_snippets(image_hits or [], max_images=max(1, int(3 + img_cap // 400)))
247
+ if img_block and _est_tokens(img_block) > img_cap:
248
+ img_block = _truncate_words(img_block, int(img_cap * _CHARS_PER_TOKEN)) + "\n[...images truncated]"
249
+
250
+ web_out = (web_block or "").strip()
251
+ if web_out and _est_tokens(web_out) > web_cap:
252
+ web_out = _truncate_words(web_out, int(web_cap * _CHARS_PER_TOKEN)) + "\n[...web truncated]"
253
+
254
+ logger.debug(
255
+ "context_fusion: rag_chunks=%d rag_tok~%d kg_tok~%d img_tok~%d web_tok~%d budget=%d",
256
+ len(selected),
257
+ rag_used,
258
+ _est_tokens(kg_out),
259
+ _est_tokens(img_block),
260
+ _est_tokens(web_out),
261
+ token_budget,
262
+ )
263
+
264
+ return selected, kg_out, img_block, web_out, rag_used